@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 +271 -18
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +271 -18
- package/dist/index.esm.js.map +1 -1
- package/dist/types/components/Dropdown.d.ts +2 -0
- package/dist/types/components/Navbar.d.ts +2 -0
- package/dist/types/elements/Tags.d.ts +7 -1
- package/dist/types/helpers/bulmaClassHelpers.d.ts +11 -3
- package/dist/types/helpers/useVisibilityClasses.d.ts +8 -0
- package/package.json +1 -1
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
|
-
|
|
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 &&
|
|
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
|
-
|
|
582
|
-
|
|
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,
|
|
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 }) =>
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
'
|
|
2227
|
-
|
|
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
|
|
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,
|
|
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);
|