create-bestax 4.2.18 → 4.3.0
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 +13 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +69 -1
- package/dist/file-system.d.ts +5 -1
- package/dist/file-system.d.ts.map +1 -1
- package/dist/file-system.js +11 -1
- package/dist/library-range.d.ts +16 -0
- package/dist/library-range.d.ts.map +1 -0
- package/dist/library-range.js +40 -0
- package/dist/project-creator.d.ts +8 -0
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +62 -3
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +15 -1
- package/package.json +8 -8
- package/templates/skills/bestax-custom-component/references/component-catalog.md +5 -1
- package/templates/skills/bestax-form/SKILL.md +40 -15
- package/templates/skills/bestax-form/references/api.md +10 -8
- package/templates/skills/bestax-form/references/patterns.md +68 -0
- package/templates/skills/bestax-icons/references/icon-libraries.md +14 -1
- package/templates/skills/bestax-layout-scaffold/SKILL.md +31 -5
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +2 -2
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +52 -5
- package/templates/skills/bestax-migrate/SKILL.md +19 -5
- package/templates/skills/bestax-migrate/references/bloomer/component-map.md +1 -1
- package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +9 -6
- package/templates/skills/bestax-migrate/references/bloomer/unmappables.md +4 -1
- package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +182 -105
- package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +50 -17
- package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +372 -64
- package/templates/skills/bestax-migrate/references/css-migration.md +12 -5
- package/templates/skills/bestax-migrate/references/rbx/prop-map.md +20 -14
- package/templates/skills/bestax-migrate/references/react-bulma-components/prop-map.md +27 -22
- package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +3 -3
- package/templates/skills/bestax-optimize/references/modular-build.md +59 -17
- package/templates/skills/bestax-theming/SKILL.md +22 -8
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +100 -17
- package/templates/skills/bestax-theming/examples/theme-config.tsx +20 -8
- package/templates/skills/bestax-theming/references/css-variables.md +268 -20
- package/templates/skills/bestax-theming/references/themeable-components.md +4 -0
- package/templates/vite/README.md +2 -2
- package/templates/vite/_oxlintrc.json +8 -0
- package/templates/vite/package.json +9 -13
- package/templates/vite/src/main.jsx +1 -1
- package/templates/vite/vite.config.js +1 -1
- package/templates/vite-ts/README.md +2 -2
- package/templates/vite-ts/_oxlintrc.json +8 -0
- package/templates/vite-ts/package.json +11 -16
- package/templates/vite-ts/src/main.tsx +1 -1
- package/templates/vite/eslint.config.js +0 -29
- package/templates/vite-ts/eslint.config.js +0 -26
|
@@ -37,7 +37,7 @@ instead of hand-writing markup.
|
|
|
37
37
|
- Raw `*Base` form exports (`InputBase`, `SelectBase`, `TextAreaBase`, …) are
|
|
38
38
|
escape-hatch variants of the convenience wrappers above them; see the Form docs.
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
106 documented components. Generated from the API docs — every exported
|
|
41
41
|
component is guaranteed to appear (the generator fails if one lacks an API page).
|
|
42
42
|
|
|
43
43
|
## Elements
|
|
@@ -100,6 +100,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
100
100
|
- [Panel](https://bestax.io/docs/api/components/panel) — The `Panel` component implements Bulma's versatile panel block for React.
|
|
101
101
|
- [Popover](https://bestax.io/docs/api/components/popover) — The `Popover` component opens a panel of interactive content, such as a filter form or a set of share options, anchored to the element that toggles it.
|
|
102
102
|
- [Reveal](https://bestax.io/docs/api/components/reveal) — The `Reveal` component animates its content into view as it scrolls into the viewport, backed by `IntersectionObserver`.
|
|
103
|
+
- [ScrollArea](https://bestax.io/docs/api/components/scrollarea) — The `ScrollArea` component renders a box whose content scrolls inside it, on the browser's own scrollbars in Bulma's colors.
|
|
103
104
|
- [SegmentedControl](https://bestax.io/docs/api/components/segmentedcontrol) — The `SegmentedControl` component renders a row of buttons where only one can be on, and tells a screen reader it's a radio group.
|
|
104
105
|
- [Sidebar](https://bestax.io/docs/api/components/sidebar) — The `Sidebar` component provides a slide-out navigation panel that appears from the left or right side of the screen.
|
|
105
106
|
- [Steps](https://bestax.io/docs/api/components/steps) — The `Steps` component provides a multi-step progress indicator for wizard flows, checkout processes, or any multi-step workflow.
|
|
@@ -114,6 +115,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
114
115
|
- [Autocomplete](https://bestax.io/docs/api/form/autocomplete) — The `Autocomplete` component provides an input field with dropdown suggestions that filter based on user input.
|
|
115
116
|
- [Checkbox](https://bestax.io/docs/api/form/checkbox) — The `Checkbox` component provides a Bulma-styled checkbox input.
|
|
116
117
|
- [Checkboxes](https://bestax.io/docs/api/form/checkboxes) — The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
|
|
118
|
+
- [Combobox](https://bestax.io/docs/api/form/combobox) — The `Combobox` component is a text field with a list to pick from under it, for a searchable select, a multi-select with tags, grouped items or a server search…
|
|
117
119
|
- [Control](https://bestax.io/docs/api/form/control) — The `Control` component is a Bulma-styled wrapper for form controls (`Input`, `Select`, `TextArea`, etc.), supporting icons (left/right), loading state…
|
|
118
120
|
- [DateInput](https://bestax.io/docs/api/form/datetime/dateinput) — The `DateInput` component is a form input that opens a popover calendar for date selection.
|
|
119
121
|
- [DateRangeInput](https://bestax.io/docs/api/form/datetime/daterangeinput) — The `DateRangeInput` component is a form input for a start and end date, picked from one popover calendar or typed into two segmented inputs.
|
|
@@ -122,6 +124,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
122
124
|
- [File](https://bestax.io/docs/api/form/file) — The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
|
|
123
125
|
- [Input](https://bestax.io/docs/api/form/input) — The `Input` component provides a Bulma-styled text input, supporting colors, sizes, rounded corners, static state, hover/focus/loading states…
|
|
124
126
|
- [Numberinput](https://bestax.io/docs/api/form/numberinput) — The `Numberinput` component provides a number input with increment/decrement buttons.
|
|
127
|
+
- [PinInput](https://bestax.io/docs/api/form/pininput) — The `PinInput` component is a field for a short code, such as a one-time code or a PIN, drawn as a row of boxes with one character in each.
|
|
125
128
|
- [Radio](https://bestax.io/docs/api/form/radio) — The `Radio` component provides a Bulma-styled radio button input with flexible labels and helper classes.
|
|
126
129
|
- [Radios](https://bestax.io/docs/api/form/radios) — The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.
|
|
127
130
|
- [Rate](https://bestax.io/docs/api/form/rate) — The `Rate` component provides a star/icon-based rating system.
|
|
@@ -150,6 +153,7 @@ component is guaranteed to appear (the generator fails if one lacks an API page)
|
|
|
150
153
|
- [Level](https://bestax.io/docs/api/layout/level) — The `Level` component provides a flexible horizontal layout for your Bulma React UI, perfect for aligning items on the left and right…
|
|
151
154
|
- [Media](https://bestax.io/docs/api/layout/media) — The `Media` component implements Bulma’s powerful media object layout for React.
|
|
152
155
|
- [Section](https://bestax.io/docs/api/layout/section) — The `Section` component provides vertical spacing and visual separation for your Bulma React UI.
|
|
156
|
+
- [Splitter](https://bestax.io/docs/api/layout/splitter) — The `Splitter` component lays out two or more panes side by side or one above the other, with a handle between each pair to drag or move by keyboard.
|
|
153
157
|
|
|
154
158
|
## Helpers
|
|
155
159
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bestax-form
|
|
3
|
-
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Slider, Numberinput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
3
|
+
description: Build forms with @allxsmith/bestax-bulma — Field/Control/Label/Help composition, inputs, selects, checkboxes, radios, switches, and advanced controls (Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, File, date/time). There is no form/validation library; this skill shows the components and the validate-it-yourself error pattern. Use when building a form, wiring inputs to state, or showing validation/error state.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -93,9 +93,11 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
93
93
|
| `Radio` / `Radios` | Single radio / managed single-select group. |
|
|
94
94
|
| `Switch` | Toggle switch (`isRounded`, `isThin`, `isOutlined`, RTL). |
|
|
95
95
|
| `File` | File upload input with label/message. |
|
|
96
|
-
| `Autocomplete` |
|
|
96
|
+
| `Autocomplete` | Free text with suggestions it filters for you, + keyboard nav. |
|
|
97
|
+
| `Combobox` | A value from a list: `data` for a select, tags or groups, or parts. |
|
|
97
98
|
| `Slider` | Range slider; single/dual thumbs, steps, tooltips, vertical. |
|
|
98
99
|
| `Numberinput` | Numeric input with increment/decrement, min/max, step, stepper. |
|
|
100
|
+
| `PinInput` | A one-time code or PIN as a row of boxes, all in one real input. |
|
|
99
101
|
| `Rate` | Star rating; `max`, `precision` (half/quarter), custom icons, `disabled`. |
|
|
100
102
|
| `Taginput` | Tag/chip input; suggestions, confirm keys, closable tags. |
|
|
101
103
|
| `DateInput` / `TimeInput` / `DateTimeInput` (+ `*Base`) | Date / time / datetime pickers; month or year via `granularity`. |
|
|
@@ -104,6 +106,27 @@ All import from `@allxsmith/bestax-bulma`. Convenience components auto-wrap Fiel
|
|
|
104
106
|
(`NumberInput` and `TagInput` also exist as deprecated aliases of `Numberinput`/`Taginput` —
|
|
105
107
|
same components; prefer the lowercase-second-word spellings.)
|
|
106
108
|
|
|
109
|
+
**Autocomplete or Combobox?** A field whose text is the value, with suggestions to help type it,
|
|
110
|
+
is `Autocomplete`, which filters its `data` itself. A value picked from a list is `Combobox`. Given
|
|
111
|
+
`data`, it filters by label, keeps the value (`value`/`onChange`) and shows the picks as tags with
|
|
112
|
+
`multiple`, so a searchable select, a multi-select or a grouped list is one element. Build it from
|
|
113
|
+
parts for a server search whose answer must show unfiltered, or a "Create" item: then it keeps no
|
|
114
|
+
value and never sets the field's text, so you filter the items, keep the value and set the text in
|
|
115
|
+
`onSelect`.
|
|
116
|
+
|
|
117
|
+
**A verification code or a PIN is `PinInput`**, not a row of one-character `Input`s with focus
|
|
118
|
+
moved by hand. It holds the code in one real input under a row of boxes, so a screen reader hears
|
|
119
|
+
one field named by its `label`, and paste, autofill from a text message (`autoComplete` is
|
|
120
|
+
`"one-time-code"`) and password managers work as they do on any input. Give it `length` (6 by
|
|
121
|
+
default), `value`/`onChange` with the code as a string, and verify in `onComplete`, which fires
|
|
122
|
+
once each time a change fills the last box. It takes digits unless `type="alphanumeric"`, turns a
|
|
123
|
+
digit from any script (Arabic-Indic, Persian) into its ASCII digit, drops anything else typed or
|
|
124
|
+
pasted, and `isMasked` shows dots for a PIN. A paste replaces the whole code. Its default `pattern`
|
|
125
|
+
asks for the full length, so `required` rejects a partial code, not just an empty one. Say the
|
|
126
|
+
length in the `label` ("Enter the 6-digit code"): the boxes are hidden from a screen reader, and the
|
|
127
|
+
`message` isn't tied to the input, so it isn't read on focus. Separate help text needs
|
|
128
|
+
`aria-describedby` on the PinInput.
|
|
129
|
+
|
|
107
130
|
## Common props
|
|
108
131
|
|
|
109
132
|
Across the convenience inputs (`Input`, `Select`, `TextArea`, and similar):
|
|
@@ -130,13 +153,15 @@ spellings compile only where they historically existed: `isFullWidth` everywhere
|
|
|
130
153
|
`Sidebar`, `fullwidth` on `Tabs` only, `fullWidth` on `Sidebar` only.
|
|
131
154
|
|
|
132
155
|
**Labels.** Default to the convenience `label` prop, with `horizontal` for a side-by-side row.
|
|
133
|
-
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `
|
|
134
|
-
`DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
135
|
-
`htmlFor`/`id`, to your `id` when you pass one
|
|
136
|
-
an explicit `labelProps={{ htmlFor }}` wins.
|
|
137
|
-
`
|
|
138
|
-
|
|
139
|
-
|
|
156
|
+
`Input`, `Select`, `TextArea`, `File`, `Numberinput`, `PinInput`, `Slider`, `DateInput`,
|
|
157
|
+
`TimeInput`, `DateTimeInput`, `Autocomplete` and `Taginput` then render the `Field` and the label
|
|
158
|
+
and wire `htmlFor`/`id`, to your `id` when you pass one
|
|
159
|
+
(`<Input label="Email" id="email" horizontal />`); an explicit `labelProps={{ htmlFor }}` wins. `Combobox` renders its `Field` and label too, and wires
|
|
160
|
+
them to its field. Given `data`, its `id`, `aria-label` and `aria-labelledby` go to the field;
|
|
161
|
+
built from parts, put them on `Combobox.Input`, since on the `Combobox` they land on its wrapper
|
|
162
|
+
`div`. The group inputs (`Checkboxes`, `Radios`, `Rate`, `DateRangeInput`) name their
|
|
163
|
+
`role="group"`/`"radiogroup"` wrapper through `aria-labelledby` instead. Inside a `Field` a
|
|
164
|
+
convenience input drops its `label`, and an `inline` date/time picker has no visible input to wire.
|
|
140
165
|
|
|
141
166
|
Composed, a labeled `Field` names the one control it holds: a composed `InputBase`/`SelectBase`/
|
|
142
167
|
`TextAreaBase`, a composed `DateInputBase`/`TimeInputBase`/`DateTimeInputBase` that is not
|
|
@@ -152,9 +177,9 @@ unmatched `for` out of server-rendered HTML.
|
|
|
152
177
|
|
|
153
178
|
A `range` `Slider` points the `for` at its low thumb and starts both thumbs' names with the label
|
|
154
179
|
("Price range Minimum value"); its `ariaLabel={[low, high]}` replaces those names outright, so
|
|
155
|
-
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s
|
|
156
|
-
`Taginput`'s
|
|
157
|
-
hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
180
|
+
leave it off when a label already names the Slider. Those thumbs and an `Autocomplete`'s, a
|
|
181
|
+
`Combobox`'s or a `Taginput`'s list point `aria-labelledby` at the label's `id`, which a label
|
|
182
|
+
wired by hand has only when you pass one, `labelProps={{ htmlFor, id }}`; without it they keep their
|
|
158
183
|
fallback names ("Minimum value"/"Maximum value", "Suggestions"). A `<label htmlFor>` you put in
|
|
159
184
|
`Field.Label` yourself (`references/patterns.md`) names its control through the `for` alone, so
|
|
160
185
|
label a row holding either with the `Field`'s `label` prop. An `aria-label` or `aria-labelledby`
|
|
@@ -243,9 +268,9 @@ multi-field form plus the advanced inputs.
|
|
|
243
268
|
|
|
244
269
|
## Reuse the shipped components
|
|
245
270
|
|
|
246
|
-
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch,
|
|
247
|
-
|
|
248
|
-
inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
271
|
+
bestax ships the whole form surface — Input, Select, TextArea, Checkbox(es), Radio(s), Switch, File,
|
|
272
|
+
Autocomplete, Combobox, Slider, Numberinput, PinInput, Rate, Taginput, and the date/time inputs (see
|
|
273
|
+
the inventory above). **Compose these; don't hand-roll raw `<input class="input">` markup or
|
|
249
274
|
reinvent a control.** If you think a control is missing, check `bulma-ui/src/index.ts` and
|
|
250
275
|
`docs/docs/api/form/` first — it's probably already there under a different name.
|
|
251
276
|
|
|
@@ -19,7 +19,7 @@ Container and layout. Compound parts: `Field.Label`, `Field.Body`.
|
|
|
19
19
|
| `labelProps` | label attributes | Props for the `<label>`. An explicit `htmlFor` (even `undefined`) takes over the association; otherwise it is set automatically. For its `id`, see below. |
|
|
20
20
|
| `textColor` / `bgColor` | Bulma color | Helper colors for the field. |
|
|
21
21
|
|
|
22
|
-
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete` or `Taginput`
|
|
22
|
+
An `id` in `labelProps` is what a group's, a range `Slider`'s and an `Autocomplete`'s, a `Combobox`'s or a `Taginput`'s list's `aria-labelledby` points at. A label wired by hand with `htmlFor` gets no generated id, so give it one, `labelProps={{ htmlFor, id }}`, for a range `Slider`, an `Autocomplete`, a `Combobox` or a `Taginput`. No group points at a label wired by hand.
|
|
23
23
|
|
|
24
24
|
## Control — `form/Control.tsx`
|
|
25
25
|
|
|
@@ -87,13 +87,15 @@ File input with `label`, `message`, color/size, and icon support.
|
|
|
87
87
|
|
|
88
88
|
## Advanced inputs
|
|
89
89
|
|
|
90
|
-
| Component | Key props
|
|
91
|
-
| -------------- |
|
|
92
|
-
| `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`.
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
90
|
+
| Component | Key props |
|
|
91
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `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`. |
|
|
93
|
+
| `Combobox` | Either way: `multiple`, `keepFirst`, `inline`, `size`, `position` (`'auto'` keeps the list in the viewport), `appendToBody` (in a `Modal`, point a `ConfigProvider`'s `portalContainer` inside the modal), `open`/`defaultOpen`/`onOpenChange`, `onSelect(value)` and the Field props. Given `data` (strings or `{ value, label?, disabled?, group? }`): `value`/`defaultValue`/`onChange` (`string \| null`, or `string[]` with `multiple`, shown as tags), `placeholder`, `name`, `empty`, `itemTemplate`, and `disabled`, `required`, `form`, `id`, `aria-label`/`aria-labelledby` for the field; leaving the field empty clears one value. From parts: `.Input` (an `Input`, with `size` and the Field props on the root), `.Menu`, `.List`, `.Item` (`value`, `checked`, `disabled`), `.Group` (`label`), `.Empty`; you filter, keep the value and set the field's text in `onSelect`. |
|
|
94
|
+
| `Slider` | `min`, `max`, `step`, single or dual thumb (`value` number or `[number, number]`), `tooltip` (`'auto' \| 'always' \| 'hidden'`), vertical orientation, `color`, `size`. |
|
|
95
|
+
| `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). |
|
|
96
|
+
| `PinInput` | `length` (6), `value`/`defaultValue`/`onChange` (the code as a string), `onComplete(code)` once a change fills the last box, `type` (`'number'` or `'alphanumeric'`), `isMasked`, `name`, `color`, `size`, `isRounded`, `disabled` and the Field props. One input holds the code (`autoComplete="one-time-code"`); a paste replaces it. |
|
|
97
|
+
| `Rate` | `value`/`onChange`, `max`, `precision` (1 / 0.5 / 0.25), `customIcon` or `iconName`, `showScore`/`showText`/`texts`, `disabled`. |
|
|
98
|
+
| `Taginput` | `value` (array)/`onChange`, autocomplete suggestions, configurable confirm keys, closable tags, `color`, `size`. |
|
|
97
99
|
|
|
98
100
|
## Date / time inputs
|
|
99
101
|
|
|
@@ -214,6 +214,60 @@ const [city, setCity] = useState('');
|
|
|
214
214
|
openOnFocus
|
|
215
215
|
/>;
|
|
216
216
|
|
|
217
|
+
// Combobox: a value from a list. Given data, it filters by label, keeps the
|
|
218
|
+
// value and shows its label in the field.
|
|
219
|
+
const [country, setCountry] = useState<string | null>(null);
|
|
220
|
+
<Combobox
|
|
221
|
+
label="Country"
|
|
222
|
+
data={['Canada', 'France', 'Japan']}
|
|
223
|
+
value={country}
|
|
224
|
+
onChange={setCountry}
|
|
225
|
+
empty="No countries match"
|
|
226
|
+
/>;
|
|
227
|
+
|
|
228
|
+
// Several values, shown as tags under the field; groups come from `group`.
|
|
229
|
+
const [labels, setLabels] = useState<string[]>([]);
|
|
230
|
+
<Combobox
|
|
231
|
+
label="Labels"
|
|
232
|
+
multiple
|
|
233
|
+
data={[
|
|
234
|
+
{ value: 'bug', label: 'Bug', group: 'Type' },
|
|
235
|
+
{ value: 'docs', label: 'Docs', group: 'Area' },
|
|
236
|
+
]}
|
|
237
|
+
value={labels}
|
|
238
|
+
onChange={setLabels}
|
|
239
|
+
/>;
|
|
240
|
+
|
|
241
|
+
// From parts, when data doesn't fit (a server search, a "Create" item): you
|
|
242
|
+
// filter, keep the value and set the text, and a pick calls onSelect.
|
|
243
|
+
const countries = ['Canada', 'France', 'Japan'];
|
|
244
|
+
const [choice, setChoice] = useState<string | null>(null);
|
|
245
|
+
const [query, setQuery] = useState('');
|
|
246
|
+
const term = query === choice ? '' : query.trim().toLowerCase();
|
|
247
|
+
const matches = countries.filter(c => c.toLowerCase().includes(term));
|
|
248
|
+
<Combobox
|
|
249
|
+
label="Country"
|
|
250
|
+
onSelect={picked => {
|
|
251
|
+
setChoice(picked);
|
|
252
|
+
setQuery(picked);
|
|
253
|
+
}}
|
|
254
|
+
>
|
|
255
|
+
<Combobox.Input value={query} onChange={e => setQuery(e.target.value)} />
|
|
256
|
+
<Combobox.Menu>
|
|
257
|
+
{matches.length > 0 ? (
|
|
258
|
+
<Combobox.List>
|
|
259
|
+
{matches.map(c => (
|
|
260
|
+
<Combobox.Item key={c} value={c} checked={c === choice}>
|
|
261
|
+
{c}
|
|
262
|
+
</Combobox.Item>
|
|
263
|
+
))}
|
|
264
|
+
</Combobox.List>
|
|
265
|
+
) : (
|
|
266
|
+
<Combobox.Empty>No countries match</Combobox.Empty>
|
|
267
|
+
)}
|
|
268
|
+
</Combobox.Menu>
|
|
269
|
+
</Combobox>;
|
|
270
|
+
|
|
217
271
|
// Slider (single and dual thumb)
|
|
218
272
|
const [volume, setVolume] = useState(50);
|
|
219
273
|
<Slider value={volume} onChange={setVolume} min={0} max={100} tooltip="auto" />;
|
|
@@ -233,6 +287,20 @@ const [range, setRange] = useState<[number, number]>([20, 80]);
|
|
|
233
287
|
const [qty, setQty] = useState(1);
|
|
234
288
|
<Numberinput value={qty} onChange={setQty} min={1} max={10} step={1} />;
|
|
235
289
|
|
|
290
|
+
// PinInput: a one-time code or PIN, a box per character, all in one input.
|
|
291
|
+
// onComplete fires once each time a change fills the last box.
|
|
292
|
+
const [code, setCode] = useState('');
|
|
293
|
+
const verify = (otp: string) => {
|
|
294
|
+
// Send it to your server, then show the result with color and message.
|
|
295
|
+
};
|
|
296
|
+
<PinInput
|
|
297
|
+
label="Enter the 6-digit code"
|
|
298
|
+
name="otp"
|
|
299
|
+
value={code}
|
|
300
|
+
onChange={setCode}
|
|
301
|
+
onComplete={verify}
|
|
302
|
+
/>;
|
|
303
|
+
|
|
236
304
|
// Rate
|
|
237
305
|
const [stars, setStars] = useState(3);
|
|
238
306
|
<Rate value={stars} onChange={setStars} max={5} />;
|
|
@@ -26,9 +26,22 @@ Set it once on `ConfigProvider` and omit `library` everywhere else.
|
|
|
26
26
|
- **Variants** (`variant` → class): `solid` → `fas` (default), `regular` → `far`, `brands` →
|
|
27
27
|
`fab`, `light` → `fal`, `duotone` → `fad`, `thin` → `fat`. The free package includes only
|
|
28
28
|
solid, regular (partial), and brands — light/duotone/thin need a Font Awesome Pro kit.
|
|
29
|
+
Font Awesome 6's long class names (`fa-solid`, `fa-brands`, …) are the same styles, so write
|
|
30
|
+
them as these variants. Font Awesome 4's `fa` is `variant="fa"`, which `Icon` writes as given:
|
|
31
|
+
4 reads nothing else, and 5 and later read it as solid.
|
|
29
32
|
- **Features:** Font Awesome utility classes — `'fa-lg'`, `'fa-2x'`…`'fa-10x'`, `'fa-spin'`,
|
|
30
33
|
`'fa-pulse'`, `'fa-border'`, `'fa-fw'`, `'fa-flip-horizontal'`, `'fa-rotate-90'`.
|
|
31
34
|
- Brand icons **require** `variant="brands"`: `<Icon name="github" variant="brands" />`.
|
|
35
|
+
- **Older names:** 6 and 7 still read most of 5's names, many since renamed (`search` is
|
|
36
|
+
`magnifying-glass` now), and 4's through the v4 shims (`css/v4-shims.min.css`). Two kinds
|
|
37
|
+
draw something else:
|
|
38
|
+
- **4's brand glyphs under `fa`** (`fa fa-github`): 5 moved brands to a style of their own,
|
|
39
|
+
so without the shims they show a missing-glyph box. Write `variant="brands"`, and the
|
|
40
|
+
current name where 5 renamed one (4's `facebook` is `facebook-f`, `linkedin` is
|
|
41
|
+
`linkedin-in`, `youtube-play` is `youtube`).
|
|
42
|
+
- **5's handsets:** 6 turned them round. 5's `phone` and `phone-square` draw as 6's
|
|
43
|
+
`phone-flip` and `square-phone-flip`, and 5's `phone-alt` and `phone-square-alt` as `phone`
|
|
44
|
+
and `square-phone`, so write those names to keep a 5 page's look.
|
|
32
45
|
|
|
33
46
|
## Material Design Icons — `'mdi'`
|
|
34
47
|
|
|
@@ -140,7 +153,7 @@ Docs: https://bestax.io/docs/guides/getting-started/alternative-icons#making-the
|
|
|
140
153
|
3. **The library's CSS/script is actually loaded** — the import in `main.tsx` (or the
|
|
141
154
|
Ionicons script in `index.html`) must exist; bestax ships none of them.
|
|
142
155
|
4. **Variant availability** — Font Awesome free has no `light`/`duotone`/`thin`; brand
|
|
143
|
-
glyphs need `variant="brands"
|
|
156
|
+
glyphs need `variant="brands"`, an old Font Awesome 4 one under `fa` included.
|
|
144
157
|
|
|
145
158
|
## IconText
|
|
146
159
|
|
|
@@ -110,8 +110,33 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
110
110
|
other: `<TreeView aria-label="Files" selectionMode="single">` with `selected`/`onSelectedChange`
|
|
111
111
|
picks one, and `selectionMode="checkbox"` with `checked`/`onCheckedChange` draws boxes with
|
|
112
112
|
mixed parents, where a parent's value checks its whole branch. Map the data with a recursive
|
|
113
|
-
function, and render an item's children whether it's open or not.
|
|
114
|
-
|
|
113
|
+
function, and render an item's children whether it's open or not. A folder that loads when it
|
|
114
|
+
opens takes `hasChildren`, `isLoading` while its children are on their way, and
|
|
115
|
+
`loadError="Couldn't load src"` with `onRetry` when the load fails, setting `isLoading` again
|
|
116
|
+
while the retry runs: the tree draws the Loading… and Retry rows and announces them, so don't
|
|
117
|
+
hand-write a placeholder item. Its items aren't
|
|
118
|
+
links, so the app's sidebar and any list of pages stay on `Menu` with `Menu.Item href`.
|
|
119
|
+
- **A sidebar whose groups of links open and close is `Menu.Section`**, one per group, directly
|
|
120
|
+
in `Menu` (never inside a `Menu.Item` or `Menu.List`):
|
|
121
|
+
`<Menu.Section label="Components" defaultOpen>` around that group's `Menu.List`, with
|
|
122
|
+
`defaultOpen` on the group holding the current page (or `open`/`onOpenChange` to hold the state).
|
|
123
|
+
Its heading is a real button with `aria-expanded`, and its links stay links, each a tab stop.
|
|
124
|
+
Not `TreeView`, and not a `Collapse` inside a hand-written `<li>`.
|
|
125
|
+
- **Panes the user resizes are `Splitter`** (a file tree beside an editor, a list beside its
|
|
126
|
+
detail, a query above its results), not hand-written drag code or fixed `Columns`:
|
|
127
|
+
`<Splitter height={480}>` with a `<Splitter.Pane label="Files" defaultSize={25} minSize="12rem">`
|
|
128
|
+
per pane. The handles go in on their own, each named by its pane's `label`, so give every pane
|
|
129
|
+
one. Sizes are percentages, limits also take `px` or `rem`, `orientation="vertical"` stacks
|
|
130
|
+
them (and needs a `height`), and `collapsible` with `collapsed`/`onCollapsedChange` lets a
|
|
131
|
+
button hide a pane. Put scrolling content in a `ScrollArea` inside the pane.
|
|
132
|
+
- **Content taller or wider than its space is `ScrollArea`** (release notes in a Card, a sidebar
|
|
133
|
+
longer than the screen, a feed that grows then scrolls, a wide table on a phone), not a `Box`
|
|
134
|
+
with `overflowY="auto"` and a height in a style: `<ScrollArea height={240} aria-label="…">`,
|
|
135
|
+
or `maxHeight="20rem"` to grow with its content first. It needs `aria-label` or
|
|
136
|
+
`aria-labelledby`, since while it overflows it's a region a keyboard user tabs to. The
|
|
137
|
+
helper props and `className` go on its frame and the `ref` on the element that scrolls.
|
|
138
|
+
`hasFade` fades the edges that have more, `orientation="horizontal"` suits a wide table, and
|
|
139
|
+
`onEdgeReach={edge => …}` loads more at an edge.
|
|
115
140
|
- Link lists (footer nav, sidebars): a bare `UnorderedList` of `ListItem`s is already
|
|
116
141
|
marker-less and flush — Bulma's reset unstyles `ul` — so no prop or CSS is needed;
|
|
117
142
|
bullets appear only inside `Content`.
|
|
@@ -167,9 +192,10 @@ Centered; a collection of items → Card grid. For mixed requests, pick the domi
|
|
|
167
192
|
(`Hero color="primary"`, a dark banner), use **filled** buttons — `color="light"` or
|
|
168
193
|
`color="primary" isInverted` — never a thin `isOutlined` secondary: a light outline + light
|
|
169
194
|
label on a dark surface is low-contrast and gets worse under OS dark mode. And when the page's
|
|
170
|
-
design is single-mode (a fixed light or dark look), pin it
|
|
171
|
-
`<
|
|
172
|
-
colors out from under the fixed palette
|
|
195
|
+
design is single-mode (a fixed light or dark look), pin it on the `<html>` in `index.html`,
|
|
196
|
+
`<html lang="en" data-theme="light">`, so a visitor's OS dark mode can't flip Bulma's text
|
|
197
|
+
colors out from under the fixed palette, from the first paint on (details: the
|
|
198
|
+
`bestax-theming` skill's contrast rules).
|
|
173
199
|
|
|
174
200
|
## Three components core Bulma will talk you out of
|
|
175
201
|
|
|
@@ -164,8 +164,8 @@ style, so bands cost zero custom CSS. The same values work on `Hero` (and its
|
|
|
164
164
|
**Hero CTAs:** on a colored hero use **filled** buttons only — `color="light"` for the primary
|
|
165
165
|
CTA and `color="primary" isInverted` (solid white, primary text) for a secondary. A thin
|
|
166
166
|
`isOutlined` button on a fixed-color surface is low-contrast and degrades further under OS dark
|
|
167
|
-
mode. Single-mode page designs should also pin the scheme
|
|
168
|
-
(`<
|
|
167
|
+
mode. Single-mode page designs should also pin the scheme on the `<html>` in `index.html`
|
|
168
|
+
(`<html lang="en" data-theme="light">`); see the `bestax-theming` skill's contrast rules.
|
|
169
169
|
|
|
170
170
|
---
|
|
171
171
|
|
|
@@ -39,12 +39,16 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
|
|
|
39
39
|
|
|
40
40
|
| Prop | Type | Notes |
|
|
41
41
|
| --------------------------------- | --------------------------------------- | ----------------------------------------- |
|
|
42
|
+
| `as` | `'div'` (default) or a landmark tag | Renders that tag in place of the `div`. |
|
|
42
43
|
| `fluid` | `boolean` | Full width with a small gutter. |
|
|
43
44
|
| `widescreen` / `fullhd` | `boolean` | Only constrain at that breakpoint and up. |
|
|
44
45
|
| `breakpoint` | `'tablet' \| 'desktop' \| 'widescreen'` | Max-width breakpoint. |
|
|
45
46
|
| `isMax` | `boolean` | Use the `is-max-*` width. |
|
|
46
47
|
| `color` / `textColor` / `bgColor` | Bulma color | — |
|
|
47
48
|
|
|
49
|
+
`as` takes `'main' | 'section' | 'article' | 'aside' | 'header' | 'footer' | 'nav'`, so
|
|
50
|
+
`<Container as="main">` renders `<main class="container">` with no wrapper.
|
|
51
|
+
|
|
48
52
|
## Section
|
|
49
53
|
|
|
50
54
|
`<Section>` adds vertical page rhythm (padding).
|
|
@@ -63,6 +67,7 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
|
|
|
63
67
|
| `color` | `primary \| link \| info \| success \| warning \| danger \| black \| white \| light \| dark` (CSS-backed). Also accepted but deprecated, no CSS, dev-warn: every other `validColors` member, plus `inherit` and `current` |
|
|
64
68
|
| `size` | `'small' \| 'medium' \| 'large' \| 'fullheight' \| 'fullheight-with-navbar'` |
|
|
65
69
|
| `fullheightWithNavbar` | `boolean` |
|
|
70
|
+
| `as` | `'section'` (default) \| `'div'` \| `'main'` \| `'header'` \| `'footer'` \| `'article'` \| `'aside'` |
|
|
66
71
|
|
|
67
72
|
```tsx
|
|
68
73
|
<Hero color="primary" size="medium">
|
|
@@ -70,6 +75,11 @@ Every component also accepts the shared Bulma helper props (`m`/`p` spacing, `te
|
|
|
70
75
|
</Hero>
|
|
71
76
|
```
|
|
72
77
|
|
|
78
|
+
`Hero.Head` takes `as="header"` or `as="nav"` and `Hero.Foot` `as="footer"` or `as="nav"` (both
|
|
79
|
+
`div` by default), so a page that is all hero is `<Hero as="main">` with no wrapper. A `header` or
|
|
80
|
+
`footer` is the page's banner or footer landmark only outside a `section`, `article`, `aside`,
|
|
81
|
+
`nav` or `main`, so for that render the hero itself `as="div"`.
|
|
82
|
+
|
|
73
83
|
## Footer
|
|
74
84
|
|
|
75
85
|
`<Footer>` is the page footer.
|
|
@@ -230,6 +240,8 @@ flexDirection="column"` + `flexGrow="1"` pattern applies to `Cell` + `Card`.
|
|
|
230
240
|
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
231
241
|
| `fixed` | `'top' \| 'bottom'` |
|
|
232
242
|
| `transparent` | `boolean` |
|
|
243
|
+
| `isSpaced` | `boolean` (Bulma's `is-spaced`, from desktop up) |
|
|
244
|
+
| `hasShadow` | `boolean` (default `false`, unlike `Card`'s; Bulma's `has-shadow`) |
|
|
233
245
|
| `color` | `'primary' \| 'link' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'black' \| 'dark' \| 'light' \| 'white'` |
|
|
234
246
|
| `Navbar.Item` | `as?`, `active?`, `href` |
|
|
235
247
|
| `Navbar.Burger` | `active?`, `onClick` |
|
|
@@ -239,7 +251,12 @@ The burger/menu is controlled state — toggle `Navbar.Burger active`/`onClick`
|
|
|
239
251
|
flag to `Navbar.Menu active`. **A `fixed="top"` navbar requires `has-navbar-fixed-top` on `<html>`**
|
|
240
252
|
(Bulma offsets the page from it); the library adds no helper. Set it statically —
|
|
241
253
|
`<html class="has-navbar-fixed-top">` in `index.html` — reserving a `classList.add` effect for
|
|
242
|
-
a navbar that mounts conditionally.
|
|
254
|
+
a navbar that mounts conditionally. **With `isSpaced`, add `has-spaced-navbar-fixed-top` too**
|
|
255
|
+
(`<html class="has-navbar-fixed-top has-spaced-navbar-fixed-top">`): from desktop up the spaced
|
|
256
|
+
navbar is taller than the plain class pads the page for, and there the spaced class pads it for
|
|
257
|
+
the taller navbar, so with the plain class alone the top of the page sits under the navbar. A
|
|
258
|
+
`fixed="bottom"` navbar takes `has-navbar-fixed-bottom`, and `has-spaced-navbar-fixed-bottom`
|
|
259
|
+
beside it with `isSpaced`.
|
|
243
260
|
|
|
244
261
|
**Routing:** in a routed app, don't use `href="#"` — render items as the router's link
|
|
245
262
|
component. `Menu.Item as={Link} to="/x"` and `Navbar.Item as={Link} to="/x"` both compile
|
|
@@ -250,11 +267,13 @@ https://bestax.io/docs/guides/features/routing.
|
|
|
250
267
|
|
|
251
268
|
## Menu
|
|
252
269
|
|
|
253
|
-
`<Menu>` is a vertical sidebar menu. Subcomponents: `Menu.Label`, `Menu.List`, `Menu.Item
|
|
270
|
+
`<Menu>` is a vertical sidebar menu. Subcomponents: `Menu.Label`, `Menu.List`, `Menu.Item`,
|
|
271
|
+
`Menu.Section`.
|
|
254
272
|
|
|
255
|
-
| Prop
|
|
256
|
-
|
|
|
257
|
-
| `Menu.Item`
|
|
273
|
+
| Prop | Type |
|
|
274
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
275
|
+
| `Menu.Item` | `active?` (the current page: `is-active` plus `aria-current="page"`, so don't add your own), `href?`, `as?` (custom link component); nest a `Menu.List` inside an item for submenus |
|
|
276
|
+
| `Menu.Section` | `label` (the heading, a `ReactNode`), `defaultOpen?`, or `open?` with `onOpenChange?(open)` to hold the state yourself, as `Collapse` does; its child is the group's `Menu.List`, and a closed section's links are out of reach |
|
|
258
277
|
|
|
259
278
|
```tsx
|
|
260
279
|
<Menu>
|
|
@@ -268,6 +287,34 @@ https://bestax.io/docs/guides/features/routing.
|
|
|
268
287
|
</Menu>
|
|
269
288
|
```
|
|
270
289
|
|
|
290
|
+
A sidebar whose groups of links open and close (a docs sidebar, an admin app's sections) puts each
|
|
291
|
+
group's `Menu.List` in a `Menu.Section`. Its heading is a real button drawn like `Menu.Label`, with
|
|
292
|
+
`aria-expanded` and a chevron, and its links stay ordinary links, each a tab stop. Open the
|
|
293
|
+
section that holds the current page. A section goes directly in `Menu`, never inside a `Menu.Item`
|
|
294
|
+
(it would render inside the item's link) or a `Menu.List` (it isn't a list item). Don't build this
|
|
295
|
+
from `TreeView` (its items aren't links) or from a `Collapse` in a hand-written `<li>` (its trigger
|
|
296
|
+
is a `div` with `role="button"`).
|
|
297
|
+
|
|
298
|
+
```tsx
|
|
299
|
+
<nav aria-label="Documentation">
|
|
300
|
+
<Menu>
|
|
301
|
+
<Menu.Section label="Getting started">
|
|
302
|
+
<Menu.List>
|
|
303
|
+
<Menu.Item href="/docs/install">Installation</Menu.Item>
|
|
304
|
+
</Menu.List>
|
|
305
|
+
</Menu.Section>
|
|
306
|
+
<Menu.Section label="Components" defaultOpen>
|
|
307
|
+
<Menu.List>
|
|
308
|
+
<Menu.Item href="/docs/button" active>
|
|
309
|
+
Button
|
|
310
|
+
</Menu.Item>
|
|
311
|
+
<Menu.Item href="/docs/card">Card</Menu.Item>
|
|
312
|
+
</Menu.List>
|
|
313
|
+
</Menu.Section>
|
|
314
|
+
</Menu>
|
|
315
|
+
</nav>
|
|
316
|
+
```
|
|
317
|
+
|
|
271
318
|
## Card
|
|
272
319
|
|
|
273
320
|
`<Card>` for catalog/grid items. Use the props form (`header`, `image`, `footer`) or the compound
|
|
@@ -52,11 +52,16 @@ Run these steps in order. Don't hand-convert what the codemod converts automatic
|
|
|
52
52
|
Each page becomes a module beside it (`index.tsx`, or `.jsx` when the package isn't
|
|
53
53
|
TypeScript) whose default export is `<Name>Page`, named from the file (its folder's for an
|
|
54
54
|
`index.html`). The page stays as it was, and the codemod never writes over a module that's
|
|
55
|
-
already there, so a re-run writes nothing
|
|
55
|
+
already there, so a re-run on the page writes nothing: after fixing a TODO, re-run on the
|
|
56
|
+
module, which is what each TODO says. Its head, its `<html>` and `<body>` attributes and
|
|
56
57
|
its scripts become `html-*` TODOs: put the app-wide parts in the app's `index.html` and
|
|
57
58
|
entry, and port each script's work into the component. Under `--css bestax` or `--css bulma`
|
|
58
59
|
the module imports the stylesheet in place of the page's Bulma `<link>`.
|
|
59
60
|
|
|
61
|
+
A file that fails to parse or transform is marked `✖`, listed under `failed` in the report
|
|
62
|
+
and left as it was, and the run exits 1 while every other file still converts. Fix that file
|
|
63
|
+
and run the codemod on it again. TODOs alone leave the exit code at 0.
|
|
64
|
+
|
|
60
65
|
2. **Apply it** (same command without `--dry`), then run the project's formatter — the
|
|
61
66
|
codemod preserves surrounding formatting but doesn't prettify what it rewrites.
|
|
62
67
|
Besides the components, it also migrates **stylesheets** (CSS imports →
|
|
@@ -132,9 +137,17 @@ code per the references, or deliberately keep the old markup with `className` st
|
|
|
132
137
|
drift than the 0.9 → 1 guide alone describes.
|
|
133
138
|
- **bulma-classes**: there is no library to remove. An element converts only when the bestax
|
|
134
139
|
component renders the same markup (tag, classes, attributes), so a converted page renders
|
|
135
|
-
the same HTML
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
the same HTML, but for deliberate exceptions. Font Awesome's own hidden `<i>` in a `.icon`
|
|
141
|
+
that carries nothing else assistive tech reads becomes `Icon`, which hides the `.icon`
|
|
142
|
+
instead, the glyph hidden either way. Such a `.icon` around a bare glyph, in a link or button
|
|
143
|
+
its own `aria-label` names, becomes `Icon` too, which hides it, since the label is what a
|
|
144
|
+
screen reader reads there. Font Awesome 6's long style names (`fa-brands`) become the
|
|
145
|
+
`bestax-icons` skill's `variant` (`brands`), which renders the short name (`fab`), the same
|
|
146
|
+
style. And Bulma 0.9's `is-marginless` and `is-paddingless`,
|
|
147
|
+
which style nothing under v1, become `m="0"` and `p="0"`. Everything else stays as written, with a
|
|
148
|
+
TODO when there is something to decide. Static class strings convert, and so does a `clsx` or
|
|
149
|
+
`classnames` call of strings and conditional classes, where a condition on a flag becomes
|
|
150
|
+
its prop (`isLoading={busy}`);
|
|
138
151
|
any other computed className gets `dynamic-class:<Target>`, naming the component the element
|
|
139
152
|
would become. Files a Next.js
|
|
140
153
|
App Router may render as server components are left alone (`rsc`), and the manifest gains
|
|
@@ -156,7 +169,8 @@ code per the references, or deliberately keep the old markup with `className` st
|
|
|
156
169
|
|
|
157
170
|
- The codemod is safe to re-run after partial manual work: a library source only touches files
|
|
158
171
|
that still import the library, and `bulma-classes` skips elements that are already
|
|
159
|
-
components
|
|
172
|
+
components, never repeats a TODO it already wrote, and drops one that asked for a re-run once
|
|
173
|
+
it no longer applies.
|
|
160
174
|
- Don't downgrade converted props back to the old names; bestax uses `is*` booleans
|
|
161
175
|
(`isLoading`), string size unions (`textSize="4"`), and `as` rather than `renderAs`.
|
|
162
176
|
- If a component the app uses isn't in the component map, it wasn't part of that library's
|
|
@@ -151,7 +151,7 @@ Some targets depend on a prop or a child, so the codemod picks them element by e
|
|
|
151
151
|
| `PageControl` | `Pagination.Previous` / `Pagination.Next` | `isNext` (bloomer's default is previous) |
|
|
152
152
|
| `NavbarItem` | `Navbar.Item` / `Navbar.Dropdown` | `hasDropdown` |
|
|
153
153
|
| `Page` | _folded into its child_ / `<li>` | whether the child is a `PageLink`/`PageEllipsis`; attributes on the Page move onto the link, with a `component:Page` note |
|
|
154
|
-
| `PanelBlock` | `Panel.Block` / `<div
|
|
154
|
+
| `PanelBlock` | `Panel.Block` / _the plain block_ | the element: an `<a>`, a `<div>` or a `<label>` is the component; any other `tag`, or a dynamic one, stays plain markup |
|
|
155
155
|
| `MenuLink` in a literal `<li>` | `Menu.Item`, the `<li>` folded away | bestax's item renders its own `<li>` |
|
|
156
156
|
| `Breadcrumb` around a literal `<ul>` | `Breadcrumb`, the `<ul>` folded away | bestax's breadcrumb renders its own `<ul>` |
|
|
157
157
|
| `Icon`, `PanelIcon` | `name`/`library`/`variant` / an `<i>` child | whether the icon classes are FA5/6 or MDI |
|
|
@@ -119,8 +119,8 @@ narrowed to a literal union, so a value outside the union is a type error you ca
|
|
|
119
119
|
than a silent rewrite. The components whose `tag` becomes `as`:
|
|
120
120
|
|
|
121
121
|
`Button`, `Image`, `Title`, `Subtitle`, `Footer`, `Media`, `MediaLeft`, `LevelItem`, `Control`,
|
|
122
|
-
`DropdownItem`,
|
|
123
|
-
`
|
|
122
|
+
`DropdownItem`, `MenuLink`, `NavbarItem`, `NavbarLink`, `Container`, `Hero`, `HeroHeader`,
|
|
123
|
+
`HeroFooter`, `Panel`.
|
|
124
124
|
|
|
125
125
|
Everywhere else `tag` is left in place with a TODO. On the components that become plain markup
|
|
126
126
|
(`Help`, `Label`, `Heading`, `BreadcrumbItem`, `PanelTab`, `TabLink`, `Page`, `HeroVideo`) a
|
|
@@ -148,9 +148,12 @@ rendered. `Button` and `LevelItem` gain `as="a"`; on the targets that already re
|
|
|
148
148
|
stays — each of those takes the anchor's props under its default `as`. A `NavbarItem` or
|
|
149
149
|
`DropdownItem` with neither `href` nor `tag` gains `as="div"`, because bestax's `Navbar.Item`
|
|
150
150
|
and `Dropdown.Item` default to an `<a>`. An empty or false `href` selected nothing in bloomer
|
|
151
|
-
and is dropped. bestax's `Panel.Block` is
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
and is dropped. bestax's `Panel.Block` is an `<a>` by default and a `<div>` or a `<label>`
|
|
152
|
+
through `as`, so a `PanelBlock` with no `href` becomes `<Panel.Block as="div">`, or
|
|
153
|
+
`as="label"` for `tag="label"`, and one with `tag="a"` a bare `<Panel.Block>`. One on any
|
|
154
|
+
other `tag` stays the plain block, on that tag, that bloomer rendered. A dynamic `tag` becomes
|
|
155
|
+
a plain `<div class="panel-block">` with a `prop:tag` TODO, since the codemod can't tell which
|
|
156
|
+
tag it names.
|
|
154
157
|
|
|
155
158
|
A dynamic `href={expr}` was a runtime decision bloomer made and bestax's `as` is one element or
|
|
156
159
|
the other. It resolves to the anchor, which is the branch the `href` is there for, and the
|
|
@@ -189,7 +192,7 @@ value is flagged. The same conversion covers the modifiers bestax has no prop fo
|
|
|
189
192
|
bloomer forwarded no refs. bestax forwards a ref from the form controls, plus `Avatar`, `Button`,
|
|
190
193
|
`Carousel`, `CarouselItem`, `Dialog`, `Dropdown`, `EmptyState`, `Kbd`, `Kbd.Group`, `Link`,
|
|
191
194
|
`LinkButton`, `Loader`, `Menu.Item`, `Modal`, `Navbar`, `Navbar.Burger`, `Navbar.Dropdown`,
|
|
192
|
-
`Navbar.Item`, `Navbar.Link`, `SegmentedControl`, `SegmentedControl.Segment`, `Sidebar`, `Timeline`,
|
|
195
|
+
`Navbar.Item`, `Navbar.Link`, `ScrollArea`, `SegmentedControl`, `SegmentedControl.Segment`, `Sidebar`, `Splitter`, `Splitter.Pane`, `Timeline`,
|
|
193
196
|
`Timeline.Item`, `Toast`, `ToggleButton`, `TreeView` and `TreeView.Item` — pass `ref` directly on
|
|
194
197
|
those. "The form controls" means the inputs themselves: the `Field`, `Field.Label`, `Field.Body`,
|
|
195
198
|
`Checkboxes` and `Radios` wrappers around them forward nothing.
|
|
@@ -62,10 +62,13 @@ and `isSize={n}` becomes a column span:
|
|
|
62
62
|
bloomer's `Modal` was an inert shell: `isActive` toggled the class and nothing else. bestax's
|
|
63
63
|
`Modal` closes on Escape (calling `onClose`), locks body scroll, and traps focus **by default**,
|
|
64
64
|
and renders inline unless `portal` is set. So every conversion changes behaviour, and every one
|
|
65
|
-
is flagged. Pass `onClose`
|
|
65
|
+
is flagged. Pass `onClose` for Escape and `Modal.Close` to close it. `Modal.Background` doesn't
|
|
66
|
+
call `onClose` on its own, so give it the same handler as `onClick` for the background to close
|
|
67
|
+
it too:
|
|
66
68
|
|
|
67
69
|
```jsx
|
|
68
70
|
<Modal active={open} onClose={() => setOpen(false)}>
|
|
71
|
+
<Modal.Background onClick={() => setOpen(false)} />
|
|
69
72
|
```
|
|
70
73
|
|
|
71
74
|
or keep bloomer's inert behaviour with `closeOnEscape={false} lockScroll={false}`. Add
|