@allxsmith/bestax-bulma 5.8.2 → 5.8.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.
package/dist/index.esm.js CHANGED
@@ -5839,6 +5839,45 @@ const Field = withSubComponents(FieldComponent, {
5839
5839
  Control,
5840
5840
  }, 'Field');
5841
5841
 
5842
+ /**
5843
+ * Associates the convenience `label` prop with its control (#368): generates
5844
+ * an id for the control and returns labelProps carrying a matching `htmlFor`.
5845
+ * A user-supplied `id` is used as the target instead of the generated one, and
5846
+ * an explicit `labelProps.htmlFor` disables generation entirely — the user has
5847
+ * taken over the association. Internal; not part of the public API.
5848
+ */
5849
+ function useAutoLabelId({ label, id, labelProps, rendersLabel, }) {
5850
+ // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
5851
+ const generatedId = useId();
5852
+ // Truthiness mirrors Field's own `if (label)` render gate.
5853
+ const active = !!label && rendersLabel;
5854
+ const controlId = id ?? (active && !labelProps?.htmlFor ? generatedId : undefined);
5855
+ const fieldLabelProps = active
5856
+ ? { htmlFor: controlId, ...labelProps }
5857
+ : labelProps;
5858
+ return { controlId, fieldLabelProps };
5859
+ }
5860
+ /**
5861
+ * Group-input counterpart of {@link useAutoLabelId} (#494): a group of
5862
+ * controls cannot take a single `htmlFor`, so instead the rendered `<label>`
5863
+ * gets a generated id and the group container points at it with
5864
+ * `aria-labelledby`. A user-supplied `labelProps.id` is used as the target
5865
+ * instead of generating one. Any caller `htmlFor` is stripped — a group label
5866
+ * names the group, never a single control — so the merged labelProps always
5867
+ * carry an explicit `htmlFor: undefined`.
5868
+ * Internal; not part of the public API.
5869
+ */
5870
+ function useAutoLabelledBy({ label, labelProps, rendersLabel, }) {
5871
+ // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
5872
+ const generatedId = useId();
5873
+ const active = !!label && rendersLabel;
5874
+ const labelId = labelProps?.id ?? (active ? generatedId : undefined);
5875
+ const fieldLabelProps = active
5876
+ ? { ...labelProps, id: labelId, htmlFor: undefined }
5877
+ : labelProps;
5878
+ return { ariaLabelledBy: active ? labelId : undefined, fieldLabelProps };
5879
+ }
5880
+
5842
5881
  /**
5843
5882
  * The `Checkboxes` component wraps multiple `Checkbox` components in a Bulma-styled group.
5844
5883
  *
@@ -5867,6 +5906,11 @@ const Field = withSubComponents(FieldComponent, {
5867
5906
  const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
5868
5907
  const insideField = useInsideField();
5869
5908
  const insideControl = useInsideControl();
5909
+ const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
5910
+ label,
5911
+ labelProps,
5912
+ rendersLabel: !insideField,
5913
+ });
5870
5914
  const { bulmaHelperClasses, rest } = useBulmaClasses({
5871
5915
  ...props,
5872
5916
  });
@@ -5889,37 +5933,18 @@ const CheckboxesComponent = ({ label, labelSize, labelProps, horizontal, message
5889
5933
  name,
5890
5934
  ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
5891
5935
  }), [name, groupActive, currentValue, handleChange]);
5892
- const checkboxesElement = (jsx("div", { className: wrapperClass, ...rest, children: jsx(CheckboxesProvider, { value: ctx, children: children }) }));
5936
+ const checkboxesElement = (jsx("div", { className: wrapperClass, role: "group", "aria-labelledby": ariaLabelledBy, ...rest, children: jsx(CheckboxesProvider, { value: ctx, children: children }) }));
5893
5937
  let content = checkboxesElement;
5894
5938
  if (!insideControl) {
5895
5939
  content = jsx(Control, { children: content });
5896
5940
  }
5897
5941
  if (!insideField) {
5898
- return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
5942
+ return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
5899
5943
  }
5900
5944
  return (jsxs(Fragment, { children: [content, messageEl] }));
5901
5945
  };
5902
5946
  const Checkboxes = withSubComponents(CheckboxesComponent, { Checkbox }, 'Checkboxes');
5903
5947
 
5904
- /**
5905
- * Associates the convenience `label` prop with its control (#368): generates
5906
- * an id for the control and returns labelProps carrying a matching `htmlFor`.
5907
- * A user-supplied `id` is used as the target instead of the generated one, and
5908
- * an explicit `labelProps.htmlFor` disables generation entirely — the user has
5909
- * taken over the association. Internal; not part of the public API.
5910
- */
5911
- function useAutoLabelId({ label, id, labelProps, rendersLabel, }) {
5912
- // Called unconditionally per the rules of hooks; SSR-safe on React 18 and 19.
5913
- const generatedId = useId();
5914
- // Truthiness mirrors Field's own `if (label)` render gate.
5915
- const active = !!label && rendersLabel;
5916
- const controlId = id ?? (active && !labelProps?.htmlFor ? generatedId : undefined);
5917
- const fieldLabelProps = active
5918
- ? { htmlFor: controlId, ...labelProps }
5919
- : labelProps;
5920
- return { controlId, fieldLabelProps };
5921
- }
5922
-
5923
5948
  /**
5924
5949
  * The `File` component provides a Bulma-styled file input, supporting color, size, boxed/fullwidth/align styles, icons, "has name", and filename display.
5925
5950
  *
@@ -6075,6 +6100,11 @@ Radio.displayName = 'Radio';
6075
6100
  const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, name, value, defaultValue, onChange, children, className, ...props }) => {
6076
6101
  const insideField = useInsideField();
6077
6102
  const insideControl = useInsideControl();
6103
+ const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
6104
+ label,
6105
+ labelProps,
6106
+ rendersLabel: !insideField,
6107
+ });
6078
6108
  const { bulmaHelperClasses, rest } = useBulmaClasses({
6079
6109
  ...props,
6080
6110
  });
@@ -6100,13 +6130,13 @@ const RadiosComponent = ({ label, labelSize, labelProps, horizontal, message, me
6100
6130
  name,
6101
6131
  ...(groupActive ? { value: currentValue, onChange: handleChange } : {}),
6102
6132
  }), [name, groupActive, currentValue, handleChange]);
6103
- const radiosElement = (jsx("div", { className: wrapperClass, ...rest, children: jsx(RadiosProvider, { value: ctx, children: children }) }));
6133
+ const radiosElement = (jsx("div", { className: wrapperClass, role: "radiogroup", "aria-labelledby": ariaLabelledBy, ...rest, children: jsx(RadiosProvider, { value: ctx, children: children }) }));
6104
6134
  let content = radiosElement;
6105
6135
  if (!insideControl) {
6106
6136
  content = jsx(Control, { children: content });
6107
6137
  }
6108
6138
  if (!insideField) {
6109
- return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
6139
+ return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
6110
6140
  }
6111
6141
  return (jsxs(Fragment, { children: [content, messageEl] }));
6112
6142
  };
@@ -6835,6 +6865,11 @@ function getFillPercent(iconIndex, value) {
6835
6865
  const Rate = 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) => {
6836
6866
  const insideField = useInsideField();
6837
6867
  const insideControl = useInsideControl();
6868
+ const { ariaLabelledBy, fieldLabelProps } = useAutoLabelledBy({
6869
+ label,
6870
+ labelProps,
6871
+ rendersLabel: !insideField,
6872
+ });
6838
6873
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
6839
6874
  const [internalValue, setInternalValue] = useState(defaultValue);
6840
6875
  const [hoverValue, setHoverValue] = useState(null);
@@ -7031,13 +7066,13 @@ const Rate = forwardRef(({ label, labelSize, labelProps, horizontal, message, me
7031
7066
  const rateScoreClass = usePrefixedClassNames('rate-score');
7032
7067
  const rateTextClass = usePrefixedClassNames('rate-text');
7033
7068
  const rateCustomTextClass = usePrefixedClassNames('rate-custom-text');
7034
- const rateElement = (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: [jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsx("span", { className: rateTextClass, children: text }), customText && (jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
7069
+ const rateElement = (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: [jsx("div", { className: rateItemsClass, children: renderIcons() }), showScore && (jsx("span", { className: rateScoreClass, children: getScoreDisplay() })), text && jsx("span", { className: rateTextClass, children: text }), customText && (jsx("span", { className: rateCustomTextClass, children: customText })), name && (jsx("input", { type: "hidden", name: name, value: currentValue, form: form }))] }));
7035
7070
  let content = rateElement;
7036
7071
  if (!insideControl) {
7037
7072
  content = jsx(Control, { children: content });
7038
7073
  }
7039
7074
  if (!insideField) {
7040
- return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
7075
+ return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [content, messageEl] }));
7041
7076
  }
7042
7077
  return (jsxs(Fragment, { children: [content, messageEl] }));
7043
7078
  });
@@ -7067,10 +7102,16 @@ Rate.displayName = 'Rate';
7067
7102
  * onSelect={(item) => console.log(item)}
7068
7103
  * />
7069
7104
  */
