create-bestax 4.2.11 → 4.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3 -2
- package/dist/constants.d.ts +7 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +19 -4
- package/dist/file-system.d.ts +7 -0
- package/dist/file-system.d.ts.map +1 -1
- package/dist/file-system.js +15 -0
- package/dist/project-creator.d.ts +21 -1
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +84 -51
- package/dist/prompts.d.ts +7 -2
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +8 -3
- package/dist/validators.d.ts +7 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +13 -1
- package/package.json +6 -3
- package/templates/skills/bestax-custom-component/SKILL.md +21 -4
- package/templates/skills/bestax-custom-component/examples/stat-card.tsx +1 -2
- package/templates/skills/bestax-custom-component/references/api.md +103 -11
- package/templates/skills/bestax-custom-component/references/component-catalog.md +12 -5
- package/templates/skills/bestax-custom-component/references/library-contributor.md +23 -0
- package/templates/skills/bestax-form/SKILL.md +28 -17
- package/templates/skills/bestax-form/references/api.md +83 -23
- package/templates/skills/bestax-form/references/patterns.md +19 -0
- package/templates/skills/bestax-layout-scaffold/SKILL.md +17 -4
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +22 -15
- package/templates/skills/bestax-migrate/SKILL.md +32 -14
- package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +9 -9
- package/templates/skills/bestax-migrate/references/bloomer/unmappables.md +19 -2
- package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +337 -0
- package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +230 -0
- package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +348 -0
- package/templates/skills/bestax-migrate/references/css-migration.md +8 -1
- package/templates/skills/bestax-migrate/references/rbx/component-map.md +15 -15
- package/templates/skills/bestax-migrate/references/rbx/prop-map.md +17 -19
- package/templates/skills/bestax-migrate/references/rbx/unmappables.md +17 -0
- package/templates/skills/bestax-migrate/references/react-bulma-components/component-map.md +34 -34
- package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +34 -7
- package/templates/skills/bestax-optimize/SKILL.md +3 -2
- package/templates/skills/bestax-optimize/references/modular-build.md +25 -5
- package/templates/skills/bestax-theming/SKILL.md +4 -3
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +4 -3
- package/templates/skills/bestax-theming/references/css-variables.md +49 -8
- package/templates/skills/bestax-theming/references/themeable-components.md +12 -10
|
@@ -40,28 +40,29 @@ Wraps a single input; adds icons and loading.
|
|
|
40
40
|
(`label`, `labelSize`, `labelProps`, `horizontal`), Control-level (`iconLeftName`,
|
|
41
41
|
`iconRightName`, `iconLeftSize`, `iconRightSize`, `hasIconsLeft`, `hasIconsRight`, `isLoading`,
|
|
42
42
|
`isExpanded`, `controlSize`), message (`message`, `messageColor`), and container class overrides
|
|
43
|
-
(`fieldClassName`, `controlClassName`).
|
|
43
|
+
(`fieldClassName`, `controlClassName`). Inside your own `Control` it renders no `Control` of its
|
|
44
|
+
own, so the Control-level props and `controlClassName` do nothing there and warn in development;
|
|
45
|
+
set them on that `Control` instead.
|
|
44
46
|
|
|
45
47
|
`InputBase` props:
|
|
46
48
|
|
|
47
|
-
| Prop | Type | Notes
|
|
48
|
-
| ------------------------------------- | --------------------------------------------------------------------- |
|
|
49
|
-
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state.
|
|
50
|
-
| `size` | `'small' \| 'medium' \| 'large'` | Size.
|
|
51
|
-
| `isRounded` | `boolean` | Pill shape.
|
|
52
|
-
| `isStatic` | `boolean` | Static
|
|
53
|
-
| `isLoading` | `boolean` |
|
|
54
|
-
| `value` / `defaultValue` / `onChange` | — | Standard React input.
|
|
55
|
-
| `disabled` / `readOnly` | `boolean` | Native states.
|
|
49
|
+
| Prop | Type | Notes |
|
|
50
|
+
| ------------------------------------- | --------------------------------------------------------------------- | -------------------------------------------------- |
|
|
51
|
+
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger'` | Visual state. |
|
|
52
|
+
| `size` | `'small' \| 'medium' \| 'large'` | Size. |
|
|
53
|
+
| `isRounded` | `boolean` | Pill shape. |
|
|
54
|
+
| `isStatic` | `boolean` | Static-text styling; add `readOnly` to stop edits. |
|
|
55
|
+
| `isLoading` | `boolean` | No spinner here; set `isLoading` on the `Control`. |
|
|
56
|
+
| `value` / `defaultValue` / `onChange` | — | Standard React input. |
|
|
57
|
+
| `disabled` / `readOnly` | `boolean` | Native states. |
|
|
56
58
|
|
|
57
59
|
`messageColor` accepts `'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'`.
|
|
58
60
|
|
|
59
61
|
## Select / SelectBase, TextArea / TextAreaBase
|
|
60
62
|
|
|
61
|
-
Same convenience/raw split as Input. `Select` supports `isLoading` (
|
|
62
|
-
`size`, `isRounded`, `isFullwidth`, `multiple` +
|
|
63
|
-
|
|
64
|
-
`hasFixedSize`.
|
|
63
|
+
Same convenience/raw split as Input. `Select` supports `isLoading` (a spinner in place of the
|
|
64
|
+
chevron), `color`, `size`, `isRounded`, `isFullwidth`, `multiple` + `multipleSize`, plus the
|
|
65
|
+
Field/Control/message props. `TextArea` adds `rows` and `hasFixedSize`.
|
|
65
66
|
|
|
66
67
|
## Checkbox / Checkboxes, Radio / Radios
|
|
67
68
|
|
|
@@ -81,18 +82,77 @@ File input with `label`, `message`, color/size, and icon support.
|
|
|
81
82
|
|
|
82
83
|
## Advanced inputs
|
|
83
84
|
|
|
84
|
-
| Component | Key props
|
|
85
|
-
| -------------- |
|
|
86
|
-
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable`, `openOnFocus`, `loading`.
|
|
87
|
-
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`.
|
|
88
|
-
| `Numberinput` | `value`/`onChange`, `min`, `max`, `step`, increment/decrement buttons, stepper variant, `color`, `size
|
|
89
|
-
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`.
|
|
90
|
-
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`.
|
|
85
|
+
| Component | Key props |
|
|
86
|
+
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
87
|
+
| `Autocomplete` | `data` (`string[]` or item objects), `value`, `onInput(value)` for typing, `onSelect(item)` for picks, `clearable` (the clear button hides while `loading`, or a loading `Control` around it, puts a spinner at the same edge), `openOnFocus`, `loading`. |
|
|
88
|
+
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
|
|
89
|
+
| `Numberinput` | `value`/`onChange`, `min`, `max`, `step`, increment/decrement buttons, stepper variant, `color`, `size`, `isLoading` (set it here, not on a `Control` around it, whose spinner covers the right-hand button). |
|
|
90
|
+
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
|
|
91
|
+
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
|
|
91
92
|
|
|
92
93
|
## Date / time inputs
|
|
93
94
|
|
|
94
|
-
`DateInput`, `TimeInput`, `DateTimeInput` (convenience) and their `*Base`
|
|
95
|
-
composition like the other convenience inputs, with picker UIs (native
|
|
95
|
+
`DateInput`, `TimeInput`, `DateTimeInput`, `DateRangeInput` (convenience) and their `*Base`
|
|
96
|
+
variants. Field/Control composition like the other convenience inputs, with picker UIs (native
|
|
97
|
+
with custom fallback). An `inline` picker renders no `Control` anywhere, so leave its
|
|
98
|
+
Control-level props out: they do nothing there and warn in development.
|
|
99
|
+
|
|
100
|
+
Every one of them has a launcher button at the right edge (`triggerIcon`) that gives way to the loading
|
|
101
|
+
spinner of the `Control` they sit in, which shares that edge. A convenience input sits in the
|
|
102
|
+
`Control` it renders for its own `isLoading`, unless it is inside your own `Control`; a `*Base`
|
|
103
|
+
variant only ever sits in yours. Inside your own `Control`, put `isLoading` on that `Control`: the
|
|
104
|
+
convenience input renders no `Control` of its own there, so its own `isLoading` draws nothing and
|
|
105
|
+
warns in development.
|
|
106
|
+
Passing `triggerIcon` explicitly overrides this.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<Field label="Date">
|
|
110
|
+
<Control isLoading>
|
|
111
|
+
<DateInput />
|
|
112
|
+
</Control>
|
|
113
|
+
</Field>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For a value that is a month or a year (card expiry, billing period, graduation year), use
|
|
117
|
+
`DateInput` with `granularity="month"` or `granularity="year"` rather than building a select or a
|
|
118
|
+
separate picker. The value is still a `Date`: the first day of the month, or 1 January of the
|
|
119
|
+
year. The default `format` becomes `'YYYY-MM'` or `'YYYY'`, and `min` / `max` /
|
|
120
|
+
`shouldDisableDate` judge whole periods (a month is selectable while any day in it is).
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
<DateInput
|
|
124
|
+
label="Card expiry"
|
|
125
|
+
granularity="month"
|
|
126
|
+
format="MM/YY"
|
|
127
|
+
min={new Date()}
|
|
128
|
+
value={expiry}
|
|
129
|
+
onChange={setExpiry}
|
|
130
|
+
/>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
For a start and an end (a stay, a report period, a leave request), use `DateRangeInput`
|
|
134
|
+
rather than two `DateInput`s with hand-wired `min`/`max`. Its value is a `[start, end]` pair
|
|
135
|
+
of `Date | null`, kept in order. The calendar picks the start, then the end, and reports
|
|
136
|
+
only finished ranges; typing reports each end as it changes, so handle `[start, null]`.
|
|
137
|
+
`min`, `max`, `shouldDisableDate` and `unselectableDates` apply to both ends, and a range
|
|
138
|
+
can't include a disabled day unless you pass `allowDisabledInRange`. With a `name`, two
|
|
139
|
+
hidden inputs submit `name[start]` and `name[end]` as `YYYY-MM-DD` (`startName` / `endName`
|
|
140
|
+
rename them). `label` names the whole field as a group; the two inputs are named by
|
|
141
|
+
`labels.rangeStart` / `labels.rangeEnd`.
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const [stay, setStay] = useState<DateRangeValue>([null, null]);
|
|
145
|
+
|
|
146
|
+
<DateRangeInput
|
|
147
|
+
label="Stay"
|
|
148
|
+
name="stay"
|
|
149
|
+
min={new Date()}
|
|
150
|
+
value={stay}
|
|
151
|
+
onChange={setStay}
|
|
152
|
+
color={stay[0] && !stay[1] ? 'danger' : undefined}
|
|
153
|
+
message={stay[0] && !stay[1] ? 'Pick a check-out date.' : undefined}
|
|
154
|
+
/>;
|
|
155
|
+
```
|
|
96
156
|
|
|
97
157
|
## Validation-related props (no library)
|
|
98
158
|
|
|
@@ -149,6 +149,25 @@ Notes:
|
|
|
149
149
|
</Field>
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
+
Recipes built on addons, each written out in full in the docs:
|
|
153
|
+
|
|
154
|
+
- **Password reveal** ([docs](https://bestax.io/docs/api/form/input#password-reveal)): the
|
|
155
|
+
toggle is a `Button` in its own addon `Control`, never `iconRight`. Bulma gives control icons
|
|
156
|
+
`pointer-events: none`, so a click on one lands on the input. The button takes
|
|
157
|
+
`type="button"`, a fixed `aria-label` ("Show password") and `aria-pressed`, and switches the
|
|
158
|
+
input's `type` between `password` and `text`.
|
|
159
|
+
- **Copy button** ([docs](https://bestax.io/docs/api/form/input#copy-button)): a read-only input
|
|
160
|
+
and a `Button` calling `navigator.clipboard.writeText`. The clipboard is missing outside secure
|
|
161
|
+
contexts and the write can be refused, so catch the failure, select the text, and say so in a
|
|
162
|
+
`role="alert"` region. Confirm success in a `role="status"` region. Mount both regions empty,
|
|
163
|
+
before their text arrives.
|
|
164
|
+
- **Split button** ([docs](https://bestax.io/docs/api/components/dropdown#split-button)): a
|
|
165
|
+
`Button` and a `Dropdown` in `Field hasAddons`, one `Control` each. `Buttons hasAddons` does
|
|
166
|
+
not join the Dropdown's trigger. Keep the default color, since the trigger takes no `color`.
|
|
167
|
+
- To label an addon row, put the label on an outer `Field` with `labelProps={{ htmlFor }}` and a
|
|
168
|
+
matching `id` on the input. A label inside the `hasAddons` field sits in the row beside the
|
|
169
|
+
controls.
|
|
170
|
+
|
|
152
171
|
## Horizontal field with explicit label/body
|
|
153
172
|
|
|
154
173
|
```tsx
|
|
@@ -46,10 +46,23 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
46
46
|
- Rely on Bulma's responsive defaults: `Columns` sit side by side on tablet and up and stack on
|
|
47
47
|
mobile. Add responsive `size*` props only to tune the breakpoints.
|
|
48
48
|
- Interactive extras don't share a state API — never transfer one by analogy:
|
|
49
|
-
`Collapse trigger={node} open/defaultOpen
|
|
49
|
+
`Collapse trigger={node} open/defaultOpen onOpenChange` (`onOpenChange(open)` reports every
|
|
50
|
+
trigger; `onOpen/onClose` fire only when the Collapse holds its own state), an accordion is
|
|
51
|
+
`Collapses value/defaultValue/onChange` around plain `Collapse` children indexed from 0 by
|
|
52
|
+
position (`number | null`, or `number[]` with `multiple`; hide an item with
|
|
53
|
+
`visibility="hidden"`, never `{cond && …}`, or the open state shifts to its neighbour) rather
|
|
54
|
+
than hand-rolled open-index state,
|
|
55
|
+
`Tabs value={i}/onChange`
|
|
50
56
|
(each `Tabs.Tab`/`Tabs.Content.Item` requires `index={i}`, and `Tabs.Content` must be a
|
|
51
57
|
**child of `<Tabs>`** — the active-tab context lives on it; a sibling panel never
|
|
52
|
-
switches
|
|
58
|
+
switches. The arrow keys, Home/End, Enter/Space, the single tab stop and the
|
|
59
|
+
`aria-controls`/`aria-labelledby` links come built in, so add no `onKeyDown` or ARIA
|
|
60
|
+
of your own; `onChange` fires when a tab is activated, never on
|
|
61
|
+
arrow-key focus), `Dropdown active/onActiveChange`,
|
|
62
|
+
`Popover trigger={<Button>…</Button>} open/defaultOpen onOpenChange` (`trigger` is one
|
|
63
|
+
element, not a node: Popover clones it to add the click and ARIA, so never wrap it in a
|
|
64
|
+
clickable of your own; build the panel from `Popover.Header`/`Body`/`Footer`/`Close`, and a
|
|
65
|
+
panel without a `Popover.Header` needs `ariaLabel`),
|
|
53
66
|
`Steps value={i}/onStepClick items={[{label, icon?}]}` (child form is `Steps.Step`, not
|
|
54
67
|
`Steps.Item`). `Reveal cascade` staggers only its **direct children** — to stagger a grid,
|
|
55
68
|
put `<Reveal delay={i * 80}>` inside each `Cell`, not around the container.
|
|
@@ -121,7 +134,7 @@ every miss shipped the "not this" column instead.
|
|
|
121
134
|
|
|
122
135
|
| You need | Use | Not this |
|
|
123
136
|
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
124
|
-
| A brief confirmation after an action, self-dismissing | `Toast` — mount `<ToastContainer position="top-right" />` once at the app root, then `toast.success('Saved')` (also `.danger`/`.warning`/`.info`) from anywhere | `Notification`/`Message`
|
|
137
|
+
| A brief confirmation after an action, self-dismissing | `Toast` — mount `<ToastContainer position="top-right" />` once at the app root, then `toast.success('Saved')` (also `.danger`/`.warning`/`.info`) from anywhere | `Notification`/`Message` |
|
|
125
138
|
| A confirm or alert the user must answer before anything | `Dialog` — mount `<DialogContainer />` at the root, then `if (await dialog.confirm({ title, message })) …` | `Modal` — an empty shell; the title, message, button row and confirm/cancel wiring are all yours to rebuild |
|
|
126
139
|
| A control that reads as text or a link but _does_ something | `LinkButton` (`variant="text" \| "ghost" \| "underline"`, optional `color`) | `<a href="#">`/`<div onClick>` (no keyboard or screen-reader support) or `Button color="text"` (still button-shaped) |
|
|
127
140
|
|
|
@@ -153,7 +166,7 @@ background comes from the semantic `color` prop, not `bgColor`.
|
|
|
153
166
|
| `display: 'flex'` + flex properties | same-named props: `display="flex"`, `flexDirection`, `justifyContent`, `alignItems`, `flexWrap` |
|
|
154
167
|
| `height: '100%'` on a flex child | `flexGrow="1"` |
|
|
155
168
|
| `display: 'none'` | `visibility="hidden"`, or responsive `display*` props (`displayMobile`, `displayTablet`, …) |
|
|
156
|
-
| `gap:
|
|
169
|
+
| `gap: '1rem'` (flex or grid) | `gap="2"`, or one axis: `columnGap`, `rowGap`; gap scale: `1`=0.5rem, `2`=1rem, `4`=2rem, half steps (`"1.5"`) |
|
|
157
170
|
|
|
158
171
|
No helper matches (e.g. `maxWidth`, a one-off gradient)? Add a named class to the project
|
|
159
172
|
stylesheet (`src/App.css` in a scaffolded app) and pass it via `className` — still never
|
|
@@ -97,15 +97,15 @@ The responsive grid. `Columns` is the row; `Column` is a cell (also reachable as
|
|
|
97
97
|
|
|
98
98
|
**Columns**
|
|
99
99
|
|
|
100
|
-
| Prop | Type
|
|
101
|
-
| -------------------------------------------------------------------------------- |
|
|
102
|
-
| `isMultiline` | `boolean` (wrap cells onto new rows)
|
|
103
|
-
| `isCentered` | `boolean` (center the row)
|
|
104
|
-
| `isVCentered` | `boolean` (vertical centering — capital V)
|
|
105
|
-
| `isGapless` | `boolean`
|
|
106
|
-
| `isMobile` | `boolean` (stay side-by-side on mobile)
|
|
107
|
-
| `isDesktop` | `boolean`
|
|
108
|
-
| `gap` / `gapMobile` / `gapTablet` / `gapDesktop` / `gapWidescreen` / `gapFullhd` | `0`–`8` (number or string,
|
|
100
|
+
| Prop | Type |
|
|
101
|
+
| -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
102
|
+
| `isMultiline` | `boolean` (wrap cells onto new rows) |
|
|
103
|
+
| `isCentered` | `boolean` (center the row) |
|
|
104
|
+
| `isVCentered` | `boolean` (vertical centering — capital V) |
|
|
105
|
+
| `isGapless` | `boolean` |
|
|
106
|
+
| `isMobile` | `boolean` (stay side-by-side on mobile) |
|
|
107
|
+
| `isDesktop` | `boolean` |
|
|
108
|
+
| `gap` / `gapMobile` / `gapTablet` / `gapDesktop` / `gapWidescreen` / `gapFullhd` | `0`–`8` whole steps (number or string), the columns gutter |
|
|
109
109
|
|
|
110
110
|
**Column**
|
|
111
111
|
|
|
@@ -182,12 +182,12 @@ per-breakpoint column _sizes_ (a 2/3 + 1/3 split, different counts per breakpoin
|
|
|
182
182
|
|
|
183
183
|
**Grid**
|
|
184
184
|
|
|
185
|
-
| Prop | Type
|
|
186
|
-
| ---------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
-
| `gap` / `columnGap` / `rowGap` | `0`–`8` (number or string, same
|
|
188
|
-
| `minCol` | `1`–`32` (smart grid: min column width step, `is-col-min-X`)
|
|
189
|
-
| `isFixed` | `boolean` (fixed column count instead of auto-fill)
|
|
190
|
-
| `fixedCols` (+ `fixedColsMobile` / `fixedColsTablet` / `fixedColsDesktop` / `fixedColsWidescreen` / `fixedColsFullhd`) | `0`–`12` or `'auto'` (fixed grid only)
|
|
185
|
+
| Prop | Type |
|
|
186
|
+
| ---------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
187
|
+
| `gap` / `columnGap` / `rowGap` | `0`–`8` with half steps such as `1.5` (number or string), the same values as the `gap` helper props |
|
|
188
|
+
| `minCol` | `1`–`32` (smart grid: min column width step, `is-col-min-X`) |
|
|
189
|
+
| `isFixed` | `boolean` (fixed column count instead of auto-fill) |
|
|
190
|
+
| `fixedCols` (+ `fixedColsMobile` / `fixedColsTablet` / `fixedColsDesktop` / `fixedColsWidescreen` / `fixedColsFullhd`) | `0`–`12` or `'auto'` (fixed grid only) |
|
|
191
191
|
|
|
192
192
|
**Cell**
|
|
193
193
|
|
|
@@ -282,3 +282,10 @@ subcomponents (`Card.Header` + `.Title`/`.Icon`, `Card.Image`, `Card.Content`, `
|
|
|
282
282
|
| `footer` | `ReactNode \| ReactNode[]` |
|
|
283
283
|
| `hasShadow` | `boolean` (default `true`) |
|
|
284
284
|
| `headerCentered` / `headerIcon` | `boolean` / `ReactNode` |
|
|
285
|
+
|
|
286
|
+
`Card.Header.Title` and `Card.FooterItem` both take an `as` prop for Bulma's own card markup —
|
|
287
|
+
`Card.Header.Title` renders `div` by default (`as="p"` or a heading `h2`-`h4` also work), and
|
|
288
|
+
`Card.FooterItem` renders `span` by default (`as="a"` or `as="button"` also work). For a footer
|
|
289
|
+
action, prefer `<Card.FooterItem as="a" href="...">Save</Card.FooterItem>` over nesting a link
|
|
290
|
+
inside the default `span` — Bulma puts the padding and flex centering on `.card-footer-item`
|
|
291
|
+
itself, so nesting a link only makes the text clickable instead of the whole cell.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bestax-migrate
|
|
3
|
-
description: Migrate an existing app from an unmaintained React Bulma library
|
|
3
|
+
description: Migrate an existing React app to @allxsmith/bestax-bulma on Bulma v1, from raw Bulma CSS classes on plain JSX (className="button is-primary") or from an unmaintained React Bulma library (react-bulma-components v4, rbx v2, bloomer 0.6). Run the bestax-migrate codemod, then resolve every TODO(bestax-migrate) comment it leaves using the per-source mapping references. Use when a React app styles its markup with Bulma classes and wants bestax components instead, when a repo imports react-bulma-components, rbx or bloomer, when TODO(bestax-migrate) comments are present in a codebase, or when asked to convert Bulma classNames to bestax-bulma.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -15,11 +15,12 @@ this skill drives the codemod and finishes what it flags.
|
|
|
15
15
|
The codemod's first argument names the library you are migrating _from_. Check the app's
|
|
16
16
|
`package.json` and imports:
|
|
17
17
|
|
|
18
|
-
| Source
|
|
19
|
-
|
|
|
20
|
-
| `react-bulma-components` v4 (unmaintained since 2022, Bulma 0.9.x)
|
|
21
|
-
| `rbx` v2 (abandoned 2019, pins Bulma **0.7.5** plus four extensions)
|
|
22
|
-
| `bloomer` 0.6 (archived 2018, Bulma 0.6 era, React 16 + `create-react-class`)
|
|
18
|
+
| Source | Argument | References |
|
|
19
|
+
| -------------------------------------------------------------------------------- | ------------------------ | ------------------------------------------------------------------------------------------ |
|
|
20
|
+
| `react-bulma-components` v4 (unmaintained since 2022, Bulma 0.9.x) | `react-bulma-components` | [`references/react-bulma-components/`](references/react-bulma-components/component-map.md) |
|
|
21
|
+
| `rbx` v2 (abandoned 2019, pins Bulma **0.7.5** plus four extensions) | `rbx` | [`references/rbx/`](references/rbx/component-map.md) |
|
|
22
|
+
| `bloomer` 0.6 (archived 2018, Bulma 0.6 era, React 16 + `create-react-class`) | `bloomer` | [`references/bloomer/`](references/bloomer/component-map.md) |
|
|
23
|
+
| Raw Bulma classes on plain JSX (`<div className="box">`), no React Bulma library | `bulma-classes` | [`references/bulma-classes/`](references/bulma-classes/component-map.md) |
|
|
23
24
|
|
|
24
25
|
Everything below is written as `<source>`; substitute the argument from that table. The
|
|
25
26
|
reference paths follow the same split — `references/<source>/component-map.md`,
|
|
@@ -43,7 +44,9 @@ Run these steps in order. Don't hand-convert what the codemod converts automatic
|
|
|
43
44
|
`@allxsmith/bestax-bulma/bestax.css`; SCSS `@import 'bulma/bulma'` + `$var` overrides
|
|
44
45
|
→ `@use 'bulma/sass' with (…)` plus the bestax extras) and **package.json**.
|
|
45
46
|
Flags: `--css bulma|keep` for other stylesheet targets, `--no-deps` to leave
|
|
46
|
-
package.json alone.
|
|
47
|
+
package.json alone. `bulma-classes` is the exception: it defaults to `--css keep`,
|
|
48
|
+
because the app's own Bulma already styles every class it converts, so its stylesheets
|
|
49
|
+
and Bulma version stay unless you pass `--css bestax` or `--css bulma`.
|
|
47
50
|
|
|
48
51
|
3. **Install** — the codemod edits package.json but never runs a package manager. Resolve the
|
|
49
52
|
retained imports first: a component with no bestax equivalent keeps a trimmed,
|
|
@@ -70,9 +73,12 @@ Run these steps in order. Don't hand-convert what the codemod converts automatic
|
|
|
70
73
|
|
|
71
74
|
4. **Resolve every TODO**: `grep -rn "TODO(bestax-migrate)" src/`. Each comment names the
|
|
72
75
|
prop/component and a hint. Recipes for every recurring case are in
|
|
73
|
-
`references/<source>/unmappables.md
|
|
74
|
-
`references/<source>/component-map.md` and `references/<source>/prop-map.md
|
|
75
|
-
|
|
76
|
+
`references/<source>/unmappables.md`. Before writing a component by hand, look it up in
|
|
77
|
+
`references/<source>/component-map.md` and its classes in `references/<source>/prop-map.md`:
|
|
78
|
+
they name the component and the prop for each class, which the library's type declarations
|
|
79
|
+
only tell you slowly. For form markup (`.field`, `.control`, inputs, selects), load the
|
|
80
|
+
`bestax-form` skill too: it covers the label, `id` and help-text wiring. Delete each comment
|
|
81
|
+
as you resolve it.
|
|
76
82
|
|
|
77
83
|
5. **Finish the stylesheet layer** — flagged Sass cases (computed variables,
|
|
78
84
|
indented-syntax `.sass` files), CSS flavor choice, and Bulma 0.9→1 styling changes:
|
|
@@ -105,10 +111,21 @@ code per the references, or deliberately keep the old markup with `className` st
|
|
|
105
111
|
become real wrapping `<Badge>` / `<Tooltip>` components. Its `as` is universal, bestax's is
|
|
106
112
|
not. Because rbx pinned Bulma 0.7.5, you cross **two** Bulma majors — expect more visual
|
|
107
113
|
drift than the 0.9 → 1 guide alone describes.
|
|
114
|
+
- **bulma-classes**: there is no library to remove. An element converts only when the bestax
|
|
115
|
+
component renders the same markup (tag, classes, attributes), so a converted page renders
|
|
116
|
+
the same HTML; everything else stays as written, with a TODO when there is something to
|
|
117
|
+
decide. Static class strings convert, and so does a `clsx` or `classnames` call of strings
|
|
118
|
+
and conditional classes, where a condition on a flag becomes its prop (`isLoading={busy}`);
|
|
119
|
+
any other computed className gets `dynamic-class:<Target>`, naming the component the element
|
|
120
|
+
would become. Files a Next.js
|
|
121
|
+
App Router may render as server components are left alone (`rsc`), and the manifest gains
|
|
122
|
+
`@allxsmith/bestax-bulma` while the app's Bulma and stylesheets stay (the default
|
|
123
|
+
`--css keep`). Its rules are `kind:<name>` shaped; see
|
|
124
|
+
`references/bulma-classes/unmappables.md`.
|
|
108
125
|
- **bloomer**: every export is a flat name and most become dotted bestax compounds
|
|
109
126
|
(`CardHeaderTitle` → `Card.Header.Title`). Most of its `is*` booleans already are bestax's
|
|
110
|
-
(`isActive` becomes `active` where bestax names it so; `isFullWidth`
|
|
111
|
-
|
|
127
|
+
(`isActive` becomes `active` where bestax names it so; `isFullWidth` becomes `isFullwidth`
|
|
128
|
+
where bestax has the prop); the work is in `isSize`/`isColor`/`isAlign` (renamed per component), the `isDisplay`/`isHidden`
|
|
112
129
|
helpers (flattened, arrays and objects included), `tag` (→ `as` where bestax has one) and
|
|
113
130
|
`render` (always a TODO). Its icons are className-based and Font Awesome 4 — the biggest
|
|
114
131
|
visual risk, see `references/bloomer/unmappables.md`. The app declared its own Bulma 0.6,
|
|
@@ -116,8 +133,9 @@ code per the references, or deliberately keep the old markup with `className` st
|
|
|
116
133
|
|
|
117
134
|
## Rules
|
|
118
135
|
|
|
119
|
-
- The codemod is
|
|
120
|
-
|
|
136
|
+
- The codemod is safe to re-run after partial manual work: a library source only touches files
|
|
137
|
+
that still import the library, and `bulma-classes` skips elements that are already
|
|
138
|
+
components and never repeats a TODO it already wrote.
|
|
121
139
|
- Don't downgrade converted props back to the old names; bestax uses `is*` booleans
|
|
122
140
|
(`isLoading`), string size unions (`textSize="4"`), and `as` rather than `renderAs`.
|
|
123
141
|
- If a component the app uses isn't in the component map, it wasn't part of that library's
|
|
@@ -4,7 +4,7 @@ Most of bloomer's boolean modifiers are already bestax's names — `isLoading`,
|
|
|
4
4
|
`isInverted`, `isStatic`, `isHovered`, `isFocused`, `isBordered`, `isStriped`, `isNarrow`,
|
|
5
5
|
`isMultiline`, `isVCentered`, `isMobile`, … pass through untouched. The exceptions are in
|
|
6
6
|
the tables below: `isActive` becomes `active` on the components whose bestax counterpart
|
|
7
|
-
names it that way, `isFullWidth`
|
|
7
|
+
names it that way, `isFullWidth` becomes `isFullwidth` where bestax declares it, and a few
|
|
8
8
|
(`Input isActive`, `NavbarLink isActive`, `PageLink isActive`) have no counterpart and become
|
|
9
9
|
the Bulma class in `className` instead (see the end of this page). What else changes is the value-carrying props, the helper props every component
|
|
10
10
|
inherited from `withHelpersModifiers`, and the two props bestax spells differently on every
|
|
@@ -24,7 +24,7 @@ bloomer's `withHelpersModifiers` mixed these into every component.
|
|
|
24
24
|
| `isUnselectable` | `interaction="unselectable"` | |
|
|
25
25
|
| `isMarginless` | `m="0"` | bestax expresses the `is-*less` helpers as spacing |
|
|
26
26
|
| `isPaddingless` | `p="0"` | |
|
|
27
|
-
| `isFullWidth` | `
|
|
27
|
+
| `isFullWidth` | `isFullwidth` | on Button, Select, Table, Tabs |
|
|
28
28
|
| `isDisplay`, `isHidden` | `display*`, `visibility*` | flattened — see below |
|
|
29
29
|
| `tag` | `as` | where bestax declares one — see below |
|
|
30
30
|
| `render` | — | always a TODO |
|
|
@@ -151,8 +151,8 @@ A few bestax parts extend only React's HTML attributes and take no Bulma helper
|
|
|
151
151
|
`Pagination.Previous`/`Next`/`Ellipsis`, `Navbar.Dropdown`/`DropdownMenu`/`Divider`,
|
|
152
152
|
`Panel.Heading`/`Tabs`/`Block`, `Tabs.List`/`Item`, `Message.Header`/`Body` and the `Modal`
|
|
153
153
|
parts. A bloomer helper on one of those becomes the Bulma class in `className` — except on
|
|
154
|
-
`
|
|
155
|
-
|
|
154
|
+
`Pagination.Ellipsis` and `Dropdown.Divider`, which write their own className last or take no
|
|
155
|
+
props at all: there the helper is named in a TODO instead, and an element
|
|
156
156
|
carrying a spread is left as bloomer's. Otherwise it is the Bulma class in `className` (`is-pulled-right`,
|
|
157
157
|
`m-0`, `is-hidden-mobile`, …), since Bulma v1 still ships every one of them; only a dynamic
|
|
158
158
|
value is flagged. The same conversion covers the modifiers bestax has no prop for anywhere —
|
|
@@ -162,11 +162,11 @@ value is flagged. The same conversion covers the modifiers bestax has no prop fo
|
|
|
162
162
|
## Refs
|
|
163
163
|
|
|
164
164
|
bloomer forwarded no refs. bestax forwards a ref from the form controls, plus `Avatar`,
|
|
165
|
-
`Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `
|
|
166
|
-
`Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`,
|
|
167
|
-
and `Toast` — pass `ref` directly on those. "The form controls" means
|
|
168
|
-
themselves: the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios`
|
|
169
|
-
around them forward nothing.
|
|
165
|
+
`Button`, `Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `Link`, `LinkButton`, `Loader`,
|
|
166
|
+
`Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`, `Navbar.Item`,
|
|
167
|
+
`Navbar.Link`, `Sidebar` and `Toast` — pass `ref` directly on those. "The form controls" means
|
|
168
|
+
the inputs themselves: the `Field`, `Field.Label`, `Field.Body`, `Checkboxes` and `Radios`
|
|
169
|
+
wrappers around them forward nothing.
|
|
170
170
|
|
|
171
171
|
A `ref` on anything else is unsupported, and the two React majors fail differently: React 18
|
|
172
172
|
drops it and logs "Function components cannot be given refs", while React 19 hands it to the
|
|
@@ -160,6 +160,23 @@ these either. `Level.Item` is not narrower: it declares the anchor's whole attri
|
|
|
160
160
|
every `as` and forwards it only on the `<a>`, so all of these survive at `as="a"` and none of
|
|
161
161
|
them reaches a `<p>` or a `<div>`.
|
|
162
162
|
|
|
163
|
+
## `prop:type` — a `<button>` item that no longer submits its form
|
|
164
|
+
|
|
165
|
+
`Navbar.Item`, `Navbar.Link`, `Menu.Item` and `Dropdown.Item` write `type="button"` on the
|
|
166
|
+
`<button>` an `as` makes them render, whenever the element sets no `type` or one HTML doesn't
|
|
167
|
+
define. A plain `<button>` with neither submits the form around it, so an item you rendered as a
|
|
168
|
+
button with `tag="button"` stops submitting once migrated. The codemod can't tell whether one sits
|
|
169
|
+
in a form, so each gets a TODO:
|
|
170
|
+
|
|
171
|
+
```jsx
|
|
172
|
+
<NavbarItem tag="button">Search</NavbarItem>
|
|
173
|
+
<Navbar.Item as="button">Search</Navbar.Item>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Add `type="submit"` if it should still submit, or `type="button"` to say it shouldn't, then
|
|
177
|
+
delete the comment. An item whose `type` is written out as `button`, `submit` or `reset`, or
|
|
178
|
+
comes from a spread or an expression, gets no TODO.
|
|
179
|
+
|
|
163
180
|
## `prop:render` — the render prop
|
|
164
181
|
|
|
165
182
|
bloomer's `render` handed the computed props (`className` included) to your own renderer:
|
|
@@ -191,8 +208,8 @@ bloomer rendered, but Bulma v1 no longer ships styles for that class, so restyle
|
|
|
191
208
|
## `prop:isFullWidth` — not a universal helper in bestax
|
|
192
209
|
|
|
193
210
|
bloomer accepted `isFullWidth` on everything; Bulma's `is-fullwidth` only means something on a
|
|
194
|
-
few elements
|
|
195
|
-
`
|
|
211
|
+
few elements. Where bestax has a prop for it (`Button`, `Select`, `Table`, `Tabs`), it becomes
|
|
212
|
+
`isFullwidth`. Elsewhere, drop it, or add `className="is-fullwidth"` if your
|
|
196
213
|
own CSS styled the class.
|
|
197
214
|
|
|
198
215
|
## Modifiers bestax has no prop for — converted to classes
|