@allxsmith/bestax-bulma 5.12.0 → 5.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -324,6 +324,7 @@ const validDisplays = [
324
324
  'inline',
325
325
  'inline-block',
326
326
  'inline-flex',
327
+ 'grid',
327
328
  ];
328
329
  /**
329
330
  * Valid Bulma visibility classes.
@@ -412,8 +413,12 @@ const validFlexGrowShrink = ['0', '1', '2', '3', '4', '5'];
412
413
  const validViewports = [
413
414
  'mobile',
414
415
  'tablet',
416
+ 'tablet-only',
417
+ 'touch',
415
418
  'desktop',
419
+ 'desktop-only',
416
420
  'widescreen',
421
+ 'widescreen-only',
417
422
  'fullhd',
418
423
  ];
419
424
  /**
@@ -433,9 +438,17 @@ const createBulmaClassHelpers = (classPrefix, viewport) => {
433
438
  classes.push(classPrefix ? `${classPrefix}${className}` : className);
434
439
  };
435
440
  // Helper to add class with optional viewport
436
- const addClass = (prefix, value, validValues, supportsViewport = false) => {
441
+ //
442
+ // `supportedViewports` narrows which viewports may be suffixed onto this
443
+ // class. It defaults to every viewport, but a few Bulma helper families
444
+ // (notably `is-size-*`, which ships no `-only` bands) support only a subset,
445
+ // so passing the emitting set keeps us from producing dead classes.
446
+ const addClass = (prefix, value, validValues, supportsViewport = false, supportedViewports = validViewports) => {
437
447
  if (value && validValues.includes(value)) {
438
- const className = supportsViewport && viewport && validViewports.includes(viewport)
448
+ const className = supportsViewport &&
449
+ viewport &&
450
+ validViewports.includes(viewport) &&
451
+ supportedViewports.includes(viewport)
439
452
  ? `${prefix}-${value}-${viewport}`
440
453
  : `${prefix}-${value}`;
441
454
  addPrefixedClass(className);
@@ -560,6 +573,19 @@ const useSpacingClasses = (props) => {
560
573
  }, [classPrefix, m, mt, mr, mb, ml, mx, my, p, pt, pr, pb, pl, px, py]);
561
574
  };
562
575
 
576
+ /**
577
+ * Viewports Bulma generates `is-size-*` classes for. Unlike text alignment,
578
+ * text size ships no `-only` bands (`tablet-only`/`desktop-only`/`widescreen-only`),
579
+ * so the `viewport` prop's suffix is limited to these on the `is-size` path.
580
+ */
581
+ const sizeSupportedViewports = [
582
+ 'mobile',
583
+ 'tablet',
584
+ 'touch',
585
+ 'desktop',
586
+ 'widescreen',
587
+ 'fullhd',
588
+ ];
563
589
  /**
564
590
  * A hook that generates Bulma typography helper classes, including
565
591
  * viewport-specific text size and alignment variants.
@@ -580,8 +606,11 @@ const useTypographyClasses = (props) => {
580
606
  return React.useMemo(() => {
581
607
  const { classes, addPrefixedClass, addClass, addClassNoViewport } = createBulmaClassHelpers(classPrefix, viewport);
582
608
  // Typography
583
- addClass('is-size', textSize, validTextSizes, true); // supports viewport
584
- addClass('has-text', textAlign, validAlignments$1, true); // supports viewport
609
+ // Bulma emits `is-size-*` for the six main/`touch` breakpoints but not the
610
+ // three `-only` bands, so restrict the viewport suffix to the ones it ships
611
+ // — otherwise `viewport="tablet-only"` would render a dead `is-size-N-tablet-only`.
612
+ addClass('is-size', textSize, validTextSizes, true, sizeSupportedViewports); // supports viewport (no `-only` bands)
613
+ addClass('has-text', textAlign, validAlignments$1, true); // supports viewport (all bands)
585
614
  addClassNoViewport('is', textTransform, validTextTransforms); // no viewport support
586
615
  addClassNoViewport('has-text-weight', textWeight, validTextWeights); // no viewport support
587
616
  addClassNoViewport('is-family', fontFamily, validFontFamilies); // no viewport support
@@ -647,7 +676,7 @@ const useTypographyClasses = (props) => {
647
676
  */
