@allxsmith/bestax-bulma 5.20.0 → 5.23.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.
Files changed (40) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/extras.css +1 -1
  4. package/dist/extras.css.map +1 -1
  5. package/dist/index.cjs +658 -86
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.esm.js +654 -87
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/types/components/Collapse.d.ts +13 -9
  10. package/dist/types/components/Collapses.d.ts +53 -0
  11. package/dist/types/components/collapsesContext.d.ts +19 -0
  12. package/dist/types/helpers/ClientOnly.d.ts +40 -0
  13. package/dist/types/helpers/inertProps.d.ts +11 -0
  14. package/dist/types/helpers/portal.d.ts +62 -0
  15. package/dist/types/helpers/shadowDom.d.ts +3 -1
  16. package/dist/types/helpers/useFocusTrap.d.ts +74 -0
  17. package/dist/types/helpers/useIsHydrated.d.ts +5 -0
  18. package/dist/types/index.d.ts +8 -0
  19. package/dist/types-cjs/components/Collapse.d.ts +13 -9
  20. package/dist/types-cjs/components/Collapses.d.ts +53 -0
  21. package/dist/types-cjs/components/collapsesContext.d.ts +19 -0
  22. package/dist/types-cjs/helpers/ClientOnly.d.ts +40 -0
  23. package/dist/types-cjs/helpers/inertProps.d.ts +11 -0
  24. package/dist/types-cjs/helpers/portal.d.ts +62 -0
  25. package/dist/types-cjs/helpers/shadowDom.d.ts +3 -1
  26. package/dist/types-cjs/helpers/useFocusTrap.d.ts +74 -0
  27. package/dist/types-cjs/helpers/useIsHydrated.d.ts +5 -0
  28. package/dist/types-cjs/index.d.ts +8 -0
  29. package/dist/versions/bestax-no-dark-mode.css +1 -1
  30. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  31. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  32. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  33. package/dist/versions/bestax-no-helpers.css +1 -1
  34. package/dist/versions/bestax-no-helpers.css.map +1 -1
  35. package/dist/versions/bestax-prefixed.css +1 -1
  36. package/dist/versions/bestax-prefixed.css.map +1 -1
  37. package/package.json +1 -1
  38. package/src/scss/components/_collapse.scss +13 -4
  39. package/dist/types/form/_pickerInternals/useFocusTrap.d.ts +0 -7
  40. package/dist/types-cjs/form/_pickerInternals/useFocusTrap.d.ts +0 -7
package/dist/index.cjs CHANGED
@@ -1685,12 +1685,12 @@ Avatar.displayName = 'Avatar';
1685
1685
  * prefixing the fragment's own key — otherwise a fragment sibling of a direct
1686
1686
  * child would collide (both ".0") and trigger duplicate-key warnings.
1687
1687
  */
