@allxsmith/bestax-bulma 5.31.0 → 5.32.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 CHANGED
@@ -4645,6 +4645,361 @@ const Tabs = withSubComponents(TabsComponent, {
4645
4645
  Content: TabsContent,
4646
4646
  }, 'Tabs');
4647
4647
 
4648
+ const validButtonColors = [...validColors, 'text', 'ghost'];
4649
+ /**
4650
+ * The `Button` component provides a flexible and highly customizable button for your Bulma React UI.
4651
+ *
4652
+ * @function
4653
+ * @param {ButtonProps} props - Props for the Button component.
4654
+ * @param {React.Ref} ref - Forwarded ref to the element `as` renders.
4655
+ * @returns {JSX.Element} The rendered button, anchor, or custom element.
4656
+ * @see {@link https://bulma.io/documentation/elements/button/ | Bulma Button documentation}
4657
+ */
4658
+ const Button = React.forwardRef(function Button(props, ref) {
4659
+ const { color, size, isLight, isRounded, isLoading, isStatic, isFullwidth, isFullWidth, isOutlined, isInverted, isFocused, isActive, isHovered, isDisabled, className, children, textColor, bgColor, as: Component = 'button', href, onClick, target, rel, ...bulmaProps } = props;
4660
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
4661
+ color: textColor,
4662
+ backgroundColor: bgColor,
4663
+ ...bulmaProps,
4664
+ });
4665
+ // Generate Bulma classes with prefix
4666
+ const bulmaClasses = usePrefixedClassNames('button', {
4667
+ [`is-${color}`]: color &&
4668
+ validButtonColors.includes(color),
4669
+ [`is-${size}`]: size,
4670
+ 'is-outlined': isOutlined,
4671
+ 'is-light': isLight,
4672
+ 'is-loading': isLoading,
4673
+ 'is-static': isStatic,
4674
+ 'is-disabled': isDisabled,
4675
+ 'is-rounded': isRounded,
4676
+ 'is-hovered': isHovered,
4677
+ 'is-focused': isFocused,
4678
+ 'is-active': isActive,
4679
+ 'is-inverted': isInverted,
4680
+ 'is-fullwidth': isFullwidth ?? isFullWidth,
4681
+ });
4682
+ // Combine prefixed Bulma classes with unprefixed user className and prefixed helper classes
4683
+ const buttonClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
4684
+ if (Component !== 'button') {
4685
+ // Strip the form-control attributes a link-like element cannot carry, so a
4686
+ // JavaScript consumer or a spread object doesn't put them on the DOM. The
4687
+ // types no longer allow them through; this is the runtime backstop.
4688
+ //
4689
+ // Only for INTRINSIC tags. A custom component (a router Link, ...) owns its
4690
+ // prop contract, and `ComponentPropsWithoutRef<T>` promises the caller that
4691
+ // its props reach it. Stripping names by their spelling alone broke that
4692
+ // promise silently: `<Button as={Custom} name="x" />` type-checked while
4693
+ // `Custom` never received the `name` it requires.
4694
+ //
4695
+ // `autoFocus` and `type` are deliberately NOT stripped: both are valid on
4696
+ // targets this list was filtering them from. `autoFocus` is a global
4697
+ // attribute (React decides per element whether to honour it, which is not
4698
+ // ours to pre-empt), and `type` on an anchor is the MIME hint that
4699
+ // `ButtonProps<'a'>` now types it as.
4700
+ //
4701
+ // The types already answer this for a TypeScript caller: `ButtonProps<T>`
4702
+ // derives from `ComponentPropsWithoutRef<T>`, so only what the target
4703
+ // accepts can be passed. What is left is a runtime backstop for JavaScript
4704
+ // consumers and spread objects.
4705
+ //
4706
+ // Keep it to the two cases where a stray attribute does something. The
4707
+ // submit-overrides belong to a submit control and never make sense on the
4708
+ // anchor path. `disabled` is the one that is VISIBLE: Bulma styles
4709
+ // `.button[disabled]` (background, border, shadow, opacity), so letting it
4710
+ // through to a `<span>` greys the element out — main stripped it, and not
4711
+ // stripping it would be a silent visual change on a types-only release.
4712
+ //
4713
+ // Everything else (`name`, `value`, `form`) is inert where it does not
4714
+ // belong, and enumerating which tags own it is what kept going wrong:
4715
+ // first all nine names on every non-button tag (so `<Button as="input"
4716
+ // name="query" value="Search">` submitted nothing), then a form-control
4717
+ // allowlist that still lost `formAction` on an input and `value` on an
4718
+ // option. Those now ride on the types instead.
4719
+ //
4720
+ // A custom component is exempt entirely — it owns its prop contract, and
4721
+ // `ComponentPropsWithoutRef<T>` promises the caller its props arrive.
4722
+ //
4723
+ // The elements that own `disabled`, per the HTML spec. A closed set, unlike
4724
+ // the open-ended question of which tag owns which attribute.
4725
+ const SUBMIT_OVERRIDES = [
4726
+ 'formAction',
4727
+ 'formEncType',
4728
+ 'formMethod',
4729
+ 'formNoValidate',
4730
+ 'formTarget',
4731
+ ];
4732
+ const OWNS_DISABLED = [
4733
+ 'button',
4734
+ 'fieldset',
4735
+ 'input',
4736
+ 'optgroup',
4737
+ 'option',
4738
+ 'select',
4739
+ 'textarea',
4740
+ ];
4741
+ // Two independent filters. They are not exclusive — an `<a>` owns neither
4742
+ // the submit-overrides nor `disabled`, so it takes both — and deriving them
4743
+ // from one destructure, or from one if/else, is what broke each of them in
4744
+ // turn: first the overrides came off every intrinsic (an `as="input"` lost
4745
+ // `formAction`), then scoping them to the anchor let `disabled` back onto
4746
+ // it.
4747
+ const forwardedRest = { ...rest };
4748
+ if (typeof Component === 'string' && !isCustomElement(Component)) {
4749
+ // Meaningless anywhere but a submit control, and an anchor is not one.
4750
+ if (Component === 'a') {
4751
+ for (const key of SUBMIT_OVERRIDES)
4752
+ delete forwardedRest[key];
4753
+ }
4754
+ // Visible via Bulma's `.button[disabled]`, so withheld from any element
4755
+ // that does not own it.
4756
+ if (!OWNS_DISABLED.includes(Component))
4757
+ delete forwardedRest.disabled;
4758
+ }
4759
+ return (jsxRuntime.jsx(Component, { ref: ref, className: buttonClasses, href: href, target: target, rel: rel, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, onClick: isDisabled
4760
+ ? // Guarded, because this handler is installed into whatever `as`
4761
+ // renders and a custom component's `onClick` need not be a DOM
4762
+ // event handler at all. One declaring `onClick: (v: string) =>
4763
+ // void` calls this with a string, and an unguarded
4764
+ // `e.preventDefault()` throws `is not a function`. Blocking the
4765
+ // real event is the point; a foreign payload is simply not ours
4766
+ // to act on.
4767
+ (e) => {
4768
+ if (typeof e?.preventDefault === 'function')
4769
+ e.preventDefault();
4770
+ }
4771
+ : onClick, ...forwardedRest, children: children }));
4772
+ }
4773
+ return (jsxRuntime.jsx("button", { ref: ref, className: buttonClasses, disabled: isDisabled, onClick: onClick, ...rest, children: children }));
4774
+ });
4775
+ Button.displayName = 'Button';
4776
+
4777
+ // INTERNAL: deliberately not exported from src/index.ts.
4778
+ /**
4779
+ * `useLayoutEffect` in the browser, `useEffect` on the server. A server
4780
+ * render runs neither, and React 18 warns about a layout effect there, so the
4781
+ * server is handed the plain one.
4782
+ */
4783
+ const useClientLayoutEffect = typeof document === 'undefined' ? React.useEffect : React.useLayoutEffect;
4784
+
4785
+ // Not exported, so it stays out of the public API that `index.ts` re-exports.
4786
+ const SegmentedControlContext = React.createContext(null);
4787
+ /** Sorts mounted segments into the order they sit in the page. */
4788
+ const inDocumentOrder = (a, b) => a.node.compareDocumentPosition(b.node) & Node.DOCUMENT_POSITION_FOLLOWING
4789
+ ? -1
4790
+ : 1;
4791
+ /** How far each arrow key moves along the row. */
4792
+ const STEPS = {
4793
+ ArrowRight: 1,
4794
+ ArrowDown: 1,
4795
+ ArrowLeft: -1,
4796
+ ArrowUp: -1,
4797
+ };
4798
+ /**
4799
+ * The segment a key moves to, found among the segments of the same group in
4800
+ * DOM order, skipping disabled ones and wrapping at either end. `undefined`
4801
+ * for a key the group leaves alone, when no enabled segment is left, or when
4802
+ * the segment isn't inside its row in the page, as a portaled one isn't.
4803
+ */
4804
+ function segmentFor(current, key) {
4805
+ const group = current.closest('[role="radiogroup"]');
4806
+ if (!group)
4807
+ return undefined;
4808
+ const segments = Array.from(group.querySelectorAll('[role="radio"]'));
4809
+ const enabled = segments.filter(segment => !segment.disabled);
4810
+ if (key === 'Home')
4811
+ return enabled[0];
4812
+ if (key === 'End')
4813
+ return enabled[enabled.length - 1];
4814
+ const step = STEPS[key];
4815
+ if (!step)
4816
+ return undefined;
4817
+ const at = segments.indexOf(current);
4818
+ for (let i = 1; i <= segments.length; i += 1) {
4819
+ const candidate = segments[(at + step * i + segments.length) % segments.length];
4820
+ if (!candidate.disabled)
4821
+ return candidate;
4822
+ }
4823
+ return undefined;
4824
+ }
4825
+ /**
4826
+ * The `SegmentedControl` component renders a row of buttons where only one can be on, and tells a screen reader it's a radio group.
4827
+ *
4828
+ * It's Bulma's `.buttons.has-addons` row, with the checked segment filled and
4829
+ * marked `is-selected`, the way Bulma's button docs show it. To a screen
4830
+ * reader it's `role="radiogroup"`, with `role="radio"` and `aria-checked` on
4831
+ * each segment, which is how the APG toolbar example marks its alignment
4832
+ * buttons. The keys follow the APG radio group: the row is one tab stop, the
4833
+ * arrow keys move to the next or previous enabled segment and check it,
4834
+ * wrapping at either end, Home and End go to the first and last, and Space
4835
+ * checks the focused segment. Once a segment is checked, one always is.
4836
+ *
4837
+ * It carries no form value. A choice that's sent with a form is `Radios`.
4838
+ *
4839
+ * @function
4840
+ * @param {SegmentedControlProps} props - Props for the SegmentedControl component.
4841
+ * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the row's `<div>`.
4842
+ * @returns {JSX.Element} The rendered segmented control.
4843
+ * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/radio/ | APG radio group pattern}
4844
+ * @see {@link https://bulma.io/documentation/elements/button/#list-of-buttons | Bulma list of buttons}
4845
+ *
4846
+ * @example
4847
+ * <SegmentedControl aria-label="Text alignment" defaultValue="left">
4848
+ * <SegmentedControl.Segment value="left">Left</SegmentedControl.Segment>
4849
+ * <SegmentedControl.Segment value="center">Center</SegmentedControl.Segment>
4850
+ * <SegmentedControl.Segment value="right">Right</SegmentedControl.Segment>
4851
+ * </SegmentedControl>
4852
+ */
4853
+ const SegmentedControlRoot = React.forwardRef(function SegmentedControl({ value, defaultValue, onChange, color = 'link', size, hasAddons = true, isCentered, isRight, className, children, ...props }, ref) {
4854
+ const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
4855
+ // Controlled vs uncontrolled, as in Tabs and Collapses.
4856
+ const isControlled = value !== undefined;
4857
+ const [internalValue, setInternalValue] = React.useState(defaultValue);
4858
+ const checked = isControlled ? (value ?? undefined) : internalValue;
4859
+ const select = (next) => {
4860
+ if (next === checked)
4861
+ return;
4862
+ if (!isControlled)
4863
+ setInternalValue(next);
4864
+ onChange?.(next);
4865
+ };
4866
+ // The segments mount and register from an effect, so the group knows
4867
+ // which can hold the tab stop wherever in the tree they sit.
4868
+ const [segments, setSegments] = React.useState([]);
4869
+ const register = React.useCallback((record) => {
4870
+ setSegments(prev => [...prev, record]);
4871
+ return () => setSegments(prev => prev.filter(r => r !== record));
4872
+ }, []);
4873
+ const [focused, setFocused] = React.useState();
4874
+ // The first enabled segment in the page, sorted when it's read rather
4875
+ // than when a segment registers, so reordered segments are seen too. It's
4876
+ // read after the commit: a render runs before React moves the nodes, so
4877
+ // sorting there would find the old order.
4878
+ const [firstEnabled, setFirstEnabled] = React.useState();
4879
+ useClientLayoutEffect(() => {
4880
+ const inPage = segments.filter(s => !s.disabled).sort(inDocumentOrder);
4881
+ setFirstEnabled(inPage[0]?.value);
4882
+ }, [segments, children]);
4883
+ // One tab stop for the row (roving tabindex). While focus is in the row
4884
+ // the stop follows it, so Tab leaves from wherever focus is even when a
4885
+ // controlled group hasn't taken the new value. Otherwise it's the checked
4886
+ // segment, or the first enabled one when none is checked or the checked
4887
+ // one is disabled. Before the segments register (the server render and
4888
+ // the first client render) the checked one holds it, so a row with
4889
+ // nothing checked has no stop until the client has rendered.
4890
+ const canHoldStop = (candidate) => segments.some(s => !s.disabled && s.value === candidate);
4891
+ let tabStop;
4892
+ if (canHoldStop(focused))
4893
+ tabStop = focused;
4894
+ else if (segments.length === 0 || canHoldStop(checked))
4895
+ tabStop = checked;
4896
+ else
4897
+ tabStop = firstEnabled;
4898
+ const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);
4899
+ React.useEffect(() => {
4900
+ if (!named) {
4901
+ warnOnce('SegmentedControl:unnamed', '[bestax-bulma] <SegmentedControl> has no accessible name. Pass ' +
4902
+ 'aria-label or aria-labelledby, so a screen reader can say what ' +
4903
+ 'the choice is.');
4904
+ }
4905
+ }, [named]);
4906
+ const rowClasses = usePrefixedClassNames('buttons', {
4907
+ 'has-addons': hasAddons,
4908
+ 'is-centered': isCentered,
4909
+ 'is-right': isRight,
4910
+ [`are-${size}`]: size,
4911
+ });
4912
+ // The props are spread first, so the role a screen reader relies on wins
4913
+ // over anything a JavaScript caller or a spread object passes along.
4914
+ return (jsxRuntime.jsx(SegmentedControlContext.Provider, { value: { checked, tabStop, color, select, setFocused, register }, children: jsxRuntime.jsx("div", { ...rest, ref: ref, role: "radiogroup", className: classNames(rowClasses, bulmaHelperClasses, className), children: children }) }));
4915
+ });
4916
+ /**
4917
+ * One option in the row: a `<button role="radio">` that's checked while the group's value is its `value`, filled with the group's `color` and marked `is-selected`.
4918
+ *
4919
+ * @function
4920
+ * @param {SegmentedControlSegmentProps} props - Props for the segment.
4921
+ * @param {React.Ref<HTMLButtonElement>} ref - Forwarded ref to the `<button>`.
4922
+ * @returns {JSX.Element} The rendered segment.
4923
+ */
4924
+ const Segment = React.forwardRef(function Segment({ value, isDisabled, disabled, className, onClick, onKeyDown, onFocus, onBlur, ...props }, ref) {
4925
+ const ctx = React.useContext(SegmentedControlContext);
4926
+ const checked = ctx !== null && ctx.checked === value;
4927
+ const off = Boolean(isDisabled || disabled);
4928
+ // Prefixed like Button's own classes, which a hand-written
4929
+ // `className="is-selected"` could not be under ConfigProvider classPrefix.
4930
+ // Bulma lifts `is-selected` above its neighbours in `.buttons.has-addons`.
4931
+ const selectedClass = usePrefixedClassNames({ 'is-selected': checked });
4932
+ const nodeRef = React.useRef(null);
4933
+ const setRefs = React.useCallback((node) => {
4934
+ nodeRef.current = node;
4935
+ if (typeof ref === 'function')
4936
+ ref(node);
4937
+ else if (ref)
4938
+ ref.current = node;
4939
+ }, [ref]);
4940
+ const register = ctx?.register;
4941
+ React.useEffect(() => {
4942
+ if (!register) {
4943
+ warnOnce('SegmentedControl.Segment:outside', '[bestax-bulma] <SegmentedControl.Segment> is outside a ' +
4944
+ '<SegmentedControl>, so nothing can check it. Put it inside one.');
4945
+ return undefined;
4946
+ }
4947
+ return register({
4948
+ value,
4949
+ disabled: off,
4950
+ node: nodeRef.current,
4951
+ });
4952
+ }, [register, value, off]);
4953
+ // The caller's onClick runs first, so a `preventDefault()` there keeps
4954
+ // the segment from being checked. A disabled button gets no click.
4955
+ const handleClick = (event) => {
4956
+ onClick?.(event);
4957
+ if (event.defaultPrevented)
4958
+ return;
4959
+ ctx?.select(value);
4960
+ };
4961
+ const handleKeyDown = (event) => {
4962
+ onKeyDown?.(event);
4963
+ if (!ctx ||
4964
+ event.defaultPrevented ||
4965
+ event.altKey ||
4966
+ event.ctrlKey ||
4967
+ event.metaKey) {
4968
+ return;
4969
+ }
4970
+ const target = segmentFor(event.currentTarget, event.key);
4971
+ if (!target)
4972
+ return;
4973
+ event.preventDefault();
4974
+ // The row has taken this key, so nothing above it acts on it too: a
4975
+ // Carousel moves its slide on the arrows from a document listener that
4976
+ // doesn't read `defaultPrevented`. Keys the row leaves alone still bubble.
4977
+ event.stopPropagation();
4978
+ target.focus();
4979
+ // The check follows focus, APG's rule for a radio group outside a
4980
+ // toolbar. It goes through a click on the segment, as a native radio's
4981
+ // does, so its onClick sees it. Inside a toolbar the arrows would move
4982
+ // focus alone; that is the line a toolbar would change.
4983
+ target.click();
4984
+ };
4985
+ const handleFocus = (event) => {
4986
+ onFocus?.(event);
4987
+ ctx?.setFocused(value);
4988
+ };
4989
+ // The segment focus moves to, if any, records itself straight after.
4990
+ const handleBlur = (event) => {
4991
+ onBlur?.(event);
4992
+ ctx?.setFocused(undefined);
4993
+ };
4994
+ // The props are spread first, so what a radio depends on wins over
4995
+ // anything a JavaScript caller or a spread object passes along: it stays
4996
+ // a button, never submits, reports whether it's checked, and the group
4997
+ // decides whether Tab reaches it.
4998
+ return (jsxRuntime.jsx(Button, { ...props, ref: setRefs, as: "button", type: "button", role: "radio", "aria-checked": checked, tabIndex: ctx ? (ctx.tabStop === value ? 0 : -1) : undefined, isDisabled: off, color: checked ? ctx.color : undefined, className: classNames(selectedClass, className), onClick: handleClick, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur }));
4999
+ });
5000
+ Segment.displayName = 'SegmentedControl.Segment';
5001
+ const SegmentedControl = withSubComponents(SegmentedControlRoot, { Segment }, 'SegmentedControl');
5002
+
4648
5003
  /**
4649
5004
  * The `Loading` component provides a loading overlay with a spinner animation.
4650
5005
  *
@@ -5022,14 +5377,6 @@ const CollapsesComponent = ({ multiple = false, value, defaultValue, onChange, s
5022
5377
  };
5023
5378
  const Collapses = withSubComponents(CollapsesComponent, { Collapse }, 'Collapses');
5024
5379
 
5025
- // INTERNAL: deliberately not exported from src/index.ts.
5026
- /**
5027
- * `useLayoutEffect` in the browser, `useEffect` on the server. A server
5028
- * render runs neither, and React 18 warns about a layout effect there, so the
5029
- * server is handed the plain one.
5030
- */
5031
- const useClientLayoutEffect = typeof document === 'undefined' ? React.useEffect : React.useLayoutEffect;
5032
-
5033
5380
  // INTERNAL: deliberately not exported from src/index.ts.
