@allxsmith/bestax-bulma 5.57.1 → 5.57.2
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/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Toast.js +21 -7
- package/dist/esm/components/Toast.js.map +1 -1
- package/dist/esm/elements/Tr.js.map +1 -1
- package/dist/esm/form/Checkbox.js +21 -14
- package/dist/esm/form/Checkbox.js.map +1 -1
- package/dist/esm/form/CheckboxCard.js +17 -11
- package/dist/esm/form/CheckboxCard.js.map +1 -1
- package/dist/esm/form/Checkboxes.js +30 -3
- package/dist/esm/form/Checkboxes.js.map +1 -1
- package/dist/esm/form/FormContext.js +31 -1
- package/dist/esm/form/FormContext.js.map +1 -1
- package/dist/esm/form/Radio.js +13 -10
- package/dist/esm/form/Radio.js.map +1 -1
- package/dist/esm/form/RadioCard.js +10 -8
- package/dist/esm/form/RadioCard.js.map +1 -1
- package/dist/esm/form/Radios.js +15 -3
- package/dist/esm/form/Radios.js.map +1 -1
- package/dist/esm/form/Rate.js +2 -2
- package/dist/esm/form/Rate.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +152 -52
- package/dist/index.cjs.map +1 -1
- package/dist/types/components/Toast.d.ts +14 -3
- package/dist/types/elements/Tr.d.ts +3 -3
- package/dist/types/form/CheckboxCard.d.ts +1 -1
- package/dist/types/form/Checkboxes.d.ts +7 -1
- package/dist/types/form/FormContext.d.ts +34 -1
- package/dist/types/form/RadioCard.d.ts +1 -1
- package/dist/types/form/Radios.d.ts +5 -1
- package/dist/types/form/Rate.d.ts +5 -1
- package/dist/types-cjs/components/Toast.d.ts +14 -3
- package/dist/types-cjs/elements/Tr.d.ts +3 -3
- package/dist/types-cjs/form/CheckboxCard.d.ts +1 -1
- package/dist/types-cjs/form/Checkboxes.d.ts +7 -1
- package/dist/types-cjs/form/FormContext.d.ts +34 -1
- package/dist/types-cjs/form/RadioCard.d.ts +1 -1
- package/dist/types-cjs/form/Radios.d.ts +5 -1
- package/dist/types-cjs/form/Rate.d.ts +5 -1
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/CLAUDE.md +4 -1
- package/src/scss/_selected-row.scss +43 -0
- package/src/scss/components/_index.scss +1 -0
- package/src/scss/components/_pagination.scss +25 -0
- package/src/scss/components/_toast.scss +2 -1
- package/src/scss/form/_checkbox.scss +18 -42
- package/src/scss/form/_combobox.scss +11 -0
- package/src/scss/form/_radio.scss +28 -2
- package/src/scss/form/_switch.scss +9 -5
- package/src/scss/form/_taginput.scss +6 -2
|
@@ -243,6 +243,36 @@ const useFieldLabelTarget = (id, hasInput = true) => {
|
|
|
243
243
|
useReportFieldLabelFor(fieldLabelId !== undefined && target === fieldLabelId);
|
|
244
244
|
return target;
|
|
245
245
|
};
|
|
246
|
+
/**
|
|
247
|
+
* Tells a surrounding `<Radios>` that one of its radios was picked. A radio
|
|
248
|
+
* with a value hands the group that value. One without a value tells the
|
|
249
|
+
* group nothing it reports, but when it shares the group's name the browser
|
|
250
|
+
* has just unchecked the radio that showed checked, so the group learns the
|
|
251
|
+
* pick left its radios and React doesn't check that one again, undoing the
|
|
252
|
+
* pick. `Radio` and `RadioCard` call it with their effective name.
|
|
253
|
+
*/
|
|
254
|
+
const pickInRadiosGroup = (group, value, name) => {
|
|
255
|
+
if (!group?.onChange)
|
|
256
|
+
return;
|
|
257
|
+
if (value !== undefined)
|
|
258
|
+
group.onChange(String(value));
|
|
259
|
+
else if (name !== undefined && name === group.name)
|
|
260
|
+
group.pickWithoutValue();
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Registers a choice with its surrounding `<Checkboxes>` while it starts
|
|
264
|
+
* checked, so a group with no value yet starts its first change from the
|
|
265
|
+
* choices that were checked. `Checkbox` and `CheckboxCard` call it. It does
|
|
266
|
+
* nothing outside a group that manages the selection, or without a value.
|
|
267
|
+
*/
|
|
268
|
+
const useRegisterChecked = (group, value, startsChecked) => {
|
|
269
|
+
const register = group?.registerChecked;
|
|
270
|
+
useClientLayoutEffect(() => {
|
|
271
|
+
if (!register || value === undefined || !startsChecked)
|
|
272
|
+
return;
|
|
273
|
+
return register(value);
|
|
274
|
+
}, [register, value, startsChecked]);
|
|
275
|
+
};
|
|
246
276
|
const RadiosContext = createContext(undefined);
|
|
247
277
|
const CheckboxesContext = createContext(undefined);
|
|
248
278
|
/**
|
|
@@ -260,5 +290,5 @@ const RadiosProvider = RadiosContext.Provider;
|
|
|
260
290
|
/** Provider for the Checkboxes group context — used internally by Checkboxes. */
|
|
261
291
|
const CheckboxesProvider = CheckboxesContext.Provider;
|
|
262
292
|
|
|
263
|
-
export { CheckboxesProvider, ControlProvider, FieldLabelElementIdProvider, FieldLabelForProvider, FieldLabelForReportProvider, FieldLabelIdProvider, FieldProvider, FieldsetDisabledProvider, RadiosProvider, rendersOwnControl, rendersOwnField, useCheckboxesGroup, useFieldLabelElementId, useFieldLabelFor, useFieldLabelId, useFieldLabelTarget, useFieldsetDisabled, useInsideControl, useInsideField, useRadiosGroup, useReportFieldLabelFor };
|
|
293
|
+
export { CheckboxesProvider, ControlProvider, FieldLabelElementIdProvider, FieldLabelForProvider, FieldLabelForReportProvider, FieldLabelIdProvider, FieldProvider, FieldsetDisabledProvider, RadiosProvider, pickInRadiosGroup, rendersOwnControl, rendersOwnField, useCheckboxesGroup, useFieldLabelElementId, useFieldLabelFor, useFieldLabelId, useFieldLabelTarget, useFieldsetDisabled, useInsideControl, useInsideField, useRadiosGroup, useRegisterChecked, useReportFieldLabelFor };
|
|
264
294
|
//# sourceMappingURL=FormContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormContext.js","sources":["../../../src/form/FormContext.tsx"],"sourcesContent":["import React, { createContext, useContext } from 'react';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport type { ControlBaseProps } from './Control';\n\nconst FieldContext = createContext(false);\nconst ControlContext = createContext(false);\n\n/**\n * Hook to detect if the component is inside a Field wrapper.\n * Form components use this to skip rendering their own Field.\n */\nexport const useInsideField = () => useContext(FieldContext);\n\n/**\n * Hook to detect if the component is inside a Control wrapper.\n * Form components use this to skip rendering their own Control.\n */\nexport const useInsideControl = () => useContext(ControlContext);\n\n/** Provider for Field context — used internally by Field component. */\nexport const FieldProvider = FieldContext.Provider;\n\n/** Provider for Control context — used internally by Control component. */\nexport const ControlProvider = ControlContext.Provider;\n\nconst FieldsetDisabledContext = createContext(false);\n\n/**\n * An input's `disabled`: its own prop when the caller set one, otherwise\n * whether a disabled `Fieldset` holds it. A `<fieldset disabled>` disables the\n * native controls inside on its own, and this carries the same state to what\n * the browser doesn't reach, such as Rate's stars, the time wheels, and the\n * `is-disabled` classes. The content of a Fieldset's legend reads the state\n * from outside that Fieldset, as the browser leaves the first legend's\n * controls enabled. Internal; not part of the public API.\n */\nexport const useFieldsetDisabled = (disabled: boolean | undefined): boolean => {\n const inherited = useContext(FieldsetDisabledContext);\n return disabled ?? inherited;\n};\n\n/** Provider for a Fieldset's disabled state, used internally by Fieldset. */\nexport const FieldsetDisabledProvider = FieldsetDisabledContext.Provider;\n\n/** The wrapper props that change what its own `Field` renders. */\ninterface FieldShapingProps {\n /** Rendered as the `Field`'s label. */\n label?: React.ReactNode;\n /** Rendered as help text after the widget. */\n message?: React.ReactNode;\n /** Lays the `Field` out horizontally. */\n horizontal?: boolean;\n /** Set on the `Field` as its class. */\n fieldClassName?: string;\n}\n\ninterface OwnFieldOptions extends FieldShapingProps {\n /** `useInsideField()` as the wrapper read it. */\n insideField: boolean;\n /** `useInsideControl()` as the wrapper read it. */\n insideControl: boolean;\n}\n\n// Each Field-shaping prop, with what takes its place on a `Field` the caller\n// wraps around the `Control`. `message` needs nothing there, because the\n// wrapper still renders it inside `Field > Control`. The other Field-level\n// props (`labelSize`, `labelProps`, `messageColor`) change nothing unless a\n// `label` or `message` renders, so on their own they keep no `Field`.\n// `bare-control.test.tsx` holds this list to what each wrapper hands its\n// `Field` and to which props change its markup, so a new Field-level prop\n// fails there until it is sorted into one group or the other.\nconst FIELD_SHAPING: ReadonlyArray<\n [keyof FieldShapingProps, string | undefined]\n> = [\n ['label', 'label'],\n ['message', undefined],\n ['horizontal', 'horizontal'],\n ['fieldClassName', 'className (for fieldClassName)'],\n];\n\n/** `a`, `a and b`, `a, b and c`. */\nconst listOf = (items: string[]): string =>\n items.length > 1\n ? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n : items[0];\n\n/**\n * Whether a convenience form wrapper renders a `Field` of its own (#905).\n * Never inside an outer `Field`. Inside a `Control` with no `Field` around\n * it, a `Field` of its own would put a `.field` inside the `.control`, where\n * Bulma's control styles break (its icon rules are sibling selectors on the\n * input). So it renders one there only when a prop that shapes the `Field`'s\n * markup is set (`label`, `message`, `horizontal`, `fieldClassName`), keeping\n * the markup it always had, and warns in development that the fix is a\n * `Field` around that `Control`. A prop counts when it is truthy, the same\n * test that decides whether it changes the `Field`'s output.\n * Internal; not part of the public API.\n */\nexport const rendersOwnField = (\n component: string,\n options: OwnFieldOptions\n): boolean => {\n if (options.insideField) return false;\n if (!options.insideControl) return true;\n const held = FIELD_SHAPING.filter(([prop]) => options[prop]);\n if (held.length === 0) return false;\n const names = held.map(([prop]) => prop);\n const moved = held\n .map(([, onField]) => onField)\n .filter((onField): onField is string => onField !== undefined);\n warnOnce(\n `${component}:Field-in-bare-Control:${names.join('+')}`,\n `[bestax-bulma] <${component} ${names.join(' ')}> inside a <Control> ` +\n `with no <Field> around it renders its own <Field> for ` +\n `${names.length > 1 ? 'those props' : 'that prop'}, which puts a ` +\n `.field inside the .control, a nesting Bulma's styles are not ` +\n `written for. Wrap the <Control> in a <Field> instead` +\n (moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.')\n );\n return true;\n};\n\n/** The props a convenience form wrapper hands its own `Control`. Internal. */\nexport interface ControlLevelProps {\n /** Shows the `Control`'s loading spinner. */\n isLoading?: boolean;\n /** The `Control`'s left icon. */\n iconLeft?: ControlBaseProps['iconLeft'];\n /** Shortcut for the left icon's name. */\n iconLeftName?: string;\n /** Shortcut for the left icon's size. */\n iconLeftSize?: ControlBaseProps['iconLeftSize'];\n /** The `Control`'s right icon. */\n iconRight?: ControlBaseProps['iconRight'];\n /** Shortcut for the right icon's name. */\n iconRightName?: string;\n /** Shortcut for the right icon's size. */\n iconRightSize?: ControlBaseProps['iconRightSize'];\n /** Reserves the `Control`'s left icon space. */\n hasIconsLeft?: boolean;\n /** Reserves the `Control`'s right icon space. */\n hasIconsRight?: boolean;\n /** Expands the `Control`. */\n isExpanded?: boolean;\n /** Set on the `Control` as its `size`. */\n controlSize?: ControlBaseProps['size'];\n /** Set on the `Control` as its class. */\n controlClassName?: string;\n}\n\ninterface OwnControlOptions extends ControlLevelProps {\n /** `useInsideControl()` as the wrapper read it. */\n insideControl: boolean;\n /** A picker's `inline`, which renders no `Control` inside one or out. */\n inline?: boolean;\n /**\n * The left glyph the wrapper's own `Control` shows when the caller sets no\n * `iconLeftName`; read only then. `bare-control.test.tsx` reads that glyph\n * from what each wrapper hands its own `Control`, so a wrapper that gains\n * one fails there until it passes it here.\n */\n defaultIconLeftName?: string;\n}\n\n// Each Control-level prop, with what takes its place on the `Control` the\n// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to\n// what each wrapper hands its own `Control`, so a new Control-level prop\n// fails there until it is added here and passed to `rendersOwnControl`.\nconst ON_CONTROL: Record<keyof ControlLevelProps, string> = {\n isLoading: 'isLoading',\n iconLeft: 'iconLeft',\n iconLeftName: 'iconLeftName',\n iconLeftSize: 'iconLeftSize',\n iconRight: 'iconRight',\n iconRightName: 'iconRightName',\n iconRightSize: 'iconRightSize',\n hasIconsLeft: 'hasIconsLeft',\n hasIconsRight: 'hasIconsRight',\n isExpanded: 'isExpanded',\n controlSize: 'size (for controlSize)',\n controlClassName: 'className (for controlClassName)',\n};\n\n/**\n * Whether a convenience form wrapper renders a `Control` of its own (#921):\n * only outside one. Inside a `Control` it renders none, so the props it\n * would have handed its own do nothing there, and it warns in development,\n * naming them and saying to set them on that `Control`. An `inline` picker\n * renders none in either place, so it drops them inside a `Control` or not,\n * and its warning says so without pointing at a `Control`. A wrapper passes\n * only the props it hands its own `Control`, as the caller gave them, so a\n * default the wrapper fills in itself never warns. A prop counts when it is\n * truthy, since a falsy one would change nothing on a `Control` either.\n * `iconLeftSize` and `hasIconsLeft` show nothing on a `Control` without a\n * glyph, so when they move and the caller chose no left icon, the advice\n * names the wrapper's default glyph too, and following it draws what the\n * wrapper's own `Control` did.\n * Internal; not part of the public API.\n */\nexport const rendersOwnControl = (\n component: string,\n options: OwnControlOptions\n): boolean => {\n if (!options.inline && !options.insideControl) return true;\n const held = (\n Object.keys(ON_CONTROL) as Array<keyof ControlLevelProps>\n ).filter(prop => options[prop]);\n if (held.length === 0) return false;\n const names = held.join(' ');\n const doNothing = held.length > 1 ? 'those props do' : 'that prop does';\n let message: string;\n if (options.inline) {\n message =\n `[bestax-bulma] <${component} inline ${names}> renders no <Control> ` +\n `in inline mode, inside a <Control> or not, so ${doNothing} ` +\n `nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +\n `inline picker.`;\n } else {\n const toSet = held.map(prop => ON_CONTROL[prop]);\n const needsGlyph =\n (options.iconLeftSize || options.hasIconsLeft) &&\n !options.iconLeft &&\n options.iconLeftName === undefined &&\n options.defaultIconLeftName;\n if (needsGlyph) {\n toSet.push(\n `iconLeftName=\"${options.defaultIconLeftName}\" (its default icon)`\n );\n }\n message =\n `[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +\n `no <Control> of its own, so ${doNothing} nothing there. Set ` +\n `${listOf(toSet)} on that <Control> instead.`;\n }\n // Keyed by the message itself: the props named do not settle the advice\n // (an empty `iconLeftName` and an unset one name the same props), so two\n // calls that would advise differently must not share a key.\n warnOnce(message, message);\n return false;\n};\n\nconst FieldLabelIdContext = createContext<string | undefined>(undefined);\n\n/**\n * The id a labeled Field wants its single composed control to adopt (#495).\n * `undefined` outside a Field, in unlabeled/grouped/addons Fields, or when the\n * user took over the association with an explicit `labelProps.htmlFor`.\n * Consumed through `useFieldLabelTarget` by the single-control bases\n * (InputBase, SelectBase, TextAreaBase, and the date and time picker bases\n * unless inline, #968) and, through `useAutoLabelId`, by the convenience\n * inputs that render an input of their own (#939). Read it only through\n * those, which report what they take: a control that read it directly would\n * report nothing and lose its label (#1004), and\n * `__tests__/field-label-readers.test.ts` fails on one. Internal; not part of\n * the public API.\n */\nexport const useFieldLabelId = () => useContext(FieldLabelIdContext);\n\n/** Provider for the Field label-target id — used internally by Field. */\nexport const FieldLabelIdProvider = FieldLabelIdContext.Provider;\n\nconst FieldLabelElementIdContext = createContext<string | undefined>(undefined);\n\n/**\n * The id of a labeled Field's own `<label>`, for a group control (Radios,\n * Checkboxes, Rate, DateRangeInput and DateRangeInputBase) to point\n * `aria-labelledby` at, since a group cannot take the label's `htmlFor`\n * (#939, #1005). Set under the same conditions as\n * {@link useFieldLabelId}. Consumed through `useAutoLabelledBy`.\n * Internal; not part of the public API.\n */\nexport const useFieldLabelElementId = () =>\n useContext(FieldLabelElementIdContext);\n\n/** Provider for the Field label's own id, used internally by Field. */\nexport const FieldLabelElementIdProvider = FieldLabelElementIdContext.Provider;\n\n/** The nearest labeled Field's `<label>`, as {@link useFieldLabelFor} reads it. */\nexport interface FieldLabelFor {\n /** What the label's `htmlFor` points at, generated or the caller's. */\n htmlFor?: string;\n /** The id the label renders with, the caller's or a generated one, if any. */\n id?: string;\n}\n\nconst FieldLabelForContext = createContext<FieldLabelFor>({});\n\n/**\n * The nearest labeled Field's `<label>`: what it points `htmlFor` at, the id\n * it generated or the caller's own `labelProps.htmlFor`, and the id the label\n * renders with, if it has one. An unlabeled Field passes both through\n * together, so a control in the inner Field of a horizontal layout can tell\n * the outer label names it, and point at that label by its id, as a range\n * Slider's thumbs (#981) and Autocomplete's and Taginput's suggestion lists\n * (#998, #1082) do. The\n * `htmlFor` is only ever compared with a control's own id, never adopted, so\n * it hands out no id for a control. It keeps the generated id after the\n * label drops a `for` nothing took (#1004), which no control's id matches\n * then, so the value does not change under the content that reported it.\n * The `id` is set only while that label renders with it. Unlike\n * {@link useFieldLabelId}, it is set in a `grouped` or `hasAddons` Field\n * too, so a label wired by hand names the control its `for` picks in those\n * rows as well. Consumed through `useAutoLabelId`.\n * Internal; not part of the public API.\n */\nexport const useFieldLabelFor = () => useContext(FieldLabelForContext);\n\n/** Provider for the Field label's `htmlFor` target and id, used internally by Field. */\nexport const FieldLabelForProvider = FieldLabelForContext.Provider;\n\n/**\n * Records that one piece of a labeled Field's content took the label's\n * generated id, and returns the function that forgets it.\n */\ntype FieldLabelForReport = () => () => void;\n\nconst FieldLabelForReportContext = createContext<\n FieldLabelForReport | undefined\n>(undefined);\n\n/**\n * Tells the nearest labeled Field that this content took its generated id\n * while `takesFor` is true (#1004). Once the Field's content has mounted, its\n * label keeps the generated `for` only while something has said so, so the\n * label renders none over a group, a `Checkbox`, `Radio` or `Switch`, an\n * inner `Field`, an input with an `id` of its own, markup of your own, or\n * nothing at all. Anything that puts the id from {@link useFieldLabelId} on\n * an element has to say so, or the label drops the `for` that names it, and\n * content that takes no id has nothing to do. Set under the same conditions\n * as {@link useFieldLabelId}, so a label wired by hand and a `grouped` or\n * `hasAddons` Field hear nothing. It reports from a layout effect, which a\n * server never runs and a hydrating client runs only after its first render,\n * so that render writes the `for` the way the server did.\n * Internal; not part of the public API.\n */\nexport const useReportFieldLabelFor = (takesFor: boolean): void => {\n const report = useContext(FieldLabelForReportContext);\n useClientLayoutEffect(\n () => (takesFor ? report?.() : undefined),\n [report, takesFor]\n );\n};\n\n/** Provider for the labeled Field's report function, used internally by Field. */\nexport const FieldLabelForReportProvider = FieldLabelForReportContext.Provider;\n\n/**\n * The id a control with one input of its own renders with: the caller's\n * `id` when one arrived, otherwise the one a labeled Field offers (#495),\n * unless it has no input to put it on (`hasInput: false`, as in an `inline`\n * picker). It also tells that Field when it took the Field's id, through\n * {@link useReportFieldLabelFor}, so the label keeps its `for` (#1004). Used\n * by the single-control bases. Internal; not part of the public API.\n */\nexport const useFieldLabelTarget = (\n id: string | undefined,\n hasInput = true\n): string | undefined => {\n const fieldLabelId = useFieldLabelId();\n const target = id ?? (hasInput ? fieldLabelId : undefined);\n useReportFieldLabelFor(fieldLabelId !== undefined && target === fieldLabelId);\n return target;\n};\n\n/**\n * Shape of the Radios group context. The group provides:\n * - `name`: shared form field name (Stage 1)\n * - `value`: currently-selected radio value (Stage 2 — group-managed selection)\n * - `onChange`: dispatched by child Radios when clicked (Stage 2)\n *\n * Group sets `value`/`onChange` only when actively managing selection\n * (i.e., the user passed `value`, `defaultValue`, or `onChange` to `<Radios>`).\n * When only `name` is provided, the group is in \"name-only\" mode and child\n * Radios manage their own checked state independently.\n */\nexport interface RadiosGroupContextValue {\n name?: string;\n value?: string;\n onChange?: (value: string) => void;\n}\n\n/**\n * Shape of the Checkboxes group context. Like Radios but with array semantics\n * for multi-select.\n */\nexport interface CheckboxesGroupContextValue {\n name?: string;\n value?: string[];\n onChange?: (values: string[]) => void;\n}\n\nconst RadiosContext = createContext<RadiosGroupContextValue | undefined>(\n undefined\n);\nconst CheckboxesContext = createContext<\n CheckboxesGroupContextValue | undefined\n>(undefined);\n\n/**\n * Hook to read the full surrounding `<Radios>` group context (name + selection).\n * Returns `undefined` when not inside a `<Radios>` group.\n */\nexport const useRadiosGroup = () => useContext(RadiosContext);\n\n/**\n * Hook to read the full surrounding `<Checkboxes>` group context.\n * Returns `undefined` when not inside a `<Checkboxes>` group.\n */\nexport const useCheckboxesGroup = () => useContext(CheckboxesContext);\n\n/**\n * Hook to read just the shared `name` from a surrounding `<Radios>` group.\n * Child `<Radio>` components fall back to this when no local `name` prop is set.\n * Returns `undefined` outside of a `<Radios>` group.\n */\nexport const useRadiosName = () => useRadiosGroup()?.name;\n\n/**\n * Hook to read just the shared `name` from a surrounding `<Checkboxes>` group.\n * Returns `undefined` outside of a `<Checkboxes>` group.\n */\nexport const useCheckboxesName = () => useCheckboxesGroup()?.name;\n\n/** Provider for the Radios group context — used internally by Radios. */\nexport const RadiosProvider = RadiosContext.Provider;\n\n/** Provider for the Checkboxes group context — used internally by Checkboxes. */\nexport const CheckboxesProvider = CheckboxesContext.Provider;\n\n// --- Backward-compat aliases (Stage 1 names) -----------------------------\n// Internal callers may still import these. Both still work because they refer\n// to the same providers/hooks under the new shape.\n\n/** @deprecated Use `RadiosProvider` (provides {name, value, onChange}). */\nexport const RadiosNameProvider = RadiosProvider;\n\n/** @deprecated Use `CheckboxesProvider` (provides {name, value, onChange}). */\nexport const CheckboxesNameProvider = CheckboxesProvider;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FormContext.js","sources":["../../../src/form/FormContext.tsx"],"sourcesContent":["import React, { createContext, useContext } from 'react';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport type { ControlBaseProps } from './Control';\n\nconst FieldContext = createContext(false);\nconst ControlContext = createContext(false);\n\n/**\n * Hook to detect if the component is inside a Field wrapper.\n * Form components use this to skip rendering their own Field.\n */\nexport const useInsideField = () => useContext(FieldContext);\n\n/**\n * Hook to detect if the component is inside a Control wrapper.\n * Form components use this to skip rendering their own Control.\n */\nexport const useInsideControl = () => useContext(ControlContext);\n\n/** Provider for Field context — used internally by Field component. */\nexport const FieldProvider = FieldContext.Provider;\n\n/** Provider for Control context — used internally by Control component. */\nexport const ControlProvider = ControlContext.Provider;\n\nconst FieldsetDisabledContext = createContext(false);\n\n/**\n * An input's `disabled`: its own prop when the caller set one, otherwise\n * whether a disabled `Fieldset` holds it. A `<fieldset disabled>` disables the\n * native controls inside on its own, and this carries the same state to what\n * the browser doesn't reach, such as Rate's stars, the time wheels, and the\n * `is-disabled` classes. The content of a Fieldset's legend reads the state\n * from outside that Fieldset, as the browser leaves the first legend's\n * controls enabled. Internal; not part of the public API.\n */\nexport const useFieldsetDisabled = (disabled: boolean | undefined): boolean => {\n const inherited = useContext(FieldsetDisabledContext);\n return disabled ?? inherited;\n};\n\n/** Provider for a Fieldset's disabled state, used internally by Fieldset. */\nexport const FieldsetDisabledProvider = FieldsetDisabledContext.Provider;\n\n/** The wrapper props that change what its own `Field` renders. */\ninterface FieldShapingProps {\n /** Rendered as the `Field`'s label. */\n label?: React.ReactNode;\n /** Rendered as help text after the widget. */\n message?: React.ReactNode;\n /** Lays the `Field` out horizontally. */\n horizontal?: boolean;\n /** Set on the `Field` as its class. */\n fieldClassName?: string;\n}\n\ninterface OwnFieldOptions extends FieldShapingProps {\n /** `useInsideField()` as the wrapper read it. */\n insideField: boolean;\n /** `useInsideControl()` as the wrapper read it. */\n insideControl: boolean;\n}\n\n// Each Field-shaping prop, with what takes its place on a `Field` the caller\n// wraps around the `Control`. `message` needs nothing there, because the\n// wrapper still renders it inside `Field > Control`. The other Field-level\n// props (`labelSize`, `labelProps`, `messageColor`) change nothing unless a\n// `label` or `message` renders, so on their own they keep no `Field`.\n// `bare-control.test.tsx` holds this list to what each wrapper hands its\n// `Field` and to which props change its markup, so a new Field-level prop\n// fails there until it is sorted into one group or the other.\nconst FIELD_SHAPING: ReadonlyArray<\n [keyof FieldShapingProps, string | undefined]\n> = [\n ['label', 'label'],\n ['message', undefined],\n ['horizontal', 'horizontal'],\n ['fieldClassName', 'className (for fieldClassName)'],\n];\n\n/** `a`, `a and b`, `a, b and c`. */\nconst listOf = (items: string[]): string =>\n items.length > 1\n ? `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`\n : items[0];\n\n/**\n * Whether a convenience form wrapper renders a `Field` of its own (#905).\n * Never inside an outer `Field`. Inside a `Control` with no `Field` around\n * it, a `Field` of its own would put a `.field` inside the `.control`, where\n * Bulma's control styles break (its icon rules are sibling selectors on the\n * input). So it renders one there only when a prop that shapes the `Field`'s\n * markup is set (`label`, `message`, `horizontal`, `fieldClassName`), keeping\n * the markup it always had, and warns in development that the fix is a\n * `Field` around that `Control`. A prop counts when it is truthy, the same\n * test that decides whether it changes the `Field`'s output.\n * Internal; not part of the public API.\n */\nexport const rendersOwnField = (\n component: string,\n options: OwnFieldOptions\n): boolean => {\n if (options.insideField) return false;\n if (!options.insideControl) return true;\n const held = FIELD_SHAPING.filter(([prop]) => options[prop]);\n if (held.length === 0) return false;\n const names = held.map(([prop]) => prop);\n const moved = held\n .map(([, onField]) => onField)\n .filter((onField): onField is string => onField !== undefined);\n warnOnce(\n `${component}:Field-in-bare-Control:${names.join('+')}`,\n `[bestax-bulma] <${component} ${names.join(' ')}> inside a <Control> ` +\n `with no <Field> around it renders its own <Field> for ` +\n `${names.length > 1 ? 'those props' : 'that prop'}, which puts a ` +\n `.field inside the .control, a nesting Bulma's styles are not ` +\n `written for. Wrap the <Control> in a <Field> instead` +\n (moved.length > 0 ? `, and set ${listOf(moved)} on that <Field>.` : '.')\n );\n return true;\n};\n\n/** The props a convenience form wrapper hands its own `Control`. Internal. */\nexport interface ControlLevelProps {\n /** Shows the `Control`'s loading spinner. */\n isLoading?: boolean;\n /** The `Control`'s left icon. */\n iconLeft?: ControlBaseProps['iconLeft'];\n /** Shortcut for the left icon's name. */\n iconLeftName?: string;\n /** Shortcut for the left icon's size. */\n iconLeftSize?: ControlBaseProps['iconLeftSize'];\n /** The `Control`'s right icon. */\n iconRight?: ControlBaseProps['iconRight'];\n /** Shortcut for the right icon's name. */\n iconRightName?: string;\n /** Shortcut for the right icon's size. */\n iconRightSize?: ControlBaseProps['iconRightSize'];\n /** Reserves the `Control`'s left icon space. */\n hasIconsLeft?: boolean;\n /** Reserves the `Control`'s right icon space. */\n hasIconsRight?: boolean;\n /** Expands the `Control`. */\n isExpanded?: boolean;\n /** Set on the `Control` as its `size`. */\n controlSize?: ControlBaseProps['size'];\n /** Set on the `Control` as its class. */\n controlClassName?: string;\n}\n\ninterface OwnControlOptions extends ControlLevelProps {\n /** `useInsideControl()` as the wrapper read it. */\n insideControl: boolean;\n /** A picker's `inline`, which renders no `Control` inside one or out. */\n inline?: boolean;\n /**\n * The left glyph the wrapper's own `Control` shows when the caller sets no\n * `iconLeftName`; read only then. `bare-control.test.tsx` reads that glyph\n * from what each wrapper hands its own `Control`, so a wrapper that gains\n * one fails there until it passes it here.\n */\n defaultIconLeftName?: string;\n}\n\n// Each Control-level prop, with what takes its place on the `Control` the\n// caller wrapped around the wrapper. `bare-control.test.tsx` holds this to\n// what each wrapper hands its own `Control`, so a new Control-level prop\n// fails there until it is added here and passed to `rendersOwnControl`.\nconst ON_CONTROL: Record<keyof ControlLevelProps, string> = {\n isLoading: 'isLoading',\n iconLeft: 'iconLeft',\n iconLeftName: 'iconLeftName',\n iconLeftSize: 'iconLeftSize',\n iconRight: 'iconRight',\n iconRightName: 'iconRightName',\n iconRightSize: 'iconRightSize',\n hasIconsLeft: 'hasIconsLeft',\n hasIconsRight: 'hasIconsRight',\n isExpanded: 'isExpanded',\n controlSize: 'size (for controlSize)',\n controlClassName: 'className (for controlClassName)',\n};\n\n/**\n * Whether a convenience form wrapper renders a `Control` of its own (#921):\n * only outside one. Inside a `Control` it renders none, so the props it\n * would have handed its own do nothing there, and it warns in development,\n * naming them and saying to set them on that `Control`. An `inline` picker\n * renders none in either place, so it drops them inside a `Control` or not,\n * and its warning says so without pointing at a `Control`. A wrapper passes\n * only the props it hands its own `Control`, as the caller gave them, so a\n * default the wrapper fills in itself never warns. A prop counts when it is\n * truthy, since a falsy one would change nothing on a `Control` either.\n * `iconLeftSize` and `hasIconsLeft` show nothing on a `Control` without a\n * glyph, so when they move and the caller chose no left icon, the advice\n * names the wrapper's default glyph too, and following it draws what the\n * wrapper's own `Control` did.\n * Internal; not part of the public API.\n */\nexport const rendersOwnControl = (\n component: string,\n options: OwnControlOptions\n): boolean => {\n if (!options.inline && !options.insideControl) return true;\n const held = (\n Object.keys(ON_CONTROL) as Array<keyof ControlLevelProps>\n ).filter(prop => options[prop]);\n if (held.length === 0) return false;\n const names = held.join(' ');\n const doNothing = held.length > 1 ? 'those props do' : 'that prop does';\n let message: string;\n if (options.inline) {\n message =\n `[bestax-bulma] <${component} inline ${names}> renders no <Control> ` +\n `in inline mode, inside a <Control> or not, so ${doNothing} ` +\n `nothing. Leave ${held.length > 1 ? 'them' : 'it'} out of an ` +\n `inline picker.`;\n } else {\n const toSet = held.map(prop => ON_CONTROL[prop]);\n const needsGlyph =\n (options.iconLeftSize || options.hasIconsLeft) &&\n !options.iconLeft &&\n options.iconLeftName === undefined &&\n options.defaultIconLeftName;\n if (needsGlyph) {\n toSet.push(\n `iconLeftName=\"${options.defaultIconLeftName}\" (its default icon)`\n );\n }\n message =\n `[bestax-bulma] <${component} ${names}> inside a <Control> renders ` +\n `no <Control> of its own, so ${doNothing} nothing there. Set ` +\n `${listOf(toSet)} on that <Control> instead.`;\n }\n // Keyed by the message itself: the props named do not settle the advice\n // (an empty `iconLeftName` and an unset one name the same props), so two\n // calls that would advise differently must not share a key.\n warnOnce(message, message);\n return false;\n};\n\nconst FieldLabelIdContext = createContext<string | undefined>(undefined);\n\n/**\n * The id a labeled Field wants its single composed control to adopt (#495).\n * `undefined` outside a Field, in unlabeled/grouped/addons Fields, or when the\n * user took over the association with an explicit `labelProps.htmlFor`.\n * Consumed through `useFieldLabelTarget` by the single-control bases\n * (InputBase, SelectBase, TextAreaBase, and the date and time picker bases\n * unless inline, #968) and, through `useAutoLabelId`, by the convenience\n * inputs that render an input of their own (#939). Read it only through\n * those, which report what they take: a control that read it directly would\n * report nothing and lose its label (#1004), and\n * `__tests__/field-label-readers.test.ts` fails on one. Internal; not part of\n * the public API.\n */\nexport const useFieldLabelId = () => useContext(FieldLabelIdContext);\n\n/** Provider for the Field label-target id — used internally by Field. */\nexport const FieldLabelIdProvider = FieldLabelIdContext.Provider;\n\nconst FieldLabelElementIdContext = createContext<string | undefined>(undefined);\n\n/**\n * The id of a labeled Field's own `<label>`, for a group control (Radios,\n * Checkboxes, Rate, DateRangeInput and DateRangeInputBase) to point\n * `aria-labelledby` at, since a group cannot take the label's `htmlFor`\n * (#939, #1005). Set under the same conditions as\n * {@link useFieldLabelId}. Consumed through `useAutoLabelledBy`.\n * Internal; not part of the public API.\n */\nexport const useFieldLabelElementId = () =>\n useContext(FieldLabelElementIdContext);\n\n/** Provider for the Field label's own id, used internally by Field. */\nexport const FieldLabelElementIdProvider = FieldLabelElementIdContext.Provider;\n\n/** The nearest labeled Field's `<label>`, as {@link useFieldLabelFor} reads it. */\nexport interface FieldLabelFor {\n /** What the label's `htmlFor` points at, generated or the caller's. */\n htmlFor?: string;\n /** The id the label renders with, the caller's or a generated one, if any. */\n id?: string;\n}\n\nconst FieldLabelForContext = createContext<FieldLabelFor>({});\n\n/**\n * The nearest labeled Field's `<label>`: what it points `htmlFor` at, the id\n * it generated or the caller's own `labelProps.htmlFor`, and the id the label\n * renders with, if it has one. An unlabeled Field passes both through\n * together, so a control in the inner Field of a horizontal layout can tell\n * the outer label names it, and point at that label by its id, as a range\n * Slider's thumbs (#981) and Autocomplete's and Taginput's suggestion lists\n * (#998, #1082) do. The\n * `htmlFor` is only ever compared with a control's own id, never adopted, so\n * it hands out no id for a control. It keeps the generated id after the\n * label drops a `for` nothing took (#1004), which no control's id matches\n * then, so the value does not change under the content that reported it.\n * The `id` is set only while that label renders with it. Unlike\n * {@link useFieldLabelId}, it is set in a `grouped` or `hasAddons` Field\n * too, so a label wired by hand names the control its `for` picks in those\n * rows as well. Consumed through `useAutoLabelId`.\n * Internal; not part of the public API.\n */\nexport const useFieldLabelFor = () => useContext(FieldLabelForContext);\n\n/** Provider for the Field label's `htmlFor` target and id, used internally by Field. */\nexport const FieldLabelForProvider = FieldLabelForContext.Provider;\n\n/**\n * Records that one piece of a labeled Field's content took the label's\n * generated id, and returns the function that forgets it.\n */\ntype FieldLabelForReport = () => () => void;\n\nconst FieldLabelForReportContext = createContext<\n FieldLabelForReport | undefined\n>(undefined);\n\n/**\n * Tells the nearest labeled Field that this content took its generated id\n * while `takesFor` is true (#1004). Once the Field's content has mounted, its\n * label keeps the generated `for` only while something has said so, so the\n * label renders none over a group, a `Checkbox`, `Radio` or `Switch`, an\n * inner `Field`, an input with an `id` of its own, markup of your own, or\n * nothing at all. Anything that puts the id from {@link useFieldLabelId} on\n * an element has to say so, or the label drops the `for` that names it, and\n * content that takes no id has nothing to do. Set under the same conditions\n * as {@link useFieldLabelId}, so a label wired by hand and a `grouped` or\n * `hasAddons` Field hear nothing. It reports from a layout effect, which a\n * server never runs and a hydrating client runs only after its first render,\n * so that render writes the `for` the way the server did.\n * Internal; not part of the public API.\n */\nexport const useReportFieldLabelFor = (takesFor: boolean): void => {\n const report = useContext(FieldLabelForReportContext);\n useClientLayoutEffect(\n () => (takesFor ? report?.() : undefined),\n [report, takesFor]\n );\n};\n\n/** Provider for the labeled Field's report function, used internally by Field. */\nexport const FieldLabelForReportProvider = FieldLabelForReportContext.Provider;\n\n/**\n * The id a control with one input of its own renders with: the caller's\n * `id` when one arrived, otherwise the one a labeled Field offers (#495),\n * unless it has no input to put it on (`hasInput: false`, as in an `inline`\n * picker). It also tells that Field when it took the Field's id, through\n * {@link useReportFieldLabelFor}, so the label keeps its `for` (#1004). Used\n * by the single-control bases. Internal; not part of the public API.\n */\nexport const useFieldLabelTarget = (\n id: string | undefined,\n hasInput = true\n): string | undefined => {\n const fieldLabelId = useFieldLabelId();\n const target = id ?? (hasInput ? fieldLabelId : undefined);\n useReportFieldLabelFor(fieldLabelId !== undefined && target === fieldLabelId);\n return target;\n};\n\n/**\n * Shape of the Radios group context. The group provides:\n * - `name`: shared form field name (Stage 1)\n * - `value`: currently-selected radio value (Stage 2 — group-managed selection)\n * - `onChange`: dispatched by child Radios when clicked (Stage 2)\n *\n * Group sets `value`/`onChange` only when actively managing selection\n * (i.e., the user passed `value`, `defaultValue`, or `onChange` to `<Radios>`).\n * When only `name` is provided, the group is in \"name-only\" mode and child\n * Radios manage their own checked state independently.\n *\n * A group given only `onChange` manages the selection but holds no value\n * until its first change. Until then a Radio with a value shows its own\n * `defaultChecked`, still controlled, so that change doesn't flip it from\n * uncontrolled to controlled.\n */\nexport interface RadiosGroupContextValue {\n name?: string;\n /** `null` once a radio without a value was picked before the group held one, so no radio with a value shows checked. */\n value?: string | null;\n onChange?: (value: string) => void;\n /** Set with `onChange`. Tells the group a radio without a value, sharing its name, was picked. */\n pickWithoutValue?: () => void;\n}\n\n/**\n * Tells a surrounding `<Radios>` that one of its radios was picked. A radio\n * with a value hands the group that value. One without a value tells the\n * group nothing it reports, but when it shares the group's name the browser\n * has just unchecked the radio that showed checked, so the group learns the\n * pick left its radios and React doesn't check that one again, undoing the\n * pick. `Radio` and `RadioCard` call it with their effective name.\n */\nexport const pickInRadiosGroup = (\n group: RadiosGroupContextValue | undefined,\n value: string | number | readonly string[] | undefined,\n name: string | undefined\n) => {\n if (!group?.onChange) return;\n if (value !== undefined) group.onChange(String(value));\n else if (name !== undefined && name === group.name) group.pickWithoutValue!();\n};\n\n/**\n * Shape of the Checkboxes group context. Like Radios but with array semantics\n * for multi-select.\n *\n * While the group manages the selection it also sets `registerChecked` and\n * `startingValue`. A group given only `onChange` holds no value until its\n * first change, so its choices register the values they start checked with,\n * and that change starts from them rather than from an empty array.\n */\nexport interface CheckboxesGroupContextValue {\n name?: string;\n value?: string[];\n onChange?: (values: string[]) => void;\n /** Registers a value a choice starts checked with, and returns the function that takes it back. */\n registerChecked?: (value: string) => () => void;\n /** The values registered as starting checked, each once, in the order they came. */\n startingValue?: () => string[];\n}\n\n/**\n * Registers a choice with its surrounding `<Checkboxes>` while it starts\n * checked, so a group with no value yet starts its first change from the\n * choices that were checked. `Checkbox` and `CheckboxCard` call it. It does\n * nothing outside a group that manages the selection, or without a value.\n */\nexport const useRegisterChecked = (\n group: CheckboxesGroupContextValue | undefined,\n value: string | undefined,\n startsChecked: boolean\n) => {\n const register = group?.registerChecked;\n useClientLayoutEffect(() => {\n if (!register || value === undefined || !startsChecked) return;\n return register(value);\n }, [register, value, startsChecked]);\n};\n\nconst RadiosContext = createContext<RadiosGroupContextValue | undefined>(\n undefined\n);\nconst CheckboxesContext = createContext<\n CheckboxesGroupContextValue | undefined\n>(undefined);\n\n/**\n * Hook to read the full surrounding `<Radios>` group context (name + selection).\n * Returns `undefined` when not inside a `<Radios>` group.\n */\nexport const useRadiosGroup = () => useContext(RadiosContext);\n\n/**\n * Hook to read the full surrounding `<Checkboxes>` group context.\n * Returns `undefined` when not inside a `<Checkboxes>` group.\n */\nexport const useCheckboxesGroup = () => useContext(CheckboxesContext);\n\n/**\n * Hook to read just the shared `name` from a surrounding `<Radios>` group.\n * Child `<Radio>` components fall back to this when no local `name` prop is set.\n * Returns `undefined` outside of a `<Radios>` group.\n */\nexport const useRadiosName = () => useRadiosGroup()?.name;\n\n/**\n * Hook to read just the shared `name` from a surrounding `<Checkboxes>` group.\n * Returns `undefined` outside of a `<Checkboxes>` group.\n */\nexport const useCheckboxesName = () => useCheckboxesGroup()?.name;\n\n/** Provider for the Radios group context — used internally by Radios. */\nexport const RadiosProvider = RadiosContext.Provider;\n\n/** Provider for the Checkboxes group context — used internally by Checkboxes. */\nexport const CheckboxesProvider = CheckboxesContext.Provider;\n\n// --- Backward-compat aliases (Stage 1 names) -----------------------------\n// Internal callers may still import these. Both still work because they refer\n// to the same providers/hooks under the new shape.\n\n/** @deprecated Use `RadiosProvider` (provides {name, value, onChange}). */\nexport const RadiosNameProvider = RadiosProvider;\n\n/** @deprecated Use `CheckboxesProvider` (provides {name, value, onChange}). */\nexport const CheckboxesNameProvider = CheckboxesProvider;\n"],"names":[],"mappings":"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"}
|
package/dist/esm/form/Radio.js
CHANGED
|
@@ -4,7 +4,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
4
4
|
import { forwardRef, useCallback } from 'react';
|
|
5
5
|
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
6
6
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
7
|
-
import { useFieldsetDisabled, useRadiosGroup } from './FormContext.js';
|
|
7
|
+
import { useFieldsetDisabled, useRadiosGroup, pickInRadiosGroup } from './FormContext.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* The values the Radio `color` prop accepts, as a readonly tuple.
|
|
@@ -75,14 +75,19 @@ const Radio = forwardRef(({ color, size, className, children, textColor, disable
|
|
|
75
75
|
// Local props always win over the group (explicit > implicit).
|
|
76
76
|
const group = useRadiosGroup();
|
|
77
77
|
const effectiveName = name ?? group?.name;
|
|
78
|
-
// Group-managed checked state — only when the group
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
78
|
+
// Group-managed checked state — only when the group manages the selection
|
|
79
|
+
// (it was given `value`, `defaultValue` or `onChange`) AND this Radio has
|
|
80
|
+
// a value to compare against. Local `checked` always wins. A group given
|
|
81
|
+
// only `onChange` holds no value until its first change, and until then
|
|
82
|
+
// the radio shows its own `defaultChecked`, controlled all the same, so
|
|
83
|
+
// that change doesn't flip it from uncontrolled to controlled.
|
|
84
|
+
const groupManaged = group?.onChange !== undefined && value !== undefined;
|
|
82
85
|
const effectiveChecked = checked !== undefined
|
|
83
86
|
? checked
|
|
84
87
|
: groupManaged
|
|
85
|
-
? group.value
|
|
88
|
+
? group.value !== undefined
|
|
89
|
+
? group.value === value
|
|
90
|
+
: !!defaultChecked
|
|
86
91
|
: undefined;
|
|
87
92
|
// When the group manages checked, suppress local defaultChecked to avoid
|
|
88
93
|
// the controlled/uncontrolled React warning.
|
|
@@ -91,10 +96,8 @@ const Radio = forwardRef(({ color, size, className, children, textColor, disable
|
|
|
91
96
|
// Local onChange always fires.
|
|
92
97
|
onChange?.(e);
|
|
93
98
|
// Dispatch to the group additionally — keeps group state in sync.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
}, [onChange, group, value]);
|
|
99
|
+
pickInRadiosGroup(group, value, effectiveName);
|
|
100
|
+
}, [onChange, group, value, effectiveName]);
|
|
98
101
|
const mainClass = usePrefixedClassNames('styled-radio', 'radio', {
|
|
99
102
|
[`is-${color}`]: color && radioColors.includes(color),
|
|
100
103
|
[`is-${size}`]: size && radioSizes.includes(size),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Radio.js","sources":["../../../src/form/Radio.tsx"],"sourcesContent":["import React, { forwardRef, useCallback } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport {
|
|
1
|
+
{"version":3,"file":"Radio.js","sources":["../../../src/form/Radio.tsx"],"sourcesContent":["import React, { forwardRef, useCallback } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport {\n pickInRadiosGroup,\n useFieldsetDisabled,\n useRadiosGroup,\n} from './FormContext';\n\n/**\n * The values the Radio `color` prop accepts, as a readonly tuple.\n *\n * `RadioProps['color']` is typed from it, so the two list the same values.\n * Map over it to build a color picker, or check a value that arrives at\n * runtime before passing it in: the component adds no color class for a value\n * outside the tuple.\n *\n * @example\n * import { Radio, Radios, radioColors } from '@allxsmith/bestax-bulma';\n *\n * <Radios name=\"accent\" defaultValue=\"primary\">\n * {radioColors.map(color => (\n * <Radio key={color} value={color} color={color}>\n * {color}\n * </Radio>\n * ))}\n * </Radios>;\n */\nexport const radioColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n\n/**\n * The values the Radio `size` prop accepts, as a readonly tuple.\n *\n * `RadioProps['size']` is typed from it, so the two list the same values. Use\n * it to offer a size choice or to check a value that arrives at runtime: the\n * component adds no size class for a value outside the tuple. These are\n * element sizes, not the spacing scale in `validSizes`.\n *\n * @example\n * import { radioSizes } from '@allxsmith/bestax-bulma';\n *\n * type RadioSize = (typeof radioSizes)[number];\n */\nexport const radioSizes = ['small', 'normal', 'medium', 'large'] as const;\n\n/**\n * Props for the Radio component.\n *\n * @helperProps Helper props go on the outer `<label>`, with `className`. The\n * other attributes go on the `<input>`.\n */\nexport interface RadioProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'size' | 'type' | 'color'\n >,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size'> {\n /** Color of the radio button. */\n color?: (typeof radioColors)[number];\n /** Size of the radio button. */\n size?: (typeof radioSizes)[number];\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Whether the radio is disabled. */\n disabled?: boolean;\n /**\n * Additional CSS classes, on the outer `<label>`. That's the element that\n * declares the `--bulma-radio-*` variables, so a class is how to set one:\n * `style` and the other input attributes go to the `<input>`. Name\n * `.styled-radio` beside your class in the selector, so it beats the\n * label's own declaration whatever order the stylesheets load in. A\n * `color` prop declares the active color again on\n * `.styled-radio.is-<color>`, which ties that selector, so leave `color`\n * unset when a class sets it.\n */\n className?: string;\n /** The label/content for the radio. */\n children?: React.ReactNode;\n}\n\n/**\n * The `Radio` component provides a Bulma-styled radio button input with flexible labels and helper classes.\n *\n * @function\n * @param {RadioProps} props - Props for the Radio component.\n * @returns {JSX.Element} The rendered radio element.\n *\n * @example\n * // Basic radio\n * <Radio name=\"option\">Option A</Radio>\n *\n * @example\n * // Inside a group\n * <Radios name=\"color\" defaultValue=\"red\">\n * <Radio value=\"red\">Red</Radio>\n * <Radio value=\"blue\">Blue</Radio>\n * </Radios>\n */\nexport const Radio = forwardRef<HTMLInputElement, RadioProps>(\n (\n {\n color,\n size,\n className,\n children,\n textColor,\n disabled: disabledProp,\n name,\n value,\n checked,\n defaultChecked,\n onChange,\n ...props\n },\n ref\n ) => {\n const disabled = useFieldsetDisabled(disabledProp);\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n // Inherit name + selection state from a surrounding <Radios> group.\n // Local props always win over the group (explicit > implicit).\n const group = useRadiosGroup();\n const effectiveName = name ?? group?.name;\n\n // Group-managed checked state — only when the group manages the selection\n // (it was given `value`, `defaultValue` or `onChange`) AND this Radio has\n // a value to compare against. Local `checked` always wins. A group given\n // only `onChange` holds no value until its first change, and until then\n // the radio shows its own `defaultChecked`, controlled all the same, so\n // that change doesn't flip it from uncontrolled to controlled.\n const groupManaged = group?.onChange !== undefined && value !== undefined;\n const effectiveChecked =\n checked !== undefined\n ? checked\n : groupManaged\n ? group!.value !== undefined\n ? group!.value === value\n : !!defaultChecked\n : undefined;\n // When the group manages checked, suppress local defaultChecked to avoid\n // the controlled/uncontrolled React warning.\n const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n // Local onChange always fires.\n onChange?.(e);\n // Dispatch to the group additionally — keeps group state in sync.\n pickInRadiosGroup(group, value, effectiveName);\n },\n [onChange, group, value, effectiveName]\n );\n\n const mainClass = usePrefixedClassNames('styled-radio', 'radio', {\n [`is-${color}`]: color && radioColors.includes(color),\n [`is-${size}`]: size && radioSizes.includes(size),\n });\n const radioClass = classNames(mainClass, bulmaHelperClasses, className);\n const checkClass = usePrefixedClassNames('check');\n const controlLabelClass = usePrefixedClassNames('control-label');\n\n return (\n <label className={radioClass}>\n <input\n ref={ref}\n type=\"radio\"\n disabled={disabled}\n name={effectiveName}\n value={value}\n checked={effectiveChecked}\n defaultChecked={effectiveDefaultChecked}\n onChange={handleChange}\n {...rest}\n />\n <span className={checkClass} />\n {children && <span className={controlLabelClass}>{children}</span>}\n </label>\n );\n }\n);\n\nRadio.displayName = 'Radio';\n\nexport default Radio;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
4
4
|
import { forwardRef, useCallback, useId } from 'react';
|
|
5
5
|
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
6
6
|
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
7
|
-
import { useFieldsetDisabled, useRadiosGroup } from './FormContext.js';
|
|
7
|
+
import { useFieldsetDisabled, useRadiosGroup, pickInRadiosGroup } from './FormContext.js';
|
|
8
8
|
|
|
9
9
|
// The colors `Radio` takes, as `radioColors` lists them. Declared here as
|
|
10
10
|
// well, so the API page can read them off this module, and a type test holds
|
|
@@ -44,22 +44,24 @@ const RadioCard = forwardRef(({ title, description, children, hasIndicator = tru
|
|
|
44
44
|
});
|
|
45
45
|
// A surrounding <Radios> hands down its name and, when it manages the
|
|
46
46
|
// selection, whether this card is the one picked. The card's own props
|
|
47
|
-
// win, as they do on Radio.
|
|
47
|
+
// win, as they do on Radio. A group given only `onChange` holds no value
|
|
48
|
+
// until its first change, and until then the card shows its own
|
|
49
|
+
// `defaultChecked`, controlled all the same, as Radio does.
|
|
48
50
|
const group = useRadiosGroup();
|
|
49
51
|
const effectiveName = name ?? group?.name;
|
|
50
|
-
const groupManaged = group?.
|
|
52
|
+
const groupManaged = group?.onChange !== undefined && value !== undefined;
|
|
51
53
|
const effectiveChecked = checked !== undefined
|
|
52
54
|
? checked
|
|
53
55
|
: groupManaged
|
|
54
|
-
? group.value
|
|
56
|
+
? group.value !== undefined
|
|
57
|
+
? group.value === value
|
|
58
|
+
: !!defaultChecked
|
|
55
59
|
: undefined;
|
|
56
60
|
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
57
61
|
const handleChange = useCallback((e) => {
|
|
58
62
|
onChange?.(e);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
}, [onChange, group, value]);
|
|
63
|
+
pickInRadiosGroup(group, value, effectiveName);
|
|
64
|
+
}, [onChange, group, value, effectiveName]);
|
|
63
65
|
// The title names the input and the description describes it. A name
|
|
64
66
|
// passed in wins, and without one of either the label's own text names
|
|
65
67
|
// the input, description included, so it isn't described again.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RadioCard.js","sources":["../../../src/form/RadioCard.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useId } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useFieldsetDisabled, useRadiosGroup } from './FormContext';\n\n// The colors `Radio` takes, as `radioColors` lists them. Declared here as\n// well, so the API page can read them off this module, and a type test holds\n// the two lists to each other.\nconst radioCardColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n\n/**\n * Props for the RadioCard component.\n *\n * @helperProps Helper props go on the outer `<label>`, the `.card`, with\n * `className`. The other attributes go on the `<input>`.\n * @extraProp {string} [name] - The form field name, which the cards of one choice share, so the arrow keys move between them. Inside `Radios`, the group's `name` when this is unset.\n * @extraProp {string} [value] - What a form sends when this card is the one picked. Inside a `Radios` group that takes `value`, `defaultValue` or `onChange`, the card is checked while the group's value is this.\n */\nexport interface RadioCardProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'size' | 'type' | 'color' | 'title'\n >,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size'> {\n /**\n * What the option is, such as \"Express\". It names the radio, so a screen\n * reader says it with the state and its place in the group. Without it,\n * the card's whole text names the radio, as a label's text does.\n */\n title?: React.ReactNode;\n /**\n * A line or two about the option, read after the name and the state. It's\n * the radio's description when `title`, `aria-label` or `aria-labelledby`\n * names it. Without one of those it's part of the name already, so it\n * isn't read twice.\n */\n description?: React.ReactNode;\n /**\n * Anything else in the card, after the description, such as a price. A\n * label holds phrasing content only, so spans, images and links: a link\n * works and doesn't pick the card, but a button or another input can't go\n * inside a label at all. It isn't read when the radio takes focus, so put\n * what a screen reader should hear in `title` and `description`.\n */\n children?: React.ReactNode;\n /**\n * Shows the radio at the start of the card. Without it, the card's edge\n * alone shows the selection, and it's thicker when picked, so the\n * selection never rests on color.\n */\n hasIndicator?: boolean;\n /** The color of the radio and of a picked card's edge. */\n color?: (typeof radioCardColors)[number];\n /**\n * Text color helper, on the card. The title and the children take it, and\n * the description keeps its own color.\n */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Whether the card is disabled. A disabled card dims, takes no clicks, and the arrow keys skip it. */\n disabled?: boolean;\n /**\n * Additional CSS classes, on the outer `<label>`, the card. That's the\n * element that declares the `--bulma-radio-card-*` variables, and Bulma's\n * `--bulma-card-*` ones, so a class is how to set one: `style` and the\n * other input attributes go to the `<input>`. Name `.radio-card` beside\n * your class in the selector, so it beats the card's own declaration\n * whatever order the stylesheets load in. A `color` prop declares the\n * active color again on `.radio-card.is-<color>`, which ties that\n * selector, so leave `color` unset when a class sets it.\n */\n className?: string;\n}\n\n/**\n * The `RadioCard` component is a radio button drawn as a card, with a title and a description, picked wherever you click it.\n *\n * It's a `<label>` around a real radio, so it sends its `value` with a form,\n * and the cards that share a `name` are one choice the arrow keys move\n * through. The title names the radio and the description describes it.\n * Inside `Radios` it takes the group's name and selection, as `Radio` does.\n *\n * @function\n * @param {RadioCardProps} props - Props for the RadioCard component.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the `<input>`.\n * @returns {JSX.Element} The rendered card.\n *\n * @example\n * <Radios label=\"Shipping\" name=\"shipping\" defaultValue=\"standard\">\n * <RadioCard value=\"standard\" title=\"Standard\" description=\"Arrives in 4 to 6 days.\" />\n * <RadioCard value=\"express\" title=\"Express\" description=\"Arrives tomorrow.\" />\n * </Radios>\n */\nexport const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(\n (\n {\n title,\n description,\n children,\n hasIndicator = true,\n color,\n className,\n textColor,\n disabled: disabledProp,\n name,\n value,\n checked,\n defaultChecked,\n onChange,\n ...props\n },\n ref\n ) => {\n const disabled = useFieldsetDisabled(disabledProp);\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n // A surrounding <Radios> hands down its name and, when it manages the\n // selection, whether this card is the one picked. The card's own props\n // win, as they do on Radio.\n const group = useRadiosGroup();\n const effectiveName = name ?? group?.name;\n const groupManaged = group?.value !== undefined && value !== undefined;\n const effectiveChecked =\n checked !== undefined\n ? checked\n : groupManaged\n ? group!.value === value\n : undefined;\n const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(e);\n if (group?.onChange && value !== undefined) {\n group.onChange(String(value));\n }\n },\n [onChange, group, value]\n );\n\n // The title names the input and the description describes it. A name\n // passed in wins, and without one of either the label's own text names\n // the input, description included, so it isn't described again.\n const titleId = useId();\n const descriptionId = useId();\n const ariaLabel = rest['aria-label'];\n const ariaLabelledBy =\n rest['aria-labelledby'] || (!ariaLabel && title ? titleId : undefined);\n const ariaDescribedBy =\n rest['aria-describedby'] ||\n (description && (ariaLabel || ariaLabelledBy)\n ? descriptionId\n : undefined);\n\n const cardClass = usePrefixedClassNames('radio-card', 'card', {\n [`is-${color}`]: color && radioCardColors.includes(color),\n });\n const contentClass = usePrefixedClassNames(\n 'card-content',\n 'radio-card-content'\n );\n const checkClass = usePrefixedClassNames('radio-card-check');\n const bodyClass = usePrefixedClassNames('radio-card-body');\n const titleClass = usePrefixedClassNames('radio-card-title');\n const descriptionClass = usePrefixedClassNames('radio-card-description');\n\n return (\n <label className={classNames(cardClass, bulmaHelperClasses, className)}>\n <input\n ref={ref}\n type=\"radio\"\n disabled={disabled}\n name={effectiveName}\n value={value}\n checked={effectiveChecked}\n defaultChecked={effectiveDefaultChecked}\n onChange={handleChange}\n {...rest}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n />\n <span className={contentClass}>\n {hasIndicator && <span className={checkClass} />}\n <span className={bodyClass}>\n {title ? (\n <span id={titleId} className={titleClass}>\n {title}\n </span>\n ) : null}\n {description ? (\n <span id={descriptionId} className={descriptionClass}>\n {description}\n </span>\n ) : null}\n {children}\n </span>\n </span>\n </label>\n );\n }\n);\n\nRadioCard.displayName = 'RadioCard';\n\nexport default RadioCard;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RadioCard.js","sources":["../../../src/form/RadioCard.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useId } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport {\n pickInRadiosGroup,\n useFieldsetDisabled,\n useRadiosGroup,\n} from './FormContext';\n\n// The colors `Radio` takes, as `radioColors` lists them. Declared here as\n// well, so the API page can read them off this module, and a type test holds\n// the two lists to each other.\nconst radioCardColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n\n/**\n * Props for the RadioCard component.\n *\n * @helperProps Helper props go on the outer `<label>`, the `.card`, with\n * `className`. The other attributes go on the `<input>`.\n * @extraProp {string} [name] - The form field name, which the cards of one choice share, so the arrow keys move between them. Inside `Radios`, the group's `name` when this is unset.\n * @extraProp {string} [value] - What a form sends when this card is the one picked. Inside a `Radios` group that takes `value`, `defaultValue` or `onChange`, the card is checked while the group's value is this. A group given only `onChange` starts from the card with `defaultChecked`.\n */\nexport interface RadioCardProps\n extends\n Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'size' | 'type' | 'color' | 'title'\n >,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor' | 'size'> {\n /**\n * What the option is, such as \"Express\". It names the radio, so a screen\n * reader says it with the state and its place in the group. Without it,\n * the card's whole text names the radio, as a label's text does.\n */\n title?: React.ReactNode;\n /**\n * A line or two about the option, read after the name and the state. It's\n * the radio's description when `title`, `aria-label` or `aria-labelledby`\n * names it. Without one of those it's part of the name already, so it\n * isn't read twice.\n */\n description?: React.ReactNode;\n /**\n * Anything else in the card, after the description, such as a price. A\n * label holds phrasing content only, so spans, images and links: a link\n * works and doesn't pick the card, but a button or another input can't go\n * inside a label at all. It isn't read when the radio takes focus, so put\n * what a screen reader should hear in `title` and `description`.\n */\n children?: React.ReactNode;\n /**\n * Shows the radio at the start of the card. Without it, the card's edge\n * alone shows the selection, and it's thicker when picked, so the\n * selection never rests on color.\n */\n hasIndicator?: boolean;\n /** The color of the radio and of a picked card's edge. */\n color?: (typeof radioCardColors)[number];\n /**\n * Text color helper, on the card. The title and the children take it, and\n * the description keeps its own color.\n */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Whether the card is disabled. A disabled card dims, takes no clicks, and the arrow keys skip it. */\n disabled?: boolean;\n /**\n * Additional CSS classes, on the outer `<label>`, the card. That's the\n * element that declares the `--bulma-radio-card-*` variables, and Bulma's\n * `--bulma-card-*` ones, so a class is how to set one: `style` and the\n * other input attributes go to the `<input>`. Name `.radio-card` beside\n * your class in the selector, so it beats the card's own declaration\n * whatever order the stylesheets load in. A `color` prop declares the\n * active color again on `.radio-card.is-<color>`, which ties that\n * selector, so leave `color` unset when a class sets it.\n */\n className?: string;\n}\n\n/**\n * The `RadioCard` component is a radio button drawn as a card, with a title and a description, picked wherever you click it.\n *\n * It's a `<label>` around a real radio, so it sends its `value` with a form,\n * and the cards that share a `name` are one choice the arrow keys move\n * through. The title names the radio and the description describes it.\n * Inside `Radios` it takes the group's name and selection, as `Radio` does.\n *\n * @function\n * @param {RadioCardProps} props - Props for the RadioCard component.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the `<input>`.\n * @returns {JSX.Element} The rendered card.\n *\n * @example\n * <Radios label=\"Shipping\" name=\"shipping\" defaultValue=\"standard\">\n * <RadioCard value=\"standard\" title=\"Standard\" description=\"Arrives in 4 to 6 days.\" />\n * <RadioCard value=\"express\" title=\"Express\" description=\"Arrives tomorrow.\" />\n * </Radios>\n */\nexport const RadioCard = forwardRef<HTMLInputElement, RadioCardProps>(\n (\n {\n title,\n description,\n children,\n hasIndicator = true,\n color,\n className,\n textColor,\n disabled: disabledProp,\n name,\n value,\n checked,\n defaultChecked,\n onChange,\n ...props\n },\n ref\n ) => {\n const disabled = useFieldsetDisabled(disabledProp);\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n // A surrounding <Radios> hands down its name and, when it manages the\n // selection, whether this card is the one picked. The card's own props\n // win, as they do on Radio. A group given only `onChange` holds no value\n // until its first change, and until then the card shows its own\n // `defaultChecked`, controlled all the same, as Radio does.\n const group = useRadiosGroup();\n const effectiveName = name ?? group?.name;\n const groupManaged = group?.onChange !== undefined && value !== undefined;\n const effectiveChecked =\n checked !== undefined\n ? checked\n : groupManaged\n ? group!.value !== undefined\n ? group!.value === value\n : !!defaultChecked\n : undefined;\n const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange?.(e);\n pickInRadiosGroup(group, value, effectiveName);\n },\n [onChange, group, value, effectiveName]\n );\n\n // The title names the input and the description describes it. A name\n // passed in wins, and without one of either the label's own text names\n // the input, description included, so it isn't described again.\n const titleId = useId();\n const descriptionId = useId();\n const ariaLabel = rest['aria-label'];\n const ariaLabelledBy =\n rest['aria-labelledby'] || (!ariaLabel && title ? titleId : undefined);\n const ariaDescribedBy =\n rest['aria-describedby'] ||\n (description && (ariaLabel || ariaLabelledBy)\n ? descriptionId\n : undefined);\n\n const cardClass = usePrefixedClassNames('radio-card', 'card', {\n [`is-${color}`]: color && radioCardColors.includes(color),\n });\n const contentClass = usePrefixedClassNames(\n 'card-content',\n 'radio-card-content'\n );\n const checkClass = usePrefixedClassNames('radio-card-check');\n const bodyClass = usePrefixedClassNames('radio-card-body');\n const titleClass = usePrefixedClassNames('radio-card-title');\n const descriptionClass = usePrefixedClassNames('radio-card-description');\n\n return (\n <label className={classNames(cardClass, bulmaHelperClasses, className)}>\n <input\n ref={ref}\n type=\"radio\"\n disabled={disabled}\n name={effectiveName}\n value={value}\n checked={effectiveChecked}\n defaultChecked={effectiveDefaultChecked}\n onChange={handleChange}\n {...rest}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={ariaDescribedBy}\n />\n <span className={contentClass}>\n {hasIndicator && <span className={checkClass} />}\n <span className={bodyClass}>\n {title ? (\n <span id={titleId} className={titleClass}>\n {title}\n </span>\n ) : null}\n {description ? (\n <span id={descriptionId} className={descriptionClass}>\n {description}\n </span>\n ) : null}\n {children}\n </span>\n </span>\n </label>\n );\n }\n);\n\nRadioCard.displayName = 'RadioCard';\n\nexport default RadioCard;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
package/dist/esm/form/Radios.js
CHANGED
|
@@ -68,7 +68,9 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
|
|
|
68
68
|
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
69
69
|
// The group is "active" (manages child selection) when the user opted in by
|
|
70
70
|
// passing value, defaultValue, or onChange. Otherwise it's name-only and
|
|
71
|
-
// children manage their own checked state (Stage 1 behavior).
|
|
71
|
+
// children manage their own checked state (Stage 1 behavior). With only
|
|
72
|
+
// onChange it holds no value until the first change, and the radios show
|
|
73
|
+
// their own defaultChecked until then.
|
|
72
74
|
const groupActive = value !== undefined || defaultValue !== undefined || onChange !== undefined;
|
|
73
75
|
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
74
76
|
const isControlled = value !== undefined;
|
|
@@ -78,10 +80,20 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
|
|
|
78
80
|
setInternalValue(newValue);
|
|
79
81
|
onChange?.(newValue);
|
|
80
82
|
}, [isControlled, onChange]);
|
|
83
|
+
// A radio without a value, sharing the group's name, was picked, and the
|
|
84
|
+
// browser unchecked the radio showing checked. A group with no value yet
|
|
85
|
+
// holds `null`, which no radio's value matches, so React leaves that radio
|
|
86
|
+
// unchecked instead of undoing the pick. A group that holds a value keeps
|
|
87
|
+
// it, as it always has.
|
|
88
|
+
const pickWithoutValue = useCallback(() => {
|
|
89
|
+
setInternalValue(current => (current === undefined ? null : current));
|
|
90
|
+
}, []);
|
|
81
91
|
const ctx = useMemo(() => ({
|
|
82
92
|
name,
|
|
83
|
-
...(groupActive
|
|
84
|
-
|
|
93
|
+
...(groupActive
|
|
94
|
+
? { value: currentValue, onChange: handleChange, pickWithoutValue }
|
|
95
|
+
: {}),
|
|
96
|
+
}), [name, groupActive, currentValue, handleChange, pickWithoutValue]);
|
|
85
97
|
const choices = jsx(RadiosProvider, { value: ctx, children: children });
|
|
86
98
|
// ARIA in HTML allows `radiogroup` on a fieldset, so it keeps the role.
|
|
87
99
|
const Group = asFieldset ? 'fieldset' : 'div';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Radios.js","sources":["../../../src/form/Radios.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport {\n useInsideField,\n useInsideControl,\n rendersOwnField,\n rendersOwnControl,\n RadiosProvider,\n RadiosGroupContextValue,\n} from './FormContext';\nimport { Field } from './Field';\nimport { Control } from './Control';\nimport { FormFieldProps } from './fieldProps';\nimport { useAutoLabelledBy } from './useAutoLabelId';\nimport { Radio } from './Radio';\nimport { GroupFieldset } from './groupFieldset';\n\n/**\n * Props for the Radios component.\n * Inside an existing `Control` it renders no `Control` of its own. Inside an\n * outer `Field`, or a `Control` with no `Field` around it, it renders no\n * `Field` of its own either. The exception is `label`, `message`, `horizontal`\n * or `fieldClassName` in that bare `Control`: it keeps a `Field` for them,\n * nested in the `.control`, and warns in development. Wrap the `Control` in a\n * `Field` instead, and set the `label`, `horizontal` and class name on that\n * `Field`.\n */\nexport interface RadiosProps\n extends Omit<BulmaClassesProps, 'color'>, FormFieldProps {\n /** Field label naming the whole group. Automatically associated via `aria-labelledby` on the `role=\"radiogroup\"` wrapper — uses your `labelProps.id` when provided, otherwise a generated one. Dropped inside an outer `Field`, whose own label names the group instead through `aria-labelledby` when that `Field` generates a target id (not `grouped`/`hasAddons`, no explicit `labelProps.htmlFor`). An `aria-label` or `aria-labelledby` you set on the group wins over either label, which still renders but no longer names it. An `aria-labelledby` key counts even when undefined, since it is spread over the group's own. */\n label?: React.ReactNode;\n /** Props for the label element. An explicit `id` here is used as the `aria-labelledby` target instead of a generated one; any `htmlFor` is ignored (a group label names the group, never a single control). */\n labelProps?: React.LabelHTMLAttributes<HTMLLabelElement> & {\n [key: string]: unknown;\n };\n /** Additional CSS classes to apply. */\n className?: string;\n /** Form field name shared by every Radio in the group (via context). */\n name?: string;\n /** Currently-selected value (controlled mode). */\n value?: string;\n /** Initial selected value (uncontrolled mode). */\n defaultValue?: string;\n /** Fires when the selection changes. */\n onChange?: (value: string) => void;\n /** Radio elements to render in the group. */\n children: React.ReactNode;\n /**\n * The element the group renders as. With `fieldset` it's a native\n * `<fieldset>` that keeps `role=\"radiogroup\"`. Where the group renders a\n * `Field` of its own, that `Field` is the `<fieldset>` and the `label` is\n * its `<legend>`, in the label column when `horizontal`, so the legend names\n * the group with no ids to wire. Your other attributes then go on the\n * `<fieldset>`, while `className` and the helper props stay on the box that\n * lays out the radios. Inside an outer `Field` there's no legend: the group\n * element itself is the `<fieldset>`, named by that `Field`'s label as\n * before. `div`, the default, renders the group as it always has.\n * @defaultValue 'div'\n */\n as?: 'div' | 'fieldset';\n}\n\n/**\n * The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.\n *\n * @function\n * @param {RadiosProps} props - Props for the Radios component.\n * @returns {JSX.Element} The rendered radios group.\n * @see {@link https://bulma.io/documentation/form/radio/#grouped-radios | Bulma Radios documentation}\n *\n * @example\n * // Controlled\n * const [color, setColor] = useState('red');\n * <Radios name=\"color\" value={color} onChange={setColor}>\n * <Radio value=\"red\">Red</Radio>\n * <Radio value=\"green\">Green</Radio>\n * <Radio value=\"blue\">Blue</Radio>\n * </Radios>\n *\n * @example\n * // Uncontrolled\n * <Radios name=\"color\" defaultValue=\"red\" onChange={v => console.log(v)}>\n * <Radio value=\"red\">Red</Radio>\n * <Radio value=\"green\">Green</Radio>\n * </Radios>\n */\nconst RadiosComponent: React.FC<RadiosProps> = ({\n label,\n labelSize,\n labelProps,\n horizontal,\n message,\n messageColor,\n fieldClassName,\n name,\n value,\n defaultValue,\n onChange,\n children,\n className,\n as,\n ...props\n}) => {\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('Radios', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n // As a fieldset with a Field of its own, the group's label is its legend,\n // which names it with no id, so nothing points `aria-labelledby` at it.\n const asFieldset = as === 'fieldset';\n const fieldsetField = asFieldset && ownField;\n const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel: ownField && !fieldsetField,\n callerProps: props,\n });\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n ...props,\n });\n\n const mainClass = usePrefixedClassNames('radios');\n const wrapperClass = classNames(mainClass, bulmaHelperClasses, className);\n\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n const messageEl = message ? <p className={helpClass}>{message}</p> : null;\n\n // The group is \"active\" (manages child selection) when the user opted in by\n // passing value, defaultValue, or onChange. Otherwise it's name-only and\n // children manage their own checked state (Stage 1 behavior).\n const groupActive =\n value !== undefined || defaultValue !== undefined || onChange !== undefined;\n\n const [internalValue, setInternalValue] = useState<string | undefined>(\n defaultValue\n );\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : internalValue;\n\n const handleChange = useCallback(\n (newValue: string) => {\n if (!isControlled) setInternalValue(newValue);\n onChange?.(newValue);\n },\n [isControlled, onChange]\n );\n\n const ctx = useMemo<RadiosGroupContextValue>(\n () => ({\n name,\n ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),\n }),\n [name, groupActive, currentValue, handleChange]\n );\n\n const choices = <RadiosProvider value={ctx}>{children}</RadiosProvider>;\n // ARIA in HTML allows `radiogroup` on a fieldset, so it keeps the role.\n const Group = asFieldset ? 'fieldset' : 'div';\n const radiosElement = fieldsetField ? (\n <div className={wrapperClass}>{choices}</div>\n ) : (\n <Group\n className={wrapperClass}\n role=\"radiogroup\"\n aria-labelledby={ariaLabelledBy}\n {...rest}\n >\n {choices}\n </Group>\n );\n\n let content = radiosElement;\n\n if (rendersOwnControl('Radios', { insideControl })) {\n content = <Control>{content}</Control>;\n }\n\n if (fieldsetField) {\n return (\n <GroupFieldset\n label={label}\n labelSize={labelSize}\n labelProps={labelProps}\n horizontal={horizontal}\n fieldClassName={fieldClassName}\n role=\"radiogroup\"\n attributes={rest}\n message={messageEl}\n >\n {content}\n </GroupFieldset>\n );\n }\n\n if (ownField) {\n return (\n <Field\n label={label}\n labelSize={labelSize}\n labelProps={fieldLabelProps}\n horizontal={horizontal}\n className={fieldClassName}\n >\n {content}\n {messageEl}\n </Field>\n );\n }\n\n return (\n <>\n {content}\n {messageEl}\n </>\n );\n};\n\nexport const Radios = withSubComponents(RadiosComponent, { Radio }, 'Radios');\n\nexport default Radios;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Radios.js","sources":["../../../src/form/Radios.tsx"],"sourcesContent":["import React, { useCallback, useMemo, useState } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport {\n useInsideField,\n useInsideControl,\n rendersOwnField,\n rendersOwnControl,\n RadiosProvider,\n RadiosGroupContextValue,\n} from './FormContext';\nimport { Field } from './Field';\nimport { Control } from './Control';\nimport { FormFieldProps } from './fieldProps';\nimport { useAutoLabelledBy } from './useAutoLabelId';\nimport { Radio } from './Radio';\nimport { GroupFieldset } from './groupFieldset';\n\n/**\n * Props for the Radios component.\n * Inside an existing `Control` it renders no `Control` of its own. Inside an\n * outer `Field`, or a `Control` with no `Field` around it, it renders no\n * `Field` of its own either. The exception is `label`, `message`, `horizontal`\n * or `fieldClassName` in that bare `Control`: it keeps a `Field` for them,\n * nested in the `.control`, and warns in development. Wrap the `Control` in a\n * `Field` instead, and set the `label`, `horizontal` and class name on that\n * `Field`.\n */\nexport interface RadiosProps\n extends Omit<BulmaClassesProps, 'color'>, FormFieldProps {\n /** Field label naming the whole group. Automatically associated via `aria-labelledby` on the `role=\"radiogroup\"` wrapper — uses your `labelProps.id` when provided, otherwise a generated one. Dropped inside an outer `Field`, whose own label names the group instead through `aria-labelledby` when that `Field` generates a target id (not `grouped`/`hasAddons`, no explicit `labelProps.htmlFor`). An `aria-label` or `aria-labelledby` you set on the group wins over either label, which still renders but no longer names it. An `aria-labelledby` key counts even when undefined, since it is spread over the group's own. */\n label?: React.ReactNode;\n /** Props for the label element. An explicit `id` here is used as the `aria-labelledby` target instead of a generated one; any `htmlFor` is ignored (a group label names the group, never a single control). */\n labelProps?: React.LabelHTMLAttributes<HTMLLabelElement> & {\n [key: string]: unknown;\n };\n /** Additional CSS classes to apply. */\n className?: string;\n /** Form field name shared by every Radio in the group (via context). */\n name?: string;\n /** Currently-selected value (controlled mode). */\n value?: string;\n /** Initial selected value (uncontrolled mode). */\n defaultValue?: string;\n /**\n * Fires when the selection changes; receives the value picked. Passed\n * without `value` or `defaultValue`, it still has the group manage the\n * selection, starting from the radio with `defaultChecked`.\n */\n onChange?: (value: string) => void;\n /** Radio elements to render in the group. */\n children: React.ReactNode;\n /**\n * The element the group renders as. With `fieldset` it's a native\n * `<fieldset>` that keeps `role=\"radiogroup\"`. Where the group renders a\n * `Field` of its own, that `Field` is the `<fieldset>` and the `label` is\n * its `<legend>`, in the label column when `horizontal`, so the legend names\n * the group with no ids to wire. Your other attributes then go on the\n * `<fieldset>`, while `className` and the helper props stay on the box that\n * lays out the radios. Inside an outer `Field` there's no legend: the group\n * element itself is the `<fieldset>`, named by that `Field`'s label as\n * before. `div`, the default, renders the group as it always has.\n * @defaultValue 'div'\n */\n as?: 'div' | 'fieldset';\n}\n\n/**\n * The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.\n *\n * @function\n * @param {RadiosProps} props - Props for the Radios component.\n * @returns {JSX.Element} The rendered radios group.\n * @see {@link https://bulma.io/documentation/form/radio/#grouped-radios | Bulma Radios documentation}\n *\n * @example\n * // Controlled\n * const [color, setColor] = useState('red');\n * <Radios name=\"color\" value={color} onChange={setColor}>\n * <Radio value=\"red\">Red</Radio>\n * <Radio value=\"green\">Green</Radio>\n * <Radio value=\"blue\">Blue</Radio>\n * </Radios>\n *\n * @example\n * // Uncontrolled\n * <Radios name=\"color\" defaultValue=\"red\" onChange={v => console.log(v)}>\n * <Radio value=\"red\">Red</Radio>\n * <Radio value=\"green\">Green</Radio>\n * </Radios>\n */\nconst RadiosComponent: React.FC<RadiosProps> = ({\n label,\n labelSize,\n labelProps,\n horizontal,\n message,\n messageColor,\n fieldClassName,\n name,\n value,\n defaultValue,\n onChange,\n children,\n className,\n as,\n ...props\n}) => {\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('Radios', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n // As a fieldset with a Field of its own, the group's label is its legend,\n // which names it with no id, so nothing points `aria-labelledby` at it.\n const asFieldset = as === 'fieldset';\n const fieldsetField = asFieldset && ownField;\n const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel: ownField && !fieldsetField,\n callerProps: props,\n });\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n ...props,\n });\n\n const mainClass = usePrefixedClassNames('radios');\n const wrapperClass = classNames(mainClass, bulmaHelperClasses, className);\n\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n const messageEl = message ? <p className={helpClass}>{message}</p> : null;\n\n // The group is \"active\" (manages child selection) when the user opted in by\n // passing value, defaultValue, or onChange. Otherwise it's name-only and\n // children manage their own checked state (Stage 1 behavior). With only\n // onChange it holds no value until the first change, and the radios show\n // their own defaultChecked until then.\n const groupActive =\n value !== undefined || defaultValue !== undefined || onChange !== undefined;\n\n const [internalValue, setInternalValue] = useState<string | null | undefined>(\n defaultValue\n );\n const isControlled = value !== undefined;\n const currentValue = isControlled ? value : internalValue;\n\n const handleChange = useCallback(\n (newValue: string) => {\n if (!isControlled) setInternalValue(newValue);\n onChange?.(newValue);\n },\n [isControlled, onChange]\n );\n\n // A radio without a value, sharing the group's name, was picked, and the\n // browser unchecked the radio showing checked. A group with no value yet\n // holds `null`, which no radio's value matches, so React leaves that radio\n // unchecked instead of undoing the pick. A group that holds a value keeps\n // it, as it always has.\n const pickWithoutValue = useCallback(() => {\n setInternalValue(current => (current === undefined ? null : current));\n }, []);\n\n const ctx = useMemo<RadiosGroupContextValue>(\n () => ({\n name,\n ...(groupActive\n ? { value: currentValue, onChange: handleChange, pickWithoutValue }\n : {}),\n }),\n [name, groupActive, currentValue, handleChange, pickWithoutValue]\n );\n\n const choices = <RadiosProvider value={ctx}>{children}</RadiosProvider>;\n // ARIA in HTML allows `radiogroup` on a fieldset, so it keeps the role.\n const Group = asFieldset ? 'fieldset' : 'div';\n const radiosElement = fieldsetField ? (\n <div className={wrapperClass}>{choices}</div>\n ) : (\n <Group\n className={wrapperClass}\n role=\"radiogroup\"\n aria-labelledby={ariaLabelledBy}\n {...rest}\n >\n {choices}\n </Group>\n );\n\n let content = radiosElement;\n\n if (rendersOwnControl('Radios', { insideControl })) {\n content = <Control>{content}</Control>;\n }\n\n if (fieldsetField) {\n return (\n <GroupFieldset\n label={label}\n labelSize={labelSize}\n labelProps={labelProps}\n horizontal={horizontal}\n fieldClassName={fieldClassName}\n role=\"radiogroup\"\n attributes={rest}\n message={messageEl}\n >\n {content}\n </GroupFieldset>\n );\n }\n\n if (ownField) {\n return (\n <Field\n label={label}\n labelSize={labelSize}\n labelProps={fieldLabelProps}\n horizontal={horizontal}\n className={fieldClassName}\n >\n {content}\n {messageEl}\n </Field>\n );\n }\n\n return (\n <>\n {content}\n {messageEl}\n </>\n );\n};\n\nexport const Radios = withSubComponents(RadiosComponent, { Radio }, 'Radios');\n\nexport default Radios;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
package/dist/esm/form/Rate.js
CHANGED
|
@@ -261,7 +261,7 @@ const Rate = forwardRef(({ label, labelSize, labelProps, horizontal, message, me
|
|
|
261
261
|
'is-hovered': isHovered,
|
|
262
262
|
}), onClick: e => handleClick(iconIndex, e), onMouseEnter: () => handleMouseEnter(iconIndex), onMouseMove: e => handleMouseMove(iconIndex, e), onMouseLeave: handleMouseLeave, id: `${starBaseId}-${iconIndex}`, role: "radio", "aria-checked": iconIndex === checkedIndex, "aria-label": iconIndex === checkedIndex && shownValue % 1 !== 0
|
|
263
263
|
? `${shownValue} stars`
|
|
264
|
-
: `${iconIndex + 1} star${iconIndex === 0 ? '' : 's'}`, tabIndex: -1, children: icon }, iconIndex));
|
|
264
|
+
: `${iconIndex + 1} star${iconIndex === 0 ? '' : 's'}`, "aria-disabled": disabled || undefined, tabIndex: -1, children: icon }, iconIndex));
|
|
265
265
|
}
|
|
266
266
|
return icons;
|
|
267
267
|
};
|
|
@@ -274,7 +274,7 @@ const Rate = forwardRef(({ label, labelSize, labelProps, horizontal, message, me
|
|
|
274
274
|
const rateScoreClass = usePrefixedClassNames('rate-score');
|
|
275
275
|
const rateTextClass = usePrefixedClassNames('rate-text');
|
|
276
276
|
const rateCustomTextClass = usePrefixedClassNames('rate-custom-text');
|
|
277
|
-
const rateElement = (jsxs("div", { ref: ref, className: combinedClasses, role: "radiogroup", "aria-label": ariaLabelledBy ? undefined : 'Rating', "aria-labelledby": ariaLabelledBy, "aria-activedescendant": checkedIndex >= 0 ? `${starBaseId}-${checkedIndex}` : undefined, tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, ...rest, children: [jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsx("span", { className: rateTextClass, children: text }), customText && (jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
|
|
277
|
+
const rateElement = (jsxs("div", { ref: ref, className: combinedClasses, role: "radiogroup", "aria-label": ariaLabelledBy ? undefined : 'Rating', "aria-labelledby": ariaLabelledBy, "aria-activedescendant": checkedIndex >= 0 ? `${starBaseId}-${checkedIndex}` : undefined, "aria-disabled": disabled || undefined, tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, ...rest, children: [jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsx("span", { className: rateTextClass, children: text }), customText && (jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
|
|
278
278
|
let content = rateElement;
|
|
279
279
|
if (rendersOwnControl('Rate', { insideControl })) {
|
|
280
280
|
content = jsx(Control, { children: content });
|