@kaizen/components 1.80.2 → 1.80.4

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 (74) hide show
  1. package/codemods/README.md +24 -0
  2. package/codemods/migrateV2NextToCurrent/index.ts +40 -0
  3. package/codemods/migrateV2NextToCurrent/migrateV2NextToCurrent.spec.ts +555 -0
  4. package/codemods/migrateV2NextToCurrent/migrateV2NextToCurrent.ts +104 -0
  5. package/codemods/renameV2ComponentImportsAndUsages/index.ts +30 -0
  6. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.spec.ts +390 -0
  7. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.ts +151 -0
  8. package/codemods/utils/createModulePathTransformer.spec.ts +209 -0
  9. package/codemods/utils/createModulePathTransformer.ts +59 -0
  10. package/codemods/utils/createRenameMapFromGroups.ts +31 -0
  11. package/codemods/utils/index.ts +3 -0
  12. package/codemods/utils/updateJsxElementTagName.spec.ts +129 -0
  13. package/codemods/utils/updateJsxElementTagName.ts +56 -0
  14. package/codemods/utils/updateKaioImports.spec.ts +82 -0
  15. package/codemods/utils/updateKaioImports.ts +16 -7
  16. package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +69 -16
  17. package/dist/cjs/src/__alpha__/SingleSelect/context/SingleSelectContext.cjs +13 -0
  18. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +54 -0
  19. package/dist/cjs/src/__alpha__/SingleSelect/{SingleSelect.module.css.cjs → subcomponents/Popover/Popover.module.css.cjs} +1 -1
  20. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +94 -0
  21. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +69 -0
  22. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.cjs +12 -0
  23. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +41 -5
  24. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +60 -10
  25. package/dist/esm/src/__alpha__/SingleSelect/context/SingleSelectContext.mjs +10 -0
  26. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +49 -0
  27. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +4 -0
  28. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +92 -0
  29. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +67 -0
  30. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.mjs +10 -0
  31. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +43 -7
  32. package/dist/styles.css +43 -21
  33. package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +7 -9
  34. package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +12 -0
  35. package/dist/types/__alpha__/SingleSelect/context/index.d.ts +1 -0
  36. package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -1
  37. package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -1
  38. package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -1
  39. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +6 -0
  40. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/index.d.ts +1 -0
  41. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +2 -0
  42. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +4 -0
  43. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +4 -0
  44. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.d.ts +1 -0
  45. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +2 -1
  46. package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +1 -0
  47. package/dist/types/__alpha__/SingleSelect/types.d.ts +45 -0
  48. package/package.json +4 -4
  49. package/src/__alpha__/SingleSelect/SingleSelect.tsx +79 -14
  50. package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +5 -2
  51. package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +100 -0
  52. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +4 -4
  53. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +21 -2
  54. package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +21 -0
  55. package/src/__alpha__/SingleSelect/context/index.ts +1 -0
  56. package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +0 -1
  57. package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +2 -1
  58. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +7 -0
  59. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +2 -1
  60. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +3 -1
  61. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +24 -0
  62. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +54 -0
  63. package/src/__alpha__/SingleSelect/subcomponents/Popover/index.ts +1 -0
  64. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +2 -0
  65. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +108 -0
  66. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +75 -0
  67. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts +13 -0
  68. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +1 -0
  69. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +29 -7
  70. package/src/__alpha__/SingleSelect/subcomponents/index.ts +1 -0
  71. package/src/__alpha__/SingleSelect/types.ts +58 -0
  72. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.module.css.mjs +0 -4
  73. package/src/__alpha__/SingleSelect/SingleSelect.module.css +0 -9
  74. package/src/__alpha__/SingleSelect/SingleSelect.spec.tsx +0 -26