1688
- function flattenChildren(children, keyPrefix = '') {
1688
+ function flattenChildren$1(children, keyPrefix = '') {
1689
1689
  return React.Children.toArray(children).flatMap(child => {
1690
1690
  if (!React.isValidElement(child))
1691
1691
  return [];
1692
1692
  if (child.type === React.Fragment) {
1693
- return flattenChildren(child.props.children, keyPrefix + child.key);
1693
+ return flattenChildren$1(child.props.children, keyPrefix + child.key);
1694
1694
  }
1695
1695
  const el = child;
1696
1696
  return [
@@ -1724,7 +1724,7 @@ const AvatarsComponent = ({ className, max, size, shape, spacing = 'md', spaced
1724
1724
  : undefined;
1725
1725
  const surplusClass = usePrefixedClassNames('is-surplus');
1726
1726
  const combinedClasses = classNames(avatarsClasses, bulmaHelperClasses, className);
1727
- const childArray = flattenChildren(children);
1727
+ const childArray = flattenChildren$1(children);
1728
1728
  const maxCount = typeof max === 'number' && Number.isInteger(max) && max >= 0
1729
1729
  ? max
1730
1730
  : undefined;
@@ -2256,10 +2256,12 @@ function getActiveElementInTree(node) {
2256
2256
  * opener sits in. A closed shadow root cannot be entered, so focus inside one
2257
2257
  * reads as its host.
2258
2258
  *
2259
+ * @param doc - The document to start from: the component's own, when it may
2260
+ * render into one other than the page's (an iframe's). Defaults to `document`.
2259
2261
  * @returns The focused element, or `null` when the document has none.
2260
2262
  */
2261
- function getDeepestActiveElement() {
2262
- let active = document.activeElement;
2263
+ function getDeepestActiveElement(doc = document) {
2264
+ let active = doc.activeElement;
2263
2265
  while (active?.shadowRoot?.activeElement) {
2264
2266
  active = active.shadowRoot.activeElement;
2265
2267
  }
@@ -2796,6 +2798,32 @@ function useScrollLock(active) {
2796
2798
  }, [active]);
2797
2799
  }
2798
2800
 
2801
+ // "Are we past hydration?" as a store that never changes: React reads the
2802
+ // server snapshot both while server-rendering and while hydrating, and the
2803
+ // client snapshot from the first post-hydration render onward.
2804
+ const subscribeToNothing = () => () => { };
2805
+ const getClientSnapshot = () => true;
2806
+ const getServerSnapshot = () => false;
2807
+ /**
2808
+ * Reports whether the component has passed hydration.
2809
+ *
2810
+ * Returns `false` during server rendering and during the hydrating client
2811
+ * render, then `true` from the commit that follows. Use it to defer
2812
+ * DOM-only behaviour (such as portalling) past hydration so the first client
2813
+ * render matches the server markup instead of tripping hydration recovery.
2814
+ *
2815
+ * Only a component that hydrates ever sees `false` on the client. One that
2816
+ * mounts later (after a route change, or in an app with no server rendering)
2817
+ * gets `true` on its first render, so it pays for no extra render. It
2818
+ * subscribes to nothing, so no later render flips it back.
2819
+ *
2820
+ * @function useIsHydrated
2821
+ * @returns `true` once the hydrating render has completed, otherwise `false`.
2822
+ */
2823
+ function useIsHydrated() {
2824
+ return React.useSyncExternalStore(subscribeToNothing, getClientSnapshot, getServerSnapshot);
2825
+ }
2826
+
2799
2827
  /**
2800
2828
  * Resolves a portal target: an `HTMLElement` is used directly, a non-empty
2801
2829
  * `string` is treated as a `document.querySelector` selector (falling back to
@@ -2819,26 +2847,49 @@ function resolvePortalContainer(container) {
2819
2847
  }
2820
2848
  return container;
2821
2849
  }
2822
-
2823
- // "Are we past hydration?" as a store that never changes: React reads the
2824
- // server snapshot both while server-rendering and while hydrating, and the
2825
- // client snapshot from the first post-hydration render onward.
2826
- const subscribeToNothing = () => () => { };
2827
- const getClientSnapshot = () => true;
2828
- const getServerSnapshot = () => false;
2829
2850
  /**
2830
- * Reports whether the component has passed hydration.
2851
+ * Renders its children into another part of the page, `document.body` unless `container` says otherwise, so floating content escapes an ancestor's `overflow`, `transform` or stacking context.
2831
2852
  *
2832
- * Returns `false` during server rendering and during the hydrating client
2833
- * render, then `true` from the commit that follows. Use it to defer
2834
- * DOM-only behaviour (such as portalling) past hydration so the first client
2835
- * render matches the server markup instead of tripping hydration recovery.
2853
+ * React's server renderer can't render a portal, so `Portal` renders nothing
2854
+ * on the server and during hydration, and mounts its children in the commit
2855
+ * that follows. The first client render then matches the server markup. In an app
2856
+ * without server rendering it portals on the first render.
2836
2857
  *
2837
- * @function useIsHydrated
2838
- * @returns `true` once the hydrating render has completed, otherwise `false`.
2839
- */
2840
- function useIsHydrated() {
2841
- return React.useSyncExternalStore(subscribeToNothing, getClientSnapshot, getServerSnapshot);
2858
+ * React context, such as `ConfigProvider`'s class prefix, reaches the
2859
+ * portaled children, and their React events bubble through the React tree.
2860
+ * CSS inheritance follows the DOM instead: a scoped (non-`isRoot`) `Theme`
2861
+ * sets its variables on a wrapper element the portaled content has left, so
2862
+ * that content sees the page-level theme. Point `container` at an element
2863
+ * inside the themed area to keep it.
2864
+ *
2865
+ * Tab order and focus follow the DOM too. Content portaled from inside a
2866
+ * focus trap is no longer inside the trapped element, so the trap neither
2867
+ * reaches it nor holds focus there: render nested overlays inside the
2868
+ * trapped element, or give them a trap of their own.
2869
+ *
2870
+ * @function
2871
+ * @param {PortalProps} props - Props for the Portal component.
2872
+ * @returns The portal, the children in place when `disabled`, or nothing
2873
+ * before hydration.
2874
+ *
2875
+ * @example
2876
+ * <Portal>
2877
+ * <div className="box">Rendered at the end of document.body</div>
2878
+ * </Portal>
2879
+ *
2880
+ * @example
2881
+ * // Into a dedicated overlay root
2882
+ * <Portal container="#overlays">
2883
+ * <CommandPalette />
2884
+ * </Portal>
2885
+ */
2886
+ function Portal({ children, container, disabled = false }) {
2887
+ const hydrated = useIsHydrated();
2888
+ if (disabled)
2889
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: children });
2890
+ if (!hydrated)
2891
+ return null;
2892
+ return reactDom.createPortal(children, resolvePortalContainer(container));
2842
2893
  }
2843
2894
 
2844
2895
  /**
@@ -2852,7 +2903,7 @@ const activeModalStack = [];
2852
2903
  * excluded because `focus()` on them is a no-op, which would leave focus
2853
2904
  * outside the modal.
2854
2905
  */
2855
- const FOCUSABLE_SELECTOR$1 = 'button:not(:disabled), [href], input:not(:disabled):not([type="hidden"]), select:not(:disabled), textarea:not(:disabled), [tabindex]';
2906
+ const FOCUSABLE_SELECTOR = 'button:not(:disabled), [href], input:not(:disabled):not([type="hidden"]), select:not(:disabled), textarea:not(:disabled), [tabindex]';
2856
2907
  /**
2857
2908
  * The modal's tab stops, in document order. The selector alone is not the
2858
2909
  * tabbable set — `[href]` and `button` match regardless of `tabindex`, so an
@@ -2861,7 +2912,7 @@ const FOCUSABLE_SELECTOR$1 = 'button:not(:disabled), [href], input:not(:disabled
2861
2912
  * resolved value, so filtering on it drops every negative index (not just
2862
2913
  * `-1`) without a second selector to keep in sync.
2863
2914
  */
2864
- const getTabbable = (node) => Array.from(node.querySelectorAll(FOCUSABLE_SELECTOR$1)).filter(el => el.tabIndex >= 0);
2915
+ const getTabbable = (node) => Array.from(node.querySelectorAll(FOCUSABLE_SELECTOR)).filter(el => el.tabIndex >= 0);
2865
2916
  /**
2866
2917
  * Modal.Background - Renders the modal background overlay.
2867
2918
  *
@@ -4257,6 +4308,35 @@ const Loading = ({ active = false, isFullPage = false, size, color, canCancel =
4257
4308
  return (jsxRuntime.jsxs("div", { className: combinedClasses, role: "alert", "aria-busy": "true", "aria-label": "Loading", ...rest, children: [jsxRuntime.jsx("div", { className: combinedOverlayClasses, onClick: handleOverlayClick, "aria-hidden": "true" }), jsxRuntime.jsxs("div", { className: loadingContentClass, children: [indicator ? (jsxRuntime.jsx("span", { className: loadingIconCustomClass, children: indicator })) : (jsxRuntime.jsx("span", { className: combinedIconClasses })), children && jsxRuntime.jsx("div", { className: loadingTextClass, children: children }), canCancel && (jsxRuntime.jsx("button", { type: "button", className: loadingCancelClass, onClick: onCancel, "aria-label": "Cancel loading", children: "Cancel" }))] })] }));
4258
4309
  };
4259
4310
 
4311
+ // INTERNAL: deliberately not exported from src/index.ts.
4312
+ /** The major version of the React this bundle is running against. */
4313
+ const runningMajor = () => Number(React.version.split('.')[0]);
4314
+ /**
4315
+ * The prop that makes an element inert, spelled for the React in use, or
4316
+ * nothing when `inert` is false.
4317
+ *
4318
+ * Both majors must render `inert=""`, and each warns on the other's spelling
4319
+ * and then drops the attribute. React 19 treats `inert` as a boolean and
4320
+ * rejects the empty string. React 18 does not know the attribute, so it
4321
+ * passes a string through and rejects `true`.
4322
+ */
4323
+ const inertProps = (inert, major = runningMajor()) => {
4324
+ if (!inert)
4325
+ return {};
4326
+ // The cast is for React 18's spelling: this package's types are React 19's,
4327
+ // where `inert` is a boolean.
4328
+ return (major >= 19
4329
+ ? { inert: true }
4330
+ : { inert: '' });
4331
+ };
4332
+
4333
+ /**
4334
+ * `null` outside a group. A `Collapse` also resets it to `null` for its own
4335
+ * subtree, so a Collapse nested in an item's content keeps its own state
4336
+ * instead of answering to its ancestor's slot in the group.
4337
+ */
4338
+ const CollapsesItemContext = React.createContext(null);
4339
+
4260
4340
  /**
4261
4341
  * The `Collapse` component provides an expandable/collapsible content panel.
4262
4342
  *
@@ -4271,12 +4351,9 @@ const Loading = ({ active = false, isFullPage = false, size, color, canCancel =
4271
4351
  * </Collapse>
4272
4352
  *
4273
4353
  * @example
4274
- * // Controlled collapse
4354
+ * // Controlled collapse, driven by its own trigger
4275
4355
  * const [isOpen, setIsOpen] = useState(false);
4276
- * <Collapse
4277
- * open={isOpen}
4278
- * trigger={<button onClick={() => setIsOpen(!isOpen)}>Toggle</button>}
4279
- * >
4356
+ * <Collapse open={isOpen} onOpenChange={setIsOpen} trigger={<strong>Toggle</strong>}>
4280
4357
  * <p>Content</p>
4281
4358
  * </Collapse>
4282
4359
  *
@@ -4290,14 +4367,49 @@ const Loading = ({ active = false, isFullPage = false, size, color, canCancel =
4290
4367
  * <p>Content</p>
4291
4368
  * </Collapse>
4292
4369
  */
4293
- const Collapse = ({ open: controlledOpen, defaultOpen = false, onOpen, onClose, trigger, animation = 'fade', position = 'top', ariaId, bordered, children, className, triggerClassName, contentClassName, ...props }) => {
4370
+ const Collapse = ({ open: controlledOpen, defaultOpen = false, onOpen, onClose, onOpenChange, trigger, animation = 'fade', position = 'top', ariaId, bordered, children, className, triggerClassName, contentClassName, ...props }) => {
4294
4371
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
4295
4372
  const contentRef = React.useRef(null);
4296
- const [height, setHeight] = React.useState(defaultOpen ? 'auto' : 0);
4297
- // Determine if controlled or uncontrolled
4373
+ // Three possible owners of the open state, in order: the caller when `open`
4374
+ // is set, an enclosing Collapses group, and this Collapse itself. A caller's
4375
+ // `open` wins over the group, which is how the group leaves such an item
4376
+ // alone.
4298
4377
  const isControlled = controlledOpen !== undefined;
4378
+ const group = React.useContext(CollapsesItemContext);
4379
+ const groupItem = isControlled ? null : group;
4299
4380
  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
4300
- const isOpen = isControlled ? controlledOpen : internalOpen;
4381
+ const isOpen = controlledOpen ?? groupItem?.open ?? internalOpen;
4382
+ // A group owns the state of the items it manages, so these props do
4383
+ // nothing there. That is easy to miss, so development says which ones and
4384
+ // what to use instead, once per combination (the way Avatar keys its
4385
+ // warning), and `warnOnce` keeps production silent.
4386
+ if (groupItem) {
4387
+ const ignored = [
4388
+ defaultOpen && 'defaultOpen',
4389
+ onOpen && 'onOpen',
4390
+ onClose && 'onClose',
4391
+ ].filter((name) => Boolean(name));
4392
+ if (ignored.length > 0) {
4393
+ const callbacks = ignored.filter(name => name !== 'defaultOpen');
4394
+ warnOnce(`Collapse:ignored-in-Collapses:${ignored.join('+')}`, `[bestax-bulma] <Collapse ${ignored.join(' ')}> inside <Collapses>: ` +
4395
+ [
4396
+ ignored.includes('defaultOpen') &&
4397
+ `defaultOpen has no effect, because the group decides which ` +
4398
+ `items start open. Pass the item's index in the group's ` +
4399
+ `defaultValue instead.`,
4400
+ callbacks.length > 0 &&
4401
+ `${callbacks.join(' and ')} never fire${callbacks.length > 1 ? '' : 's'}, ` +
4402
+ `because the group holds the item's open state. Use the ` +
4403
+ `item's onOpenChange, or the group's onChange, instead.`,
4404
+ ]
4405
+ .filter(Boolean)
4406
+ .join(' '));
4407
+ }
4408
+ }
4409
+ // Start from the resolved state rather than `defaultOpen`, so an item that
4410
+ // mounts open through `open` or a group is not first painted (or rendered
4411
+ // on the server) at height 0 under the slide animation.
4412
+ const [height, setHeight] = React.useState(isOpen ? 'auto' : 0);
4301
4413
  // Generate unique ID for accessibility (useId is SSR-safe and stable across
4302
4414
  // renders; works in React 18 and 19).
4303
4415
  const generatedId = React.useId();
@@ -4342,19 +4454,25 @@ const Collapse = ({ open: controlledOpen, defaultOpen = false, onOpen, onClose,
4342
4454
  // excluded from the deps.
4343
4455
  // eslint-disable-next-line react-hooks/exhaustive-deps
4344
4456
  }, [isOpen, animation]);
4345
- // Handle toggle
4457
+ // The trigger reports in every mode, then changes only the state this
4458
+ // Collapse owns. `onOpen`/`onClose` belong to that last case alone.
4346
4459
  const handleToggle = React.useCallback(() => {
4347
- if (!isControlled) {
4348
- const newOpen = !internalOpen;
4349
- setInternalOpen(newOpen);
4350
- if (newOpen) {
4351
- onOpen?.();
4352
- }
4353
- else {
4354
- onClose?.();
4355
- }
4460
+ const next = !isOpen;
4461
+ onOpenChange?.(next);
4462
+ if (isControlled)
4463
+ return;
4464
+ if (groupItem) {
4465
+ groupItem.setOpen(next);
4466
+ return;
4356
4467
  }
4357
- }, [isControlled, internalOpen, onOpen, onClose]);
4468
+ setInternalOpen(next);
4469
+ if (next) {
4470
+ onOpen?.();
4471
+ }
4472
+ else {
4473
+ onClose?.();
4474
+ }
4475
+ }, [isOpen, isControlled, groupItem, onOpenChange, onOpen, onClose]);
4358
4476
  // Handle keyboard interaction
4359
4477
  const handleKeyDown = React.useCallback((e) => {
4360
4478
  if (e.key === 'Enter' || e.key === ' ') {
@@ -4395,9 +4513,99 @@ const Collapse = ({ open: controlledOpen, defaultOpen = false, onOpen, onClose,
4395
4513
  };
4396
4514
  }
4397
4515
  const triggerElement = (jsxRuntime.jsx("div", { className: combinedTriggerClasses, onClick: handleToggle, onKeyDown: handleKeyDown, role: "button", tabIndex: 0, "aria-expanded": isOpen, "aria-controls": uniqueId, children: trigger }));
4398
- const contentElement = (jsxRuntime.jsx("div", { style: contentWrapperStyle, children: jsxRuntime.jsx("div", { ref: contentRef, id: uniqueId, className: combinedContentClasses, "aria-hidden": !isOpen, children: children }) }));
4399
- return (jsxRuntime.jsx("div", { className: combinedClasses, ...rest, children: position === 'bottom' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [contentElement, triggerElement] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [triggerElement, contentElement] })) }));
4516
+ const contentElement = (jsxRuntime.jsx("div", { style: contentWrapperStyle, children: jsxRuntime.jsx("div", { ref: contentRef, id: uniqueId, className: combinedContentClasses, "aria-hidden": !isOpen, ...inertProps(!isOpen), children: children }) }));
4517
+ // The group's item context stops here: a Collapse nested in this one's
4518
+ // content is not an item of this one's group.
4519
+ return (jsxRuntime.jsx(CollapsesItemContext.Provider, { value: null, children: jsxRuntime.jsx("div", { className: combinedClasses, ...rest, children: position === 'bottom' ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [contentElement, triggerElement] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [triggerElement, contentElement] })) }) }));
4520
+ };
4521
+
4522
+ /** Either mode's value as the list of open indexes. */
4523
+ function toIndexes(value) {
4524
+ if (Array.isArray(value))
4525
+ return value;
4526
+ return typeof value === 'number' ? [value] : [];
4527
+ }
4528
+ /**
4529
+ * Flattens Fragment children (recursively) so each item inside one gets its
4530
+ * own index rather than sharing the fragment's. Children.toArray keys restart
4531
+ * at ".0" inside each fragment, so those children are re-keyed with the
4532
+ * fragment's own key as a prefix, the way Avatars does it.
4533
+ */
4534
+ function flattenChildren(children, keyPrefix = '') {
4535
+ return React.Children.toArray(children).flatMap(child => {
4536
+ if (!React.isValidElement(child))
4537
+ return [child];
4538
+ if (child.type === React.Fragment) {
4539
+ return flattenChildren(child.props.children, keyPrefix + child.key);
4540
+ }
4541
+ return [
4542
+ keyPrefix
4543
+ ? React.cloneElement(child, { key: keyPrefix + child.key })
4544
+ : child,
4545
+ ];
4546
+ });
4547
+ }
4548
+ /**
4549
+ * The `Collapses` component groups `Collapse` items into an accordion that keeps one item open at a time, or any number with `multiple`.
4550
+ *
4551
+ * @function
4552
+ * @param {CollapsesProps} props - Props for the Collapses component.
4553
+ * @returns {JSX.Element} The rendered group.
4554
+ *
4555
+ * @example
4556
+ * // Accordion: one item open at a time, the first to start with
4557
+ * <Collapses defaultValue={0}>
4558
+ * <Collapse bordered trigger={<strong>Shipping</strong>}>…</Collapse>
4559
+ * <Collapse bordered trigger={<strong>Returns</strong>}>…</Collapse>
4560
+ * </Collapses>
4561
+ *
4562
+ * @example
4563
+ * // Controlled, with items that open independently
4564
+ * const [open, setOpen] = useState<number[]>([]);
4565
+ * <Collapses multiple value={open} onChange={setOpen}>…</Collapses>
4566
+ */
4567
+ const CollapsesComponent = ({ multiple = false, value, defaultValue, onChange, seamless = false, className, children, ...props }) => {
4568
+ const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
4569
+ // Controlled vs uncontrolled, as in Tabs. State is held as a list of open
4570
+ // indexes in both modes; `multiple` only decides how a change is computed
4571
+ // and which shape `onChange` receives.
4572
+ const isControlled = value !== undefined;
4573
+ const [internalOpen, setInternalOpen] = React.useState(() => toIndexes(defaultValue));
4574
+ const openIndexes = isControlled ? toIndexes(value) : internalOpen;
4575
+ // Each mode types `onChange` for its own shape, and the value built below
4576
+ // always matches `multiple`, the union's discriminant.
4577
+ const reportChange = onChange;
4578
+ const setItemOpen = (index, open) => {
4579
+ const others = openIndexes.filter(i => i !== index);
4580
+ // Single mode keeps at most one item open, so opening one drops the rest.
4581
+ // Ascending order, whatever order the items were opened in, so the value
4582
+ // reads and compares the same way however the user got there.
4583
+ const next = (open ? (multiple ? [...others, index] : [index]) : others).sort((a, b) => a - b);
4584
+ if (!isControlled)
4585
+ setInternalOpen(next);
4586
+ reportChange?.(multiple ? next : (next[0] ?? null));
4587
+ };
4588
+ const groupClasses = usePrefixedClassNames('collapses', {
4589
+ 'is-seamless': seamless,
4590
+ });
4591
+ const combinedClasses = classNames(groupClasses, bulmaHelperClasses, className);
4592
+ const items = [];
4593
+ let index = 0;
4594
+ for (const child of flattenChildren(children)) {
4595
+ if (!React.isValidElement(child)) {
4596
+ items.push(child);
4597
+ continue;
4598
+ }
4599
+ const itemIndex = index;
4600
+ index += 1;
4601
+ items.push(jsxRuntime.jsx(CollapsesItemContext.Provider, { value: {
4602
+ open: openIndexes.includes(itemIndex),
4603
+ setOpen: open => setItemOpen(itemIndex, open),
4604
+ }, children: child }, child.key));
4605
+ }
4606
+ return (jsxRuntime.jsx("div", { className: combinedClasses, ...rest, children: items }));
4400
4607
  };
4608
+ const Collapses = withSubComponents(CollapsesComponent, { Collapse }, 'Collapses');
4401
4609
 
4402
4610
  /**
4403
4611
  * The `Tooltip` component displays helpful information when users hover over or focus on an element.
@@ -10764,63 +10972,395 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min, max,
10764
10972
  };
10765
10973
  Calendar.displayName = 'Calendar';
10766
10974
 
10767
- const FOCUSABLE_SELECTOR = [
10768
- 'a[href]',
10769
- 'button:not([disabled])',
10770
- 'input:not([disabled]):not([type="hidden"])',
10771
- 'select:not([disabled])',
10772
- 'textarea:not([disabled])',
10773
- '[tabindex]:not([tabindex="-1"])',
10774
- ].join(',');
10775
- function useFocusTrap(containerRef, active, options = {}) {
10776
- const { initialFocusRef, restoreFocus = true } = options;
10975
+ /**
10976
+ * Whether `el` is editable while its parent is not, which makes it an editing
10977
+ * host: the one tab stop an editable region gets. `contenteditable="false"`
10978
+ * turns editing off, and a missing or unknown value inherits the parent's.
10979
+ */
10980
+ function isEditingHost(el) {
10981
+ const editable = (node) => {
10982
+ const value = node.getAttribute('contenteditable')?.toLowerCase();
10983
+ if (value === undefined)
10984
+ return null;
10985
+ if (value === '' || value === 'true' || value === 'plaintext-only') {
10986
+ return true;
10987
+ }
10988
+ return value === 'false' ? false : null;
10989
+ };
10990
+ if (editable(el) !== true)
10991
+ return false;
10992
+ for (let node = el.parentElement; node; node = node.parentElement) {
10993
+ const state = editable(node);
10994
+ if (state !== null)
10995
+ return !state;
10996
+ }
10997
+ return true;
10998
+ }
10999
+ /** Whether `el` is the summary of its `<details>`: the first one, not a stray. */
11000
+ const isDetailsSummary = (el) => el.parentElement?.localName === 'details' &&
11001
+ Array.from(el.parentElement.children).find(child => child.localName === 'summary') === el;
11002
+ /**
11003
+ * Whether the browser puts `el` in the tab order, before anything that hides
11004
+ * or disables it. The `tabIndex` property can't answer this on its own: it
11005
+ * can read `-1` for an editable region or an `<embed>` that Tab still visits,
11006
+ * and `0` for a `<video>` without controls that it doesn't. So a valid
11007
+ * `tabindex` attribute decides, and otherwise the element's kind does.
11008
+ */
11009
+ function isTabbableKind(el) {
11010
+ const tabindex = el.getAttribute('tabindex');
11011
+ if (tabindex !== null && /^\s*[+-]?\d/.test(tabindex)) {
11012
+ return el.tabIndex >= 0;
11013
+ }
11014
+ switch (el.localName) {
11015
+ case 'a':
11016
+ case 'area':
11017
+ return el.hasAttribute('href');
11018
+ case 'input':
11019
+ return el.type !== 'hidden';
11020
+ case 'button':
11021
+ case 'select':
11022
+ case 'textarea':
11023
+ case 'iframe':
11024
+ case 'embed':
11025
+ case 'object':
11026
+ return true;
11027
+ case 'audio':
11028
+ case 'video':
11029
+ return el.hasAttribute('controls');
11030
+ case 'summary':
11031
+ return isDetailsSummary(el);
11032
+ default:
11033
+ return isEditingHost(el);
11034
+ }
11035
+ }
11036
+ /**
11037
+ * The node's parent in the flattened tree, the one the browser renders and
11038
+ * tabs through: the slot it is assigned to, else its parent, else the host of
11039
+ * the shadow root it tops, and nothing above the document. A closed shadow
11040
+ * root hides its slots (`assignedSlot` reads `null` under one), so a child of
11041
+ * its host goes straight to the host, whether a slot takes it or not.
11042
+ */
11043
+ const flatParent = (node) => node.assignedSlot ??
11044
+ node.parentElement ??
11045
+ node.parentNode?.host ??
11046
+ null;
11047
+ /**
11048
+ * The element's children in the flattened tree, in the order they render: a
11049
+ * host's shadow root content, a slot's assigned elements (or its fallback
11050
+ * content when nothing is assigned), anything else's own children. A host
11051
+ * whose shadow root is closed has no root to read, so its own children stand
11052
+ * in, including any that no slot takes and that aren't rendered.
11053
+ */
11054
+ function flatChildren(el) {
11055
+ if (el.shadowRoot)
11056
+ return Array.from(el.shadowRoot.children);
11057
+ if (el.localName === 'slot') {
11058
+ const assigned = el.assignedNodes();
11059
+ if (assigned.length > 0) {
11060
+ return assigned.filter((node) => node.nodeType === Node.ELEMENT_NODE);
11061
+ }
11062
+ }
11063
+ return Array.from(el.children);
11064
+ }
11065
+ const isShown = (el) => !el.hasAttribute('inert') && getComputedStyle(el).display !== 'none';
11066
+ const isVisible = (el) => {
11067
+ const { visibility } = getComputedStyle(el);
11068
+ return visibility !== 'hidden' && visibility !== 'collapse';
11069
+ };
11070
+ /**
11071
+ * Whether nothing from `el` up to `top` hides it: `inert`, `display: none`
11072
+ * (not inherited, so each element is checked) or a closed `<details>` it
11073
+ * isn't the summary of. `top` itself isn't checked, and `null` walks to the
11074
+ * top of the page.
11075
+ */
11076
+ function isShownUpTo(el, top) {
11077
+ let below = null;
11078
+ for (let node = el; node && node !== top; node = flatParent(node)) {
11079
+ if (!isShown(node))
11080
+ return false;
11081
+ if (below &&
11082
+ node.localName === 'details' &&
11083
+ !node.hasAttribute('open') &&
11084
+ !isDetailsSummary(below)) {
11085
+ return false;
11086
+ }
11087
+ below = node;
11088
+ }
11089
+ return true;
11090
+ }
11091
+ /**
11092
+ * The images that use the image map `area` belongs to. An image names its
11093
+ * map with `usemap="#…"`, matching the map's `name` or `id`.
11094
+ */
11095
+ function imagesUsingMapOf(area) {
11096
+ const map = area.closest('map');
11097
+ if (!map)
11098
+ return [];
11099
+ const names = [map.getAttribute('name'), map.id].filter(name => name);
11100
+ return Array.from(area.getRootNode().querySelectorAll('img[usemap]')).filter(img => {
11101
+ const ref = img.getAttribute('usemap');
11102
+ return ref.startsWith('#') && names.includes(ref.slice(1));
11103
+ });
11104
+ }
11105
+ /**
11106
+ * Whether an image map area is shown. An area has no box of its own: the
11107
+ * HTML rendering rules give it `display: none`, though a browser may not
11108
+ * report that. So it counts where an image using its map is shown, as long
11109
+ * as nothing makes it inert.
11110
+ */
11111
+ function isAreaShown(area, container) {
11112
+ for (let node = area; node && node !== container; node = flatParent(node)) {
11113
+ if (node.hasAttribute('inert'))
11114
+ return false;
11115
+ }
11116
+ return imagesUsingMapOf(area).some(img => isShownUpTo(img, null) && isVisible(img));
11117
+ }
11118
+ /**
11119
+ * Whether Tab can land on `el`, one of the kinds it visits. A disabled control
11120
+ * (including one inside a disabled `<fieldset>`), an inert subtree and
11121
+ * anything not rendered are skipped. `visibility` is inherited, so the
11122
+ * element's own computed value covers its ancestors.
11123
+ */
11124
+ function isRenderedAndEnabled(el, container) {
11125
+ if (el.matches(':disabled'))
11126
+ return false;
11127
+ if (el.localName === 'area')
11128
+ return isAreaShown(el, container);
11129
+ return isShownUpTo(el, container) && isVisible(el);
11130
+ }
11131
+ /** A radio button with a name, which shares one tab stop with its group. */
11132
+ const isGroupedRadio = (el) => el.localName === 'input' &&
11133
+ el.type === 'radio' &&
11134
+ el.name !== '';
11135
+ /** The flattened-tree ancestors of `node` below the container, outermost first, then `node`. */
11136
+ function flatChain(node, container) {
11137
+ const chain = [];
11138
+ for (let n = node; n && n !== container; n = flatParent(n)) {
11139
+ chain.unshift(n);
11140
+ }
11141
+ return chain;
11142
+ }
11143
+ /**
11144
+ * Whether `node` is inside the container in the flattened tree: in it, in a
11145
+ * shadow root it holds, or slotted into it. A child of a closed-root host
11146
+ * counts as inside wherever the host is, as its slot can't be seen.
11147
+ */
11148
+ function inside(container, node) {
11149
+ for (let n = node; n; n = flatParent(n)) {
11150
+ if (n === container)
11151
+ return true;
11152
+ }
11153
+ return false;
11154
+ }
11155
+ /**
11156
+ * `true` when Tab reaches `b` after `a`, which is their order in the
11157
+ * flattened tree: a shadow host comes before its shadow root's content, and
11158
+ * slotted content comes where its slot is. A closed root's slots can't be
11159
+ * seen, so its host's children keep their place in the light DOM. Two
11160
+ * flattened-tree siblings are always in one DOM tree, so their document
11161
+ * position decides between them.
11162
+ */
11163
+ function follows(container, a, b) {
11164
+ const chainA = flatChain(a, container);
11165
+ const chainB = flatChain(b, container);
11166
+ for (let i = 0; i < Math.max(chainA.length, chainB.length); i++) {
11167
+ const [x, y] = [chainA[i], chainB[i]];
11168
+ if (x === y)
11169
+ continue;
11170
+ if (!x)
11171
+ return true;
11172
+ if (!y)
11173
+ return false;
11174
+ return Boolean(x.compareDocumentPosition(y) & Node.DOCUMENT_POSITION_FOLLOWING);
11175
+ }
11176
+ return false;
11177
+ }
11178
+ /**
11179
+ * Every element of a tabbable kind under `el` in the flattened tree, depth
11180
+ * first, which is the order Tab visits them. Under a host whose shadow root
11181
+ * is closed, that takes in the host's own children instead, rendered or not.
11182
+ */
11183
+ function collectCandidates(el, out = []) {
11184
+ for (const child of flatChildren(el)) {
11185
+ if (isTabbableKind(child))
11186
+ out.push(child);
11187
+ collectCandidates(child, out);
11188
+ }
11189
+ return out;
11190
+ }
11191
+ /**
11192
+ * The first and last tab stops inside `container`, or `null` when there are
11193
+ * none. Only the ends matter for wrapping, so the style checks run from each
11194
+ * end until they find a stop rather than over every candidate.
11195
+ *
11196
+ * A radio group is one stop, the way the browser treats it: its checked
11197
+ * button, or its first when none is checked. Counting every button would make
11198
+ * the group's last one an end, and Tab from the checked one would leave.
11199
+ */
11200
+ function findTabStops(container) {
11201
+ const candidates = collectCandidates(container);
11202
+ const checked = new Map();
11203
+ const usable = (el) => {
11204
+ let ok = checked.get(el);
11205
+ if (ok === undefined) {
11206
+ ok = isRenderedAndEnabled(el, container);
11207
+ checked.set(el, ok);
11208
+ }
11209
+ return ok;
11210
+ };
11211
+ const groupStop = (radio) => {
11212
+ const group = candidates.filter((el) => isGroupedRadio(el) &&
11213
+ el.name === radio.name &&
11214
+ el.form === radio.form &&
11215
+ el.getRootNode() === radio.getRootNode());
11216
+ return group.find(el => el.checked && usable(el)) ?? group.find(usable);
11217
+ };
11218
+ const isStop = (el) => usable(el) && (!isGroupedRadio(el) || groupStop(el) === el);
11219
+ const first = candidates.find(isStop);
11220
+ if (!first)
11221
+ return null;
11222
+ let last = first;
11223
+ for (let i = candidates.length - 1; candidates[i] !== first; i--) {
11224
+ if (isStop(candidates[i])) {
11225
+ last = candidates[i];
11226
+ break;
11227
+ }
11228
+ }
11229
+ return {
11230
+ first,
11231
+ last,
11232
+ stopFor: node => (isGroupedRadio(node) && groupStop(node)) || node,
11233
+ };
11234
+ }
11235
+ /**
11236
+ * The element that has focus in the container's own document (an iframe's,
11237
+ * when it renders into one), inside open shadow roots too. Focus inside a
11238
+ * closed one reads as its host, which the comparisons above place like any
11239
+ * other element. Browsers fall back to <body> when nothing has
11240
+ * focus. jsdom, where consumers run their tests, reports nothing once focus
11241
+ * was inside a shadow root that was removed, so this falls back for it.
11242
+ */
11243
+ const focusedElement = (doc) => getDeepestActiveElement(doc) ?? doc.body;
11244
+ /**
11245
+ * Keeps keyboard focus inside a container while it is active: focus moves in
11246
+ * when the trap turns on, Tab and Shift+Tab wrap between the first and last
11247
+ * tab stops, and focus goes back where it came from when the trap turns off.
11248
+ *
11249
+ * Tab stops are what the browser's Tab visits, in document order: links,
11250
+ * form controls, frames, media with controls, a `<details>` summary, editable
11251
+ * regions, anything with `tabIndex={0}`, and the content of open shadow
11252
+ * roots. Hidden, disabled, inert and `tabIndex={-1}` elements are skipped,
11253
+ * and a radio group counts once. Give the container `tabIndex={-1}` so it can
11254
+ * hold focus itself when nothing inside can. The trap listens on the
11255
+ * container, so a trap nested inside another handles Tab first and the outer
11256
+ * one stands aside.
11257
+ *
11258
+ * The trap is about Tab. If a Tab it let through still takes focus out (a
11259
+ * browser can leave out a stop the page has, such as links when its settings
11260
+ * say to), it sends focus back to the other end. A pointer or a script moving
11261
+ * focus out is left alone, because pulling focus back from a click is modal
11262
+ * behaviour, and a modal also has to block the page behind it, which `Modal`
11263
+ * does. Content the container renders through a portal lives elsewhere in
11264
+ * the DOM, so it is outside the trap: render nested overlays inside the
11265
+ * container, or give them a trap of their own.
11266
+ *
11267
+ * The container has to be in the DOM when the trap turns on. The trap waits
11268
+ * for hydration, so a container that only appears once the page has
11269
+ * hydrated, the way portaled content does, still has it attach in the commit
11270
+ * where the container appears.
11271
+ *
11272
+ * @function useFocusTrap
11273
+ * @param containerRef - Ref to the element focus stays inside.
11274
+ * @param options - When the trap is on, and where focus starts and ends.
11275
+ *
11276
+ * @example
11277
+ * const panelRef = useRef<HTMLDivElement>(null);
11278
+ * useFocusTrap(panelRef, { active: isOpen });
11279
+ * return isOpen ? (
11280
+ * <div ref={panelRef} role="dialog" aria-label="Filters" tabIndex={-1}>
11281
+ * …
11282
+ * </div>
11283
+ * ) : null;
11284
+ */
11285
+ function useFocusTrap(containerRef, options = {}) {
11286
+ const { active = true, initialFocusRef, restoreFocus = true } = options;
11287
+ // Portaled content has no server-rendered counterpart, so a portaled
11288
+ // container only exists from the commit after hydration.
11289
+ const hydrated = useIsHydrated();
10777
11290
  React.useEffect(() => {
10778
- if (!active)
11291
+ if (!active || !hydrated)
10779
11292
  return undefined;
10780
11293
  const container = containerRef.current;
10781
11294
  if (!container)
10782
11295
  return undefined;
10783
- const previouslyFocused = typeof document !== 'undefined'
10784
- ? document.activeElement
10785
- : null;
10786
- const focusInitial = () => {
10787
- if (initialFocusRef?.current) {
10788
- initialFocusRef.current.focus();
10789
- return;
10790
- }
10791
- const focusables = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(el => !el.hasAttribute('disabled'));
10792
- const target = focusables[0] ?? container;
10793
- target.focus();
10794
- };
10795
- focusInitial();
11296
+ const doc = container.ownerDocument;
11297
+ const opener = focusedElement(doc);
11298
+ (initialFocusRef?.current ??
11299
+ findTabStops(container)?.first ??
11300
+ container).focus();
11301
+ // Where focus goes if a Tab the trap let the browser handle takes it out,
11302
+ // held until that Tab has finished moving focus.
11303
+ let wrapTo = null;
11304
+ let wrapTimer;
10796
11305
  const handleKeyDown = (e) => {
10797
- if (e.key !== 'Tab')
11306
+ // A trap nested inside this one has already wrapped focus.
11307
+ if (e.key !== 'Tab' || e.defaultPrevented)
10798
11308
  return;
10799
- const focusables = Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR)).filter(el => !el.hasAttribute('disabled'));
10800
- if (focusables.length === 0) {
11309
+ const stops = findTabStops(container);
11310
+ if (!stops) {
10801
11311
  e.preventDefault();
11312
+ container.focus();
10802
11313
  return;
10803
11314
  }
10804
- const first = focusables[0];
10805
- const last = focusables[focusables.length - 1];
10806
- const activeEl = document.activeElement;
10807
- if (e.shiftKey && activeEl === first) {
10808
- e.preventDefault();
10809
- last.focus();
10810
- }
10811
- else if (!e.shiftKey && activeEl === last) {
11315
+ const current = stops.stopFor(focusedElement(doc));
11316
+ // Wrap whenever the browser has no stop left in that direction inside
11317
+ // the container. Comparing positions rather than testing `=== first`
11318
+ // also covers focus resting on the container or on an element outside
11319
+ // the tab order, which the browser would otherwise walk straight out of.
11320
+ if (e.shiftKey
11321
+ ? !follows(container, stops.first, current)
11322
+ : !follows(container, current, stops.last)) {
10812
11323
  e.preventDefault();
10813
- first.focus();
11324
+ (e.shiftKey ? stops.last : stops.first).focus();
11325
+ return;
10814
11326
  }
11327
+ wrapTo = e.shiftKey ? stops.last : stops.first;
11328
+ clearTimeout(wrapTimer);
11329
+ wrapTimer = setTimeout(() => {
11330
+ wrapTo = null;
11331
+ });
11332
+ };
11333
+ // The browser moves focus as soon as the keydown is over, so focus that
11334
+ // lands outside while `wrapTo` is set got there by that Tab.
11335
+ const handleFocusIn = (e) => {
11336
+ if (!wrapTo || inside(container, e.composedPath()[0]))
11337
+ return;
11338
+ const target = wrapTo;
11339
+ wrapTo = null;
11340
+ target.focus();
10815
11341
  };
10816
11342
  container.addEventListener('keydown', handleKeyDown);
11343
+ doc.addEventListener('focusin', handleFocusIn, true);
10817
11344
  return () => {
10818
11345
  container.removeEventListener('keydown', handleKeyDown);
10819
- if (restoreFocus && previouslyFocused?.focus) {
10820
- previouslyFocused.focus();
11346
+ doc.removeEventListener('focusin', handleFocusIn, true);
11347
+ clearTimeout(wrapTimer);
11348
+ let target = restoreFocus ? opener : null;
11349
+ // `null` is an object too, and a caller without types can pass it.
11350
+ if (restoreFocus && typeof restoreFocus === 'object') {
11351
+ // Read now rather than when the trap turned on: a trigger that
11352
+ // re-mounted in between has a new node.
11353
+ // eslint-disable-next-line react-hooks/exhaustive-deps
11354
+ target = restoreFocus.current;
11355
+ }
11356
+ // Hand focus back only while the trap still holds it: inside the
11357
+ // container, or dropped to <body> because the container left the page.
11358
+ const now = focusedElement(doc);
11359
+ if (target && (now === doc.body || inside(container, now))) {
11360
+ target.focus();
10821
11361
  }
10822
11362
  };
10823
- }, [active, containerRef, initialFocusRef, restoreFocus]);
11363
+ }, [active, hydrated, containerRef, initialFocusRef, restoreFocus]);
10824
11364
  }
10825
11365
 
10826
11366
  const isBrowser = typeof window !== 'undefined';
@@ -10927,7 +11467,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
10927
11467
  document.addEventListener('keydown', handler);
10928
11468
  return () => document.removeEventListener('keydown', handler);
10929
11469
  }, [isOpen, closeOnEscape, onClose]);
