@allxsmith/bestax-bulma 5.8.3 → 5.9.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/index.cjs.js CHANGED
@@ -5560,6 +5560,17 @@ const useInsideControl = () => React.useContext(ControlContext);
5560
5560
  const FieldProvider = FieldContext.Provider;
5561
5561
  /** Provider for Control context — used internally by Control component. */
5562
5562
  const ControlProvider = ControlContext.Provider;
5563
+ const FieldLabelIdContext = React.createContext(undefined);
5564
+ /**
5565
+ * The id a labeled Field wants its single composed control to adopt (#495).
5566
+ * `undefined` outside a Field, in unlabeled/grouped/addons Fields, or when the
5567
+ * user took over the association with an explicit `labelProps.htmlFor`.
5568
+ * Consumed only by the single-control bases (InputBase, SelectBase,
5569
+ * TextAreaBase). Internal; not part of the public API.
5570
+ */
5571
+ const useFieldLabelId = () => React.useContext(FieldLabelIdContext);
5572
+ /** Provider for the Field label-target id — used internally by Field. */
5573
+ const FieldLabelIdProvider = FieldLabelIdContext.Provider;
5563
5574
  const RadiosContext = React.createContext(undefined);
5564
5575
  const CheckboxesContext = React.createContext(undefined);
5565
5576
  /**
@@ -5767,15 +5778,19 @@ const FieldBody = ({ textColor, bgColor, className, children, ...props }) => {
5767
5778
  * @see {@link https://bulma.io/documentation/form/general/#field | Bulma Field documentation}
5768
5779
  *
5769
5780
  * @example
5770
- * // Labelled field
5781
+ * // Labelled field — the label auto-associates with the composed base
5771
5782
  * <Field label="Email">
5772
- * <input className="input" type="email" />
5783
+ * <Control>
5784
+ * <InputBase type="email" />
5785
+ * </Control>
5773
5786
  * </Field>
5774
5787
  *
5775
5788
  * @example
5776
5789
  * // Horizontal field
5777
5790
  * <Field horizontal label="Name">
5778
- * <input className="input" />
5791
+ * <Control>
5792
+ * <InputBase />
5793
+ * </Control>
5779
5794
  * </Field>
5780
5795
  */