5034
5381
  /**
5035
5382
  * Runs the caller's own handler on the element that holds the trigger, then
@@ -6207,135 +6554,6 @@ function useAnchoredPosition(anchorRef, panelRef, { active, position, fixed, off
6207
6554
  return resolved;
6208
6555
  }
6209
6556
 
6210
- const validButtonColors = [...validColors, 'text', 'ghost'];
6211
- /**
6212
- * The `Button` component provides a flexible and highly customizable button for your Bulma React UI.
6213
- *
6214
- * @function
6215
- * @param {ButtonProps} props - Props for the Button component.
6216
- * @param {React.Ref} ref - Forwarded ref to the element `as` renders.
6217
- * @returns {JSX.Element} The rendered button, anchor, or custom element.
6218
- * @see {@link https://bulma.io/documentation/elements/button/ | Bulma Button documentation}
6219
- */
6220
- const Button = React.forwardRef(function Button(props, ref) {
6221
- const { color, size, isLight, isRounded, isLoading, isStatic, isFullwidth, isFullWidth, isOutlined, isInverted, isFocused, isActive, isHovered, isDisabled, className, children, textColor, bgColor, as: Component = 'button', href, onClick, target, rel, ...bulmaProps } = props;
6222
- const { bulmaHelperClasses, rest } = useBulmaClasses({
6223
- color: textColor,
6224
- backgroundColor: bgColor,
6225
- ...bulmaProps,
6226
- });
6227
- // Generate Bulma classes with prefix
6228
- const bulmaClasses = usePrefixedClassNames('button', {
6229
- [`is-${color}`]: color &&
6230
- validButtonColors.includes(color),
6231
- [`is-${size}`]: size,
6232
- 'is-outlined': isOutlined,
6233
- 'is-light': isLight,
6234
- 'is-loading': isLoading,
6235
- 'is-static': isStatic,
6236
- 'is-disabled': isDisabled,
6237
- 'is-rounded': isRounded,
6238
- 'is-hovered': isHovered,
6239
- 'is-focused': isFocused,
6240
- 'is-active': isActive,
6241
- 'is-inverted': isInverted,
6242
- 'is-fullwidth': isFullwidth ?? isFullWidth,
6243
- });
6244
- // Combine prefixed Bulma classes with unprefixed user className and prefixed helper classes
6245
- const buttonClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
6246
- if (Component !== 'button') {
6247
- // Strip the form-control attributes a link-like element cannot carry, so a
6248
- // JavaScript consumer or a spread object doesn't put them on the DOM. The
6249
- // types no longer allow them through; this is the runtime backstop.
6250
- //
6251
- // Only for INTRINSIC tags. A custom component (a router Link, ...) owns its
6252
- // prop contract, and `ComponentPropsWithoutRef<T>` promises the caller that
6253
- // its props reach it. Stripping names by their spelling alone broke that
6254
- // promise silently: `<Button as={Custom} name="x" />` type-checked while
6255
- // `Custom` never received the `name` it requires.
6256
- //
6257
- // `autoFocus` and `type` are deliberately NOT stripped: both are valid on
6258
- // targets this list was filtering them from. `autoFocus` is a global
6259
- // attribute (React decides per element whether to honour it, which is not
6260
- // ours to pre-empt), and `type` on an anchor is the MIME hint that
6261
- // `ButtonProps<'a'>` now types it as.
6262
- //
6263
- // The types already answer this for a TypeScript caller: `ButtonProps<T>`
6264
- // derives from `ComponentPropsWithoutRef<T>`, so only what the target
6265
- // accepts can be passed. What is left is a runtime backstop for JavaScript
6266
- // consumers and spread objects.
6267
- //
6268
- // Keep it to the two cases where a stray attribute does something. The
6269
- // submit-overrides belong to a submit control and never make sense on the
6270
- // anchor path. `disabled` is the one that is VISIBLE: Bulma styles
6271
- // `.button[disabled]` (background, border, shadow, opacity), so letting it
6272
- // through to a `<span>` greys the element out — main stripped it, and not
6273
- // stripping it would be a silent visual change on a types-only release.
6274
- //
6275
- // Everything else (`name`, `value`, `form`) is inert where it does not
6276
- // belong, and enumerating which tags own it is what kept going wrong:
6277
- // first all nine names on every non-button tag (so `<Button as="input"
6278
- // name="query" value="Search">` submitted nothing), then a form-control
6279
- // allowlist that still lost `formAction` on an input and `value` on an
6280
- // option. Those now ride on the types instead.
6281
- //
6282
- // A custom component is exempt entirely — it owns its prop contract, and
6283
- // `ComponentPropsWithoutRef<T>` promises the caller its props arrive.
6284
- //
6285
- // The elements that own `disabled`, per the HTML spec. A closed set, unlike
6286
- // the open-ended question of which tag owns which attribute.
6287
- const SUBMIT_OVERRIDES = [
6288
- 'formAction',
6289
- 'formEncType',
6290
- 'formMethod',
6291
- 'formNoValidate',
6292
- 'formTarget',
6293
- ];
6294
- const OWNS_DISABLED = [
6295
- 'button',
6296
- 'fieldset',
6297
- 'input',
6298
- 'optgroup',
6299
- 'option',
6300
- 'select',
6301
- 'textarea',
6302
- ];
6303
- // Two independent filters. They are not exclusive — an `<a>` owns neither
6304
- // the submit-overrides nor `disabled`, so it takes both — and deriving them
6305
- // from one destructure, or from one if/else, is what broke each of them in
6306
- // turn: first the overrides came off every intrinsic (an `as="input"` lost
6307
- // `formAction`), then scoping them to the anchor let `disabled` back onto
6308
- // it.
6309
- const forwardedRest = { ...rest };
6310
- if (typeof Component === 'string' && !isCustomElement(Component)) {
6311
- // Meaningless anywhere but a submit control, and an anchor is not one.
6312
- if (Component === 'a') {
6313
- for (const key of SUBMIT_OVERRIDES)
6314
- delete forwardedRest[key];
6315
- }
6316
- // Visible via Bulma's `.button[disabled]`, so withheld from any element
6317
- // that does not own it.
6318
- if (!OWNS_DISABLED.includes(Component))
6319
- delete forwardedRest.disabled;
6320
- }
6321
- return (jsxRuntime.jsx(Component, { ref: ref, className: buttonClasses, href: href, target: target, rel: rel, "aria-disabled": isDisabled, tabIndex: isDisabled ? -1 : undefined, onClick: isDisabled
6322
- ? // Guarded, because this handler is installed into whatever `as`
6323
- // renders and a custom component's `onClick` need not be a DOM
6324
- // event handler at all. One declaring `onClick: (v: string) =>
6325
- // void` calls this with a string, and an unguarded
6326
- // `e.preventDefault()` throws `is not a function`. Blocking the
6327
- // real event is the point; a foreign payload is simply not ours
6328
- // to act on.
6329
- (e) => {
6330
- if (typeof e?.preventDefault === 'function')
6331
- e.preventDefault();
6332
- }
6333
- : onClick, ...forwardedRest, children: children }));
6334
- }
6335
- return (jsxRuntime.jsx("button", { ref: ref, className: buttonClasses, disabled: isDisabled, onClick: onClick, ...rest, children: children }));
6336
- });
6337
- Button.displayName = 'Button';
6338
-
6339
6557
  const PopoverContext = React.createContext(null);