7070
- const Autocomplete = forwardRef(({ data = [], value: controlledValue, placeholder, field = 'label', clearable = false, openOnFocus = false, keepFirst = false, keepOpen = false, selectOnClickOutside = false, maxHeight = 200, loading = false, disabled = false, checkInfiniteScroll = false, infiniteScrollDistance = 50, color, size, onInput, onSelect, onActiveChange, onInfiniteScroll, itemTemplate, header, footer, empty, name, form, required,
7105
+ const Autocomplete = forwardRef(({ data = [], value: controlledValue, placeholder, field = 'label', clearable = false, openOnFocus = false, keepFirst = false, keepOpen = false, selectOnClickOutside = false, maxHeight = 200, loading = false, disabled = false, checkInfiniteScroll = false, infiniteScrollDistance = 50, color, size, onInput, onSelect, onActiveChange, onInfiniteScroll, itemTemplate, header, footer, empty, name, form, required, id,
7071
7106
  // Field props
7072
7107
  label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, className, ...props }, ref) => {
7073
7108
  const insideField = useInsideField();
7109
+ const { controlId, fieldLabelProps } = useAutoLabelId({
7110
+ label,
7111
+ id,
7112
+ labelProps,
7113
+ rendersLabel: !insideField,
7114
+ });
7074
7115
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
7075
7116
  const { classPrefix } = useConfig();
7076
7117
  const containerRef = useRef(null);
@@ -7266,7 +7307,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
7266
7307
  [`is-${messageColor}`]: !!messageColor,
7267
7308
  });
7268
7309
  const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
7269
- const autocompleteElement = (jsxs("div", { ref: containerRef, className: combinedClasses, ...rest, children: [jsxs("div", { className: controlClasses, children: [jsx("input", { ref: combinedRef, type: "text", className: inputClasses, value: inputValue, placeholder: placeholder, disabled: disabled, name: name, form: form, required: required, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, role: "combobox", "aria-expanded": isActive, "aria-haspopup": "listbox", "aria-autocomplete": "list", autoComplete: "off" }), clearable && inputValue && !disabled && (jsx("span", { className: iconRightClickableClass, onClick: handleClear, role: "button", "aria-label": "Clear", children: jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "16", height: "16", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })), loading && (jsx("span", { className: iconRightClass, children: jsx("span", { className: loaderClass }) }))] }), isActive && (filteredData.length > 0 || empty) && (jsx("div", { className: dropdownMenuClasses, children: jsxs("div", { ref: dropdownRef, className: dropdownContentClass, style: { maxHeight: `${maxHeight}px`, overflowY: 'auto' }, role: "listbox", onScroll: handleDropdownScroll, children: [header && jsx("div", { className: dropdownHeaderClass, children: header }), filteredData.length > 0 ? (filteredData.map((item, index) => {
7310
+ const autocompleteElement = (jsxs("div", { ref: containerRef, className: combinedClasses, ...rest, children: [jsxs("div", { className: controlClasses, children: [jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: placeholder, disabled: disabled, name: name, form: form, required: required, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, role: "combobox", "aria-expanded": isActive, "aria-haspopup": "listbox", "aria-autocomplete": "list", autoComplete: "off" }), clearable && inputValue && !disabled && (jsx("span", { className: iconRightClickableClass, onClick: handleClear, role: "button", "aria-label": "Clear", children: jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", width: "16", height: "16", children: [jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })), loading && (jsx("span", { className: iconRightClass, children: jsx("span", { className: loaderClass }) }))] }), isActive && (filteredData.length > 0 || empty) && (jsx("div", { className: dropdownMenuClasses, children: jsxs("div", { ref: dropdownRef, className: dropdownContentClass, style: { maxHeight: `${maxHeight}px`, overflowY: 'auto' }, role: "listbox", onScroll: handleDropdownScroll, children: [header && jsx("div", { className: dropdownHeaderClass, children: header }), filteredData.length > 0 ? (filteredData.map((item, index) => {
7270
7311
  const isDisabled = typeof item !== 'string' && item.disabled;
7271
7312
  const isHighlighted = index === highlightedIndex;
7272
7313
  const itemClasses = prefixedClassNames(classPrefix, 'dropdown-item', {
@@ -7278,7 +7319,7 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
7278
7319
  : getDisplayValue(item) }, index));
7279
7320
  })) : (jsx("div", { className: emptyItemClasses, children: empty })), footer && jsx("div", { className: dropdownFooterClass, children: footer })] }) }))] }));
7280
7321
  if (!insideField) {
7281
- return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [autocompleteElement, messageEl] }));
7322
+ return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [autocompleteElement, messageEl] }));
7282
7323
  }
7283
7324
  return (jsxs(Fragment, { children: [autocompleteElement, messageEl] }));
7284
7325
  });
@@ -7309,7 +7350,7 @@ Autocomplete.displayName = 'Autocomplete';
7309
7350
  */
7310
7351
  const Taginput = forwardRef(({
7311
7352
  // Field props
7312
- label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, value: controlledValue, defaultValue = [], data = [], placeholder, field = 'label', allowNew = true, allowDuplicates = false, openOnFocus = false, removeOnKeys = true, confirmKeys = ['Enter', ','], closable = true, attached = false, maxTags, maxlength, disabled = false, readonly = false, rounded = false, ellipsis = false, hasCounter = true, onPasteSeparators = [','], beforeAdding, createTag, keepFirst = false, keepOpen = true, loading = false, ariaCloseLabel, icon, iconLibrary: iconLibraryProp, iconVariant, iconFeatures, color, tagColor, size, onChange, onAdd, onRemove, onTyping, tagTemplate, name, form, className, ...props }, ref) => {
7353
+ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, value: controlledValue, defaultValue = [], data = [], placeholder, field = 'label', allowNew = true, allowDuplicates = false, openOnFocus = false, removeOnKeys = true, confirmKeys = ['Enter', ','], closable = true, attached = false, maxTags, maxlength, disabled = false, readonly = false, rounded = false, ellipsis = false, hasCounter = true, onPasteSeparators = [','], beforeAdding, createTag, keepFirst = false, keepOpen = true, loading = false, ariaCloseLabel, icon, iconLibrary: iconLibraryProp, iconVariant, iconFeatures, color, tagColor, size, onChange, onAdd, onRemove, onTyping, tagTemplate, name, form, id, className, ...props }, ref) => {
7313
7354
  const insideField = useInsideField();
7314
7355
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
7315
7356
  const { classPrefix } = useConfig();
@@ -7325,6 +7366,15 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
7325
7366
  const [highlightedIndex, setHighlightedIndex] = useState(-1);
7326
7367
  // Use controlled or internal tags
7327
7368
  const tags = controlledValue !== undefined ? controlledValue : internalTags;
7369
+ const isMaxReached = maxTags !== undefined && tags.length >= maxTags;
7370
+ // At the tag limit the text input is not rendered, so there is nothing to
7371
+ // wire the label to.
7372
+ const { controlId, fieldLabelProps } = useAutoLabelId({
7373
+ label,
7374
+ id,
7375
+ labelProps,
7376
+ rendersLabel: !insideField && !isMaxReached,
7377
+ });
7328
7378
  // Get display value from tag
7329
7379
  const getDisplayValue = (tag) => {
7330
7380
  if (typeof tag === 'string')
@@ -7567,7 +7617,6 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
7567
7617
  [`is-${size}`]: !!size,
7568
7618
  });
7569
7619
  const combinedClasses = classNames(taginputClasses, bulmaHelperClasses, className);
7570
- const isMaxReached = maxTags !== undefined && tags.length >= maxTags;
7571
7620
  // Counter text
7572
7621
  const showCounter = hasCounter && (maxTags !== undefined || maxlength !== undefined);
7573
7622
  const counterText = maxTags !== undefined
@@ -7591,12 +7640,14 @@ label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName,
7591
7640
  e.stopPropagation();
7592
7641
  removeTag(index);
7593
7642
  }, "aria-label": ariaCloseLabel || `Remove ${displayVal}` }))] }, index));
