@allxsmith/bestax-bulma 5.66.2 → 5.67.1

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 (81) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/components/Command.js.map +1 -1
  4. package/dist/esm/components/Dropdown.js +8 -2
  5. package/dist/esm/components/Dropdown.js.map +1 -1
  6. package/dist/esm/components/HoverCard.js.map +1 -1
  7. package/dist/esm/components/Loading.js +3 -3
  8. package/dist/esm/components/Loading.js.map +1 -1
  9. package/dist/esm/components/Modal.js.map +1 -1
  10. package/dist/esm/components/Popover.js.map +1 -1
  11. package/dist/esm/components/Sidebar.js +21 -29
  12. package/dist/esm/components/Sidebar.js.map +1 -1
  13. package/dist/esm/components/Toast.js +6 -8
  14. package/dist/esm/components/Toast.js.map +1 -1
  15. package/dist/esm/elements/IconButton.js.map +1 -1
  16. package/dist/esm/form/Combobox.js.map +1 -1
  17. package/dist/esm/form/Control.js +2 -5
  18. package/dist/esm/form/Control.js.map +1 -1
  19. package/dist/esm/form/DateInputBase.js.map +1 -1
  20. package/dist/esm/form/DateRangeInputBase.js.map +1 -1
  21. package/dist/esm/form/DateTimeInputBase.js.map +1 -1
  22. package/dist/esm/form/Field.js +2 -4
  23. package/dist/esm/form/Field.js.map +1 -1
  24. package/dist/esm/form/TreeSelect.js.map +1 -1
  25. package/dist/esm/form/_pickerInternals/Calendar.js +12 -5
  26. package/dist/esm/form/_pickerInternals/Calendar.js.map +1 -1
  27. package/dist/esm/form/_pickerInternals/PickerPopover.js +6 -2
  28. package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
  29. package/dist/esm/helpers/scrollLock.js +47 -11
  30. package/dist/esm/helpers/scrollLock.js.map +1 -1
  31. package/dist/esm/helpers/useAnchoredPosition.js +35 -11
  32. package/dist/esm/helpers/useAnchoredPosition.js.map +1 -1
  33. package/dist/esm/layout/Level.js +2 -2
  34. package/dist/esm/layout/Level.js.map +1 -1
  35. package/dist/extras.css +1 -1
  36. package/dist/extras.css.map +1 -1
  37. package/dist/index.cjs +139 -75
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/types/components/Command.d.ts +5 -5
  40. package/dist/types/components/Dropdown.d.ts +5 -1
  41. package/dist/types/components/HoverCard.d.ts +2 -4
  42. package/dist/types/components/Modal.d.ts +6 -3
  43. package/dist/types/components/Popover.d.ts +1 -3
  44. package/dist/types/components/Sidebar.d.ts +6 -2
  45. package/dist/types/components/Toast.d.ts +3 -1
  46. package/dist/types/elements/IconButton.d.ts +2 -5
  47. package/dist/types/form/Combobox.d.ts +13 -13
  48. package/dist/types/form/DateInputBase.d.ts +8 -1
  49. package/dist/types/form/DateRangeInputBase.d.ts +5 -0
  50. package/dist/types/form/DateTimeInputBase.d.ts +8 -1
  51. package/dist/types/form/TreeSelect.d.ts +4 -4
  52. package/dist/types/helpers/scrollLock.d.ts +4 -4
  53. package/dist/types/helpers/useAnchoredPosition.d.ts +14 -12
  54. package/dist/types/layout/Level.d.ts +10 -0
  55. package/dist/types-cjs/components/Command.d.ts +5 -5
  56. package/dist/types-cjs/components/Dropdown.d.ts +5 -1
  57. package/dist/types-cjs/components/HoverCard.d.ts +2 -4
  58. package/dist/types-cjs/components/Modal.d.ts +6 -3
  59. package/dist/types-cjs/components/Popover.d.ts +1 -3
  60. package/dist/types-cjs/components/Sidebar.d.ts +6 -2
  61. package/dist/types-cjs/components/Toast.d.ts +3 -1
  62. package/dist/types-cjs/elements/IconButton.d.ts +2 -5
  63. package/dist/types-cjs/form/Combobox.d.ts +13 -13
  64. package/dist/types-cjs/form/DateInputBase.d.ts +8 -1
  65. package/dist/types-cjs/form/DateRangeInputBase.d.ts +5 -0
  66. package/dist/types-cjs/form/DateTimeInputBase.d.ts +8 -1
  67. package/dist/types-cjs/form/TreeSelect.d.ts +4 -4
  68. package/dist/types-cjs/helpers/scrollLock.d.ts +4 -4
  69. package/dist/types-cjs/helpers/useAnchoredPosition.d.ts +14 -12
  70. package/dist/types-cjs/layout/Level.d.ts +10 -0
  71. package/dist/versions/bestax-no-dark-mode.css +1 -1
  72. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  73. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  74. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  75. package/dist/versions/bestax-no-helpers.css +1 -1
  76. package/dist/versions/bestax-no-helpers.css.map +1 -1
  77. package/dist/versions/bestax-prefixed.css +1 -1
  78. package/dist/versions/bestax-prefixed.css.map +1 -1
  79. package/package.json +1 -1
  80. package/src/scss/components/_loading.scss +17 -3
  81. package/src/scss/form/_radio.scss +5 -2