10930
- useFocusTrap(panelRef, isOpen && trapFocus);
11470
+ useFocusTrap(panelRef, { active: isOpen && trapFocus });
10931
11471
  const panelClass = usePrefixedClassNames('picker-popover', {
10932
11472
  'is-active': isOpen,
10933
11473
  [`is-${resolved.position}`]: true,
@@ -14130,6 +14670,33 @@ const Theme = ({ bulmaVars = {}, children, className, isRoot = false, colorMode,
14130
14670
  return isRoot ? (jsxRuntime.jsx(jsxRuntime.Fragment, { children: children })) : (jsxRuntime.jsx("div", { className: combinedClassName || undefined, style: style, ...rest, children: children }));
14131
14671
  };
14132
14672
 
14673
+ /**
14674
+ * Renders its children only in the browser, after hydration, and a `fallback` until then, so content that depends on browser-only state doesn't cause a hydration mismatch.
14675
+ *
14676
+ * The server render and the hydrating client render both show `fallback`,
14677
+ * which keeps them identical; the children replace it in the commit that
14678
+ * follows. In an app without server rendering the children show on the first
14679
+ * render.
14680
+ *
14681
+ * @function
14682
+ * @param {ClientOnlyProps} props - Props for the ClientOnly component.
14683
+ * @returns The children once hydrated, otherwise the fallback.
14684
+ *
14685
+ * @example
14686
+ * // The server's time zone is not the reader's.
14687
+ * <ClientOnly fallback={<Skeleton variant="lines" lines={1} />}>
14688
+ * {() => (
14689
+ * <p>Times are shown in {Intl.DateTimeFormat().resolvedOptions().timeZone}.</p>
14690
+ * )}
14691
+ * </ClientOnly>
14692
+ */
14693
+ function ClientOnly({ children, fallback = null }) {
14694
+ const hydrated = useIsHydrated();
14695
+ if (!hydrated)
14696
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
14697
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: typeof children === 'function' ? children() : children });
14698
+ }
14699
+
14133
14700
  /**
14134
14701
  * The `Container` component provides a responsive and flexible layout wrapper for your Bulma React UI.
14135
14702
  *
@@ -14491,8 +15058,10 @@ exports.CarouselItem = CarouselItem;
14491
15058
  exports.Cell = Cell;
14492
15059
  exports.Checkbox = Checkbox;
14493
15060
  exports.Checkboxes = Checkboxes;
15061
+ exports.ClientOnly = ClientOnly;
14494
15062
  exports.Code = Code;
14495
15063
  exports.Collapse = Collapse;
15064
+ exports.Collapses = Collapses;
14496
15065
  exports.Column = Column;
14497
15066
  exports.Columns = Columns;
14498
15067
  exports.ConfigProvider = ConfigProvider;
@@ -14577,6 +15146,7 @@ exports.PanelIcon = PanelIcon;
14577
15146
  exports.PanelInputBlock = PanelInputBlock;
14578
15147
  exports.PanelTabs = PanelTabs;
14579
15148
  exports.Paragraph = Paragraph;
15149
+ exports.Portal = Portal;
14580
15150
  exports.Pre = Pre;
14581
15151
  exports.Progress = Progress;
14582
15152
  exports.Radio = Radio;
@@ -14642,9 +15212,11 @@ exports.useColorClasses = useColorClasses;
14642
15212
  exports.useColorStyles = useColorStyles;
14643
15213
  exports.useConfig = useConfig;
14644
15214
  exports.useFlexboxClasses = useFlexboxClasses;
15215
+ exports.useFocusTrap = useFocusTrap;
14645
15216
  exports.useIconLibrary = useIconLibrary;
14646
15217
  exports.useInsideControl = useInsideControl;
14647
15218
  exports.useInsideField = useInsideField;
15219
+ exports.useIsHydrated = useIsHydrated;
14648
15220
  exports.useOtherClasses = useOtherClasses;
14649
15221
  exports.usePrefixedClass = usePrefixedClass;
14650
15222
  exports.usePrefixedClassNames = usePrefixedClassNames;