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