package/dist/index.cjs CHANGED
@@ -3187,6 +3187,12 @@ const DropdownComponent = React.forwardRef(function DropdownComponent({ label, c
3187
3187
  const menuRef = React.useRef(null);
3188
3188
  const menuKeys = useMenuKeys();
3189
3189
  const pendingFocusRef = React.useRef(null);
3190
+ // The trigger names the menu and the menu is what it controls, so both
3191
+ // need an id whether or not the Dropdown has one.
3192
+ const generatedId = React.useId();
3193
+ const baseId = id || generatedId;
3194
+ const triggerId = `${baseId}-trigger`;
3195
+ const menuId = `${baseId}-menu`;
3190
3196
  // Cleanup returned by a consumer's callback ref on attach, held until detach.
3191
3197
  const consumerCleanupRef = React.useRef(null);
3192
3198
  const setRefs = React.useCallback((node) => {
@@ -3376,7 +3382,7 @@ const DropdownComponent = React.forwardRef(function DropdownComponent({ label, c
3376
3382
  menuKeys.onKeyDown(e);
3377
3383
  };
3378
3384
  const dropdownClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
3379
- return (jsxRuntime.jsxs("div", { className: dropdownClasses, ref: setRefs, id: id, "data-testid": "dropdown-root", ...rest, children: [jsxRuntime.jsx("div", { className: usePrefixedClassNames('dropdown-trigger'), children: jsxRuntime.jsxs("button", { ref: triggerRef, className: buttonClass, "aria-haspopup": "true", "aria-controls": id ? `${id}-menu` : undefined, "aria-expanded": active, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, disabled: disabled, type: "button", children: [jsxRuntime.jsx("span", { children: label }), jsxRuntime.jsx("span", { className: usePrefixedClassNames('icon', 'is-small'), "aria-hidden": "true", children: jsxRuntime.jsx("i", { className: "fas fa-angle-down" }) })] }) }), jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('dropdown-menu'), menuClassName), id: id ? `${id}-menu` : undefined, ref: menuRef, role: "menu", "data-testid": "dropdown-menu", onKeyDown: handleMenuKeyDown,
3385
+ return (jsxRuntime.jsxs("div", { className: dropdownClasses, ref: setRefs, id: id, "data-testid": "dropdown-root", ...rest, children: [jsxRuntime.jsx("div", { className: usePrefixedClassNames('dropdown-trigger'), children: jsxRuntime.jsxs("button", { ref: triggerRef, id: triggerId, className: buttonClass, "aria-haspopup": "true", "aria-controls": menuId, "aria-expanded": active, onClick: handleToggle, onKeyDown: handleTriggerKeyDown, disabled: disabled, type: "button", children: [jsxRuntime.jsx("span", { children: label }), jsxRuntime.jsx("span", { className: usePrefixedClassNames('icon', 'is-small'), "aria-hidden": "true", children: jsxRuntime.jsx("i", { className: "fas fa-angle-down" }) })] }) }), jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('dropdown-menu'), menuClassName), id: menuId, ref: menuRef, role: "menu", "aria-labelledby": triggerId, "data-testid": "dropdown-menu", onKeyDown: handleMenuKeyDown,
3380
3386
  // The menu, not its content, takes focus from a click between items,
3381
3387
  // so the menu owns nothing focusable but its items.
3382
3388
  tabIndex: -1, children: jsxRuntime.jsx("div", { className: usePrefixedClassNames('dropdown-content'), onClick: handleMenuClick, children: children }) })] }));
@@ -5250,19 +5256,55 @@ const Message = withSubComponents(MessageComponent, {
5250
5256
  }, 'Message');
5251
5257
 
5252
5258
  // Ref-counted across every caller (e.g. a Dialog rendering its own Modal)
5253
- // so overlapping/nested locks don't fight over which one restores `overflow`.
5254
- // Every overlay that locks body scroll — Modal, Dialog, Sidebar, Loading —
5255
- // goes through here; a component setting `document.body.style.overflow`
5259
+ // so overlapping/nested locks don't fight over which one restores the page.
5260
+ // Every overlay that locks the page (Modal, Dialog, Sidebar, Loading,
5261
+ // AppFrame's drawer) goes through here; a component setting these styles
5256
5262
  // itself would clear a lock another overlay still needs.
5257
5263
  let lockCount = 0;
5258
- let originalOverflow = '';
5264
+ let unlock = null;
5265
+ /**
5266
+ * Locks the page's scrolling and pads it by the room the scrollbar took, so
5267
+ * its content stays put as the scrollbar goes. It locks `<html>` rather than
5268
+ * `<body>`: the viewport takes `<body>`'s overflow only while `<html>`'s is
5269
+ * `visible`, and Bulma's isn't. The side to pad is measured rather than read
5270
+ * from the text direction, since an engine can keep the page's scrollbar on
5271
+ * the right under right-to-left text. With an overlay scrollbar, or a gutter
5272
+ * `scrollbar-gutter` keeps, nothing moves and nothing is padded. An element
5273
+ * fixed to the window is placed against the viewport, not `<html>`, so the
5274
+ * padding doesn't reach it.
5275
+ *
5276
+ * @returns A function that puts back the styles it changed.
5277
+ */
5278
+ function lockPage() {
5279
+ const root = document.documentElement;
5280
+ const { overflow, paddingLeft, paddingRight } = root.style;
5281
+ const before = root.getBoundingClientRect();
5282
+ root.style.overflow = 'hidden';
5283
+ const after = root.getBoundingClientRect();
5284
+ const left = before.left - after.left;
5285
+ const right = after.right - before.right;
5286
+ if (left > 0 || right > 0) {
5287
+ // The computed padding is in pixels, whatever unit set it.
5288
+ const style = getComputedStyle(root);
5289
+ const add = (padding, room) => `${(parseFloat(padding) || 0) + room}px`;
5290
+ if (left > 0)
5291
+ root.style.paddingLeft = add(style.paddingLeft, left);
5292
+ if (right > 0)
5293
+ root.style.paddingRight = add(style.paddingRight, right);
5294
+ }
5295
+ return () => {
5296
+ root.style.overflow = overflow;
5297
+ root.style.paddingLeft = paddingLeft;
5298
+ root.style.paddingRight = paddingRight;
5299
+ };
5300
+ }
5259
5301
  /**
5260
- * Locks (and ref-counted-ly unlocks) `document.body` scrolling while `active`
5261
- * is true. Safe to call from multiple components at once — the body scroll
5262
- * is only restored once every active caller has released its lock.
5302
+ * Locks (and ref-counted-ly unlocks) the page's scrolling while `active` is
5303
+ * true. Safe to call from multiple components at once: the page is only
5304
+ * given back once every active caller has released its lock.
5263
5305
  *
5264
5306
  * @function useScrollLock
5265
- * @param active - Whether this caller wants the body scroll locked.
5307
+ * @param active - Whether this caller wants the page's scrolling locked.
5266
5308
  */
5267
5309
  function useScrollLock(active) {
5268
5310
  React.useEffect(() => {
@@ -5270,13 +5312,13 @@ function useScrollLock(active) {
5270
5312
  return undefined;
5271
5313
  lockCount++;
5272
5314
  if (lockCount === 1) {
5273
- originalOverflow = document.body.style.overflow;
5274
- document.body.style.overflow = 'hidden';
5315
+ unlock = lockPage();
5275
5316
  }
5276
5317
  return () => {
5277
5318
  lockCount--;
5278
5319
  if (lockCount === 0) {
5279
- document.body.style.overflow = originalOverflow;
5320
+ unlock?.();
5321
+ unlock = null;
5280
5322
  }
5281
5323
  };
5282
5324
  }, [active]);
@@ -7589,9 +7631,9 @@ const Loading = ({ active = false, isFullPage = false, size, color, canCancel =
7589
7631
  window.addEventListener('keydown', handleKeyDown);
7590
7632
  return () => window.removeEventListener('keydown', handleKeyDown);
7591
7633
  }, [active, canCancel, hasOnCancel, onCancelRef, isTopmost]);
7592
- // Prevent body scroll when full page loading is active. Ref-counted through
7593
- // the shared helper so an overlapping Modal/Dialog/Sidebar doesn't have its
7594
- // lock cleared when this one releases.
7634
+ // Lock the page's scrolling when full page loading is active. Ref-counted
7635
+ // through the shared helper so an overlapping Modal/Dialog/Sidebar doesn't
7636
+ // have its lock cleared when this one releases.
7595
7637
  useScrollLock(isFullPage && active);
7596
7638
  if (!active) {
7597
7639
  return null;
@@ -8516,14 +8558,16 @@ const samePosition = (a, b) => a.position === b.position &&
8516
8558
  /**
8517
8559
  * Places a floating panel against its anchor: resolves `auto` to a corner
8518
8560
  * and, for a panel fixed to the viewport, works out its coordinates. It
8519
- * measures again on resize and on any scroll of the anchor's window (an
8520
- * iframe's, when the anchor renders into one), so a fixed panel follows its
8521
- * anchor, and with `followContent` after each render too. A measurement that
8522
- * changes nothing doesn't render again.
8561
+ * measures again on resize, on any scroll of the anchor's window (an
8562
+ * iframe's, when the anchor renders into one), and when the panel or the
8563
+ * anchor changes size, so a fixed panel follows its anchor and its own
8564
+ * content. Those measure once in the next frame, however many arrive before
8565
+ * it. With `followContent` it also measures after each render. A
8566
+ * measurement that changes nothing doesn't render again.
8523
8567
  *
8524
- * Measuring happens in a layout effect, so the panel is placed before it
8525
- * paints. It needs both elements on the page while `active` is on, and
8526
- * keeps the last placement when either is missing.
8568
+ * The first measurement happens in a layout effect, so the panel is placed
8569
+ * before it paints. It needs both elements on the page while `active` is
8570
+ * on, and keeps the last placement when either is missing.
8527
8571
  *
8528
8572
  * @function useAnchoredPosition
8529
8573
  * @param anchorRef - The element the panel opens from.
@@ -8580,12 +8624,34 @@ function useAnchoredPosition(anchorRef, panelRef, { active, position, fixed, off
8580
8624
  };
8581
8625
  updateRef.current = update;
8582
8626
  update();
8583
- view.addEventListener('resize', update, { passive: true });
8584
- view.addEventListener('scroll', update, { passive: true, capture: true });
8627
+ // A scroll, a resize or a change in size measures in the next frame,
8628
+ // once however many of them ask.
8629
+ let frame = 0;
8630
+ const schedule = () => {
8631
+ if (frame)
8632
+ return;
8633
+ frame = view.requestAnimationFrame(() => {
8634
+ frame = 0;
8635
+ update();
8636
+ });
8637
+ };
8638
+ view.addEventListener('resize', schedule, { passive: true });
8639
+ view.addEventListener('scroll', schedule, { passive: true, capture: true });
8640
+ // The panel's content can change size without the component holding the
8641
+ // hook rendering, as an image loading or a child's own state does, and
8642
+ // so can the anchor.
8643
+ const Observer = view.ResizeObserver;
8644
+ const resizes = typeof Observer === 'undefined' ? null : new Observer(schedule);
8645
+ for (const element of [anchorRef.current, panelRef.current]) {
8646
+ if (element)
8647
+ resizes?.observe(element);
8648
+ }
8585
8649
  return () => {
8586
8650
  updateRef.current = null;
8587
- view.removeEventListener('resize', update);
8588
- view.removeEventListener('scroll', update, { capture: true });
8651
+ view.cancelAnimationFrame(frame);
8652
+ resizes?.disconnect();
8653
+ view.removeEventListener('resize', schedule);
8654
+ view.removeEventListener('scroll', schedule, { capture: true });
8589
8655
  };
8590
8656
  }, [
8591
8657
  active,
@@ -9758,11 +9824,11 @@ function useOverlayFocus(ref, open, { modal, remountKey, restore }) {
9758
9824
  */
9759
9825
  const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left', width = '260px', isFullwidth, fullWidth, overlay = true, overlayClose = true, escapeClose = true, canCancel = true, children, inline = false, className, style, ...props }, ref) => {
9760
9826
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
9761
- const resolveContainer = usePortalContainer();
9762
- const { portalContainer } = useConfig();
9763
- // Where the configured default puts the sidebar. An `inline` one doesn't
9764
- // portal, so a change to the default moves nothing.
9765
- const defaultTarget = inline ? undefined : portalContainer;
9827
+ // A portal has no server-rendered counterpart, so the sidebar renders in
9828
+ // place on the server and while hydrating, and moves into its portal once
9829
+ // the client takes over, as Modal's `portal` does. An `inline` one stays
9830
+ // in place, so a change to the configured default moves nothing.
9831
+ const { isPortaled, defaultTarget } = usePortalProp(!inline);
9766
9832
  const sidebarRef = React.useRef(null);
9767
9833
  // Cleanup returned by a consumer's callback ref on attach, held until detach.
9768
9834
  const consumerCleanupRef = React.useRef(null);
@@ -9805,21 +9871,23 @@ const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left',
9805
9871
  return undefined;
9806
9872
  return trapTab(() => sidebarRef.current, window, () => isTopmost('modal'));
9807
9873
  }, [isOpen, overlay, isTopmost]);
9808
- // Prevent body scroll when sidebar is open. Ref-counted through the shared
9809
- // helper so an overlapping Modal/Dialog/Loading doesn't unlock underneath.
9874
+ // Lock the page's scrolling when sidebar is open. Ref-counted through the
9875
+ // shared helper so an overlapping Modal/Dialog/Loading doesn't unlock
9876
+ // underneath.
9810
9877
  useScrollLock(isOpen && overlay);
9811
9878
  // Move focus into the sidebar on open, to the first stop Tab wraps to or
9812
- // the sidebar itself, and hand it back on close, as `Modal` does. A
9813
- // different `defaultTarget` remounts the portaled sidebar: focus is handed
9814
- // back from the old nodes and moved into the new ones. Whether it takes
9815
- // focus is decided as it opens, so `overlay` changing while it's open
9816
- // leaves focus where the user has it. Without an overlay the sidebar holds
9817
- // no Tab, so with a modal overlay open under it, it leaves focus alone:
9818
- // the first Tab would take focus back there and leave the sidebar's other
9819
- // controls out of reach.
9879
+ // the sidebar itself, and hand it back on close, as `Modal` does. Moving
9880
+ // into the portal after hydration, or to a different `defaultTarget`,
9881
+ // remounts the sidebar: focus is handed back from the old nodes and moved
9882
+ // into the new ones. Whether it takes focus is decided as it opens, so
9883
+ // `overlay` changing while it's open leaves focus where the user has it.
9884
+ // Without an overlay the sidebar holds no Tab, so with a modal overlay
9885
+ // open under it, it leaves focus alone: the first Tab would take focus
9886
+ // back there and leave the sidebar's other controls out of reach.
9820
9887
  useOverlayFocus(sidebarRef, isOpen, {
9821
9888
  modal: overlay,
9822
- remountKey: defaultTarget,
9889
+ // In place it's `false`, which no portal target is.
9890
+ remountKey: isPortaled ? defaultTarget : false,
9823
9891
  });
9824
9892
  // Use combined ref
9825
9893
  const combinedRef = React.useCallback((node) => {
@@ -9870,15 +9938,8 @@ const SidebarComponent = React.forwardRef(({ isOpen, onClose, position = 'left',
9870
9938
  '--bulma-sidebar-width': resolvedFullwidth ? '100%' : width,
9871
9939
  };
9872
9940
  const sidebarContent = (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [overlay && (jsxRuntime.jsx("div", { className: backgroundClass, onClick: handleOverlayClick, "aria-hidden": "true" })), jsxRuntime.jsx("aside", { ref: combinedRef, className: combinedClasses, style: sidebarStyle, role: "dialog", "aria-modal": overlay ? 'true' : undefined, "aria-hidden": !isOpen, ...inertProps(!isOpen), tabIndex: -1, ...rest, children: jsxRuntime.jsx("div", { className: contentClass, children: children }) })] }));
9873
- if (inline) {
9874
- return sidebarContent;
9875
- }
9876
- // Render to portal for proper stacking
9877
- if (typeof document !== 'undefined') {
9878
- const target = resolveContainer();
9879
- return reactDom.createPortal(jsxRuntime.jsx(LocalePortalScope, { target: target, children: sidebarContent }), target);
9880
- }
9881
- return null;
9941
+ // In a portal for proper stacking.
9942
+ return jsxRuntime.jsx(Portal, { disabled: !isPortaled, children: sidebarContent });
9882
9943
  });
9883
9944
  /**
9884
9945
  * Container for the sidebar header.
@@ -10004,7 +10065,10 @@ function groupIntoPositionStacks(items, positionOf, containerPosition, order) {
10004
10065
  */
10005
10066
  const Toast = React.forwardRef(({ message, type = 'default', actionType, position = 'top-right', duration = 2000, indefinite = false, dismissible = true, closable = false, rounded = false, pauseOnHover = false, cancelable = true, actionText, cancelText, onAction, onClose, container, inline = false, className, ...props }, ref) => {
10006
10067
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
10007
- const resolveContainer = usePortalContainer();
10068
+ // A portal has no server-rendered counterpart, so the toast renders in
10069
+ // place on the server and while hydrating, and moves into its portal
10070
+ // once the client takes over, as Modal's `portal` does.
10071
+ const portal = usePortalProp(container || true);
10008
10072
  const [isVisible, setIsVisible] = React.useState(true);
10009
10073
  const [isPaused, setIsPaused] = React.useState(false);
10010
10074
  const toastRef = React.useRef(null);
@@ -10139,12 +10203,7 @@ const Toast = React.forwardRef(({ message, type = 'default', actionType, positio
10139
10203
  if (inline) {
10140
10204
  return toastElement;
10141
10205
  }
10142
- const toastContent = jsxRuntime.jsx("div", { className: containerClasses, children: toastElement });
10143
- if (typeof document !== 'undefined') {
10144
- const target = resolveContainer(container);
10145
- return reactDom.createPortal(jsxRuntime.jsx(LocalePortalScope, { target: target, children: toastContent }), target);
10146
- }
10147
- return null;
10206
+ return (jsxRuntime.jsx(Portal, { disabled: !portal.isPortaled, container: portal.container, children: jsxRuntime.jsx("div", { className: containerClasses, children: toastElement }) }));
10148
10207
  });
10149
10208
  Toast.displayName = 'Toast';
10150
10209
  let toastId = 0;
@@ -13939,8 +13998,6 @@ const allowedColors = [...validColors, 'inherit', 'current'];
13939
13998
  const Control = React.forwardRef(({ as = 'div', hasIconsLeft, hasIconsRight, isLoading, isExpanded, size, textColor, bgColor, iconLeft, iconRight, iconLeftName, iconLeftSize, iconRightName, iconRightSize, className, children, ...props }, ref) => {
13940
13999
  const Component = (as === 'p' ? 'p' : 'div');
13941
14000
  const { classPrefix } = useConfig();
13942
- // Remove textColor/bgColor from props before spreading
13943
- const { textColor: _ignoredTextColor, bgColor: _ignoredBgColor, ...restProps } = props;
13944
14001
  const safeTextColor = allowedColors.includes(textColor)
13945
14002
  ? textColor
13946
14003
  : undefined;
@@ -13950,7 +14007,7 @@ const Control = React.forwardRef(({ as = 'div', hasIconsLeft, hasIconsRight, isL
13950
14007
  const { bulmaHelperClasses, rest } = useBulmaClasses({
13951
14008
  color: safeTextColor,
13952
14009
  backgroundColor: safeBgColor,
13953
- ...restProps,
14010
+ ...props,
13954
14011
  });
13955
14012
  // Prepare icon props for the shortcut. A truthiness test (not `!== undefined`)
13956
14013
  // so a falsy node from the idiomatic `iconLeft={cond && <Node/>}` / `iconLeft={maybe ?? null}`
@@ -13979,8 +14036,7 @@ const Control = React.forwardRef(({ as = 'div', hasIconsLeft, hasIconsRight, isL
13979
14036
  [`is-${size}`]: !!size,
13980
14037
  });
13981
14038
  const controlClass = classNames(mainClass, bulmaHelperClasses, className);
13982
- // --- FIX: Spread both restProps (for data-testid, etc) AND rest (from useBulmaClasses) ---
13983
- return (jsxRuntime.jsx(ControlProvider, { value: true, children: jsxRuntime.jsxs(Component, { className: controlClass, ref: ref, ...restProps, ...rest, children: [jsxRuntime.jsx(ControlLoadingProvider, { value: !!isLoading, children: children }), leftIconValue &&
14039
+ return (jsxRuntime.jsx(ControlProvider, { value: true, children: jsxRuntime.jsxs(Component, { className: controlClass, ref: ref, ...rest, children: [jsxRuntime.jsx(ControlLoadingProvider, { value: !!isLoading, children: children }), leftIconValue &&
13984
14040
  (isIconProps(leftIconValue) ? (jsxRuntime.jsx(Icon, { ...leftIconValue, className: prefixedClassNames(classPrefix, 'is-left') })) : (jsxRuntime.jsx(Icon, { className: prefixedClassNames(classPrefix, 'is-left'), children: leftIconValue }))), rightIconValue &&
13985
14041
  (isIconProps(rightIconValue) ? (jsxRuntime.jsx(Icon, { ...rightIconValue, className: prefixedClassNames(classPrefix, 'is-right') })) : (jsxRuntime.jsx(Icon, { className: prefixedClassNames(classPrefix, 'is-right'), children: rightIconValue })))] }) }));
13986
14042
  });
@@ -14018,8 +14074,7 @@ const FieldLabel = ({ size, textColor, bgColor, className, children, ...props })
14018
14074
  [`is-${size}`]: !!size,
14019
14075
  });
14020
14076
  const fieldLabelClass = classNames(mainClass, bulmaHelperClasses, className);
14021
- // Spread ...props and ...rest so custom props like data-testid are included
14022
- return (jsxRuntime.jsx("div", { className: fieldLabelClass, ...props, ...rest, children: children }));
14077
+ return (jsxRuntime.jsx("div", { className: fieldLabelClass, ...rest, children: children }));
14023
14078
  };
14024
14079
  /**
14025
14080
  * FieldBody component for rendering Bulma field body.
@@ -14039,8 +14094,7 @@ const FieldBody = ({ textColor, bgColor, className, children, ...props }) => {
14039
14094
  });
14040
14095
  const mainClass = usePrefixedClassNames('field-body');
14041
14096
  const fieldBodyClass = classNames(mainClass, bulmaHelperClasses, className);
14042
- // Spread ...props and ...rest so custom props like data-testid are included
14043
- return (jsxRuntime.jsx("div", { className: fieldBodyClass, ...props, ...rest, children: children }));
14097
+ return (jsxRuntime.jsx("div", { className: fieldBodyClass, ...rest, children: children }));
14044
14098
  };
14045
14099
  /**
14046
14100
  * The `Field` component is a Bulma-styled form field container.
@@ -21244,6 +21298,10 @@ const isBrowser = typeof window !== 'undefined';
21244
21298
  const PickerPopover = ({ isOpen, onClose, anchorRef, children, position, appendToBody = false, className, trapFocus = true, closeOnClickOutside = true, closeOnEscape = true, ariaLabel, ariaLabelledBy, role = 'dialog', id, restoreFocusRef, initialFocusSelector, localeScope, matchWidth = false, followContent = false, }) => {
21245
21299
  const panelRef = React.useRef(null);
21246
21300
  const resolveContainer = usePortalContainer();
21301
+ // A portal has no server-rendered counterpart, so a panel open on the first
21302
+ // render stays in place on the server and while hydrating, and moves into
21303
+ // its portal once the client takes over, as Modal's `portal` does.
21304
+ const { isPortaled } = usePortalProp(appendToBody);
21247
21305
  // Read as the trap turns on, once the panel's content has rendered.
21248
21306
  const initialFocusRef = React.useMemo(() => initialFocusSelector === undefined
21249
21307
  ? undefined
@@ -21345,7 +21403,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position, appendT
21345
21403
  if (closeOnEscape && e.key === 'Escape')
21346
21404
  e.preventDefault();
21347
21405
  }, children: children }));
21348
- if (appendToBody && isBrowser) {
21406
+ if (isPortaled) {
21349
21407
  const target = resolveContainer();
21350
21408
  return reactDom.createPortal(jsxRuntime.jsx(LocalePortalScope, { target: target, scope: localeScope, children: panel }), target);
21351
21409
  }
@@ -24952,8 +25010,13 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
24952
25010
  const monthsRowClass = usePrefixedClassNames('dateinput-months-row');
24953
25011
  const yearsGridClass = usePrefixedClassNames('dateinput-years-grid');
24954
25012
  const focusedYear = focusedDate.getFullYear();
24955
- const today = new Date();
24956
- const todayYear = today.getFullYear();
25013
+ // Today is the reader's, so it's marked only once the page has hydrated: a
25014
+ // server render and the render hydrating it mark no day, month or year,
25015
+ // and the commit after marks the browser's own. A calendar mounted after
25016
+ // hydration marks it from its first render.
25017
+ const hydrated = useIsHydrated();
25018
+ const today = hydrated ? new Date() : null;
25019
+ const todayYear = today?.getFullYear();
24957
25020
  // The header steps a month at a time over the day grid and a year at a
24958
25021
  // time over the month grid, kept inside min/max. A step is off once the
24959
25022
  // month or year on show holds the bound or lies past it, as picking a year
@@ -25190,6 +25253,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
25190
25253
  .join(' ') || undefined
25191
25254
  : undefined;
25192
25255
  const isFocused = isSameDay(cell.date, tabStopDay);
25256
+ const isToday = hydrated && cell.isToday;
25193
25257
  const otherMonth = !cell.inCurrentMonth;
25194
25258
  const cellClass = prefixedClassNames(classPrefix, 'dateinput-cell', {
25195
25259
  'is-selected': isFilled,
@@ -25197,12 +25261,12 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
25197
25261
  'is-range-end': isEnd,
25198
25262
  'is-in-range': inRange,
25199
25263
  'is-preview': isPreview,
25200
- 'is-today': cell.isToday,
25264
+ 'is-today': isToday,
25201
25265
  'is-disabled': disabled,
25202
25266
  'is-other-month': otherMonth,
25203
25267
  });
25204
25268
  const display = !otherMonth || nearbyMonthDays;
25205
- return (jsxRuntime.jsx("button", { type: "button", role: "gridcell", tabIndex: isFocused ? 0 : -1, "aria-selected": isSelected, "aria-disabled": disabled, "aria-current": cell.isToday ? 'date' : undefined, "aria-describedby": describedBy, "data-focused": isFocused ? 'true' : undefined, disabled: disabled || !display, className: cellClass, onFocus: () => {
25269
+ return (jsxRuntime.jsx("button", { type: "button", role: "gridcell", tabIndex: isFocused ? 0 : -1, "aria-selected": isSelected, "aria-disabled": disabled, "aria-current": isToday ? 'date' : undefined, "aria-describedby": describedBy, "data-focused": isFocused ? 'true' : undefined, disabled: disabled || !display, className: cellClass, onFocus: () => {
25206
25270
  // Reached by Tab while the focused day is disabled, or
25207
25271
  // by pointer: keys move on from here. A nearby month's
25208
25272
  // day waits for its click, which turns the grid.
@@ -25227,7 +25291,7 @@ const Calendar = ({ value, focusedDate, onSelect, onFocusedDateChange, min: minP
25227
25291
  const disabled = disabledMonths[month];
25228
25292
  const isSelected = !!value && isSameMonth(value, date);
25229
25293
  const isFocused = month === tabStopMonth;
25230
- const isCurrent = isSameMonth(date, today);
25294
+ const isCurrent = !!today && isSameMonth(date, today);
25231
25295
  const cellClass = prefixedClassNames(classPrefix, 'dateinput-month-cell', {
25232
25296
  'is-selected': isSelected,
25233
25297
  'is-today': isCurrent,
@@ -30462,7 +30526,7 @@ const Hero = withSubComponents(HeroComponent, {
30462
30526
  * @returns {JSX.Element} The rendered level.
30463
30527
  * @see {@link https://bulma.io/documentation/layout/level/ | Bulma Level documentation}
30464
30528
  */
30465
- const LevelComponent = ({ isMobile, className, children, color, bgColor, textColor, ...props }) => {
30529
+ const LevelComponent = ({ as: Tag = 'nav', isMobile, className, children, color, bgColor, textColor, ...props }) => {
30466
30530
  const { bulmaHelperClasses, rest } = useBulmaClasses({
30467
30531
  color: textColor ?? color,
30468
30532
  backgroundColor: bgColor,
@@ -30472,7 +30536,7 @@ const LevelComponent = ({ isMobile, className, children, color, bgColor, textCol
30472
30536
  'is-mobile': isMobile,
30473
30537
  });
30474
30538
  const levelClasses = classNames(mainClass, bulmaHelperClasses, className);
30475
- return (jsxRuntime.jsx("nav", { className: levelClasses, ...rest, children: children }));
30539
+ return (jsxRuntime.jsx(Tag, { className: levelClasses, ...rest, children: children }));
30476
30540
  };
30477
30541
  /**
30478
30542
  * Left-aligned content.