5781
5796
  const FieldComponent = ({ horizontal, grouped, hasAddons, narrow, label, labelSize, labelProps, textColor, color: _fieldColor, bgColor, className, children, ...props }) => {
@@ -5802,13 +5817,24 @@ const FieldComponent = ({ horizontal, grouped, hasAddons, narrow, label, labelSi
5802
5817
  // Default labelSize to 'normal' when horizontal for proper baseline alignment
5803
5818
  const effectiveLabelSize = labelSize ?? (horizontal ? 'normal' : undefined);
5804
5819
  const labelClass = usePrefixedClassNames('label');
5820
+ // Auto-associate the label with a single composed base control (#495): the
5821
+ // label points at a generated id shared via context, which InputBase/
5822
+ // SelectBase/TextAreaBase adopt when the user supplied no id of their own.
5823
+ // Presence semantics on htmlFor — even an explicit `htmlFor: undefined`
5824
+ // means the caller owns the association. Grouped/addons fields hold several
5825
+ // controls, so no single association is generated for them.
5826
+ const generatedId = React.useId();
5827
+ const userWiredLabel = !!labelProps && 'htmlFor' in labelProps;
5828
+ const targetId = label && !userWiredLabel && !grouped && !hasAddons
5829
+ ? generatedId
5830
+ : undefined;
5805
5831
  let renderedLabel = null;
5806
5832
  if (label) {
5807
5833
  if (horizontal) {
5808
- renderedLabel = (jsxRuntime.jsx(FieldLabel, { size: effectiveLabelSize, children: jsxRuntime.jsx("label", { ...labelProps, className: classNames(labelClass, labelProps?.className), style: labelProps?.style, children: label }) }));
5834
+ renderedLabel = (jsxRuntime.jsx(FieldLabel, { size: effectiveLabelSize, children: jsxRuntime.jsx("label", { htmlFor: targetId, ...labelProps, className: classNames(labelClass, labelProps?.className), style: labelProps?.style, children: label }) }));
5809
5835
  }
5810
5836
  else {
5811
- renderedLabel = (jsxRuntime.jsx("label", { ...labelProps, className: classNames(labelClass, labelProps?.className), style: { display: 'block', ...(labelProps?.style || {}) }, children: label }));
5837
+ renderedLabel = (jsxRuntime.jsx("label", { htmlFor: targetId, ...labelProps, className: classNames(labelClass, labelProps?.className), style: { display: 'block', ...(labelProps?.style || {}) }, children: label }));
5812
5838
  }
5813
5839
  }
5814
5840
  // If horizontal, wrap children in FieldBody (unless the user already provided
@@ -5831,7 +5857,7 @@ const FieldComponent = ({ horizontal, grouped, hasAddons, narrow, label, labelSi
5831
5857
  content = jsxRuntime.jsx(FieldBody, { children: children });
5832
5858
  }
5833
5859
  }
5834
- return (jsxRuntime.jsx(FieldProvider, { value: true, children: jsxRuntime.jsxs("div", { className: fieldClass, ...rest, children: [renderedLabel, content] }) }));
5860
+ return (jsxRuntime.jsx(FieldProvider, { value: true, children: jsxRuntime.jsx(FieldLabelIdProvider, { value: targetId, children: jsxRuntime.jsxs("div", { className: fieldClass, ...rest, children: [renderedLabel, content] }) }) }));
5835
5861
  };
5836
5862
  FieldLabel.displayName = 'FieldLabel';
5837
5863
  FieldBody.displayName = 'FieldBody';
@@ -5841,6 +5867,55 @@ const Field = withSubComponents(FieldComponent, {
5841
5867
  Control,
5842
5868
  }, 'Field');
5843
5869
 
5870
+ /**
5871
+ * Associates the convenience `label` prop with its control (#368): generates
5872
+ * an id for the control and returns labelProps carrying a matching `htmlFor`.
5873
+ * A user-supplied `id` is used as the target instead of the generated one, and
5874
+ * an explicit `htmlFor` key in labelProps — even set to `undefined` — disables
5875
+ * generation entirely: the user has taken over the association (#495 presence
5876
+ * semantics). Internal; not part of the public API.
5877
+ */
5878
+ function useAutoLabelId({ label, id, labelProps, rendersLabel, }) {
5879
+ // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
5880
+ const generatedId = React.useId();
5881
+ // Truthiness mirrors Field's own `if (label)` render gate.
5882
+ const active = !!label && rendersLabel;
5883
+ // Presence, not truthiness: `htmlFor: undefined` is an explicit opt-out and
5884
+ // must not leave an orphan generated id on the control.
5885
+ const userWired = !!labelProps && 'htmlFor' in labelProps;
5886
+ const controlId = id ?? (active && !userWired ? generatedId : undefined);
5887
+ // Inactive with a label still means an own Field may render it (pickers'
5888
+ // inline mode, Taginput at maxTags) — the explicit `htmlFor: undefined`
5889
+ // tells Field the association is owned here, so it must not generate one
5890
+ // that would dangle (#495 presence semantics).
5891
+ const fieldLabelProps = active
5892
+ ? { htmlFor: controlId, ...labelProps }
5893
+ : label
5894
+ ? { htmlFor: undefined, ...labelProps }
5895
+ : labelProps;
5896
+ return { controlId, fieldLabelProps };
5897
+ }
5898
+ /**
5899
+ * Group-input counterpart of {@link useAutoLabelId} (#494): a group of
5900
+ * controls cannot take a single `htmlFor`, so instead the rendered `<label>`
5901
+ * gets a generated id and the group container points at it with
5902
+ * `aria-labelledby`. A user-supplied `labelProps.id` is used as the target
5903
+ * instead of generating one. Any caller `htmlFor` is stripped — a group label
5904
+ * names the group, never a single control — so the merged labelProps always
5905
+ * carry an explicit `htmlFor: undefined`.
5906
+ * Internal; not part of the public API.
5907
+ */
5908
+ function useAutoLabelledBy({ label, labelProps, rendersLabel, }) {
5909
+ // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
5910
+ const generatedId = React.useId();
5911
+ const active = !!label && rendersLabel;
5912
+ const labelId = labelProps?.id ?? (active ? generatedId : undefined);
5913
+ const fieldLabelProps = active
5914
+ ? { ...labelProps, id: labelId, htmlFor: undefined }
5915
+ : labelProps;
5916
+ return { ariaLabelledBy: active ? labelId : undefined, fieldLabelProps };
5917
+ }
5918
+
5844
5919
  /**
5845
5920
  * The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
5846
5921
  *
@@ -5869,6 +5944,11 @@ const Field = withSubComponents(FieldComponent, {
5869
5944
  const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
5870
5945
  const insideField = useInsideField();
5871
5946
  const insideControl = useInsideControl();
5947
+ const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
5948
+ label,
5949
+ labelProps,
5950
+ rendersLabel: !insideField,
5951
+ });
5872
5952
  const { bulmaHelperClasses, rest } = useBulmaClasses({
5873
5953
  ...props,
5874
5954
  });
@@ -5891,37 +5971,18 @@ const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message
5891
5971
  name,
5892
5972
  ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
5893
5973
  }), [name, groupActive, currentValue, handleChange]);
5894
- const checkboxesElement = (jsxRuntime.jsx("div", { className: wrapperClass, ...rest, children: jsxRuntime.jsx(CheckboxesProvider, { value: ctx, children: children }) }));
5974
+ const checkboxesElement = (jsxRuntime.jsx("div", { className: wrapperClass, role: "group", "aria-labelledby": ariaLabelledBy, ...rest, children: jsxRuntime.jsx(CheckboxesProvider, { value: ctx, children: children }) }));
5895
5975
  let content = checkboxesElement;
5896
5976
  if (!insideControl) {
5897
5977
  content = jsxRuntime.jsx(Control, { children: content });
5898
5978
  }
5899
5979
  if (!insideField) {
5900
- return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
5980
+ return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
5901
5981
  }
5902
5982
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [content, messageEl] }));
5903
5983
  };
5904
5984
  const Checkboxes = withSubComponents(CheckboxesComponent, { Checkbox }, 'Checkboxes');
5905
5985
 
5906
- /**
5907
- * Associates the convenience `label` prop with its control (#368): generates
5908
- * an id for the control and returns labelProps carrying a matching `htmlFor`.
5909
- * A user-supplied `id` is used as the target instead of the generated one, and
5910
- * an explicit `labelProps.htmlFor` disables generation entirely — the user has
5911
- * taken over the association. Internal; not part of the public API.
5912
- */
5913
- function useAutoLabelId({ label, id, labelProps, rendersLabel, }) {
5914
- // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
5915
- const generatedId = React.useId();
5916
- // Truthiness mirrors Field's own `if (label)` render gate.
5917
- const active = !!label && rendersLabel;
5918
- const controlId = id ?? (active && !labelProps?.htmlFor ? generatedId : undefined);
5919
- const fieldLabelProps = active
5920
- ? { htmlFor: controlId, ...labelProps }
5921
- : labelProps;
5922
- return { controlId, fieldLabelProps };
5923
- }
5924
-
5925
5986
  /**
5926
5987
  * The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
5927
5988
  *
@@ -6077,6 +6138,11 @@ Radio.displayName = 'Radio';
6077
6138
  const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
6078
6139
  const insideField = useInsideField();
6079
6140
  const insideControl = useInsideControl();
6141
+ const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
6142
+ label,
6143
+ labelProps,
6144
+ rendersLabel: !insideField,
6145
+ });
6080
6146
  const { bulmaHelperClasses, rest } = useBulmaClasses({
6081
6147
  ...props,
6082
6148
  });
@@ -6102,13 +6168,13 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
6102
6168
  name,
6103
6169
  ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
6104
6170
  }), [name, groupActive, currentValue, handleChange]);
6105
- const radiosElement = (jsxRuntime.jsx("div", { className: wrapperClass, ...rest, children: jsxRuntime.jsx(RadiosProvider, { value: ctx, children: children }) }));
6171
+ const radiosElement = (jsxRuntime.jsx("div", { className: wrapperClass, role: "radiogroup", "aria-labelledby": ariaLabelledBy, ...rest, children: jsxRuntime.jsx(RadiosProvider, { value: ctx, children: children }) }));
6106
6172
  let content = radiosElement;
6107
6173
  if (!insideControl) {
6108
6174
  content = jsxRuntime.jsx(Control, { children: content });
6109
6175
  }
6110
6176
  if (!insideField) {
6111
- return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
6177
+ return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
6112
6178
  }
6113
6179
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [content, messageEl] }));
6114
6180
  };
@@ -6837,6 +6903,11 @@ function getFillPercent(iconIndex, value) {
6837
6903
  const Rate = React.forwardRef(({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, value: controlledValue, defaultValue = 0, max = 5, size, disabled = false, showScore = false, showText = false, texts, onChange, customIcon, spaced = false, rtl = false, iconName, iconLibrary: iconLibraryProp, iconVariant, iconFeatures, color, precision = 1, customText, name, form, className, ...props }, ref) => {
6838
6904
  const insideField = useInsideField();
6839
6905
  const insideControl = useInsideControl();
6906
+ const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
6907
+ label,
6908
+ labelProps,
6909
+ rendersLabel: !insideField,
6910
+ });
6840
6911
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
6841
6912
  const [internalValue, setInternalValue] = React.useState(defaultValue);
6842
6913
  const [hoverValue, setHoverValue] = React.useState(null);
@@ -7033,13 +7104,13 @@ const Rate = React.forwardRef(({ label, labelSize, labelProps, horizontal, messa
7033
7104
  const rateScoreClass = usePrefixedClassNames('rate-score');
7034
7105
  const rateTextClass = usePrefixedClassNames('rate-text');
7035
7106
  const rateCustomTextClass = usePrefixedClassNames('rate-custom-text');
7036
- const rateElement = (jsxRuntime.jsxs("div", { ref: ref, className: combinedClasses, role: "radiogroup", "aria-label": "Rating", "aria-valuenow": currentValue, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuetext": ariaValueText, tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, ...rest, children: [jsxRuntime.jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsxRuntime.jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsxRuntime.jsx("span", { className: rateTextClass, children: text }), customText && (jsxRuntime.jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsxRuntime.jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
7107
+ const rateElement = (jsxRuntime.jsxs("div", { ref: ref, className: combinedClasses, role: "radiogroup", "aria-label": ariaLabelledBy ? undefined : 'Rating', "aria-labelledby": ariaLabelledBy, "aria-valuenow": currentValue, "aria-valuemin": 0, "aria-valuemax": max, "aria-valuetext": ariaValueText, tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, ...rest, children: [jsxRuntime.jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsxRuntime.jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsxRuntime.jsx("span", { className: rateTextClass, children: text }), customText && (jsxRuntime.jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsxRuntime.jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
7037
7108
  let content = rateElement;
7038
7109
  if (!insideControl) {
7039
7110
  content = jsxRuntime.jsx(Control, { children: content });
7040
7111
  }
7041
7112
  if (!insideField) {
7042
- return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
7113
+ return (jsxRuntime.jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
7043
7114
  }
7044
7115
  return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [content, messageEl] }));
7045
7116
  });
@@ -7633,6 +7704,7 @@ const InputBase = React.forwardRef(({ color, size, isRounded, isStatic, isHovere
7633
7704
  color,
7634
7705
  ...props,
7635
7706
  });
7707
+ const fieldLabelId = useFieldLabelId();
7636
7708
  const mainClass = usePrefixedClassNames('input', {
7637
7709
  [`is-${color}`]: !!color,
7638
7710
  [`is-${size}`]: !!size,
@@ -7643,7 +7715,10 @@ const InputBase = React.forwardRef(({ color, size, isRounded, isStatic, isHovere
7643
7715
  'is-loading': isLoading,
7644
7716
  });
7645
7717
  const inputClass = classNames(mainClass, bulmaHelperClasses, className);
7646
- return (jsxRuntime.jsx("input", { ref: ref, className: inputClass, disabled: disabled, readOnly: readOnly, ...rest }));
7718
+ return (jsxRuntime.jsx("input", { ref: ref, className: inputClass, disabled: disabled, readOnly: readOnly, ...rest,
7719
+ // After the spread: a labeled Field's generated id is adopted only
7720
+ // when no user id arrived (rest.id may be an undefined own key).
7721
+ id: rest.id ?? fieldLabelId }));
7647
7722
  });
7648
7723
  InputBase.displayName = 'InputBase';
7649
7724
 
@@ -7660,6 +7735,7 @@ const SelectBase = React.forwardRef(({ color, size, isRounded, isLoading, isActi
7660
7735
  color,
7661
7736
  ...props,
7662
7737
  });
7738
+ const fieldLabelId = useFieldLabelId();
7663
7739
  const mainClass = usePrefixedClassNames('select', {
7664
7740
  [`is-${color}`]: !!color,
7665
7741
  [`is-${size}`]: !!size,
@@ -7684,7 +7760,10 @@ const SelectBase = React.forwardRef(({ color, size, isRounded, isLoading, isActi
7684
7760
  if (multiple && typeof multipleSize === 'number') {
7685
7761
  selectProps.size = multipleSize;
7686
7762
  }
7687
- return (jsxRuntime.jsx("div", { className: selectClass, children: jsxRuntime.jsx("select", { ref: ref, className: innerSelectClass || undefined, ...selectProps, children: children }) }));
7763
+ return (jsxRuntime.jsx("div", { className: selectClass, children: jsxRuntime.jsx("select", { ref: ref, className: innerSelectClass || undefined, ...selectProps,
7764
+ // After the spread: a labeled Field's generated id is adopted only
7765
+ // when no user id arrived; the id belongs on the inner <select>.
7766
+ id: selectProps.id ?? fieldLabelId, children: children }) }));
7688
7767
  });
7689
7768
  SelectBase.displayName = 'SelectBase';
7690
7769
 
@@ -7701,6 +7780,7 @@ const TextAreaBase = React.forwardRef(({ color, size, isRounded, isStatic, isHov
7701
7780
  color,
7702
7781
  ...props,
7703
7782
  });
7783
+ const fieldLabelId = useFieldLabelId();
7704
7784
  // Note: `is-loading` is intentionally NOT applied to the <textarea> itself —
7705
7785
  // Bulma documents `<div class="control is-loading">` as the loading pattern
7706
7786
  // for textareas, not `<textarea class="textarea is-loading">`. The convenience
@@ -7716,7 +7796,10 @@ const TextAreaBase = React.forwardRef(({ color, size, isRounded, isStatic, isHov
7716
7796
  'has-fixed-size': hasFixedSize,
7717
7797
  });
7718
7798
  const textareaClass = classNames(mainClass, bulmaHelperClasses, className);
7719
- return (jsxRuntime.jsx("textarea", { ref: ref, className: textareaClass, disabled: disabled, readOnly: readOnly, rows: rows, ...rest }));
7799
+ return (jsxRuntime.jsx("textarea", { ref: ref, className: textareaClass, disabled: disabled, readOnly: readOnly, rows: rows, ...rest,
7800
+ // After the spread: a labeled Field's generated id is adopted only
7801
+ // when no user id arrived (rest.id may be an undefined own key).
7802
+ id: rest.id ?? fieldLabelId }));
7720
7803
  });
7721
7804
  TextAreaBase.displayName = 'TextAreaBase';
7722
7805