648
677
  const useVisibilityClasses = (props) => {
649
678
  const { classPrefix } = useConfig();
650
- const { visibility, visibilityMobile, visibilityTablet, visibilityDesktop, visibilityWidescreen, visibilityFullhd, display, displayMobile, displayTablet, displayDesktop, displayWidescreen, displayFullhd, viewport, } = props;
679
+ const { visibility, visibilityMobile, visibilityTablet, visibilityTabletOnly, visibilityTouch, visibilityDesktop, visibilityDesktopOnly, visibilityWidescreen, visibilityWidescreenOnly, visibilityFullhd, display, displayMobile, displayTablet, displayTabletOnly, displayTouch, displayDesktop, displayDesktopOnly, displayWidescreen, displayWidescreenOnly, displayFullhd, viewport, } = props;
651
680
  return React.useMemo(() => {
652
681
  const { classes, addPrefixedClass, addClass } = createBulmaClassHelpers(classPrefix, viewport);
653
682
  // Viewport-specific visibility
@@ -664,8 +693,12 @@ const useVisibilityClasses = (props) => {
664
693
  };
665
694
  addViewportSpecificVisibilityClass(visibilityMobile, '-mobile');
666
695
  addViewportSpecificVisibilityClass(visibilityTablet, '-tablet');
696
+ addViewportSpecificVisibilityClass(visibilityTabletOnly, '-tablet-only');
697
+ addViewportSpecificVisibilityClass(visibilityTouch, '-touch');
667
698
  addViewportSpecificVisibilityClass(visibilityDesktop, '-desktop');
699
+ addViewportSpecificVisibilityClass(visibilityDesktopOnly, '-desktop-only');
668
700
  addViewportSpecificVisibilityClass(visibilityWidescreen, '-widescreen');
701
+ addViewportSpecificVisibilityClass(visibilityWidescreenOnly, '-widescreen-only');
669
702
  addViewportSpecificVisibilityClass(visibilityFullhd, '-fullhd');
670
703
  // Visibility and Display
671
704
  // Handle viewport-specific display properties first (they take precedence)
@@ -682,14 +715,22 @@ const useVisibilityClasses = (props) => {
682
715
  // Apply viewport-specific display classes
683
716
  addDisplayClass(displayMobile, '-mobile');
684
717
  addDisplayClass(displayTablet, '-tablet');
718
+ addDisplayClass(displayTabletOnly, '-tablet-only');
719
+ addDisplayClass(displayTouch, '-touch');
685
720
  addDisplayClass(displayDesktop, '-desktop');
721
+ addDisplayClass(displayDesktopOnly, '-desktop-only');
686
722
  addDisplayClass(displayWidescreen, '-widescreen');
723
+ addDisplayClass(displayWidescreenOnly, '-widescreen-only');
687
724
  addDisplayClass(displayFullhd, '-fullhd');
688
725
  // Apply legacy display/viewport combination if no viewport-specific display props are set
689
726
  const hasViewportSpecificDisplay = !!(displayMobile ||
690
727
  displayTablet ||
728
+ displayTabletOnly ||
729
+ displayTouch ||
691
730
  displayDesktop ||
731
+ displayDesktopOnly ||
692
732
  displayWidescreen ||
733
+ displayWidescreenOnly ||
693
734
  displayFullhd);
694
735
  if (!hasViewportSpecificDisplay) {
695
736
  // Legacy display handling
@@ -722,14 +763,22 @@ const useVisibilityClasses = (props) => {
722
763
  visibility,
723
764
  visibilityMobile,
724
765
  visibilityTablet,
766
+ visibilityTabletOnly,
767
+ visibilityTouch,
725
768
  visibilityDesktop,
769
+ visibilityDesktopOnly,
726
770
  visibilityWidescreen,
771
+ visibilityWidescreenOnly,
727
772
  visibilityFullhd,
728
773
  display,
729
774
  displayMobile,
730
775
  displayTablet,
776
+ displayTabletOnly,
777
+ displayTouch,
731
778
  displayDesktop,
779
+ displayDesktopOnly,
732
780
  displayWidescreen,
781
+ displayWidescreenOnly,
733
782
  displayFullhd,
734
783
  viewport,
735
784
  ]);
@@ -758,7 +807,7 @@ const useVisibilityClasses = (props) => {
758
807
  */
759
808
  const useFlexboxClasses = (props) => {
760
809
  const { classPrefix } = useConfig();
761
- const { flexDirection, flexWrap, justifyContent, alignContent, alignItems, alignSelf, flexGrow, flexShrink, display, displayMobile, displayTablet, displayDesktop, displayWidescreen, displayFullhd, } = props;
810
+ const { flexDirection, flexWrap, justifyContent, alignContent, alignItems, alignSelf, flexGrow, flexShrink, display, displayMobile, displayTablet, displayTabletOnly, displayTouch, displayDesktop, displayDesktopOnly, displayWidescreen, displayWidescreenOnly, displayFullhd, } = props;
762
811
  return React.useMemo(() => {
763
812
  const { classes, addClassNoViewport } = createBulmaClassHelpers(classPrefix);
764
813
  // Flexbox
@@ -768,10 +817,18 @@ const useFlexboxClasses = (props) => {
768
817
  displayMobile === 'inline-flex' ||
769
818
  displayTablet === 'flex' ||
770
819
  displayTablet === 'inline-flex' ||
820
+ displayTabletOnly === 'flex' ||
821
+ displayTabletOnly === 'inline-flex' ||
822
+ displayTouch === 'flex' ||
823
+ displayTouch === 'inline-flex' ||
771
824
  displayDesktop === 'flex' ||
772
825
  displayDesktop === 'inline-flex' ||
826
+ displayDesktopOnly === 'flex' ||
827
+ displayDesktopOnly === 'inline-flex' ||
773
828
  displayWidescreen === 'flex' ||
774
829
  displayWidescreen === 'inline-flex' ||
830
+ displayWidescreenOnly === 'flex' ||
831
+ displayWidescreenOnly === 'inline-flex' ||
775
832
  displayFullhd === 'flex' ||
776
833
  displayFullhd === 'inline-flex';
777
834
  if (hasFlexDisplay) {
@@ -801,8 +858,12 @@ const useFlexboxClasses = (props) => {
801
858
  display,
802
859
  displayMobile,
803
860
  displayTablet,
861
+ displayTabletOnly,
862
+ displayTouch,
804
863
  displayDesktop,
864
+ displayDesktopOnly,
805
865
  displayWidescreen,
866
+ displayWidescreenOnly,
806
867
  displayFullhd,
807
868
  ]);
808
869
  };
@@ -915,7 +976,7 @@ const useOtherClasses = (props) => {
915
976
  * // rest: { className: 'custom-class' }
916
977
  */
917
978
  const useBulmaClasses = (props) => {
918
- const { color, backgroundColor, colorShade, backgroundColorShade, m, mt, mr, mb, ml, mx, my, p, pt, pr, pb, pl, px, py, textSize, textAlign, textTransform, textWeight, fontFamily, display, visibility, flexDirection, flexWrap, justifyContent, alignContent, alignItems, alignSelf, flexGrow, flexShrink, float, overflow, overlay, interaction, cursor, radius, shadow, responsive, viewport, displayMobile, displayTablet, displayDesktop, displayWidescreen, displayFullhd, textSizeMobile, textSizeTablet, textSizeDesktop, textSizeWidescreen, textSizeFullhd, textAlignMobile, textAlignTablet, textAlignDesktop, textAlignWidescreen, textAlignFullhd, visibilityMobile, visibilityTablet, visibilityDesktop, visibilityWidescreen, visibilityFullhd, skeleton, clearfix, relative, fullHeight, ...rest } = props;
979
+ const { color, backgroundColor, colorShade, backgroundColorShade, m, mt, mr, mb, ml, mx, my, p, pt, pr, pb, pl, px, py, textSize, textAlign, textTransform, textWeight, fontFamily, display, visibility, flexDirection, flexWrap, justifyContent, alignContent, alignItems, alignSelf, flexGrow, flexShrink, float, overflow, overlay, interaction, cursor, radius, shadow, responsive, viewport, displayMobile, displayTablet, displayTabletOnly, displayTouch, displayDesktop, displayDesktopOnly, displayWidescreen, displayWidescreenOnly, displayFullhd, textSizeMobile, textSizeTablet, textSizeDesktop, textSizeWidescreen, textSizeFullhd, textAlignMobile, textAlignTablet, textAlignDesktop, textAlignWidescreen, textAlignFullhd, visibilityMobile, visibilityTablet, visibilityTabletOnly, visibilityTouch, visibilityDesktop, visibilityDesktopOnly, visibilityWidescreen, visibilityWidescreenOnly, visibilityFullhd, skeleton, clearfix, relative, fullHeight, ...rest } = props;
919
980
  const colorClasses = useColorClasses({
920
981
  color,
921
982
  colorShade,
@@ -961,14 +1022,22 @@ const useBulmaClasses = (props) => {
961
1022
  visibility,
962
1023
  visibilityMobile,
963
1024
  visibilityTablet,
1025
+ visibilityTabletOnly,
1026
+ visibilityTouch,
964
1027
  visibilityDesktop,
1028
+ visibilityDesktopOnly,
965
1029
  visibilityWidescreen,
1030
+ visibilityWidescreenOnly,
966
1031
  visibilityFullhd,
967
1032
  display,
968
1033
  displayMobile,
969
1034
  displayTablet,
1035
+ displayTabletOnly,
1036
+ displayTouch,
970
1037
  displayDesktop,
1038
+ displayDesktopOnly,
971
1039
  displayWidescreen,
1040
+ displayWidescreenOnly,
972
1041
  displayFullhd,
973
1042
  viewport,
974
1043
  });
@@ -984,8 +1053,12 @@ const useBulmaClasses = (props) => {
984
1053
  display,
985
1054
  displayMobile,
986
1055
  displayTablet,
1056
+ displayTabletOnly,
1057
+ displayTouch,
987
1058
  displayDesktop,
1059
+ displayDesktopOnly,
988
1060
  displayWidescreen,
1061
+ displayWidescreenOnly,
989
1062
  displayFullhd,
990
1063
  });
991
1064
  const otherClasses = useOtherClasses({
@@ -1709,6 +1782,8 @@ const isBrowser$1 = (win, doc) => typeof win !== 'undefined' && typeof doc !== '
1709
1782
  const DropdownComponent = ({ label, children, className, menuClassName, active: activeProp, up, right, hoverable, disabled, onActiveChange, closeOnClick = true, id, ...props }) => {
1710
1783
  const [active, setActive] = React.useState(!!activeProp);
1711
1784
  const dropdownRef = React.useRef(null);
1785
+ const triggerRef = React.useRef(null);
1786
+ const pendingFocusRef = React.useRef(null);
1712
1787
  const { bulmaHelperClasses, rest } = useBulmaClasses(props);
1713
1788
  // Generate Bulma classes with prefix
1714
1789
  const bulmaClasses = usePrefixedClassNames('dropdown', {
@@ -1757,8 +1832,126 @@ const DropdownComponent = ({ label, children, className, menuClassName, active:
1757
1832
  onActiveChange?.(false);
1758
1833
  }
1759
1834
  };
1835
+ const getMenuItems = () => {
1836
+ /* istanbul ignore next: dropdownRef.current is never null once mounted */
1837
+ if (!dropdownRef.current)
1838
+ return [];
1839
+ return Array.from(dropdownRef.current.querySelectorAll('[role="menuitem"]')).filter(el => !el.hasAttribute('disabled') &&
1840
+ el.getAttribute('aria-disabled') !== 'true');
1841
+ };
1842
+ // Focus the pending menu item once the menu becomes visible; the item is
1843
+ // unfocusable while `display: none` applies, so this must wait for the
1844
+ // `is-active` class to actually land in the DOM.
1845
+ React.useEffect(() => {
1846
+ if (!active || !pendingFocusRef.current)
1847
+ return;
1848
+ const items = getMenuItems();
1849
+ if (items.length) {
1850
+ const index = pendingFocusRef.current === 'first' ? 0 : items.length - 1;
1851
+ items[index].focus();
1852
+ }
1853
+ pendingFocusRef.current = null;
1854
+ }, [active]);
1855
+ const handleTriggerKeyDown = (e) => {
1856
+ if (disabled)
1857
+ return;
1858
+ switch (e.key) {
1859
+ case 'ArrowDown':
1860
+ e.preventDefault();
1861
+ pendingFocusRef.current = 'first';
1862
+ if (!active) {
1863
+ setActive(true);
1864
+ onActiveChange?.(true);
1865
+ }
1866
+ else {
1867
+ const items = getMenuItems();
1868
+ if (items.length)
1869
+ items[0].focus();
1870
+ pendingFocusRef.current = null;
1871
+ }
1872
+ break;
1873
+ case 'ArrowUp':
1874
+ e.preventDefault();
1875
+ pendingFocusRef.current = 'last';
1876
+ if (!active) {
1877
+ setActive(true);
1878
+ onActiveChange?.(true);
1879
+ }
1880
+ else {
1881
+ const items = getMenuItems();
1882
+ if (items.length)
1883
+ items[items.length - 1].focus();
1884
+ pendingFocusRef.current = null;
1885
+ }
1886
+ break;
1887
+ case 'Enter':
1888
+ case ' ':
1889
+ e.preventDefault();
1890
+ if (!active) {
1891
+ pendingFocusRef.current = 'first';
1892
+ setActive(true);
1893
+ onActiveChange?.(true);
1894
+ }
1895
+ else {
1896
+ setActive(false);
1897
+ onActiveChange?.(false);
1898
+ }
1899
+ break;
1900
+ case 'Escape':
1901
+ if (active) {
1902
+ e.preventDefault();
1903
+ setActive(false);
1904
+ onActiveChange?.(false);
1905
+ }
1906
+ break;
1907
+ }
1908
+ };
1909
+ const handleMenuKeyDown = (e) => {
1910
+ // Escape and Tab close the menu regardless of whether it has any focusable
1911
+ // items — an empty or all-disabled menu must still honor the close contract.
1912
+ if (e.key === 'Escape') {
1913
+ e.preventDefault();
1914
+ setActive(false);
1915
+ onActiveChange?.(false);
1916
+ triggerRef.current?.focus();
1917
+ return;
1918
+ }
1919
+ if (e.key === 'Tab') {
1920
+ setActive(false);
1921
+ onActiveChange?.(false);
1922
+ return;
1923
+ }
1924
+ const items = getMenuItems();
1925
+ if (!items.length)
1926
+ return;
1927
+ const currentIndex = items.indexOf(document.activeElement);
1928
+ switch (e.key) {
1929
+ case 'ArrowDown': {
1930
+ e.preventDefault();
1931
+ const next = currentIndex >= 0 ? (currentIndex + 1) % items.length : 0;
1932
+ items[next].focus();
1933
+ break;
1934
+ }
1935
+ case 'ArrowUp': {
1936
+ e.preventDefault();
1937
+ const prev = currentIndex >= 0
1938
+ ? (currentIndex - 1 + items.length) % items.length
1939
+ : items.length - 1;
1940
+ items[prev].focus();
1941
+ break;
1942
+ }
1943
+ case 'Home':
1944
+ e.preventDefault();
1945
+ items[0].focus();
1946
+ break;
1947
+ case 'End':
1948
+ e.preventDefault();
1949
+ items[items.length - 1].focus();
1950
+ break;
1951
+ }
1952
+ };
1760
1953
  const dropdownClasses = classNames(bulmaClasses, bulmaHelperClasses, className);
1761
- return (jsxRuntime.jsxs("div", { className: dropdownClasses, ref: dropdownRef, id: id, "data-testid": "dropdown-root", ...rest, children: [jsxRuntime.jsx("div", { className: usePrefixedClassNames('dropdown-trigger'), children: jsxRuntime.jsxs("button", { className: buttonClass, "aria-haspopup": "true", "aria-controls": id ? `${id}-menu` : undefined, "aria-expanded": active, onClick: handleToggle, 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, role: "menu", "data-testid": "dropdown-menu", children: jsxRuntime.jsx("div", { className: usePrefixedClassNames('dropdown-content'), onClick: handleMenuClick, tabIndex: -1, children: children }) })] }));
1954
+ return (jsxRuntime.jsxs("div", { className: dropdownClasses, ref: dropdownRef, 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, role: "menu", "data-testid": "dropdown-menu", onKeyDown: handleMenuKeyDown, children: jsxRuntime.jsx("div", { className: usePrefixedClassNames('dropdown-content'), onClick: handleMenuClick, tabIndex: -1, children: children }) })] }));
1762
1955
  };
1763
1956
  /**
1764
1957
  * Bulma Dropdown item.
@@ -2087,6 +2280,7 @@ const Modal = withSubComponents(ModalRoot, {
2087
2280
  Close: ModalClose,
2088
2281
  }, 'Modal');
2089
2282
 
2283
+ const NavbarDropdownContext = React.createContext(null);
2090
2284
  /**
2091
2285
  * The `Navbar` component implements Bulma's powerful, responsive navigation bar for your Bulma React UI.
2092
2286
  *
@@ -2210,9 +2404,45 @@ const NavbarLink = ({ className, as: Component = 'a', arrowless, textColor, bgCo
2210
2404
  backgroundColor: bgColor,
2211
2405
  ...props,
2212
2406
  });
2407
+ const dropdownContext = React.useContext(NavbarDropdownContext);
2408
+ const hasHref = rest.href !== undefined;
2409
+ const isNativeInteractive = Component === 'button' || hasHref;
2410
+ const handleKeyDown = (e) => {
2411
+ rest.onKeyDown?.(e);
2412
+ if (!dropdownContext || e.defaultPrevented)
2413
+ return;
2414
+ if (e.key === 'Enter' || e.key === ' ') {
2415
+ e.preventDefault();
2416
+ dropdownContext.toggle();
2417
+ }
2418
+ else if (e.key === 'Escape' && dropdownContext.active) {
2419
+ e.preventDefault();
2420
+ dropdownContext.close();
2421
+ }
2422
+ };
2423
+ // When the link is not natively interactive (no `href`, not a `<button>`) it
2424
+ // renders as `role="button"`, so a mouse/touch click has no default action to
2425
+ // open the dropdown. Compose the caller's handler and toggle the dropdown
2426
+ // unless the caller prevented the default. Native links/buttons keep their own
2427
+ // click semantics (navigation / the caller's handler).
2428
+ const handleClick = (e) => {
2429
+ rest.onClick?.(e);
2430
+ if (!dropdownContext || isNativeInteractive || e.defaultPrevented)
2431
+ return;
2432
+ dropdownContext.toggle();
2433
+ };
2213
2434
  return (jsxRuntime.jsx(Component, { className: classNames(usePrefixedClassNames('navbar-link', {
2214
2435
  'is-arrowless': arrowless,
2215
- }), bulmaHelperClasses, className), ...rest, children: children }));
2436
+ }), bulmaHelperClasses, className), ...rest, ...(dropdownContext && {
2437
+ 'aria-haspopup': 'true',
2438
+ 'aria-expanded': dropdownContext.active,
2439
+ onKeyDown: handleKeyDown,
2440
+ ...(!isNativeInteractive && {
2441
+ role: 'button',
2442
+ tabIndex: 0,
2443
+ onClick: handleClick,
2444
+ }),
2445
+ }), children: children }));
2216
2446
  };
2217
2447
  /**
2218
2448
  * Dropdown parent (with options for hover, up, right, active)
@@ -2221,12 +2451,34 @@ const NavbarLink = ({ className, as: Component = 'a', arrowless, textColor, bgCo
2221
2451
  * @param {NavbarDropdownProps} props - Props for the NavbarDropdown component.
2222
2452
  * @returns {JSX.Element} The rendered dropdown.
2223
2453
  */
2224
- const NavbarDropdown = ({ className, right, up, hoverable, active, children, ...props }) => (jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('navbar-item', 'has-dropdown', {
2225
- 'has-dropdown-up': up,
2226
- 'is-right': right,
2227
- 'is-hoverable': hoverable,
2228
- 'is-active': active,
2229
- }), className), ...props, children: children }));
2454
+ const NavbarDropdown = ({ className, right, up, hoverable, active: activeProp, onActiveChange, children, ...props }) => {
2455
+ const [active, setActive] = React.useState(!!activeProp);
2456
+ React.useEffect(() => {
2457
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- syncing to controlled prop
2458
+ if (typeof activeProp === 'boolean')
2459
+ setActive(activeProp);
2460
+ }, [activeProp]);
2461
+ // Keep `onActiveChange` out of the state updater (a pure function) so Strict
2462
+ // Mode's double-invoked updaters don't double-fire it — mirrors `Dropdown`.
2463
+ const toggle = () => {
2464
+ const next = !active;
2465
+ setActive(next);
2466
+ onActiveChange?.(next);
2467
+ };
2468
+ const close = () => {
2469
+ /* istanbul ignore next: close() is only invoked once already active */
2470
+ if (!active)
2471
+ return;
2472
+ setActive(false);
2473
+ onActiveChange?.(false);
2474
+ };
2475
+ return (jsxRuntime.jsx(NavbarDropdownContext.Provider, { value: { active, toggle, close }, children: jsxRuntime.jsx("div", { className: classNames(usePrefixedClassNames('navbar-item', 'has-dropdown', {
2476
+ 'has-dropdown-up': up,
2477
+ 'is-right': right,
2478
+ 'is-hoverable': hoverable,
2479
+ 'is-active': active,
2480
+ }), className), ...props, children: children }) }));
2481
+ };
2230
2482
  /**
2231
2483
  * Dropdown menu container
2232
2484
  *
@@ -5601,21 +5853,22 @@ const Tag = ({ className, color, size, isLight, isRounded, isDelete, isHoverable
5601
5853
 
5602
5854
  const validTagsSizes = ['medium', 'large'];
5603
5855
  /**
5604
- * The `Tags` component groups multiple `Tag` components together in a horizontal (or multiline) Bulma-styled container.
5856
+ * The `Tags` component groups multiple `Tag` components together in a Bulma-styled container that wraps onto multiple lines by default.
5605
5857
  *
5606
5858
  * @function
5607
5859
  * @param {TagsProps} props - Props for the Tags component.
5608
5860
  * @returns {JSX.Element} The rendered tags container.
5609
5861
  * @see {@link https://bulma.io/documentation/elements/tag/#list-of-tags | Bulma Tags documentation}
5610
5862
  */
5611
- const TagsComponent = ({ className, hasAddons, isMultiline, size, children, ...props }) => {
5863
+ const TagsComponent = ({ className, hasAddons,
5864
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- accepted for backwards compatibility, deprecated no-op (no `are-multiline` CSS ships)
5865
+ isMultiline, size, children, ...props }) => {
5612
5866
  /**
5613
5867
  * Generates Bulma helper classes and separates out remaining props.
5614
5868
  */
5615
5869
  const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });
5616
5870
  const bulmaClasses = usePrefixedClassNames('tags', {
5617
5871
  'has-addons': hasAddons,
5618
- 'are-multiline': isMultiline,
5619
5872
  [`are-${size}`]: size && validTagsSizes.includes(size),
5620
5873
  });
5621
5874
  const tagsClasses = classNames(bulmaClasses, bulmaHelperClasses, className);