BuilderIO / mitosis

Write components once, run everywhere. Compiles to React, Vue, Qwik, Solid, Angular, Svelte, and more.
https://mitosis.builder.io
MIT License
12.36k stars 549 forks source link

Generation of Vue 2 classes could possibly avoid _classStringToObject #951

Open euoia opened 1 year ago

euoia commented 1 year ago

I am interested in helping provide a fix!

Yes

Which generators are impacted?

Reproduction case

https://mitosis.builder.io/?outputTab=G4VwpkA%3D&inputTab=M4NwpgNgLmQ%3D&code=DwZwxgTglgDgLgPgFAAIUFMAeMD2E4oA26BAJlCAIYBGxpKAvCgGaWEjoDcqRJLOYAK4hGKOBEFckwAPTho8ZNKgA7GIIJhClECAYAiZngC2AWlXq4%2BlFp0gAXOSq109W7vtGhImQiA%3D

Expected Behaviour

The Vue 2 output in the the Class Directive example uses minimal Vue code, e.g.

<input
    class="form-input"
    :class="{disabled: disabled, focus: focus}"
/>

Actual Behaviour

The Vue 2 output in the Class Directive uses code which is quite verbose, converting a string into a class with a _classStringToObject helper:

  <input
    :class="
      _classStringToObject(
        `form-input ${disabled ? 'disabled' : ''} ${focus ? 'focus' : ''}`
      )
    "
  />

Additional Information

No response

samijaber commented 1 year ago

That's because this is the only way that the JSX syntax works: it has a class string prop that concatenates everything: see example

You're right that for the Svelte syntax, and certain simpler cases, we could do away with _classStringToObject(). I am always open to improving the compiler if it means removing injected code such as this helper.

This would be a fairly involved task however...the class binding is stored in the Mitosis JSON as a string:

      "bindings": {
        "class": {
          "code": "`form-input ${props.disabled ? \"disabled\" : \"\"} ${state.focus ? \"focus\" : \"\"}`"
        }
      },

That is the case for both the JSX and Svelte syntaxes. Now, since the Svelte syntax already supports providing separate class bindings, it would be very easy the JSON output of that to something like (very pseudo-code-ish syntax):

      "bindings": {
        "class": [{
          "code": "form-input",
        }, {
          "object": "props.disabled",
        }, {
          "object": "state.focus",
        }]
      },

and then have the Vue generator (and other generators) provide a cleaner output for it.

However, I don't see how we can improve that for the JSX output without introducing a new syntax to support a class object, e.g.:

import { useStore } from "@builder.io/mitosis";

export default function MyComponent(props) {
  const state = useStore({ focus: true });

  return (
    <input
      class="form-input"
      class={{
        disabled: props.disabled,
        focus: state.focus
      }}
    />
  );
}

But I'd have to think a bit more through: