@allxsmith/bestax-bulma 5.27.3 → 5.27.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"useAutoLabelId.js","sources":["../../../src/form/useAutoLabelId.ts"],"sourcesContent":["import React, { useId } from 'react';\nimport type { FieldProps } from './Field';\nimport {\n useFieldLabelElementId,\n useFieldLabelFor,\n useFieldLabelId,\n} from './FormContext';\n\ninterface UseAutoLabelIdOptions {\n /** The convenience `label` prop as passed by the caller. */\n label: React.ReactNode;\n /** User-supplied id for the control, if any. */\n id?: string;\n /** User-supplied labelProps, if any. */\n labelProps?: FieldProps['labelProps'];\n /**\n * True when this render actually outputs the label wired to a control —\n * false inside an outer Field (label is dropped), in variants that render\n * no Field, and in modes with no labellable control (e.g. inline pickers).\n */\n rendersLabel: boolean;\n /**\n * False when this render has no input to label at all (an inline picker),\n * so it takes no id from a surrounding Field either. Defaults to true.\n */\n hasInput?: boolean;\n}\n\n/**\n * Associates the convenience `label` prop with its control (#368): generates\n * an id for the control and returns labelProps carrying a matching `htmlFor`.\n * A user-supplied `id` is used as the target instead of the generated one, and\n * an explicit `htmlFor` key in labelProps — even set to `undefined` — disables\n * generation entirely: the user has taken over the association (#495 presence\n * semantics). When the control renders no label of its own, it adopts the id\n * a labeled Field around it offers, the way the bases do, so that Field's\n * label names it (#939); a user `id` still wins there.\n * Internal; not part of the public API.\n */\nexport function useAutoLabelId({\n label,\n id,\n labelProps,\n rendersLabel,\n hasInput = true,\n}: UseAutoLabelIdOptions): {\n controlId: string | undefined;\n fieldLabelProps: FieldProps['labelProps'] | undefined;\n /** Whether a rendered label, its own or a surrounding Field's, points at `controlId`. */\n labelled: boolean;\n} {\n // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.\n const generatedId = useId();\n // Read outside the control's own Field, so it is set only when an outer\n // Field holds the control, which then renders no label of its own.\n const fieldLabelId = useFieldLabelId();\n // What an outer Field's label points at, even when wired by hand.\n const fieldLabelFor = useFieldLabelFor();\n // Truthiness mirrors Field's own `if (label)` render gate.\n const active = !!label && rendersLabel;\n // Presence, not truthiness: `htmlFor: undefined` is an explicit opt-out and\n // must not leave an orphan generated id on the control.\n const userWired = !!labelProps && 'htmlFor' in labelProps;\n const adopted = !active && hasInput ? fieldLabelId : undefined;\n const controlId = id ?? (active && !userWired ? generatedId : adopted);\n const ownLabelProps = { htmlFor: controlId, ...labelProps };\n // Inactive with a label still means an own Field may render it (pickers'\n // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`\n // tells Field the association is owned here, so it must not generate one\n // that would dangle (#495 presence semantics).\n const fieldLabelProps = active\n ? ownLabelProps\n : label\n ? { htmlFor: undefined, ...labelProps }\n : labelProps;\n const labelTarget = active ? ownLabelProps.htmlFor : fieldLabelFor;\n return {\n controlId,\n fieldLabelProps,\n labelled: !!controlId && labelTarget === controlId,\n };\n}\n\ninterface UseAutoLabelledByOptions {\n /** The convenience `label` prop as passed by the caller. */\n label: React.ReactNode;\n /** User-supplied labelProps, if any. */\n labelProps?: FieldProps['labelProps'];\n /** True when this render actually outputs the label naming the group. */\n rendersLabel: boolean;\n /**\n * The caller's remaining props. A non-empty `aria-label` among them names\n * the group, and so does any `aria-labelledby` key, even an undefined one,\n * since the group spreads them after its own attribute. Either keeps every\n * label off the group, its own and a surrounding Field's alike.\n */\n callerProps: object;\n}\n\n/**\n * Group-input counterpart of {@link useAutoLabelId} (#494): a group of\n * controls cannot take a single `htmlFor`, so instead the rendered `<label>`\n * gets a generated id and the group container points at it with\n * `aria-labelledby`. A user-supplied `labelProps.id` is used as the target\n * instead of generating one. Any caller `htmlFor` is stripped — a group label\n * names the group, never a single control — so the merged labelProps always\n * carry an explicit `htmlFor: undefined`. When the group renders no label of\n * its own, it points at the label of a labeled Field around it instead\n * (#939). A caller who named the group with `aria-label` or\n * `aria-labelledby` gets no `aria-labelledby` from either label, so their\n * name wins wherever the label sits.\n * Internal; not part of the public API.\n */\nexport function useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel,\n callerProps,\n}: UseAutoLabelledByOptions): {\n ariaLabelledBy: string | undefined;\n fieldLabelProps: FieldProps['labelProps'] | undefined;\n} {\n // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.\n const generatedId = useId();\n // Set only when an outer Field holds the group (see useAutoLabelId).\n const fieldLabelElementId = useFieldLabelElementId();\n const active = !!label && rendersLabel;\n const labelId = labelProps?.id ?? (active ? generatedId : undefined);\n const fieldLabelProps = active\n ? { ...labelProps, id: labelId, htmlFor: undefined }\n : labelProps;\n const aria = callerProps as React.AriaAttributes;\n // Presence for aria-labelledby: the caller's key replaces the attribute\n // through the spread whatever its value, and pointing it at a label would\n // only take a Rate's fallback name away. Checked before either label, so\n // the caller's name wins over the group's own `label` as it does over a\n // Field's, the way an aria-label beats a `<label for>` on a single input.\n const callerNamed = !!aria['aria-label'] || 'aria-labelledby' in aria;\n const ariaLabelledBy = callerNamed\n ? undefined\n : active\n ? labelId\n : fieldLabelElementId;\n return { ariaLabelledBy, fieldLabelProps };\n}\n"],"names":[],"mappings":";;;;AA4BA;;;;;;;;;;AAUG;AACG,SAAU,cAAc,CAAC,EAC7B,KAAK,EACL,EAAE,EACF,UAAU,EACV,YAAY,EACZ,QAAQ,GAAG,IAAI,GACO,EAAA;;AAOtB,IAAA,MAAM,WAAW,GAAG,KAAK,EAAE;;;AAG3B,IAAA,MAAM,YAAY,GAAG,eAAe,EAAE;;AAEtC,IAAA,MAAM,aAAa,GAAG,gBAAgB,EAAE;;AAExC,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,YAAY;;;IAGtC,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,IAAI,SAAS,IAAI,UAAU;AACzD,IAAA,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,QAAQ,GAAG,YAAY,GAAG,SAAS;AAC9D,IAAA,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,IAAI,CAAC,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IACtE,MAAM,aAAa,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE;;;;;IAK3D,MAAM,eAAe,GAAG;AACtB,UAAE;AACF,UAAE;cACE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU;cACnC,UAAU;AAChB,IAAA,MAAM,WAAW,GAAG,MAAM,GAAG,aAAa,CAAC,OAAO,GAAG,aAAa;IAClE,OAAO;QACL,SAAS;QACT,eAAe;AACf,QAAA,QAAQ,EAAE,CAAC,CAAC,SAAS,IAAI,WAAW,KAAK,SAAS;KACnD;AACH;AAkBA;;;;;;;;;;;;;AAaG;AACG,SAAU,iBAAiB,CAAC,EAChC,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,GACc,EAAA;;AAKzB,IAAA,MAAM,WAAW,GAAG,KAAK,EAAE;;AAE3B,IAAA,MAAM,mBAAmB,GAAG,sBAAsB,EAAE;AACpD,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,YAAY;AACtC,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,EAAE,KAAK,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACpE,MAAM,eAAe,GAAG;AACtB,UAAE,EAAE,GAAG,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS;UAChD,UAAU;IACd,MAAM,IAAI,GAAG,WAAmC;;;;;;AAMhD,IAAA,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,iBAAiB,IAAI,IAAI;IACrE,MAAM,cAAc,GAAG;AACrB,UAAE;AACF,UAAE;AACA,cAAE;cACA,mBAAmB;AACzB,IAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE;AAC5C;;"}
1
+ {"version":3,"file":"useAutoLabelId.js","sources":["../../../src/form/useAutoLabelId.ts"],"sourcesContent":["import React, { useId } from 'react';\nimport type { FieldProps } from './Field';\nimport {\n useFieldLabelElementId,\n useFieldLabelFor,\n useFieldLabelId,\n} from './FormContext';\n\ninterface UseAutoLabelIdOptions {\n /** The convenience `label` prop as passed by the caller. */\n label: React.ReactNode;\n /** User-supplied id for the control, if any. */\n id?: string;\n /** User-supplied labelProps, if any. */\n labelProps?: FieldProps['labelProps'];\n /**\n * True when this render actually outputs the label wired to a control —\n * false inside an outer Field (label is dropped), in variants that render\n * no Field, and in modes with no labellable control (e.g. inline pickers).\n */\n rendersLabel: boolean;\n /**\n * False when this render has no input to label at all (an inline picker),\n * so it takes no id from a surrounding Field either. Defaults to true.\n */\n hasInput?: boolean;\n /**\n * True when the control also points `aria-labelledby` at the label, as a\n * range Slider's thumbs do (#981), so it needs the label's own id. Its own\n * label then gets one (`labelProps.id` when given, otherwise a generated\n * one), and the hook returns it, or a surrounding Field's, as `labelId`.\n */\n needsLabelId?: boolean;\n}\n\n/**\n * Associates the convenience `label` prop with its control (#368): generates\n * an id for the control and returns labelProps carrying a matching `htmlFor`.\n * A user-supplied `id` is used as the target instead of the generated one, and\n * an explicit `htmlFor` key in labelProps — even set to `undefined` — disables\n * generation entirely: the user has taken over the association (#495 presence\n * semantics). When the control renders no label of its own, it adopts the id\n * a labeled Field around it offers, the way the bases do, so that Field's\n * label names it (#939); a user `id` still wins there. With `needsLabelId`\n * it also returns the id of whichever label names the control (#981).\n * Internal; not part of the public API.\n */\nexport function useAutoLabelId({\n label,\n id,\n labelProps,\n rendersLabel,\n hasInput = true,\n needsLabelId = false,\n}: UseAutoLabelIdOptions): {\n controlId: string | undefined;\n fieldLabelProps: FieldProps['labelProps'] | undefined;\n /** Whether a rendered label, its own or a surrounding Field's, points at `controlId`. */\n labelled: boolean;\n /**\n * With `needsLabelId`, the id of the label that names `controlId`, its own\n * or a surrounding Field's. Undefined when no label names it, or when the\n * Field's label was wired by hand, since that Field hands out no label id.\n */\n labelId: string | undefined;\n} {\n // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.\n const generatedId = useId();\n // Read outside the control's own Field, so it is set only when an outer\n // Field holds the control, which then renders no label of its own.\n const fieldLabelId = useFieldLabelId();\n // What an outer Field's label points at, even when wired by hand.\n const fieldLabelFor = useFieldLabelFor();\n // That Field's label element, set under the same conditions.\n const fieldLabelElementId = useFieldLabelElementId();\n // Truthiness mirrors Field's own `if (label)` render gate.\n const active = !!label && rendersLabel;\n // Presence, not truthiness: `htmlFor: undefined` is an explicit opt-out and\n // must not leave an orphan generated id on the control.\n const userWired = !!labelProps && 'htmlFor' in labelProps;\n const adopted = !active && hasInput ? fieldLabelId : undefined;\n const controlId = id ?? (active && !userWired ? generatedId : adopted);\n const ownLabelProps = { htmlFor: controlId, ...labelProps };\n const labelTarget = active ? ownLabelProps.htmlFor : fieldLabelFor;\n const labelled = !!controlId && labelTarget === controlId;\n // Only a label that names the control gets an id to point at, with the\n // suffix Field gives its own, from an id that is already unique.\n const ownLabelId =\n needsLabelId && active && labelled\n ? (labelProps?.id ?? `${generatedId}-label`)\n : undefined;\n // Inactive with a label still means an own Field may render it (pickers'\n // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`\n // tells Field the association is owned here, so it must not generate one\n // that would dangle (#495 presence semantics).\n const fieldLabelProps = active\n ? ownLabelId\n ? { ...ownLabelProps, id: ownLabelId }\n : ownLabelProps\n : label\n ? { htmlFor: undefined, ...labelProps }\n : labelProps;\n const labelId =\n needsLabelId && labelled\n ? active\n ? ownLabelId\n : fieldLabelElementId\n : undefined;\n return { controlId, fieldLabelProps, labelled, labelId };\n}\n\ninterface UseAutoLabelledByOptions {\n /** The convenience `label` prop as passed by the caller. */\n label: React.ReactNode;\n /** User-supplied labelProps, if any. */\n labelProps?: FieldProps['labelProps'];\n /** True when this render actually outputs the label naming the group. */\n rendersLabel: boolean;\n /**\n * The caller's remaining props. A non-empty `aria-label` among them names\n * the group, and so does any `aria-labelledby` key, even an undefined one,\n * since the group spreads them after its own attribute. Either keeps every\n * label off the group, its own and a surrounding Field's alike.\n */\n callerProps: object;\n}\n\n/**\n * Group-input counterpart of {@link useAutoLabelId} (#494): a group of\n * controls cannot take a single `htmlFor`, so instead the rendered `<label>`\n * gets a generated id and the group container points at it with\n * `aria-labelledby`. A user-supplied `labelProps.id` is used as the target\n * instead of generating one. Any caller `htmlFor` is stripped — a group label\n * names the group, never a single control — so the merged labelProps always\n * carry an explicit `htmlFor: undefined`. When the group renders no label of\n * its own, it points at the label of a labeled Field around it instead\n * (#939). A caller who named the group with `aria-label` or\n * `aria-labelledby` gets no `aria-labelledby` from either label, so their\n * name wins wherever the label sits.\n * Internal; not part of the public API.\n */\nexport function useAutoLabelledBy({\n label,\n labelProps,\n rendersLabel,\n callerProps,\n}: UseAutoLabelledByOptions): {\n ariaLabelledBy: string | undefined;\n fieldLabelProps: FieldProps['labelProps'] | undefined;\n} {\n // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.\n const generatedId = useId();\n // Set only when an outer Field holds the group (see useAutoLabelId).\n const fieldLabelElementId = useFieldLabelElementId();\n const active = !!label && rendersLabel;\n const labelId = labelProps?.id ?? (active ? generatedId : undefined);\n const fieldLabelProps = active\n ? { ...labelProps, id: labelId, htmlFor: undefined }\n : labelProps;\n const aria = callerProps as React.AriaAttributes;\n // Presence for aria-labelledby: the caller's key replaces the attribute\n // through the spread whatever its value, and pointing it at a label would\n // only take a Rate's fallback name away. Checked before either label, so\n // the caller's name wins over the group's own `label` as it does over a\n // Field's, the way an aria-label beats a `<label for>` on a single input.\n const callerNamed = !!aria['aria-label'] || 'aria-labelledby' in aria;\n const ariaLabelledBy = callerNamed\n ? undefined\n : active\n ? labelId\n : fieldLabelElementId;\n return { ariaLabelledBy, fieldLabelProps };\n}\n"],"names":[],"mappings":";;;;AAmCA;;;;;;;;;;;AAWG;SACa,cAAc,CAAC,EAC7B,KAAK,EACL,EAAE,EACF,UAAU,EACV,YAAY,EACZ,QAAQ,GAAG,IAAI,EACf,YAAY,GAAG,KAAK,GACE,EAAA;;AAatB,IAAA,MAAM,WAAW,GAAG,KAAK,EAAE;;;AAG3B,IAAA,MAAM,YAAY,GAAG,eAAe,EAAE;;AAEtC,IAAA,MAAM,aAAa,GAAG,gBAAgB,EAAE;;AAExC,IAAA,MAAM,mBAAmB,GAAG,sBAAsB,EAAE;;AAEpD,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,YAAY;;;IAGtC,MAAM,SAAS,GAAG,CAAC,CAAC,UAAU,IAAI,SAAS,IAAI,UAAU;AACzD,IAAA,MAAM,OAAO,GAAG,CAAC,MAAM,IAAI,QAAQ,GAAG,YAAY,GAAG,SAAS;AAC9D,IAAA,MAAM,SAAS,GAAG,EAAE,KAAK,MAAM,IAAI,CAAC,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;IACtE,MAAM,aAAa,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE;AAC3D,IAAA,MAAM,WAAW,GAAG,MAAM,GAAG,aAAa,CAAC,OAAO,GAAG,aAAa;IAClE,MAAM,QAAQ,GAAG,CAAC,CAAC,SAAS,IAAI,WAAW,KAAK,SAAS;;;AAGzD,IAAA,MAAM,UAAU,GACd,YAAY,IAAI,MAAM,IAAI;WACrB,UAAU,EAAE,EAAE,IAAI,CAAA,EAAG,WAAW,CAAA,MAAA,CAAQ;UACzC,SAAS;;;;;IAKf,MAAM,eAAe,GAAG;AACtB,UAAE;cACE,EAAE,GAAG,aAAa,EAAE,EAAE,EAAE,UAAU;AACpC,cAAE;AACJ,UAAE;cACE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU;cACnC,UAAU;AAChB,IAAA,MAAM,OAAO,GACX,YAAY,IAAI;AACd,UAAE;AACA,cAAE;AACF,cAAE;UACF,SAAS;IACf,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE;AAC1D;AAkBA;;;;;;;;;;;;;AAaG;AACG,SAAU,iBAAiB,CAAC,EAChC,KAAK,EACL,UAAU,EACV,YAAY,EACZ,WAAW,GACc,EAAA;;AAKzB,IAAA,MAAM,WAAW,GAAG,KAAK,EAAE;;AAE3B,IAAA,MAAM,mBAAmB,GAAG,sBAAsB,EAAE;AACpD,IAAA,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,IAAI,YAAY;AACtC,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,EAAE,KAAK,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACpE,MAAM,eAAe,GAAG;AACtB,UAAE,EAAE,GAAG,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS;UAChD,UAAU;IACd,MAAM,IAAI,GAAG,WAAmC;;;;;;AAMhD,IAAA,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,iBAAiB,IAAI,IAAI;IACrE,MAAM,cAAc,GAAG;AACrB,UAAE;AACF,UAAE;AACA,cAAE;cACA,mBAAmB;AACzB,IAAA,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE;AAC5C;;"}
package/dist/index.cjs CHANGED
@@ -9079,8 +9079,10 @@ const FieldLabelElementIdContext = React.createContext(undefined);
9079
9079
  /**
9080
9080
  * The id of a labeled Field's own `<label>`, for a group control (Radios,
9081
9081
  * Checkboxes, Rate, DateRangeInput) to point `aria-labelledby` at, since a group cannot take
9082
- * the label's `htmlFor` (#939). Set under the same conditions as
9083
- * {@link useFieldLabelId}. Consumed through `useAutoLabelledBy`.
9082
+ * the label's `htmlFor` (#939), and for a range Slider's thumbs, which each
9083
+ * need the label in a name of their own (#981). Set under the same conditions
9084
+ * as {@link useFieldLabelId}. Consumed through `useAutoLabelledBy` and
9085
+ * `useAutoLabelId`.
9084
9086
  * Internal; not part of the public API.
9085
9087
  */
9086
9088
  const useFieldLabelElementId = () => React.useContext(FieldLabelElementIdContext);
@@ -9459,10 +9461,11 @@ const Field = withSubComponents(FieldComponent, {
9459
9461
  * generation entirely: the user has taken over the association (#495 presence
9460
9462
  * semantics). When the control renders no label of its own, it adopts the id
9461
9463
  * a labeled Field around it offers, the way the bases do, so that Field's
9462
- * label names it (#939); a user `id` still wins there.
9464
+ * label names it (#939); a user `id` still wins there. With `needsLabelId`
9465
+ * it also returns the id of whichever label names the control (#981).
9463
9466
  * Internal; not part of the public API.
9464
9467
  */
9465
- function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true, }) {
9468
+ function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true, needsLabelId = false, }) {
9466
9469
  // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
9467
9470
  const generatedId = React.useId();
9468
9471
  // Read outside the control's own Field, so it is set only when an outer
@@ -9470,6 +9473,8 @@ function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true,
9470
9473
  const fieldLabelId = useFieldLabelId();
9471
9474
  // What an outer Field's label points at, even when wired by hand.
9472
9475
  const fieldLabelFor = useFieldLabelFor();
9476
+ // That Field's label element, set under the same conditions.
9477
+ const fieldLabelElementId = useFieldLabelElementId();
9473
9478
  // Truthiness mirrors Field's own `if (label)` render gate.
9474
9479
  const active = !!label && rendersLabel;
9475
9480
  // Presence, not truthiness: `htmlFor: undefined` is an explicit opt-out and
@@ -9478,21 +9483,30 @@ function useAutoLabelId({ label, id, labelProps, rendersLabel, hasInput = true,
9478
9483
  const adopted = !active && hasInput ? fieldLabelId : undefined;
9479
9484
  const controlId = id ?? (active && !userWired ? generatedId : adopted);
9480
9485
  const ownLabelProps = { htmlFor: controlId, ...labelProps };
9486
+ const labelTarget = active ? ownLabelProps.htmlFor : fieldLabelFor;
9487
+ const labelled = !!controlId && labelTarget === controlId;
9488
+ // Only a label that names the control gets an id to point at, with the
9489
+ // suffix Field gives its own, from an id that is already unique.
9490
+ const ownLabelId = needsLabelId && active && labelled
9491
+ ? (labelProps?.id ?? `${generatedId}-label`)
9492
+ : undefined;
9481
9493
  // Inactive with a label still means an own Field may render it (pickers'
9482
9494
  // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`
9483
9495
  // tells Field the association is owned here, so it must not generate one
9484
9496
  // that would dangle (#495 presence semantics).
9485
9497
  const fieldLabelProps = active
9486
- ? ownLabelProps
9498
+ ? ownLabelId
9499
+ ? { ...ownLabelProps, id: ownLabelId }
9500
+ : ownLabelProps
9487
9501
  : label
9488
9502
  ? { htmlFor: undefined, ...labelProps }
9489
9503
  : labelProps;
9490
- const labelTarget = active ? ownLabelProps.htmlFor : fieldLabelFor;
9491
- return {
9492
- controlId,
9493
- fieldLabelProps,
9494
- labelled: !!controlId && labelTarget === controlId,
9495
- };
9504
+ const labelId = needsLabelId && labelled
9505
+ ? active
9506
+ ? ownLabelId
9507
+ : fieldLabelElementId
9508
+ : undefined;
9509
+ return { controlId, fieldLabelProps, labelled, labelId };
9496
9510
  }
9497
9511
  /**
9498
9512
  * Group-input counterpart of {@link useAutoLabelId} (#494): a group of
@@ -10070,13 +10084,48 @@ const Slider = React.forwardRef((props, ref) => {
10070
10084
  });
10071
10085
  const { bulmaHelperClasses, rest } = useBulmaClasses(restProps);
10072
10086
  // In range mode the low thumb carries `rest` (and any user id), so the
10073
- // label targets it; each thumb keeps its own aria-label for AT.
10074
- const { controlId, fieldLabelProps } = useAutoLabelId({
10087
+ // label's `for` targets it.
10088
+ const { controlId, fieldLabelProps, labelId } = useAutoLabelId({
10075
10089
  label,
10076
10090
  id: restProps.id,
10077
10091
  labelProps,
10078
10092
  rendersLabel: ownField,
10079
- });
10093
+ needsLabelId: !!range,
10094
+ });
10095
+ // A range thumb's default name is an `aria-label`, which outranks the
10096
+ // label's `for`, so a label naming the Slider reaches each thumb through
10097
+ // `aria-labelledby` instead: the label, then a hidden span holding the
10098
+ // thumb's own text, as in "Price Minimum value" (#981). An `ariaLabel`
10099
+ // entry is that thumb's whole name and wins.
10100
+ const thumbTextId = React.useId();
10101
+ const thumbLabels = range
10102
+ ? ariaLabel
10103
+ : undefined;
10104
+ // A caller's `aria-label` or `aria-labelledby` names the Slider in the
10105
+ // label's place, so both thumbs read it before their own text. They are
10106
+ // routed here rather than spread, since the spread reaches only the low
10107
+ // thumb and would leave the pair with two unrelated names. Presence for
10108
+ // aria-labelledby, as on a group (#974): the caller's key takes the naming
10109
+ // over whatever its value, so an undefined one keeps every label out and
10110
+ // leaves both thumbs their bare text. A non-empty one wins over an
10111
+ // aria-label, as it does in the browser's name computation.
10112
+ const { 'aria-label': callerLabel, 'aria-labelledby': callerLabelledBy, ...thumbRest } = rest;
10113
+ const callerNamed = !!callerLabel || 'aria-labelledby' in rest;
10114
+ const stemRef = callerLabelledBy || (callerNamed ? undefined : labelId);
10115
+ const stemText = stemRef ? undefined : callerLabel;
10116
+ const nameThumb = (own, text, textId) => {
10117
+ const wired = !!stemRef && !own;
10118
+ return {
10119
+ text,
10120
+ textId,
10121
+ wired,
10122
+ aria: wired
10123
+ ? { 'aria-labelledby': `${stemRef} ${textId}` }
10124
+ : { 'aria-label': own || (stemText ? `${stemText} ${text}` : text) },
10125
+ };
10126
+ };
10127
+ const lowThumb = nameThumb(thumbLabels?.[0], 'Minimum value', `${thumbTextId}-min`);
10128
+ const highThumb = nameThumb(thumbLabels?.[1], 'Maximum value', `${thumbTextId}-max`);
10080
10129
  // Resolve tooltip mode: explicit tooltip prop takes precedence, else showOutput maps to 'auto'
10081
10130
  const tooltipMode = tooltip ?? (showOutput ? 'auto' : 'hidden');
10082
10131
  // --- Range mode state ---
@@ -10371,7 +10420,7 @@ const Slider = React.forwardRef((props, ref) => {
10371
10420
  : {
10372
10421
  '--slider-progress-low': `${progressLow}%`,
10373
10422
  '--slider-progress-high': `${progressHigh}%`,
10374
- } }), jsxRuntime.jsx("input", { ref: combinedRef, type: "range", min: min, max: max, step: step, value: currentRange[0], disabled: disabled, onChange: handleRangeLowChange, onKeyDown: handleKeyDown, onMouseEnter: () => setShowTooltip(true), onMouseLeave: () => setShowTooltip(false), onFocus: () => setShowTooltip(true), onBlur: () => setShowTooltip(false), className: prefixedClassNames(classPrefix, 'slider-input', 'slider-input-low'), "aria-valuenow": currentRange[0], "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": isVertical ? 'vertical' : undefined, "aria-label": ariaLabel?.[0] ?? 'Minimum value', ...getAriaProps(currentRange[0]), ...rest, id: controlId, ...(nameLow !== undefined ? { name: nameLow } : {}) }), jsxRuntime.jsx("input", { ref: inputHighRef, type: "range", min: min, max: max, step: step, value: currentRange[1], disabled: disabled, onChange: handleRangeHighChange, onMouseEnter: () => setShowTooltipHigh(true), onMouseLeave: () => setShowTooltipHigh(false), onFocus: () => setShowTooltipHigh(true), onBlur: () => setShowTooltipHigh(false), className: prefixedClassNames(classPrefix, 'slider-input', 'slider-input-high'), "aria-valuenow": currentRange[1], "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": isVertical ? 'vertical' : undefined, "aria-label": ariaLabel?.[1] ?? 'Maximum value', ...getAriaProps(currentRange[1]), name: nameHigh }), tooltipMode !== 'hidden' && (jsxRuntime.jsx("output", { ref: outputRef, className: prefixedClassNames(classPrefix, 'slider-output', 'slider-output-low', {
10423
+ } }), jsxRuntime.jsx("input", { ref: combinedRef, type: "range", min: min, max: max, step: step, value: currentRange[0], disabled: disabled, onChange: handleRangeLowChange, onKeyDown: handleKeyDown, onMouseEnter: () => setShowTooltip(true), onMouseLeave: () => setShowTooltip(false), onFocus: () => setShowTooltip(true), onBlur: () => setShowTooltip(false), className: prefixedClassNames(classPrefix, 'slider-input', 'slider-input-low'), "aria-valuenow": currentRange[0], "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": isVertical ? 'vertical' : undefined, ...lowThumb.aria, ...getAriaProps(currentRange[0]), ...thumbRest, id: controlId, ...(nameLow !== undefined ? { name: nameLow } : {}) }), jsxRuntime.jsx("input", { ref: inputHighRef, type: "range", min: min, max: max, step: step, value: currentRange[1], disabled: disabled, onChange: handleRangeHighChange, onMouseEnter: () => setShowTooltipHigh(true), onMouseLeave: () => setShowTooltipHigh(false), onFocus: () => setShowTooltipHigh(true), onBlur: () => setShowTooltipHigh(false), className: prefixedClassNames(classPrefix, 'slider-input', 'slider-input-high'), "aria-valuenow": currentRange[1], "aria-valuemin": min, "aria-valuemax": max, "aria-orientation": isVertical ? 'vertical' : undefined, ...highThumb.aria, ...getAriaProps(currentRange[1]), name: nameHigh }), [lowThumb, highThumb].map(thumb => thumb.wired && (jsxRuntime.jsx("span", { id: thumb.textId, hidden: true, children: thumb.text }, thumb.textId))), tooltipMode !== 'hidden' && (jsxRuntime.jsx("output", { ref: outputRef, className: prefixedClassNames(classPrefix, 'slider-output', 'slider-output-low', {
10375
10424
  'is-visible': showTipLow,
10376
10425
  'is-flipped': flipped,
10377
10426
  'is-flipped-left': isVertical && verticalFlippedLeft,