@@ -0,0 +1,67 @@
1
+ import { useMemo } from 'react';
2
+ import { useLocale } from '@react-aria/i18n';
3
+ import { usePopoverPositioning } from './usePopoverPositioning.mjs';
4
+ import { useSupportsAnchorPositioning } from './useSupportsAnchorPositioning.mjs';
5
+ var CSS_PROPS = {
6
+ POSITION_ANCHOR: '--position-anchor',
7
+ POSITION_AREA: '--position-area'
8
+ };
9
+ var DEFAULTS = {
10
+ MAX_HEIGHT: '300px'
11
+ };
12
+ /**
13
+ * Generates manual positioning styles for browsers without anchor positioning support or SSR
14
+ */
15
+ var getManualPositioningStyles = function (positionData) {
16
+ return {
17
+ top: positionData.top,
18
+ bottom: positionData.bottom,
19
+ insetInlineStart: positionData.insetInlineStart,
20
+ maxHeight: positionData.maxHeight,
21
+ left: 'auto',
22
+ right: 'auto',
23
+ position: 'fixed'
24
+ };
25
+ };
26
+ var getAnchorPositioningStyles = function (anchorName, positionData) {
27
+ var _a;
28
+ var _b;
29
+ var styles = (_a = {
30
+ maxHeight: (_b = positionData.maxHeight) !== null && _b !== void 0 ? _b : DEFAULTS.MAX_HEIGHT
31
+ }, _a[CSS_PROPS.POSITION_ANCHOR] = anchorName, _a[CSS_PROPS.POSITION_AREA] = positionData.top === 'auto' ? 'top' : 'bottom', _a);
32
+ return styles;
33
+ };
34
+ var usePositioningStyles = function (buttonRef, popoverRef, anchorName) {
35
+ var direction = useLocale().direction;
36
+ var hasAnchorSupport = useSupportsAnchorPositioning();
37
+ var _a = usePopoverPositioning({
38
+ triggerRef: buttonRef,
39
+ popoverRef: popoverRef,
40
+ direction: direction,
41
+ preferredPlacement: 'bottom'
42
+ }),
43
+ top = _a.top,
44
+ bottom = _a.bottom,
45
+ insetInlineStart = _a.insetInlineStart,
46
+ maxHeight = _a.maxHeight,
47
+ isPositioned = _a.isPositioned;
48
+ var positionData = useMemo(function () {
49
+ return {
50
+ top: top,
51
+ bottom: bottom,
52
+ insetInlineStart: insetInlineStart,
53
+ maxHeight: maxHeight
54
+ };
55
+ }, [top, bottom, insetInlineStart, maxHeight]);
56
+ var popoverStyle = useMemo(function () {
57
+ if (hasAnchorSupport === null || !hasAnchorSupport) {
58
+ return getManualPositioningStyles(positionData);
59
+ }
60
+ return getAnchorPositioningStyles(anchorName, positionData);
61
+ }, [hasAnchorSupport, anchorName, positionData]);
62
+ return {
63
+ popoverStyle: popoverStyle,
64
+ isPositioned: isPositioned
65
+ };
66
+ };
67
+ export { usePositioningStyles };
@@ -0,0 +1,10 @@
1
+ import { useMemo } from 'react';
2
+ var useSupportsAnchorPositioning = function () {
3
+ return useMemo(function () {
4
+ if (typeof window === 'undefined' || typeof CSS === 'undefined') {
5
+ return false;
6
+ }
7
+ return CSS.supports('position-anchor', 'auto') || CSS.supports('position-try-fallbacks: flip-block');
8
+ }, []);
9
+ };
10
+ export { useSupportsAnchorPositioning };
@@ -1,13 +1,49 @@
1
- import React from 'react';
2
- import { Button, SelectValue } from 'react-aria-components';
1
+ import React, { useMemo } from 'react';
2
+ import { Button } from 'react-aria-components';
3
3
  import { Icon } from '../../../../__next__/Icon/Icon.mjs';
4
+ import { useSingleSelectContext } from '../../context/SingleSelectContext.mjs';
4
5
  import styles from './Trigger.module.css.mjs';