6340
6558
  /** Intrinsic elements that are controls already, and keep their own role. */
6341
6559
  const NATIVE_CONTROLS = new Set([
@@ -8579,7 +8797,8 @@ LinkButton.displayName = 'LinkButton';
8579
8797
  * carries no form value: a setting saved with a form is a `Checkbox` or a
8580
8798
  * `Switch`. A set of toggles that each turn on and off on their own goes in
8581
8799
  * `Buttons hasAddons` with `role="group"` and an `aria-label`. A set where only
8582
- * one can be on is a radio group rather than a row of these: use `Radios`.
8800
+ * one can be on is a radio group rather than a row of these: use
8801
+ * `SegmentedControl`, or `Radios` for a choice that's sent with a form.
8583
8802
  *
8584
8803
  * @function
8585
8804
  * @param {ToggleButtonProps} props - Props for the ToggleButton component.
@@ -19206,6 +19425,7 @@ exports.Radios = Radios;
19206
19425
  exports.Rate = Rate;
19207
19426
  exports.Reveal = Reveal;
19208
19427
  exports.Section = Section;
19428
+ exports.SegmentedControl = SegmentedControl;
19209
19429
  exports.Select = Select;
19210
19430
  exports.SelectBase = SelectBase;
19211
19431
  exports.Sidebar = Sidebar;