@allxsmith/bestax-bulma 5.50.0 → 5.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/Command.js +637 -0
- package/dist/esm/components/Command.js.map +1 -0
- package/dist/esm/components/EmptyState.js +2 -2
- package/dist/esm/components/Timeline.js +5 -5
- package/dist/esm/components/TreeView.js +3 -3
- package/dist/esm/components/TreeView.js.map +1 -1
- package/dist/esm/elements/DescriptionList.js +133 -0
- package/dist/esm/elements/DescriptionList.js.map +1 -0
- package/dist/esm/form/Autocomplete.js +2 -2
- package/dist/esm/form/CheckboxCard.js +118 -0
- package/dist/esm/form/CheckboxCard.js.map +1 -0
- package/dist/esm/form/Checkboxes.js +14 -3
- package/dist/esm/form/Checkboxes.js.map +1 -1
- package/dist/esm/form/RadioCard.js +85 -0
- package/dist/esm/form/RadioCard.js.map +1 -0
- package/dist/esm/form/Radios.js +14 -3
- package/dist/esm/form/Radios.js.map +1 -1
- package/dist/esm/form/groupFieldset.js +38 -0
- package/dist/esm/form/groupFieldset.js.map +1 -0
- package/dist/esm/form/useAutoLabelId.js +1 -1
- package/dist/esm/helpers/useComboboxEngine.js +3 -3
- package/dist/esm/helpers/useFocusTrap.js +2 -2
- package/dist/esm/helpers/useFocusTrap.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +2685 -1694
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +4 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/Command.d.ts +318 -0
- package/dist/types/elements/DescriptionList.d.ts +185 -0
- package/dist/types/form/CheckboxCard.d.ts +94 -0
- package/dist/types/form/Checkboxes.d.ts +13 -0
- package/dist/types/form/RadioCard.d.ts +81 -0
- package/dist/types/form/Radios.d.ts +13 -0
- package/dist/types/form/groupFieldset.d.ts +32 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types-cjs/components/Command.d.ts +318 -0
- package/dist/types-cjs/elements/DescriptionList.d.ts +185 -0
- package/dist/types-cjs/form/CheckboxCard.d.ts +94 -0
- package/dist/types-cjs/form/Checkboxes.d.ts +13 -0
- package/dist/types-cjs/form/RadioCard.d.ts +81 -0
- package/dist/types-cjs/form/Radios.d.ts +13 -0
- package/dist/types-cjs/form/groupFieldset.d.ts +32 -0
- package/dist/types-cjs/index.d.ts +4 -0
- 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/components/_command.scss +259 -0
- package/src/scss/components/_index.scss +1 -0
- package/src/scss/elements/_descriptionlist.scss +153 -0
- package/src/scss/elements/_index.scss +1 -0
- package/src/scss/form/_checkbox.scss +44 -0
- package/src/scss/form/_checkboxcard.scss +355 -0
- package/src/scss/form/_index.scss +2 -0
- package/src/scss/form/_radio.scss +44 -0
- package/src/scss/form/_radiocard.scss +291 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DescriptionList.js","sources":["../../../src/elements/DescriptionList.tsx"],"sourcesContent":["import React, { forwardRef } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\n\nconst validDescriptionListOrientations = ['vertical', 'horizontal'] as const;\n/** Whether each label sits above its value (`vertical`) or beside it (`horizontal`). */\nexport type DescriptionListOrientation =\n (typeof validDescriptionListOrientations)[number];\n\nconst isOrientation = (\n orientation?: string\n): orientation is DescriptionListOrientation =>\n !!orientation &&\n (validDescriptionListOrientations as readonly string[]).includes(orientation);\n\n/** Whether a prop was given something to render. */\nconst isGiven = (node: React.ReactNode) =>\n node !== undefined &&\n node !== null &&\n typeof node !== 'boolean' &&\n node !== '';\n\n/**\n * Props for the DescriptionList component. The helper props, the other\n * attributes and the `ref` land on the `<dl>`.\n *\n * @extraProp {React.Ref<HTMLDListElement>} [ref] - Ref forwarded to the `<dl>`.\n * @extraProp {string} [aria-labelledby] - The id of the heading above the list, which names it.\n */\nexport interface DescriptionListProps\n extends\n Omit<React.HTMLAttributes<HTMLDListElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** `DescriptionList.Item`s, one for each fact. */\n children?: React.ReactNode;\n /**\n * Whether each label sits above its value or beside it, at every width no\n * `orientation<Viewport>` prop covers. Those props read the way a\n * `Column`'s `size<Viewport>` props do: `orientationMobile` applies below\n * Bulma's tablet breakpoint, each of the others from its own breakpoint\n * up, and where several apply the widest one wins. With none of them,\n * labels sit above their values below the tablet breakpoint and beside\n * them from it up. Beside, the labels share one column as wide as the\n * longest label, up to `--bulma-description-list-label-max-width`, so the\n * values line up.\n */\n orientation?: DescriptionListOrientation;\n /**\n * The orientation below Bulma's tablet breakpoint (769px). Falls back to\n * `orientation`, then to `vertical`.\n */\n orientationMobile?: DescriptionListOrientation;\n /**\n * The orientation from Bulma's tablet breakpoint (769px) up, until a wider\n * viewport's prop takes over. Falls back to `orientation`, then to\n * `horizontal`.\n */\n orientationTablet?: DescriptionListOrientation;\n /**\n * The orientation from Bulma's desktop breakpoint (1024px) up, until a\n * wider viewport's prop takes over. Falls back to `orientationTablet`, and\n * on down.\n */\n orientationDesktop?: DescriptionListOrientation;\n /**\n * The orientation from Bulma's widescreen breakpoint (1216px) up, until\n * `orientationFullhd` takes over. Falls back to `orientationDesktop`, and\n * on down.\n */\n orientationWidescreen?: DescriptionListOrientation;\n /**\n * The orientation from Bulma's fullhd breakpoint (1408px) up. Falls back to\n * `orientationWidescreen`, and on down.\n */\n orientationFullhd?: DescriptionListOrientation;\n /**\n * Draws a line between items, which helps the eye from a label to its\n * value across a wide list, and shows where each item ends to someone\n * zoomed in.\n */\n hasDividers?: boolean;\n /** Text color helper. It colors the values; the labels keep their own color. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for DescriptionList.Item, one fact. The helper props, the other\n * attributes and the `ref` land on its `<div>`.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the `<div>`.\n */\nexport interface DescriptionListItemProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * The label, such as \"Order\", as a `DescriptionList.Label` before the\n * children.\n */\n label?: React.ReactNode;\n /**\n * The value, such as \"#1234\", as a `DescriptionList.Value` after the\n * children. Text, a link, a `Tag`: anything that renders. `undefined`,\n * `null` and `''` render nothing, so when a value is missing, say so in\n * words, such as \"Not given\", rather than leave a label with nothing after\n * it.\n */\n value?: React.ReactNode;\n /**\n * `DescriptionList.Label`s and `DescriptionList.Value`s, for what `label`\n * and `value` don't cover, such as a label with an info tip or several\n * values. They render between the two, so a `Label` here reads before\n * `value`, and a `Value` here after `label`.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for DescriptionList.Label. The helper props, the other attributes and\n * the `ref` land on its `<dt>`.\n *\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<dt>`.\n */\nexport interface DescriptionListLabelProps\n extends\n Omit<React.HTMLAttributes<HTMLElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /**\n * The label. A button may sit in it, such as one that opens a `Popover`\n * about the label, but not a heading, which HTML doesn't allow here.\n */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * Props for DescriptionList.Value. The helper props, the other attributes and\n * the `ref` land on its `<dd>`.\n *\n * @extraProp {React.Ref<HTMLElement>} [ref] - Ref forwarded to the `<dd>`.\n */\nexport interface DescriptionListValueProps\n extends\n Omit<React.HTMLAttributes<HTMLElement>, 'color'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes to apply. */\n className?: string;\n /** The value. Anything goes, a list or a paragraph included. */\n children?: React.ReactNode;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/**\n * A label, the name of a fact: a `<dt>`, in the weaker text color.\n *\n * @function\n * @param {DescriptionListLabelProps} props - Props for DescriptionList.Label.\n * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<dt>`.\n * @returns {JSX.Element} The rendered label.\n *\n * @example\n * <DescriptionList.Label>Placed on</DescriptionList.Label>\n */\nexport const DescriptionListLabel = forwardRef<\n HTMLElement,\n DescriptionListLabelProps\n>(({ className, textColor, bgColor, children, ...props }, ref) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const labelClass = usePrefixedClassNames('description-list-label');\n return (\n <dt\n ref={ref}\n className={classNames(labelClass, bulmaHelperClasses, className)}\n {...rest}\n >\n {children}\n </dt>\n );\n});\n\nDescriptionListLabel.displayName = 'DescriptionList.Label';\n\n/**\n * A value, what the label says about the thing: a `<dd>`. An item may have several.\n *\n * @function\n * @param {DescriptionListValueProps} props - Props for DescriptionList.Value.\n * @param {React.Ref<HTMLElement>} ref - Forwarded ref to the `<dd>`.\n * @returns {JSX.Element} The rendered value.\n *\n * @example\n * <DescriptionList.Value>14 October 2026</DescriptionList.Value>\n */\nexport const DescriptionListValue = forwardRef<\n HTMLElement,\n DescriptionListValueProps\n>(({ className, textColor, bgColor, children, ...props }, ref) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const valueClass = usePrefixedClassNames('description-list-value');\n return (\n <dd\n ref={ref}\n className={classNames(valueClass, bulmaHelperClasses, className)}\n {...rest}\n >\n {children}\n </dd>\n );\n});\n\nDescriptionListValue.displayName = 'DescriptionList.Value';\n\n/**\n * One fact: its label and its value, in a `<div>`, from the `label` and `value` props or from `Label` and `Value` children.\n *\n * @function\n * @param {DescriptionListItemProps} props - Props for DescriptionList.Item.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the `<div>`.\n * @returns {JSX.Element} The rendered item.\n *\n * @example\n * <DescriptionList.Item label=\"Order\" value=\"#1234\" />\n *\n * @example\n * <DescriptionList.Item label=\"Tags\">\n * <DescriptionList.Value>Gift</DescriptionList.Value>\n * <DescriptionList.Value>Priority</DescriptionList.Value>\n * </DescriptionList.Item>\n */\nexport const DescriptionListItem = forwardRef<\n HTMLDivElement,\n DescriptionListItemProps\n>(\n (\n { className, label, value, textColor, bgColor, children, ...props },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n const itemClass = usePrefixedClassNames('description-list-item');\n return (\n <div\n ref={ref}\n className={classNames(itemClass, bulmaHelperClasses, className)}\n {...rest}\n >\n {isGiven(label) && <DescriptionListLabel>{label}</DescriptionListLabel>}\n {children}\n {isGiven(value) && <DescriptionListValue>{value}</DescriptionListValue>}\n </div>\n );\n }\n);\n\nDescriptionListItem.displayName = 'DescriptionList.Item';\n\n/**\n * The `DescriptionList` component lists facts about one thing, each a label and its value, such as an order's details or a profile.\n *\n * It's a `<dl>`, with a `<div>` for each item, a `<dt>` for each label and\n * a `<dd>` for each value, so assistive tech gets HTML's own list of terms\n * and definitions and no roles are added. Labels sit above their values on\n * a phone and beside them from Bulma's tablet breakpoint up, where every\n * label shares one column so the values line up. For data with two\n * dimensions, such as several records with the same fields, use a `Table`.\n *\n * @function\n * @param {DescriptionListProps} props - Props for the DescriptionList component.\n * @param {React.Ref<HTMLDListElement>} ref - Forwarded ref to the `<dl>`.\n * @returns {JSX.Element} The rendered list.\n *\n * @example\n * <DescriptionList aria-labelledby=\"order-heading\">\n * <DescriptionList.Item label=\"Order\" value=\"#1234\" />\n * <DescriptionList.Item label=\"Placed on\" value=\"14 October 2026\" />\n * </DescriptionList>\n */\nconst DescriptionListRoot = forwardRef<HTMLDListElement, DescriptionListProps>(\n (\n {\n className,\n orientation,\n orientationMobile,\n orientationTablet,\n orientationDesktop,\n orientationWidescreen,\n orientationFullhd,\n hasDividers,\n textColor,\n bgColor,\n children,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n // A class for each orientation given, which the stylesheet applies from\n // that viewport's breakpoint, so the layout is right on the first paint\n // and on the server. With none, the stylesheet's default is responsive.\n const rootClass = usePrefixedClassNames('description-list', {\n [`is-${orientation}`]: isOrientation(orientation),\n [`is-${orientationMobile}-mobile`]: isOrientation(orientationMobile),\n [`is-${orientationTablet}-tablet`]: isOrientation(orientationTablet),\n [`is-${orientationDesktop}-desktop`]: isOrientation(orientationDesktop),\n [`is-${orientationWidescreen}-widescreen`]: isOrientation(\n orientationWidescreen\n ),\n [`is-${orientationFullhd}-fullhd`]: isOrientation(orientationFullhd),\n 'has-dividers': !!hasDividers,\n });\n return (\n <dl\n ref={ref}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n {...rest}\n >\n {children}\n </dl>\n );\n }\n);\n\nexport const DescriptionList = withSubComponents(\n DescriptionListRoot,\n {\n Item: DescriptionListItem,\n Label: DescriptionListLabel,\n Value: DescriptionListValue,\n },\n 'DescriptionList'\n);\n\nexport default DescriptionList;\n"],"names":["_jsx","_jsxs"],"mappings":"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"}
|
|
@@ -23,7 +23,7 @@ const keepFocus = (e) => e.preventDefault();
|
|
|
23
23
|
// An IME that's composing owns every key: Enter confirms what it composed,
|
|
24
24
|
// and the arrows move through its candidates. Safari can say so with
|
|
25
25
|
// `keyCode` 229 rather than `isComposing`.
|
|
26
|
-
const isComposing$
|
|
26
|
+
const isComposing$1 = (e) => e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229;
|
|
27
27
|
// Whether an option can be picked. The arrows still reach a disabled one, so
|
|
28
28
|
// a screen reader user finds it.
|
|
29
29
|
const isEnabled = (item) => typeof item === 'string' || !item.disabled;
|
|
@@ -195,7 +195,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
|
|
|
195
195
|
}, [keepFirst, isActive, filteredData.length, inputValue]);
|
|
196
196
|
// Handle keyboard navigation
|
|
197
197
|
const handleKeyDown = useCallback((e) => {
|
|
198
|
-
if (disabled || isComposing$
|
|
198
|
+
if (disabled || isComposing$1(e))
|
|
199
199
|
return;
|
|
200
200
|
switch (e.key) {
|
|
201
201
|
case 'ArrowDown':
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, useRef, useCallback, useId } from 'react';
|
|
4
|
+
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
6
|
+
import { useClientLayoutEffect } from '../helpers/useClientLayoutEffect.js';
|
|
7
|
+
import { useCheckboxesGroup } from './FormContext.js';
|
|
8
|
+
|
|
9
|
+
// The colors `Checkbox` takes, as `checkboxColors` lists them. Declared here as
|
|
10
|
+
// well, so the API page can read them off this module, and a type test holds
|
|
11
|
+
// the two lists to each other.
|
|
12
|
+
const checkboxCardColors = [
|
|
13
|
+
'primary',
|
|
14
|
+
'link',
|
|
15
|
+
'info',
|
|
16
|
+
'success',
|
|
17
|
+
'warning',
|
|
18
|
+
'danger',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* The `CheckboxCard` component is a checkbox drawn as a card, with a title and a description, that toggles wherever you click it.
|
|
22
|
+
*
|
|
23
|
+
* It's a `<label>` around a real checkbox, so it sends its `value` with a
|
|
24
|
+
* form and Space toggles it. The title names the checkbox and the
|
|
25
|
+
* description describes it. Alone it's one choice; inside `Checkboxes` it
|
|
26
|
+
* takes the group's name and selection, as `Checkbox` does.
|
|
27
|
+
*
|
|
28
|
+
* @function
|
|
29
|
+
* @param {CheckboxCardProps} props - Props for the CheckboxCard component.
|
|
30
|
+
* @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the `<input>`.
|
|
31
|
+
* @returns {JSX.Element} The rendered card.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* <Checkboxes label="Add-ons" name="extras" defaultValue={['backups']}>
|
|
35
|
+
* <CheckboxCard value="backups" title="Daily backups" description="Kept for 30 days." />
|
|
36
|
+
* <CheckboxCard value="domain" title="Custom domain" description="Bring your own." />
|
|
37
|
+
* </Checkboxes>
|
|
38
|
+
*/
|
|
39
|
+
const CheckboxCard = forwardRef(({ title, description, children, hasIndicator = true, color, className, textColor, disabled, name, value, checked, defaultChecked, indeterminate, onChange, ...props }, ref) => {
|
|
40
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
41
|
+
color: textColor,
|
|
42
|
+
...props,
|
|
43
|
+
});
|
|
44
|
+
// The input's own ref, merged with the forwarded one, as Checkbox does,
|
|
45
|
+
// so the mixed state can be set on it.
|
|
46
|
+
const inputRef = useRef(null);
|
|
47
|
+
const setRefs = useCallback((node) => {
|
|
48
|
+
inputRef.current = node;
|
|
49
|
+
if (typeof ref === 'function')
|
|
50
|
+
return ref(node);
|
|
51
|
+
if (ref)
|
|
52
|
+
ref.current = node;
|
|
53
|
+
}, [ref]);
|
|
54
|
+
// `indeterminate` is a DOM property with no attribute, and a click clears
|
|
55
|
+
// it, so it's set after every render while the prop is set, and cleared
|
|
56
|
+
// once when the prop goes back to unset. Checkbox does the same.
|
|
57
|
+
const ownsIndeterminateRef = useRef(false);
|
|
58
|
+
useClientLayoutEffect(() => {
|
|
59
|
+
if (indeterminate !== undefined) {
|
|
60
|
+
ownsIndeterminateRef.current = true;
|
|
61
|
+
inputRef.current.indeterminate = indeterminate;
|
|
62
|
+
}
|
|
63
|
+
else if (ownsIndeterminateRef.current) {
|
|
64
|
+
ownsIndeterminateRef.current = false;
|
|
65
|
+
inputRef.current.indeterminate = false;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// A surrounding <Checkboxes> hands down its name and, when it manages
|
|
69
|
+
// the selection, whether this card's value is in it. The card's own
|
|
70
|
+
// props win, as they do on Checkbox.
|
|
71
|
+
const group = useCheckboxesGroup();
|
|
72
|
+
const effectiveName = name ?? group?.name;
|
|
73
|
+
const groupManaged = group?.value !== undefined && value !== undefined;
|
|
74
|
+
const effectiveChecked = checked !== undefined
|
|
75
|
+
? checked
|
|
76
|
+
: groupManaged
|
|
77
|
+
? group.value.includes(String(value))
|
|
78
|
+
: undefined;
|
|
79
|
+
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
80
|
+
const handleChange = useCallback((e) => {
|
|
81
|
+
if (indeterminate !== undefined)
|
|
82
|
+
e.target.indeterminate = indeterminate;
|
|
83
|
+
onChange?.(e);
|
|
84
|
+
if (group?.onChange && value !== undefined) {
|
|
85
|
+
const valStr = String(value);
|
|
86
|
+
const current = group.value ?? [];
|
|
87
|
+
group.onChange(e.target.checked
|
|
88
|
+
? current.includes(valStr)
|
|
89
|
+
? current
|
|
90
|
+
: [...current, valStr]
|
|
91
|
+
: current.filter(v => v !== valStr));
|
|
92
|
+
}
|
|
93
|
+
}, [onChange, group, value, indeterminate]);
|
|
94
|
+
// The title names the input and the description describes it. A name
|
|
95
|
+
// passed in wins, and without one of either the label's own text names
|
|
96
|
+
// the input, description included, so it isn't described again.
|
|
97
|
+
const titleId = useId();
|
|
98
|
+
const descriptionId = useId();
|
|
99
|
+
const ariaLabel = rest['aria-label'];
|
|
100
|
+
const ariaLabelledBy = rest['aria-labelledby'] || (!ariaLabel && title ? titleId : undefined);
|
|
101
|
+
const ariaDescribedBy = rest['aria-describedby'] ||
|
|
102
|
+
(description && (ariaLabel || ariaLabelledBy)
|
|
103
|
+
? descriptionId
|
|
104
|
+
: undefined);
|
|
105
|
+
const cardClass = usePrefixedClassNames('checkbox-card', 'card', {
|
|
106
|
+
[`is-${color}`]: color && checkboxCardColors.includes(color),
|
|
107
|
+
});
|
|
108
|
+
const contentClass = usePrefixedClassNames('card-content', 'checkbox-card-content');
|
|
109
|
+
const checkClass = usePrefixedClassNames('checkbox-card-check');
|
|
110
|
+
const bodyClass = usePrefixedClassNames('checkbox-card-body');
|
|
111
|
+
const titleClass = usePrefixedClassNames('checkbox-card-title');
|
|
112
|
+
const descriptionClass = usePrefixedClassNames('checkbox-card-description');
|
|
113
|
+
return (jsxs("label", { className: classNames(cardClass, bulmaHelperClasses, className), children: [jsx("input", { ref: setRefs, type: "checkbox", disabled: disabled, name: effectiveName, value: value, checked: effectiveChecked, defaultChecked: effectiveDefaultChecked, onChange: handleChange, ...rest, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy }), jsxs("span", { className: contentClass, children: [hasIndicator && jsx("span", { className: checkClass }), jsxs("span", { className: bodyClass, children: [title ? (jsx("span", { id: titleId, className: titleClass, children: title })) : null, description ? (jsx("span", { id: descriptionId, className: descriptionClass, children: description })) : null, children] })] })] }));
|
|
114
|
+
});
|
|
115
|
+
CheckboxCard.displayName = 'CheckboxCard';
|
|
116
|
+
|
|
117
|
+
export { CheckboxCard };
|
|
118
|
+
//# sourceMappingURL=CheckboxCard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CheckboxCard.js","sources":["../../../src/form/CheckboxCard.tsx"],"sourcesContent":["import React, { forwardRef, useCallback, useId, useRef } from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useCheckboxesGroup } from './FormContext';\n\n// The colors `Checkbox` takes, as `checkboxColors` 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 checkboxCardColors = [\n 'primary',\n 'link',\n 'info',\n 'success',\n 'warning',\n 'danger',\n] as const;\n\n/**\n * Props for the CheckboxCard 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. Inside `Checkboxes`, the group's `name` when this is unset.\n * @extraProp {string} [value] - What a form sends for the card when it's checked. Inside a `Checkboxes` group that takes `value`, `defaultValue` or `onChange`, the card is checked while the group's value holds it.\n */\nexport interface CheckboxCardProps\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 \"Daily backups\". It names the checkbox, so\n * a screen reader says it with the state. Without it, the card's whole\n * text names the checkbox, 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 checkbox's description when `title`, `aria-label` or\n * `aria-labelledby` names it. Without one of those it's part of the name\n * already, so it 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 toggle the card, but a button or another input can't\n * go inside a label at all. It isn't read when the checkbox takes focus,\n * so put what a screen reader should hear in `title` and `description`.\n */\n children?: React.ReactNode;\n /**\n * Shows the checkbox at the start of the card. Without it, the card's\n * edge alone shows the selection, and it's thicker when checked, so the\n * selection never rests on color.\n */\n hasIndicator?: boolean;\n /** The color of the checkbox and of a checked card's edge. */\n color?: (typeof checkboxCardColors)[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 leaves the Tab order. */\n disabled?: boolean;\n /**\n * Shows the mixed state, as in a \"Select all\" card when some of what it\n * covers is checked. It's a look and an announcement only: `checked` is\n * still the value, and a click still toggles `checked`. Set to `true` or\n * `false`, it's controlled: a browser clears the mixed state on a click,\n * so the component sets it again from this prop after every click and\n * every render. Unset isn't `false`. The component leaves the input's own\n * `indeterminate` property alone, so a ref can set it, and going from set\n * to unset clears it once and hands it back. It has no HTML attribute, so\n * server-rendered markup can't carry it, and a mixed card renders unmixed\n * until hydration.\n */\n indeterminate?: boolean;\n /**\n * Additional CSS classes, on the outer `<label>`, the card. That's the\n * element that declares the `--bulma-checkbox-card-*` variables, and\n * Bulma's `--bulma-card-*` ones, so a class is how to set one: `style` and\n * the other input attributes go to the `<input>`. Name `.checkbox-card`\n * beside your class in the selector, so it beats the card's own\n * declaration whatever order the stylesheets load in. A `color` prop\n * declares the active color again on `.checkbox-card.is-<color>`, which\n * ties that selector, so leave `color` unset when a class sets it.\n */\n className?: string;\n}\n\n/**\n * The `CheckboxCard` component is a checkbox drawn as a card, with a title and a description, that toggles wherever you click it.\n *\n * It's a `<label>` around a real checkbox, so it sends its `value` with a\n * form and Space toggles it. The title names the checkbox and the\n * description describes it. Alone it's one choice; inside `Checkboxes` it\n * takes the group's name and selection, as `Checkbox` does.\n *\n * @function\n * @param {CheckboxCardProps} props - Props for the CheckboxCard component.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the `<input>`.\n * @returns {JSX.Element} The rendered card.\n *\n * @example\n * <Checkboxes label=\"Add-ons\" name=\"extras\" defaultValue={['backups']}>\n * <CheckboxCard value=\"backups\" title=\"Daily backups\" description=\"Kept for 30 days.\" />\n * <CheckboxCard value=\"domain\" title=\"Custom domain\" description=\"Bring your own.\" />\n * </Checkboxes>\n */\nexport const CheckboxCard = forwardRef<HTMLInputElement, CheckboxCardProps>(\n (\n {\n title,\n description,\n children,\n hasIndicator = true,\n color,\n className,\n textColor,\n disabled,\n name,\n value,\n checked,\n defaultChecked,\n indeterminate,\n onChange,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n ...props,\n });\n\n // The input's own ref, merged with the forwarded one, as Checkbox does,\n // so the mixed state can be set on it.\n const inputRef = useRef<HTMLInputElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n },\n [ref]\n );\n\n // `indeterminate` is a DOM property with no attribute, and a click clears\n // it, so it's set after every render while the prop is set, and cleared\n // once when the prop goes back to unset. Checkbox does the same.\n const ownsIndeterminateRef = useRef(false);\n useClientLayoutEffect(() => {\n if (indeterminate !== undefined) {\n ownsIndeterminateRef.current = true;\n inputRef.current!.indeterminate = indeterminate;\n } else if (ownsIndeterminateRef.current) {\n ownsIndeterminateRef.current = false;\n inputRef.current!.indeterminate = false;\n }\n });\n\n // A surrounding <Checkboxes> hands down its name and, when it manages\n // the selection, whether this card's value is in it. The card's own\n // props win, as they do on Checkbox.\n const group = useCheckboxesGroup();\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!.includes(String(value))\n : undefined;\n const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n if (indeterminate !== undefined) e.target.indeterminate = indeterminate;\n onChange?.(e);\n if (group?.onChange && value !== undefined) {\n const valStr = String(value);\n const current = group.value ?? [];\n group.onChange(\n e.target.checked\n ? current.includes(valStr)\n ? current\n : [...current, valStr]\n : current.filter(v => v !== valStr)\n );\n }\n },\n [onChange, group, value, indeterminate]\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('checkbox-card', 'card', {\n [`is-${color}`]: color && checkboxCardColors.includes(color),\n });\n const contentClass = usePrefixedClassNames(\n 'card-content',\n 'checkbox-card-content'\n );\n const checkClass = usePrefixedClassNames('checkbox-card-check');\n const bodyClass = usePrefixedClassNames('checkbox-card-body');\n const titleClass = usePrefixedClassNames('checkbox-card-title');\n const descriptionClass = usePrefixedClassNames('checkbox-card-description');\n\n return (\n <label className={classNames(cardClass, bulmaHelperClasses, className)}>\n <input\n ref={setRefs}\n type=\"checkbox\"\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\nCheckboxCard.displayName = 'CheckboxCard';\n\nexport default CheckboxCard;\n"],"names":["_jsxs","_jsx"],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ import { Field } from './Field.js';
|
|
|
9
9
|
import { Control } from './Control.js';
|
|
10
10
|
import { useAutoLabelledBy } from './useAutoLabelId.js';
|
|
11
11
|
import { Checkbox } from './Checkbox.js';
|
|
12
|
+
import { GroupFieldset } from './groupFieldset.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
|
|
@@ -35,7 +36,7 @@ import { Checkbox } from './Checkbox.js';
|
|
|
35
36
|
* <Checkbox value="angular">Angular</Checkbox>
|
|
36
37
|
* </Checkboxes>
|
|
37
38
|
*/
|
|
38
|
-
const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
|
|
39
|
+
const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, as, ...props }) => {
|
|
39
40
|
const insideField = useInsideField();
|
|
40
41
|
const insideControl = useInsideControl();
|
|
41
42
|
const ownField = rendersOwnField('Checkboxes', {
|
|
@@ -46,10 +47,14 @@ const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message
|
|
|
46
47
|
horizontal,
|
|
47
48
|
fieldClassName,
|
|
48
49
|
});
|
|
50
|
+
// As a fieldset with a Field of its own, the group's label is its legend,
|
|
51
|
+
// which names it with no id, so nothing points `aria-labelledby` at it.
|
|
52
|
+
const asFieldset = as === 'fieldset';
|
|
53
|
+
const fieldsetField = asFieldset && ownField;
|
|
49
54
|
const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
|
|
50
55
|
label,
|
|
51
56
|
labelProps,
|
|
52
|
-
rendersLabel: ownField,
|
|
57
|
+
rendersLabel: ownField && !fieldsetField,
|
|
53
58
|
callerProps: props,
|
|
54
59
|
});
|
|
55
60
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
@@ -74,11 +79,17 @@ const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message
|
|
|
74
79
|
name,
|
|
75
80
|
...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
|
|
76
81
|
}), [name, groupActive, currentValue, handleChange]);
|
|
77
|
-
const
|
|
82
|
+
const choices = (jsx(CheckboxesProvider, { value: ctx, children: children }));
|
|
83
|
+
// A fieldset is a group already, so it takes no role of its own.
|
|
84
|
+
const Group = asFieldset ? 'fieldset' : 'div';
|
|
85
|
+
const checkboxesElement = fieldsetField ? (jsx("div", { className: wrapperClass, children: choices })) : (jsx(Group, { className: wrapperClass, role: asFieldset ? undefined : 'group', "aria-labelledby": ariaLabelledBy, ...rest, children: choices }));
|
|
78
86
|
let content = checkboxesElement;
|
|
79
87
|
if (rendersOwnControl('Checkboxes', { insideControl })) {
|
|
80
88
|
content = jsx(Control, { children: content });
|
|
81
89
|
}
|
|
90
|
+
if (fieldsetField) {
|
|
91
|
+
return (jsx(GroupFieldset, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, fieldClassName: fieldClassName, attributes: rest, message: messageEl, children: content }));
|
|
92
|
+
}
|
|
82
93
|
if (ownField) {
|
|
83
94
|
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
|
|
84
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkboxes.js","sources":["../../../src/form/Checkboxes.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 CheckboxesProvider,\n CheckboxesGroupContextValue,\n} from './FormContext';\nimport { Field } from './Field';\nimport { Control } from './Control';\nimport { FormFieldProps } from './fieldProps';\nimport { useAutoLabelledBy } from './useAutoLabelId';\nimport { Checkbox } from './Checkbox';\n\n/**\n * Props for the Checkboxes 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 CheckboxesProps\n extends Omit<BulmaClassesProps, 'color'>, FormFieldProps {\n /** Field label naming the whole group. Automatically associated via `aria-labelledby` on the `role=\"group\"` 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 Checkbox in the group (via context). */\n name?: string;\n /** Currently-selected values (controlled mode). */\n value?: string[];\n /** Initial selected values (uncontrolled mode). */\n defaultValue?: string[];\n /** Fires when the selection changes; receives the new array. */\n onChange?: (values: string[]) => void;\n /** Checkbox elements to render in the group. */\n children?: React.ReactNode;\n}\n\n/**\n * The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.\n *\n * @function\n * @param {CheckboxesProps} props - Props for the Checkboxes component.\n * @returns {JSX.Element} The rendered checkboxes group.\n * @see {@link https://bulma.io/documentation/form/checkbox/#grouped-checkboxes | Bulma Checkboxes documentation}\n *\n * @example\n * // Controlled\n * const [tags, setTags] = useState(['react']);\n * <Checkboxes name=\"tags\" value={tags} onChange={setTags}>\n * <Checkbox value=\"react\">React</Checkbox>\n * <Checkbox value=\"vue\">Vue</Checkbox>\n * <Checkbox value=\"angular\">Angular</Checkbox>\n * </Checkboxes>\n *\n * @example\n * // Uncontrolled\n * <Checkboxes name=\"tags\" defaultValue={['react', 'vue']}>\n * <Checkbox value=\"react\">React</Checkbox>\n * <Checkbox value=\"vue\">Vue</Checkbox>\n * <Checkbox value=\"angular\">Angular</Checkbox>\n * </Checkboxes>\n */\nconst CheckboxesComponent: React.FC<CheckboxesProps> = ({\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 ...props\n}) => {\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField('Checkboxes', {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel: ownField,\n callerProps: props,\n });\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n ...props,\n });\n\n const mainClass = usePrefixedClassNames('checkboxes');\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 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 (newValues: string[]) => {\n if (!isControlled) setInternalValue(newValues);\n onChange?.(newValues);\n },\n [isControlled, onChange]\n );\n\n const ctx = useMemo<CheckboxesGroupContextValue>(\n () => ({\n name,\n ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),\n }),\n [name, groupActive, currentValue, handleChange]\n );\n\n const checkboxesElement = (\n <div\n className={wrapperClass}\n role=\"group\"\n aria-labelledby={ariaLabelledBy}\n {...rest}\n >\n <CheckboxesProvider value={ctx}>{children}</CheckboxesProvider>\n </div>\n );\n\n let content = checkboxesElement;\n\n if (rendersOwnControl('Checkboxes', { insideControl })) {\n content = <Control>{content}</Control>;\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 Checkboxes = withSubComponents(\n CheckboxesComponent,\n { Checkbox },\n 'Checkboxes'\n);\n\nexport default Checkboxes;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Checkboxes.js","sources":["../../../src/form/Checkboxes.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 CheckboxesProvider,\n CheckboxesGroupContextValue,\n} from './FormContext';\nimport { Field } from './Field';\nimport { Control } from './Control';\nimport { FormFieldProps } from './fieldProps';\nimport { useAutoLabelledBy } from './useAutoLabelId';\nimport { Checkbox } from './Checkbox';\nimport { GroupFieldset } from './groupFieldset';\n\n/**\n * Props for the Checkboxes 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 CheckboxesProps\n extends Omit<BulmaClassesProps, 'color'>, FormFieldProps {\n /** Field label naming the whole group. Automatically associated via `aria-labelledby` on the `role=\"group\"` 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 Checkbox in the group (via context). */\n name?: string;\n /** Currently-selected values (controlled mode). */\n value?: string[];\n /** Initial selected values (uncontrolled mode). */\n defaultValue?: string[];\n /** Fires when the selection changes; receives the new array. */\n onChange?: (values: string[]) => void;\n /** Checkbox 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>`. Where the group renders a `Field` of its own, that `Field`\n * is the `<fieldset>` and the `label` is its `<legend>`, in the label\n * column when `horizontal`, so the legend names the group with no ids to\n * wire. Your other attributes then go on the `<fieldset>`, while\n * `className` and the helper props stay on the box that lays out the\n * checkboxes. Inside an outer `Field` there's no legend: the group element\n * itself is the `<fieldset>`, named by that `Field`'s label as before.\n * `div`, the default, renders the group as it always has.\n * @defaultValue 'div'\n */\n as?: 'div' | 'fieldset';\n}\n\n/**\n * The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.\n *\n * @function\n * @param {CheckboxesProps} props - Props for the Checkboxes component.\n * @returns {JSX.Element} The rendered checkboxes group.\n * @see {@link https://bulma.io/documentation/form/checkbox/#grouped-checkboxes | Bulma Checkboxes documentation}\n *\n * @example\n * // Controlled\n * const [tags, setTags] = useState(['react']);\n * <Checkboxes name=\"tags\" value={tags} onChange={setTags}>\n * <Checkbox value=\"react\">React</Checkbox>\n * <Checkbox value=\"vue\">Vue</Checkbox>\n * <Checkbox value=\"angular\">Angular</Checkbox>\n * </Checkboxes>\n *\n * @example\n * // Uncontrolled\n * <Checkboxes name=\"tags\" defaultValue={['react', 'vue']}>\n * <Checkbox value=\"react\">React</Checkbox>\n * <Checkbox value=\"vue\">Vue</Checkbox>\n * <Checkbox value=\"angular\">Angular</Checkbox>\n * </Checkboxes>\n */\nconst CheckboxesComponent: React.FC<CheckboxesProps> = ({\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('Checkboxes', {\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('checkboxes');\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 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 (newValues: string[]) => {\n if (!isControlled) setInternalValue(newValues);\n onChange?.(newValues);\n },\n [isControlled, onChange]\n );\n\n const ctx = useMemo<CheckboxesGroupContextValue>(\n () => ({\n name,\n ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),\n }),\n [name, groupActive, currentValue, handleChange]\n );\n\n const choices = (\n <CheckboxesProvider value={ctx}>{children}</CheckboxesProvider>\n );\n // A fieldset is a group already, so it takes no role of its own.\n const Group = asFieldset ? 'fieldset' : 'div';\n const checkboxesElement = fieldsetField ? (\n <div className={wrapperClass}>{choices}</div>\n ) : (\n <Group\n className={wrapperClass}\n role={asFieldset ? undefined : 'group'}\n aria-labelledby={ariaLabelledBy}\n {...rest}\n >\n {choices}\n </Group>\n );\n\n let content = checkboxesElement;\n\n if (rendersOwnControl('Checkboxes', { 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 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 Checkboxes = withSubComponents(\n CheckboxesComponent,\n { Checkbox },\n 'Checkboxes'\n);\n\nexport default Checkboxes;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { forwardRef, useCallback, useId } from 'react';
|
|
4
|
+
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
5
|
+
import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
|
|
6
|
+
import { useRadiosGroup } from './FormContext.js';
|
|
7
|
+
|
|
8
|
+
// The colors `Radio` takes, as `radioColors` lists them. Declared here as
|
|
9
|
+
// well, so the API page can read them off this module, and a type test holds
|
|
10
|
+
// the two lists to each other.
|
|
11
|
+
const radioCardColors = [
|
|
12
|
+
'primary',
|
|
13
|
+
'link',
|
|
14
|
+
'info',
|
|
15
|
+
'success',
|
|
16
|
+
'warning',
|
|
17
|
+
'danger',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* The `RadioCard` component is a radio button drawn as a card, with a title and a description, picked wherever you click it.
|
|
21
|
+
*
|
|
22
|
+
* It's a `<label>` around a real radio, so it sends its `value` with a form,
|
|
23
|
+
* and the cards that share a `name` are one choice the arrow keys move
|
|
24
|
+
* through. The title names the radio and the description describes it.
|
|
25
|
+
* Inside `Radios` it takes the group's name and selection, as `Radio` does.
|
|
26
|
+
*
|
|
27
|
+
* @function
|
|
28
|
+
* @param {RadioCardProps} props - Props for the RadioCard component.
|
|
29
|
+
* @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the `<input>`.
|
|
30
|
+
* @returns {JSX.Element} The rendered card.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* <Radios label="Shipping" name="shipping" defaultValue="standard">
|
|
34
|
+
* <RadioCard value="standard" title="Standard" description="Arrives in 4 to 6 days." />
|
|
35
|
+
* <RadioCard value="express" title="Express" description="Arrives tomorrow." />
|
|
36
|
+
* </Radios>
|
|
37
|
+
*/
|
|
38
|
+
const RadioCard = forwardRef(({ title, description, children, hasIndicator = true, color, className, textColor, disabled, name, value, checked, defaultChecked, onChange, ...props }, ref) => {
|
|
39
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
40
|
+
color: textColor,
|
|
41
|
+
...props,
|
|
42
|
+
});
|
|
43
|
+
// A surrounding <Radios> hands down its name and, when it manages the
|
|
44
|
+
// selection, whether this card is the one picked. The card's own props
|
|
45
|
+
// win, as they do on Radio.
|
|
46
|
+
const group = useRadiosGroup();
|
|
47
|
+
const effectiveName = name ?? group?.name;
|
|
48
|
+
const groupManaged = group?.value !== undefined && value !== undefined;
|
|
49
|
+
const effectiveChecked = checked !== undefined
|
|
50
|
+
? checked
|
|
51
|
+
: groupManaged
|
|
52
|
+
? group.value === value
|
|
53
|
+
: undefined;
|
|
54
|
+
const effectiveDefaultChecked = groupManaged ? undefined : defaultChecked;
|
|
55
|
+
const handleChange = useCallback((e) => {
|
|
56
|
+
onChange?.(e);
|
|
57
|
+
if (group?.onChange && value !== undefined) {
|
|
58
|
+
group.onChange(String(value));
|
|
59
|
+
}
|
|
60
|
+
}, [onChange, group, value]);
|
|
61
|
+
// The title names the input and the description describes it. A name
|
|
62
|
+
// passed in wins, and without one of either the label's own text names
|
|
63
|
+
// the input, description included, so it isn't described again.
|
|
64
|
+
const titleId = useId();
|
|
65
|
+
const descriptionId = useId();
|
|
66
|
+
const ariaLabel = rest['aria-label'];
|
|
67
|
+
const ariaLabelledBy = rest['aria-labelledby'] || (!ariaLabel && title ? titleId : undefined);
|
|
68
|
+
const ariaDescribedBy = rest['aria-describedby'] ||
|
|
69
|
+
(description && (ariaLabel || ariaLabelledBy)
|
|
70
|
+
? descriptionId
|
|
71
|
+
: undefined);
|
|
72
|
+
const cardClass = usePrefixedClassNames('radio-card', 'card', {
|
|
73
|
+
[`is-${color}`]: color && radioCardColors.includes(color),
|
|
74
|
+
});
|
|
75
|
+
const contentClass = usePrefixedClassNames('card-content', 'radio-card-content');
|
|
76
|
+
const checkClass = usePrefixedClassNames('radio-card-check');
|
|
77
|
+
const bodyClass = usePrefixedClassNames('radio-card-body');
|
|
78
|
+
const titleClass = usePrefixedClassNames('radio-card-title');
|
|
79
|
+
const descriptionClass = usePrefixedClassNames('radio-card-description');
|
|
80
|
+
return (jsxs("label", { className: classNames(cardClass, bulmaHelperClasses, className), children: [jsx("input", { ref: ref, type: "radio", disabled: disabled, name: effectiveName, value: value, checked: effectiveChecked, defaultChecked: effectiveDefaultChecked, onChange: handleChange, ...rest, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy }), jsxs("span", { className: contentClass, children: [hasIndicator && jsx("span", { className: checkClass }), jsxs("span", { className: bodyClass, children: [title ? (jsx("span", { id: titleId, className: titleClass, children: title })) : null, description ? (jsx("span", { id: descriptionId, className: descriptionClass, children: description })) : null, children] })] })] }));
|
|
81
|
+
});
|
|
82
|
+
RadioCard.displayName = 'RadioCard';
|
|
83
|
+
|
|
84
|
+
export { RadioCard };
|
|
85
|
+
//# sourceMappingURL=RadioCard.js.map
|
|
@@ -0,0 +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 { 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,\n name,\n value,\n checked,\n defaultChecked,\n onChange,\n ...props\n },\n ref\n ) => {\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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"}
|
package/dist/esm/form/Radios.js
CHANGED
|
@@ -9,6 +9,7 @@ import { Field } from './Field.js';
|
|
|
9
9
|
import { Control } from './Control.js';
|
|
10
10
|
import { useAutoLabelledBy } from './useAutoLabelId.js';
|
|
11
11
|
import { Radio } from './Radio.js';
|
|
12
|
+
import { GroupFieldset } from './groupFieldset.js';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* The `Radios` component wraps multiple `Radio` components in a Bulma-styled group.
|
|
@@ -34,7 +35,7 @@ import { Radio } from './Radio.js';
|
|
|
34
35
|
* <Radio value="green">Green</Radio>
|
|
35
36
|
* </Radios>
|
|
36
37
|
*/
|
|
37
|
-
const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
|
|
38
|
+
const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, as, ...props }) => {
|
|
38
39
|
const insideField = useInsideField();
|
|
39
40
|
const insideControl = useInsideControl();
|
|
40
41
|
const ownField = rendersOwnField('Radios', {
|
|
@@ -45,10 +46,14 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
|
|
|
45
46
|
horizontal,
|
|
46
47
|
fieldClassName,
|
|
47
48
|
});
|
|
49
|
+
// As a fieldset with a Field of its own, the group's label is its legend,
|
|
50
|
+
// which names it with no id, so nothing points `aria-labelledby` at it.
|
|
51
|
+
const asFieldset = as === 'fieldset';
|
|
52
|
+
const fieldsetField = asFieldset && ownField;
|
|
48
53
|
const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
|
|
49
54
|
label,
|
|
50
55
|
labelProps,
|
|
51
|
-
rendersLabel: ownField,
|
|
56
|
+
rendersLabel: ownField && !fieldsetField,
|
|
52
57
|
callerProps: props,
|
|
53
58
|
});
|
|
54
59
|
const { bulmaHelperClasses, rest } = useBulmaClasses({
|
|
@@ -76,11 +81,17 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
|
|
|
76
81
|
name,
|
|
77
82
|
...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
|
|
78
83
|
}), [name, groupActive, currentValue, handleChange]);
|
|
79
|
-
const
|
|
84
|
+
const choices = jsx(RadiosProvider, { value: ctx, children: children });
|
|
85
|
+
// ARIA in HTML allows `radiogroup` on a fieldset, so it keeps the role.
|
|
86
|
+
const Group = asFieldset ? 'fieldset' : 'div';
|
|
87
|
+
const radiosElement = fieldsetField ? (jsx("div", { className: wrapperClass, children: choices })) : (jsx(Group, { className: wrapperClass, role: "radiogroup", "aria-labelledby": ariaLabelledBy, ...rest, children: choices }));
|
|
80
88
|
let content = radiosElement;
|
|
81
89
|
if (rendersOwnControl('Radios', { insideControl })) {
|
|
82
90
|
content = jsx(Control, { children: content });
|
|
83
91
|
}
|
|
92
|
+
if (fieldsetField) {
|
|
93
|
+
return (jsx(GroupFieldset, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, fieldClassName: fieldClassName, role: "radiogroup", attributes: rest, message: messageEl, children: content }));
|
|
94
|
+
}
|
|
84
95
|
if (ownField) {
|
|
85
96
|
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
|
|
86
97
|
}
|
|
@@ -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';\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\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 ...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 const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel: ownField,\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 radiosElement = (\n <div\n className={wrapperClass}\n role=\"radiogroup\"\n aria-labelledby={ariaLabelledBy}\n {...rest}\n >\n <RadiosProvider value={ctx}>{children}</RadiosProvider>\n </div>\n );\n\n let content = radiosElement;\n\n if (rendersOwnControl('Radios', { insideControl })) {\n content = <Control>{content}</Control>;\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 /** 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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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
|
|
4
|
+
import { FieldProvider } from './FormContext.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `Field` a `Checkboxes` or `Radios` with `as="fieldset"` renders of its
|
|
8
|
+
* own, as a native `<fieldset>`: Bulma's field markup, with its label as the
|
|
9
|
+
* `<legend>` (inside the `field-label` column when horizontal), so the legend
|
|
10
|
+
* names the group and needs no id. It holds a `Field`'s place for what is
|
|
11
|
+
* inside, so the choices read as inside a `Field`. Internal; not part of the
|
|
12
|
+
* public API.
|
|
13
|
+
*/
|
|
14
|
+
const GroupFieldset = ({ label, labelSize, labelProps, horizontal, fieldClassName, role, attributes, message, children, }) => {
|
|
15
|
+
const fieldClass = usePrefixedClassNames('field', {
|
|
16
|
+
'is-horizontal': !!horizontal,
|
|
17
|
+
});
|
|
18
|
+
const labelClass = usePrefixedClassNames('label');
|
|
19
|
+
const fieldLabelClass = usePrefixedClassNames('field-label', {
|
|
20
|
+
[`is-${labelSize || 'normal'}`]: true,
|
|
21
|
+
});
|
|
22
|
+
const fieldBodyClass = usePrefixedClassNames('field-body');
|
|
23
|
+
// A legend names its fieldset with no `for`, so a `htmlFor` has nothing
|
|
24
|
+
// to point at.
|
|
25
|
+
const { htmlFor: _htmlFor, className: labelClassName, ...ownLabelProps } = labelProps ?? {};
|
|
26
|
+
const shownLabel = {
|
|
27
|
+
...ownLabelProps,
|
|
28
|
+
className: classNames(labelClass, labelClassName),
|
|
29
|
+
};
|
|
30
|
+
let legend = null;
|
|
31
|
+
if (label) {
|
|
32
|
+
legend = horizontal ? (jsx("legend", { className: fieldLabelClass, children: jsx("span", { ...shownLabel, children: label }) })) : (jsx("legend", { ...shownLabel, children: label }));
|
|
33
|
+
}
|
|
34
|
+
return (jsx(FieldProvider, { value: true, children: jsxs("fieldset", { className: classNames(fieldClass, fieldClassName), role: role, ...attributes, children: [legend, horizontal ? (jsxs("div", { className: fieldBodyClass, children: [children, message] })) : (jsxs(Fragment, { children: [children, message] }))] }) }));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { GroupFieldset };
|
|
38
|
+
//# sourceMappingURL=groupFieldset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupFieldset.js","sources":["../../../src/form/groupFieldset.tsx"],"sourcesContent":["import React from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { FieldProvider } from './FormContext';\nimport type { FormFieldProps } from './fieldProps';\n\ninterface GroupFieldsetProps {\n /** The group's label, rendered as the `<legend>`. */\n label: React.ReactNode;\n /** Sizes the legend's label column when `horizontal`. */\n labelSize?: FormFieldProps['labelSize'];\n /** Props for the element that shows the label; any `htmlFor` is dropped. */\n labelProps?: FormFieldProps['labelProps'];\n /** Puts the legend in the label column and the rest in the body. */\n horizontal?: boolean;\n /** Set on the `<fieldset>` beside its `field` class. */\n fieldClassName?: string;\n /** The group's role, when it isn't the fieldset's own `group`. */\n role?: 'radiogroup';\n /** The group's own attributes: they describe the group, so they go on the `<fieldset>`. */\n attributes: object;\n /** The group's help text, after the content. */\n message: React.ReactNode;\n /** The group's content: its `Control` and the box of choices. */\n children: React.ReactNode;\n}\n\n/**\n * The `Field` a `Checkboxes` or `Radios` with `as=\"fieldset\"` renders of its\n * own, as a native `<fieldset>`: Bulma's field markup, with its label as the\n * `<legend>` (inside the `field-label` column when horizontal), so the legend\n * names the group and needs no id. It holds a `Field`'s place for what is\n * inside, so the choices read as inside a `Field`. Internal; not part of the\n * public API.\n */\nexport const GroupFieldset: React.FC<GroupFieldsetProps> = ({\n label,\n labelSize,\n labelProps,\n horizontal,\n fieldClassName,\n role,\n attributes,\n message,\n children,\n}) => {\n const fieldClass = usePrefixedClassNames('field', {\n 'is-horizontal': !!horizontal,\n });\n const labelClass = usePrefixedClassNames('label');\n const fieldLabelClass = usePrefixedClassNames('field-label', {\n [`is-${labelSize || 'normal'}`]: true,\n });\n const fieldBodyClass = usePrefixedClassNames('field-body');\n\n // A legend names its fieldset with no `for`, so a `htmlFor` has nothing\n // to point at.\n const {\n htmlFor: _htmlFor,\n className: labelClassName,\n ...ownLabelProps\n } = labelProps ?? {};\n void _htmlFor;\n const shownLabel = {\n ...ownLabelProps,\n className: classNames(labelClass, labelClassName as string | undefined),\n } as React.HTMLAttributes<HTMLElement>;\n\n let legend: React.ReactNode = null;\n if (label) {\n legend = horizontal ? (\n <legend className={fieldLabelClass}>\n <span {...shownLabel}>{label}</span>\n </legend>\n ) : (\n <legend {...shownLabel}>{label}</legend>\n );\n }\n\n return (\n <FieldProvider value={true}>\n <fieldset\n className={classNames(fieldClass, fieldClassName)}\n role={role}\n {...attributes}\n >\n {legend}\n {horizontal ? (\n <div className={fieldBodyClass}>\n {children}\n {message}\n </div>\n ) : (\n <>\n {children}\n {message}\n </>\n )}\n </fieldset>\n </FieldProvider>\n );\n};\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":";;;;;AA0BA;;;;;;;AAOG;AACI,MAAM,aAAa,GAAiC,CAAC,EAC1D,KAAK,EACL,SAAS,EACT,UAAU,EACV,UAAU,EACV,cAAc,EACd,IAAI,EACJ,UAAU,EACV,OAAO,EACP,QAAQ,GACT,KAAI;AACH,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,EAAE;QAChD,eAAe,EAAE,CAAC,CAAC,UAAU;AAC9B,KAAA,CAAC;AACF,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC;AACjD,IAAA,MAAM,eAAe,GAAG,qBAAqB,CAAC,aAAa,EAAE;AAC3D,QAAA,CAAC,MAAM,SAAS,IAAI,QAAQ,CAAA,CAAE,GAAG,IAAI;AACtC,KAAA,CAAC;AACF,IAAA,MAAM,cAAc,GAAG,qBAAqB,CAAC,YAAY,CAAC;;;AAI1D,IAAA,MAAM,EACJ,OAAO,EAAE,QAAQ,EACjB,SAAS,EAAE,cAAc,EACzB,GAAG,aAAa,EACjB,GAAG,UAAU,IAAI,EAAE;AAEpB,IAAA,MAAM,UAAU,GAAG;AACjB,QAAA,GAAG,aAAa;AAChB,QAAA,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,cAAoC,CAAC;KACnC;IAEtC,IAAI,MAAM,GAAoB,IAAI;IAClC,IAAI,KAAK,EAAE;AACT,QAAA,MAAM,GAAG,UAAU,IACjBA,GAAA,CAAA,QAAA,EAAA,EAAQ,SAAS,EAAE,eAAe,EAAA,QAAA,EAChCA,GAAA,CAAA,MAAA,EAAA,EAAA,GAAU,UAAU,EAAA,QAAA,EAAG,KAAK,EAAA,CAAQ,EAAA,CAC7B,KAETA,GAAA,CAAA,QAAA,EAAA,EAAA,GAAY,UAAU,EAAA,QAAA,EAAG,KAAK,EAAA,CAAU,CACzC;IACH;IAEA,QACEA,GAAA,CAAC,aAAa,EAAA,EAAC,KAAK,EAAE,IAAI,EAAA,QAAA,EACxBC,IAAA,CAAA,UAAA,EAAA,EACE,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,cAAc,CAAC,EACjD,IAAI,EAAE,IAAI,EAAA,GACN,UAAU,EAAA,QAAA,EAAA,CAEb,MAAM,EACN,UAAU,IACTA,IAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,cAAc,EAAA,QAAA,EAAA,CAC3B,QAAQ,EACR,OAAO,CAAA,EAAA,CACJ,KAENA,IAAA,CAAAC,QAAA,EAAA,EAAA,QAAA,EAAA,CACG,QAAQ,EACR,OAAO,CAAA,EAAA,CACP,CACJ,CAAA,EAAA,CACQ,EAAA,CACG;AAEpB;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
2
|
import { useId } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { useFieldLabelId, useFieldLabelFor, useReportFieldLabelFor, useFieldLabelElementId } from './FormContext.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Associates the convenience `label` prop with its control (#368): generates
|