5
- var Trigger = function () {
6
- return /*#__PURE__*/React.createElement(Button, {
7
- className: styles.button
8
- }, /*#__PURE__*/React.createElement(SelectValue, null), /*#__PURE__*/React.createElement(Icon, {
6
+ function flattenItems(items) {
7
+ return items.flatMap(function (item) {
8
+ return 'options' in item ? item.options : item;
9
+ });
10
+ }
11
+ var Trigger = function (_a) {
12
+ var buttonRef = _a.buttonRef;
13
+ var _b = useSingleSelectContext(),
14
+ isOpen = _b.isOpen,
15
+ setOpen = _b.setOpen,
16
+ selectedKey = _b.selectedKey,
17
+ items = _b.items,
18
+ anchorName = _b.anchorName;
19
+ var flattenedItems = useMemo(function () {
20
+ return flattenItems(items);
21
+ }, [items]);
22
+ var selectedLabel = useMemo(function () {
23
+ var _a;
24
+ var key = selectedKey;
25
+ var item = flattenedItems.find(function (i) {
26
+ return i.value === key;
27
+ });
28
+ return (_a = item === null || item === void 0 ? void 0 : item.label) !== null && _a !== void 0 ? _a : /*#__PURE__*/React.createElement("div", null);
29
+ }, [flattenedItems, selectedKey]);
30
+ return /*#__PURE__*/React.createElement("div", {
31
+ style: {
32
+ position: 'relative'
33
+ }
34
+ }, /*#__PURE__*/React.createElement(Button, {
35
+ className: styles.button,
36
+ ref: buttonRef,
37
+ onPress: function () {
38
+ return setOpen(!isOpen);
39
+ },
40
+ "aria-expanded": isOpen,
41
+ style: {
42
+ '--anchor-name': anchorName
43
+ }
44
+ }, selectedLabel, /*#__PURE__*/React.createElement(Icon, {
9
45
  name: "keyboard_arrow_down",
10
46
  isPresentational: true
11
- }));
47
+ })));
12
48
  };
13
49
  export { Trigger };
package/dist/styles.css CHANGED
@@ -1,11 +1,50 @@
1
1
  @layer tokens, normalize, reset, kz-components;@layer tokens{:root{--theme-key:heart;--animation-easing-function-ease-in-out:cubic-bezier(0.455,0.03,0.515,0.955);--animation-easing-function-ease-in:cubic-bezier(0.55,0.085,0.68,0.53);--animation-easing-function-ease-out:cubic-bezier(0.25,0.46,0.45,0.94);--animation-easing-function-linear:linear;--animation-easing-function-bounce-in:cubic-bezier(0.485,0.155,0.24,1.245);--animation-easing-function-bounce-out:cubic-bezier(0.485,0.155,0.515,0.845);--animation-easing-function-bounce-in-out:cubic-bezier(0.76,-0.245,0.24,1.245);--animation-duration-instant:0ms;--animation-duration-immediate:100ms;--animation-duration-rapid:200ms;--animation-duration-fast:300ms;--animation-duration-slow:400ms;--animation-duration-deliberate:700ms;--border-solid-border-width:2px;--border-solid-border-radius:7px;--border-solid-border-style:solid;--border-solid-border-color:#e1e2ea;--border-solid-border-color-rgb:225,226,234;--border-dashed-border-width:2px;--border-dashed-border-radius:7px;--border-dashed-border-style:dashed;--border-borderless-border-width:2px;--border-borderless-border-radius:7px;--border-borderless-border-style:solid;--border-borderless-border-color:transparent;--border-borderless-border-color-rgb:0,0,0;--border-focus-ring-border-width:2px;--border-focus-ring-border-radius:10px;--border-focus-ring-border-style:solid;--border-width-1:1px;--color-purple-100:#f4edf8;--color-purple-100-rgb:244,237,248;--color-purple-200:#dfc9ea;--color-purple-200-rgb:223,201,234;--color-purple-300:#c9a5dd;--color-purple-300-rgb:201,165,221;--color-purple-400:#ae67b1;--color-purple-400-rgb:174,103,177;--color-purple-500:#844587;--color-purple-500-rgb:132,69,135;--color-purple-600:#5f3361;--color-purple-600-rgb:95,51,97;--color-purple-700:#4a234d;--color-purple-700-rgb:74,35,77;--color-purple-800:#2f2438;--color-purple-800-rgb:47,36,56;--color-blue-100:#e6f6ff;--color-blue-100-rgb:230,246,255;--color-blue-200:#bde2f5;--color-blue-200-rgb:189,226,245;--color-blue-300:#73c0e8;--color-blue-300-rgb:115,192,232;--color-blue-400:#008bd6;--color-blue-400-rgb:0,139,214;--color-blue-500:#0168b3;--color-blue-500-rgb:1,104,179;--color-blue-600:#004970;--color-blue-600-rgb:0,73,112;--color-blue-700:#003157;--color-blue-700-rgb:0,49,87;--color-green-100:#e8f8f4;--color-green-100-rgb:232,248,244;--color-green-200:#c4ede2;--color-green-200-rgb:196,237,226;--color-green-300:#8fdbc7;--color-green-300-rgb:143,219,199;--color-green-400:#5dcaad;--color-green-400-rgb:93,202,173;--color-green-500:#3f9a86;--color-green-500-rgb:63,154,134;--color-green-600:#2c7d67;--color-green-600-rgb:44,125,103;--color-green-700:#22594a;--color-green-700-rgb:34,89,74;--color-yellow-100:#fff9e4;--color-yellow-100-rgb:255,249,228;--color-yellow-200:#ffeeb3;--color-yellow-200-rgb:255,238,179;--color-yellow-300:#ffe36e;--color-yellow-300-rgb:255,227,110;--color-yellow-400:#ffca4d;--color-yellow-400-rgb:255,202,77;--color-yellow-500:#ffb600;--color-yellow-500-rgb:255,182,0;--color-yellow-600:#c68600;--color-yellow-600-rgb:198,134,0;--color-yellow-700:#876400;--color-yellow-700-rgb:135,100,0;--color-red-100:#fdeaee;--color-red-100-rgb:253,234,238;--color-red-200:#f9c2cb;--color-red-200-rgb:249,194,203;--color-red-300:#f597a8;--color-red-300-rgb:245,151,168;--color-red-400:#e0707d;--color-red-400-rgb:224,112,125;--color-red-500:#c93b55;--color-red-500-rgb:201,59,85;--color-red-600:#a82433;--color-red-600-rgb:168,36,51;--color-red-700:#6c1e20;--color-red-700-rgb:108,30,32;--color-orange-100:#fff0e8;--color-orange-100-rgb:255,240,232;--color-orange-200:#ffd1b9;--color-orange-200-rgb:255,209,185;--color-orange-300:#ffb08a;--color-orange-300-rgb:255,176,138;--color-orange-400:#ff9461;--color-orange-400-rgb:255,148,97;--color-orange-500:#e96c2f;--color-orange-500-rgb:233,108,47;--color-orange-600:#b74302;--color-orange-600-rgb:183,67,2;--color-orange-700:#903c00;--color-orange-700-rgb:144,60,0;--color-gray-100:#f9f9f9;--color-gray-100-rgb:249,249,249;--color-gray-200:#f4f4f5;--color-gray-200-rgb:244,244,245;--color-gray-300:#eaeaec;--color-gray-300-rgb:234,234,236;--color-gray-400:#cdcdd0;--color-gray-400-rgb:205,205,208;--color-gray-500:#878792;--color-gray-500-rgb:135,135,146;--color-gray-600:#524e56;--color-gray-600-rgb:82,78,86;--color-white:#fff;--color-white-rgb:255,255,255;--color-black:#000;--color-black-rgb:0,0,0;--data-viz-favorable:#7dd5bd;--data-viz-favorable-rgb:125,213,189;--data-viz-unfavorable:#e68d97;--data-viz-unfavorable-rgb:230,141,151;--layout-content-max-width:1392px;--layout-content-max-width-with-sidebar:1080px;--layout-content-side-margin:72px;--layout-mobile-actions-drawer-height:60px;--layout-navigation-bar-height:72px;--layout-breakpoints-medium:768px;--layout-breakpoints-large:1080px;--shadow-small-box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 3px 16px 0 rgba(0,0,0,.06);--shadow-large-box-shadow:0 3px 9px 0 rgba(0,0,0,.1),0 8px 40px 0 rgba(0,0,0,.08);--spacing-0:0;--spacing-1:.0625rem;--spacing-2:.125rem;--spacing-4:.25rem;--spacing-6:.375rem;--spacing-8:.5rem;--spacing-12:.75rem;--spacing-16:1rem;--spacing-20:1.25rem;--spacing-24:1.5rem;--spacing-32:2rem;--spacing-40:2.5rem;--spacing-48:3rem;--spacing-56:3.5rem;--spacing-64:4rem;--spacing-72:4.5rem;--spacing-80:5rem;--spacing-96:6rem;--spacing-112:7rem;--spacing-128:8rem;--spacing-160:10rem;--spacing-200:12.5rem;--spacing-240:15rem;--spacing-280:17.5rem;--spacing-320:20rem;--spacing-xs:0.375rem;--spacing-sm:0.75rem;--spacing-md:1.5rem;--spacing-lg:2.25rem;--spacing-xl:3rem;--spacing-xxl:3.75rem;--spacing-xxxl:4.5rem;--spacing-xxxxl:5.25rem;--spacing-xxxxxl:6rem;--typography-data-large-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-large-font-weight:700;--typography-data-large-font-size:5.25rem;--typography-data-large-line-height:5.25rem;--typography-data-large-letter-spacing:normal;--typography-data-large-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-large-units-font-weight:700;--typography-data-large-units-font-size:2.625rem;--typography-data-large-units-line-height:5.25rem;--typography-data-large-units-letter-spacing:normal;--typography-data-medium-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-medium-font-weight:700;--typography-data-medium-font-size:3rem;--typography-data-medium-line-height:5rem;--typography-data-medium-letter-spacing:normal;--typography-data-medium-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-medium-units-font-weight:700;--typography-data-medium-units-font-size:1.5rem;--typography-data-medium-units-line-height:5rem;--typography-data-medium-units-letter-spacing:normal;--typography-data-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-small-font-weight:700;--typography-data-small-font-size:1.5rem;--typography-data-small-line-height:1.5rem;--typography-data-small-letter-spacing:normal;--typography-data-small-units-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-data-small-units-font-weight:700;--typography-data-small-units-font-size:1.125rem;--typography-data-small-units-line-height:1.5rem;--typography-data-small-units-letter-spacing:normal;--typography-display-0-font-family:"Tiempos Headline",Georgia,serif;--typography-display-0-font-weight:800;--typography-display-0-font-size:4.5rem;--typography-display-0-line-height:5.25rem;--typography-display-0-letter-spacing:0em;--typography-heading-1-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-1-font-weight:500;--typography-heading-1-font-size:2.125rem;--typography-heading-1-line-height:2.625rem;--typography-heading-1-letter-spacing:normal;--typography-heading-2-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-2-font-weight:600;--typography-heading-2-font-size:1.75rem;--typography-heading-2-line-height:2.25rem;--typography-heading-2-letter-spacing:normal;--typography-heading-3-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-3-font-weight:600;--typography-heading-3-font-size:1.375rem;--typography-heading-3-line-height:1.875rem;--typography-heading-3-letter-spacing:normal;--typography-heading-4-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-4-font-weight:600;--typography-heading-4-font-size:1.125rem;--typography-heading-4-line-height:1.5rem;--typography-heading-4-letter-spacing:normal;--typography-heading-5-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-5-font-weight:600;--typography-heading-5-font-size:1rem;--typography-heading-5-line-height:1.5rem;--typography-heading-5-letter-spacing:normal;--typography-heading-6-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-heading-6-font-weight:600;--typography-heading-6-font-size:0.875rem;--typography-heading-6-line-height:1.5rem;--typography-heading-6-letter-spacing:normal;--typography-paragraph-intro-lede-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-intro-lede-font-weight:400;--typography-paragraph-intro-lede-font-size:1.25rem;--typography-paragraph-intro-lede-line-height:1.875rem;--typography-paragraph-intro-lede-letter-spacing:0;--typography-paragraph-intro-lede-max-width:975px;--typography-paragraph-body-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-body-font-weight:400;--typography-paragraph-body-font-size:1rem;--typography-paragraph-body-line-height:1.5rem;--typography-paragraph-body-letter-spacing:normal;--typography-paragraph-body-max-width:780px;--typography-paragraph-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-small-font-weight:400;--typography-paragraph-small-font-size:0.875rem;--typography-paragraph-small-line-height:1.125rem;--typography-paragraph-small-letter-spacing:normal;--typography-paragraph-small-max-width:680px;--typography-paragraph-extra-small-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-paragraph-extra-small-font-weight:400;--typography-paragraph-extra-small-font-size:0.75rem;--typography-paragraph-extra-small-line-height:1.125rem;--typography-paragraph-extra-small-letter-spacing:normal;--typography-paragraph-extra-small-max-width:600px;--typography-paragraph-bold-font-weight:600;--typography-button-primary-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-button-primary-font-weight:500;--typography-button-primary-font-size:1.125rem;--typography-button-primary-line-height:1.5rem;--typography-button-primary-letter-spacing:normal;--typography-button-secondary-font-family:"Inter","Noto Sans",Helvetica,Arial,sans-serif;--typography-button-secondary-font-weight:500;--typography-button-secondary-font-size:1rem;--typography-button-secondary-line-height:1.5rem;--typography-button-secondary-letter-spacing:normal}}@layer normalize{html{text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{appearance:auto}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{appearance:none}::-webkit-file-upload-button{appearance:auto;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}}@layer reset{@font-face{font-family:Tiempos Headline;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-bold.woff)}@font-face{font-family:Tiempos Headline;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-medium.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/tiempos/tiempos-headline-medium.woff)}@font-face{font-family:Greycliff CF;font-weight:300;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-light.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:400;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-regular.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-medium.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:600;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-demi-bold.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:700;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-bold.woff) format("woff")}@font-face{font-family:Greycliff CF;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/greycliff/greycliff-cf-extra-bold.woff) format("woff")}@font-face{font-family:Inter;font-weight:300;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-light.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-light.woff)}@font-face{font-family:Inter;font-weight:400;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-regular.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-regular.woff)}@font-face{font-family:Inter;font-weight:500;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-medium.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-medium.woff)}@font-face{font-family:Inter;font-weight:600;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-demi-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-demi-bold.woff)}@font-face{font-family:Inter;font-weight:700;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-bold.woff)}@font-face{font-family:Inter;font-weight:800;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-extra-bold.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/inter/inter-extra-bold.woff)}@font-face{font-family:IBM Plex Mono;src:url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/ibm-plex-mono/ibm-plex-mono-regular.woff2),url(https://d1e7r7b0lb8p4d.cloudfront.net/fonts/ibm-plex-mono/ibm-plex-mono-regular.woff)}}@layer reset{*,:after,:before{border-color:var(--border-solid-border-color,"currentColor");border-style:solid;border-width:0}}
2
2
  @layer kz-components {
3
- .SingleSelect-module_popover__ZjL9n {
3
+ .List-module_list__bbFPn {
4
+ display: flex;
5
+ flex-direction: column;
6
+ }
7
+ }
8
+
9
+ @layer kz-components {
10
+ .Popover-module_popover__BjY2S {
11
+ position: absolute;
12
+ height: auto;
4
13
  background-color: var(--color-white);
5
14
  border-radius: var(--spacing-8);
6
- padding: var(--spacing-8);
7
- width: 200px;
15
+ padding: 0;
8
16
  box-shadow: var(--shadow-small-box-shadow);
17
+ overflow: hidden auto;
18
+ margin: 0;
19
+ box-sizing: border-box;
20
+
21
+ /* TODO: update width based on design */
22
+ width: 200px;
23
+
24
+ @supports (anchor-name: --anchor) {
25
+ position-anchor: var(--position-anchor);
26
+ margin-block: var(--spacing-4);
27
+ position-area: var(--position-area) center;
28
+ /* stylelint-disable-next-line declaration-property-value-no-unknown */
29
+ width: anchor-size(width);
30
+ }
31
+ }
32
+ }
33
+
34
+ @layer kz-components {
35
+ .ListItem-module_listItem__xGr6A {
36
+ font-family: var(--typography-paragraph-body-font-family);
37
+ font-weight: var(--typography-paragraph-body-font-weight);
38
+ font-size: var(--typography-paragraph-body-font-size);
39
+ line-height: var(--typography-paragraph-body-line-height);
40
+ letter-spacing: var(--typography-paragraph-body-letter-spacing);
41
+ padding: var(--spacing-8) var(--spacing-16);
42
+ }
43
+
44
+ .ListItem-module_listItem__xGr6A:focus-visible {
45
+ background-color: var(--color-blue-200);
46
+ outline: none;
47
+ border-color: white;
9
48
  }
10
49
  }
11
50
 
@@ -19,16 +58,9 @@
19
58
  }
20
59
  }
21
60
 
22
- @layer kz-components {
23
- .List-module_list__bbFPn {
24
- display: flex;
25
- flex-direction: column;
26
- gap: var(--spacing-16);
27
- }
28
- }
29
-
30
61
  @layer kz-components {
31
62
  .Trigger-module_button__giSqA {
63
+ anchor-name: var(--anchor-name);
32
64
  display: flex;
33
65
  align-items: center;
34
66
  justify-content: space-between;
@@ -46,16 +78,6 @@
46
78
  }
47
79
  }
48
80
 
49
- @layer kz-components {
50
- .ListItem-module_listItem__xGr6A {
51
- font-family: var(--typography-paragraph-body-font-family);
52
- font-weight: var(--typography-paragraph-body-font-weight);
53
- font-size: var(--typography-paragraph-body-font-size);
54
- line-height: var(--typography-paragraph-body-line-height);
55
- letter-spacing: var(--typography-paragraph-body-letter-spacing);
56
- }
57
- }
58
-
59
81
  @layer kz-components {
60
82
  /*
61
83
  * This is taken from the Material Symbols CDN
@@ -1,21 +1,19 @@
1
- import React, { type HTMLAttributes, type PropsWithChildren } from 'react';
2
- import { type OverrideClassName } from "../../types/OverrideClassName";
3
- export type SingleSelectProps = {
4
- children?: React.ReactNode;
5
- } & OverrideClassName<HTMLAttributes<Element>>;
1
+ import React, { type PropsWithChildren } from 'react';
2
+ import { type ListBoxProps } from 'react-aria-components';
3
+ import { type SelectItem, type SelectSection, type SingleSelectProps } from './types';
6
4
  export declare const SingleSelect: {
7
- ({ classNameOverride, children, ...restProps }: PropsWithChildren<SingleSelectProps>): JSX.Element;
5
+ ({ items, onSelectionChange, children, ...restProps }: PropsWithChildren<SingleSelectProps>): JSX.Element;
8
6
  displayName: string;
9
7
  List: {
10
- ({ children, className, ...props }: import("react-aria-components").ListBoxProps<object> & PropsWithChildren): React.ReactElement;
8
+ ({ children, className, ...props }: ListBoxProps<SelectItem | SelectSection> & PropsWithChildren): React.ReactElement;
11
9
  displayName: string;
12
10
  };
13
11
  ListItem: {
14
- ({ children, className, ...props }: import("react-aria-components").ListBoxItemProps<object> & PropsWithChildren): React.ReactElement;
12
+ ({ children, className, ...props }: import("react-aria-components").ListBoxItemProps<SelectItem> & PropsWithChildren): React.ReactElement;
15
13
  displayName: string;
16
14
  };
17
15
  ListSection: {
18
- ({ name, className, children, ...props }: import("react-aria-components").ListBoxSectionProps<object> & PropsWithChildren & {
16
+ ({ name, className, children, ...props }: import("react-aria-components").ListBoxSectionProps<SelectSection> & PropsWithChildren & {
19
17
  name: string;
20
18
  }): React.ReactElement;
21
19
  displayName: string;
@@ -0,0 +1,12 @@
1
+ import { type Key } from '@react-types/shared';
2
+ import { type SelectItem, type SelectSection } from '../types';
3
+ type SingleSelectContextType = {
4
+ isOpen: boolean;
5
+ setOpen: (open: boolean) => void;
6
+ selectedKey: Key | null;
7
+ items: (SelectItem | SelectSection)[];
8
+ anchorName: string;
9
+ };
10
+ export declare const SingleSelectContext: import("react").Context<SingleSelectContextType | undefined>;
11
+ export declare const useSingleSelectContext: () => SingleSelectContextType;
12
+ export {};
@@ -0,0 +1 @@
1
+ export * from './SingleSelectContext';
@@ -1,6 +1,7 @@
1
1
  import React, { type PropsWithChildren } from 'react';
2
2
  import { type ListBoxProps } from 'react-aria-components';
3
+ import { type SelectItem, type SelectSection } from '../../types';
3
4
  export declare const List: {
4
- ({ children, className, ...props }: ListBoxProps<object> & PropsWithChildren): React.ReactElement;
5
+ ({ children, className, ...props }: ListBoxProps<SelectItem | SelectSection> & PropsWithChildren): React.ReactElement;
5
6
  displayName: string;
6
7
  };
@@ -1,6 +1,7 @@
1
1
  import React, { type PropsWithChildren } from 'react';
2
2
  import { type ListBoxItemProps } from 'react-aria-components';
3
+ import { type SelectItem } from '../../types';
3
4
  export declare const ListItem: {
4
- ({ children, className, ...props }: ListBoxItemProps<object> & PropsWithChildren): React.ReactElement;
5
+ ({ children, className, ...props }: ListBoxItemProps<SelectItem> & PropsWithChildren): React.ReactElement;
5
6
  displayName: string;
6
7
  };
@@ -1,7 +1,8 @@
1
1
  import React, { type PropsWithChildren } from 'react';
2
2
  import { type ListBoxSectionProps } from 'react-aria-components';
3
+ import { type SelectSection } from '../../types';
3
4
  export declare const ListSection: {
4
- ({ name, className, children, ...props }: ListBoxSectionProps<object> & PropsWithChildren & {
5
+ ({ name, className, children, ...props }: ListBoxSectionProps<SelectSection> & PropsWithChildren & {
5
6
  name: string;
6
7
  }): React.ReactElement;
7
8
  displayName: string;
@@ -0,0 +1,6 @@
1
+ import React, { type PropsWithChildren } from 'react';
2
+ import { type PopoverProps } from '../../types';
3
+ export declare const Popover: {
4
+ ({ buttonRef, popoverRef, racPopoverRef, children, }: PopoverProps & PropsWithChildren): React.ReactElement;
5
+ displayName: string;
6
+ };
@@ -0,0 +1 @@
1
+ export * from './Popover';
@@ -0,0 +1,2 @@
1
+ export * from './usePopoverPositioning';
2
+ export * from './useSupportsAnchorPositioning';
@@ -0,0 +1,4 @@
1
+ import { type Position, type UsePopoverPositioningProps } from '../../../types';
2
+ export declare function usePopoverPositioning({ triggerRef, popoverRef, direction, offset, preferredPlacement, }: UsePopoverPositioningProps): Position & {
3
+ isPositioned: boolean;
4
+ };
@@ -0,0 +1,4 @@
1
+ export declare const usePositioningStyles: (buttonRef: React.RefObject<HTMLElement>, popoverRef: React.RefObject<HTMLDivElement>, anchorName: string) => {
2
+ popoverStyle: React.CSSProperties;
3
+ isPositioned: boolean;
4
+ };
@@ -0,0 +1 @@
1
+ export declare const useSupportsAnchorPositioning: () => boolean;
@@ -1 +1,2 @@
1
- export declare const Trigger: () => JSX.Element;
1
+ import { type TriggerProps } from '../../types';
2
+ export declare const Trigger: ({ buttonRef }: TriggerProps) => JSX.Element;
@@ -2,3 +2,4 @@ export * from './List';
2
2
  export * from './ListSection';
3
3
  export * from './ListItem';
4
4
  export * from './Trigger';
5
+ export * from './Popover';
@@ -0,0 +1,45 @@
1
+ import { type RefObject } from 'react';
2
+ import { type Key } from '@react-types/shared';
3
+ export type SelectItem = {
4
+ label: string;
5
+ value: string;
6
+ };
7
+ export type SelectSection = {
8
+ label: string;
9
+ options: SelectItem[];
10
+ };
11
+ export type SingleSelectProps = {
12
+ children?: React.ReactNode;
13
+ items: (SelectItem | SelectSection)[];
14
+ onSelectionChange?: (key: Key | null) => void;
15
+ };
16
+ export type TriggerProps = {
17
+ buttonRef: React.RefObject<HTMLButtonElement>;
18
+ };
19
+ export type PopoverProps = {
20
+ buttonRef: React.RefObject<HTMLElement>;
21
+ popoverRef: React.RefObject<HTMLDivElement>;
22
+ racPopoverRef: React.Ref<any>;
23
+ };
24
+ type PositionDataProp = number | string | undefined;
25
+ export type PositionData = {
26
+ top: PositionDataProp;
27
+ bottom: PositionDataProp;
28
+ insetInlineStart: PositionDataProp;
29
+ maxHeight: PositionDataProp;
30
+ };
31
+ export type LogicalPosition = number | 'auto' | undefined;
32
+ export type Position = {
33
+ top: LogicalPosition;
34
+ bottom: LogicalPosition;
35
+ insetInlineStart: number;
36
+ maxHeight?: number;
37
+ };
38
+ export type UsePopoverPositioningProps = {
39
+ triggerRef: RefObject<HTMLElement>;
40
+ popoverRef: RefObject<HTMLElement>;
41
+ direction?: 'ltr' | 'rtl';
42
+ offset?: number;
43
+ preferredPlacement?: 'top' | 'bottom';
44
+ };
45
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.80.2",
3
+ "version": "1.80.4",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -102,8 +102,8 @@
102
102
  "kaizen-codemod": "./bin/codemod.sh"
103
103
  },
104
104
  "dependencies": {
105
- "@floating-ui/react-dom": "^2.1.4",
106
- "@headlessui/react": "^2.2.6",
105
+ "@floating-ui/react-dom": "^2.1.5",
106
+ "@headlessui/react": "^2.2.7",
107
107
  "@internationalized/date": "^3.8.2",
108
108
  "@popperjs/core": "^2.11.8",
109
109
  "@reach/tabs": "^0.18.0",
@@ -179,7 +179,7 @@
179
179
  "react-dom": "^19.1.0",
180
180
  "react-highlight": "^0.15.0",
181
181
  "react-intl": "^7.1.11",
182
- "rollup": "^4.46.1",
182
+ "rollup": "^4.46.2",
183
183
  "sass": "1.79.6",
184
184
  "serialize-query-params": "^2.0.2",
185
185
  "svgo": "^3.3.2",
@@ -1,23 +1,88 @@
1
- import React, { type HTMLAttributes, type PropsWithChildren } from 'react'
2
- import { Popover as RACPopover, Select as RACSelect } from 'react-aria-components'
3
- import { type OverrideClassName } from '~components/types/OverrideClassName'
4
- import { List, ListItem, ListSection, Trigger } from './subcomponents'
5
- import styles from './SingleSelect.module.css'
6
-
7
- export type SingleSelectProps = {
8
- children?: React.ReactNode
9
- } & OverrideClassName<HTMLAttributes<Element>>
1
+ import React, { cloneElement, isValidElement, useId, useMemo, type PropsWithChildren } from 'react'
2
+ import { useSelectState } from '@react-stately/select'
3
+ import { type Key, type Selection } from '@react-types/shared'
4
+ import { Select as RACSelect, type ListBoxProps } from 'react-aria-components'
5
+ import { SingleSelectContext } from './context'
6
+ import { List, ListItem, ListSection, Popover, Trigger } from './subcomponents'
7
+ import { type SelectItem, type SelectSection, type SingleSelectProps } from './types'
10
8
 
11
9
  export const SingleSelect = ({
12
- classNameOverride,
10
+ items,
11
+ onSelectionChange,
13
12
  children,
14
13
  ...restProps
15
14
  }: PropsWithChildren<SingleSelectProps>): JSX.Element => {
15
+ const buttonRef = React.useRef<HTMLButtonElement>(null)
16
+ const popoverRef = React.useRef<HTMLDivElement>(null)
17
+ const racPopoverRef = React.useRef<HTMLElement>(null)
18
+ const uniqueId = useId()
19
+ const anchorName = `--trigger-${uniqueId}`
20
+
21
+ const state = useSelectState({
22
+ items,
23
+ })
24
+
25
+ const handleOnSelectionChange = React.useCallback(
26
+ (keys: Selection): void => {
27
+ let key: Key | null = null
28
+
29
+ if (keys instanceof Set && keys.size > 0) {
30
+ key = Array.from(keys)[0]
31
+ }
32
+
33
+ state.setSelectedKey(key)
34
+ if (onSelectionChange) {
35
+ onSelectionChange(key)
36
+ }
37
+ },
38
+ [state, onSelectionChange],
39
+ )
40
+
41
+ // Cloning children here to allow users to pass in a custom ListItem or ListSection
42
+ // and still have the SingleSelect handle selection state
43
+ const injectedChildren = useMemo(() => {
44
+ if (!isValidElement(children)) return null
45
+
46
+ const selectedKeys: Iterable<Key> = state.selectedKey
47
+ ? new Set<Key>([state.selectedKey])
48
+ : new Set()
49
+
50
+ return cloneElement(children as React.ReactElement<ListBoxProps<SelectItem | SelectSection>>, {
51
+ selectionMode: 'single',
52
+ selectedKeys,
53
+ onSelectionChange: handleOnSelectionChange,
54
+ autoFocus: 'first',
55
+ })
56
+ }, [children, handleOnSelectionChange, state.selectedKey])
57
+
16
58
  return (
17
- <RACSelect className={classNameOverride} placeholder="" {...restProps}>
18
- <Trigger />
19
- <RACPopover className={styles.popover}>{children}</RACPopover>
20
- </RACSelect>
59
+ <SingleSelectContext.Provider
60
+ value={{
61
+ isOpen: state.isOpen,
62
+ setOpen: state.setOpen,
63
+ selectedKey: state.selectedKey,
64
+ items: items,
65
+ anchorName,
66
+ }}
67
+ >
68
+ <RACSelect
69
+ // TODO: allow user to pass in label
70
+ aria-label={'single-select'}
71
+ onSelectionChange={(key) =>
72
+ handleOnSelectionChange(key != null ? new Set([key]) : new Set())
73
+ }
74
+ placeholder=""
75
+ {...restProps}
76
+ >
77
+ <Trigger buttonRef={buttonRef} />
78
+
79
+ {state.isOpen && (
80
+ <Popover buttonRef={buttonRef} popoverRef={popoverRef} racPopoverRef={racPopoverRef}>
81
+ {injectedChildren}
82
+ </Popover>
83
+ )}
84
+ </RACSelect>
85
+ </SingleSelectContext.Provider>
21
86
  )
22
87
  }
23
88
 
@@ -18,10 +18,13 @@ import * as SingleSelectStories from './SingleSelect.stories'
18
18
 
19
19
  ## Overview
20
20
 
21
- {/* @todo: Update summary. */}
22
- Brief summary of the component here.
21
+ SingleSelect component that handles selecting items from a dropdown, can be either filterable or not.
23
22
 
24
23
  <Canvas of={SingleSelectStories.Playground} />
25
24
  <Controls of={SingleSelectStories.Playground} />
26
25
 
27
26
  ## API
27
+
28
+ ## Positioning and z-index Management
29
+
30
+ The SingleSelect component leverages the native Popover API to manage its dropdown functionality. By using popover instead of custom portal logic, the component takes full advantage of CSS layers, ensuring dropdowns appear above other content without manual z-index management.