7594
- }), !isMaxReached && (jsx("input", { ref: combinedRef, type: "text", className: inputClasses, value: inputValue, placeholder: tags.length === 0 ? placeholder : undefined, disabled: disabled, readOnly: readonly, maxLength: maxlength, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, onPaste: handlePaste, "aria-label": "Add tag" }))] }), loading && (jsx("span", { className: iconRightClass, children: jsx("span", { className: loaderClass }) }))] }), isActive && filteredData.length > 0 && (jsx("div", { className: dropdownMenuClasses, children: jsx("div", { ref: dropdownRef, className: dropdownContentClasses, role: "listbox", children: filteredData.map((item, index) => (jsx("a", { className: pcn('dropdown-item', {
7643
+ }), !isMaxReached && (jsx("input", { ref: combinedRef, type: "text", className: inputClasses, id: controlId, value: inputValue, placeholder: tags.length === 0 ? placeholder : undefined, disabled: disabled, readOnly: readonly, maxLength: maxlength, onChange: handleInputChange, onFocus: handleFocus, onKeyDown: handleKeyDown, onPaste: handlePaste, "aria-label": controlId && fieldLabelProps?.htmlFor === controlId
7644
+ ? undefined
7645
+ : 'Add tag' }))] }), loading && (jsx("span", { className: iconRightClass, children: jsx("span", { className: loaderClass }) }))] }), isActive && filteredData.length > 0 && (jsx("div", { className: dropdownMenuClasses, children: jsx("div", { ref: dropdownRef, className: dropdownContentClasses, role: "listbox", children: filteredData.map((item, index) => (jsx("a", { className: pcn('dropdown-item', {
7595
7646
  'is-active': index === highlightedIndex,
7596
7647
  }), onClick: () => addTag(item), onMouseEnter: () => setHighlightedIndex(index), role: "option", "aria-selected": index === highlightedIndex, children: item }, index))) }) })), showCounter && jsx("small", { className: counterClasses, children: counterText }), name &&
7597
7648
  tags.map((tag, i) => (jsx("input", { type: "hidden", name: name, value: getDisplayValue(tag), form: form }, `tag-input-${i}`)))] }));
7598
7649
  if (!insideField) {
7599
- return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: labelProps, horizontal: horizontal, className: fieldClassName, children: [taginputElement, messageEl] }));
7650
+ return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [taginputElement, messageEl] }));
7600
7651
  }
7601
7652
  return (jsxs(Fragment, { children: [taginputElement, messageEl] }));
7602
7653
  });