@kaizen/components 0.0.0-canary-v2-20250901045936 → 0.0.0-canary-alpha-release-20250918043833

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 (114) hide show
  1. package/alpha/README.md +28 -0
  2. package/alpha/package.json +5 -0
  3. package/dist/cjs/alpha.cjs +1 -0
  4. package/dist/cjs/src/Modal/GenericModal/GenericModal.cjs +33 -65
  5. package/dist/cjs/src/Modal/GenericModal/GenericModal.module.scss.cjs +1 -3
  6. package/dist/cjs/src/Notification/InlineNotification/InlineNotification.cjs +1 -1
  7. package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +35 -74
  8. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.cjs +105 -0
  9. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.cjs +11 -0
  10. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.cjs +112 -0
  11. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.cjs +16 -0
  12. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/List/List.cjs +35 -10
  13. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.cjs +61 -8
  14. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.cjs +10 -1
  15. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.cjs +38 -9
  16. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.cjs +4 -1
  17. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +60 -30
  18. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.cjs +2 -1
  19. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +2 -1
  20. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +4 -2
  21. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.cjs +87 -0
  22. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.cjs +11 -0
  23. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.cjs +52 -0
  24. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.cjs +13 -0
  25. package/dist/esm/alpha.mjs +1 -1
  26. package/dist/esm/src/Modal/GenericModal/GenericModal.mjs +34 -65
  27. package/dist/esm/src/Modal/GenericModal/GenericModal.module.scss.mjs +1 -3
  28. package/dist/esm/src/Notification/InlineNotification/InlineNotification.mjs +1 -1
  29. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +39 -73
  30. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.mjs +96 -0
  31. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.mjs +9 -0
  32. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.mjs +103 -0
  33. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.mjs +14 -0
  34. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/List/List.mjs +37 -14
  35. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.mjs +63 -13
  36. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.mjs +10 -1
  37. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.mjs +41 -15
  38. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.mjs +4 -1
  39. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +69 -43
  40. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +2 -1
  41. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +2 -1
  42. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +4 -2
  43. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.mjs +78 -0
  44. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.mjs +9 -0
  45. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.mjs +43 -0
  46. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.mjs +11 -0
  47. package/dist/styles.css +385 -21
  48. package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +14 -19
  49. package/dist/types/__alpha__/SingleSelect/_docs/mockData.d.ts +3 -0
  50. package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +15 -7
  51. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.d.ts +2 -0
  52. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/index.d.ts +1 -0
  53. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.d.ts +2 -0
  54. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.d.ts +1 -0
  55. package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -7
  56. package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -7
  57. package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -9
  58. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +3 -6
  59. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +1 -0
  60. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +1 -0
  61. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +1 -0
  62. package/dist/types/__alpha__/SingleSelect/subcomponents/Select/Select.d.ts +2 -0
  63. package/dist/types/__alpha__/SingleSelect/subcomponents/Select/index.d.ts +1 -0
  64. package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.d.ts +2 -0
  65. package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.d.ts +1 -0
  66. package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +4 -1
  67. package/dist/types/__alpha__/SingleSelect/types.d.ts +68 -11
  68. package/locales/en.json +9 -1
  69. package/package.json +10 -3
  70. package/src/Modal/GenericModal/GenericModal.spec.tsx +1 -1
  71. package/src/Modal/GenericModal/GenericModal.tsx +38 -70
  72. package/src/Notification/InlineNotification/InlineNotification.tsx +1 -1
  73. package/src/RichTextEditor/RichTextEditor/RichTextEditor.spec.stories.tsx +10 -3
  74. package/src/__alpha__/SingleSelect/SingleSelect.tsx +35 -88
  75. package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +96 -6
  76. package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +22 -24
  77. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +389 -33
  78. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +41 -22
  79. package/src/__alpha__/SingleSelect/_docs/mockData.ts +20 -14
  80. package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +18 -7
  81. package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css +35 -0
  82. package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.tsx +106 -0
  83. package/src/__alpha__/SingleSelect/subcomponents/ComboBox/index.ts +1 -0
  84. package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css +130 -0
  85. package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.tsx +121 -0
  86. package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.ts +1 -0
  87. package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +5 -0
  88. package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +36 -13
  89. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +84 -3
  90. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +67 -11
  91. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css +20 -5
  92. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +46 -19
  93. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +7 -5
  94. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +90 -37
  95. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +1 -0
  96. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +2 -2
  97. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +9 -8
  98. package/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css +35 -0
  99. package/src/__alpha__/SingleSelect/subcomponents/Select/Select.tsx +84 -0
  100. package/src/__alpha__/SingleSelect/subcomponents/Select/index.ts +1 -0
  101. package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css +77 -0
  102. package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.tsx +52 -0
  103. package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.ts +1 -0
  104. package/src/__alpha__/SingleSelect/subcomponents/index.ts +4 -1
  105. package/src/__alpha__/SingleSelect/types.ts +94 -14
  106. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +0 -57
  107. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.cjs +0 -6
  108. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +0 -49
  109. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.mjs +0 -4
  110. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +0 -2
  111. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/index.d.ts +0 -1
  112. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +0 -19
  113. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +0 -35
  114. package/src/__alpha__/SingleSelect/subcomponents/Trigger/index.ts +0 -1
@@ -1,4 +1,8 @@
1
1
  @layer kz-components {
2
+ ul {
3
+ padding-inline-start: 0;
4
+ }
5
+
2
6
  .listItem {
3
7
  font-family: var(--typography-paragraph-body-font-family);
4
8
  font-weight: var(--typography-paragraph-body-font-weight);
@@ -6,11 +10,88 @@
6
10
  line-height: var(--typography-paragraph-body-line-height);
7
11
  letter-spacing: var(--typography-paragraph-body-letter-spacing);
8
12
  padding: var(--spacing-8) var(--spacing-16);
13
+ margin: 0;
14
+ list-style-type: none;
15
+ border-radius: var(--spacing-8);
16
+ border: var(--spacing-2) solid transparent;
17
+ display: flex;
18
+ flex-direction: row;
19
+ justify-content: space-between;
20
+ outline: none;
21
+ gap: var(--spacing-12);
22
+ }
23
+
24
+ .listItem.focused {
25
+ border: var(--spacing-2) solid var(--color-blue-500);
26
+ }
27
+
28
+ .listItem.focused:hover {
29
+ border-color: transparent;
30
+ }
31
+
32
+ .listItem:hover {
33
+ background-color: var(--color-gray-200);
9
34
  }
10
35
 
11
- .listItem:focus-visible {
36
+ .listItem.focused:active {
37
+ background-color: var(--color-gray-300);
38
+ }
39
+
40
+ .startSelection {
41
+ justify-content: flex-start;
42
+ }
43
+
44
+ .selected {
45
+ background-color: var(--color-blue-100);
46
+ border: var(--spacing-2) solid transparent;
47
+ }
48
+
49
+ .selected:hover {
12
50
  background-color: var(--color-blue-200);
13
- outline: none;
14
- border-color: white;
51
+ }
52
+
53
+ .selected:active {
54
+ background-color: var(--color-blue-100);
55
+ }
56
+
57
+ .disabled {
58
+ color: var(--color-gray-500);
59
+ }
60
+
61
+ .disabled:hover {
62
+ background-color: var(--color-white);
63
+ }
64
+
65
+ .iconChecked {
66
+ color: var(--color-blue-500);
67
+ }
68
+
69
+ .itemContent {
70
+ display: flex;
71
+ flex-direction: row;
72
+ overflow: hidden;
73
+ min-width: 0;
74
+ flex: 1;
75
+ }
76
+
77
+ .itemText {
78
+ white-space: nowrap;
79
+ overflow: hidden;
80
+ text-overflow: ellipsis;
81
+ flex: 1;
82
+ min-width: 0;
83
+ }
84
+
85
+ .selectedStartPosition {
86
+ justify-content: flex-start;
87
+ }
88
+
89
+ .selectedIconWrapper {
90
+ display: flex;
91
+ align-items: center;
92
+ }
93
+
94
+ .emptySpacer {
95
+ width: var(--spacing-20);
15
96
  }
16
97
  }
@@ -1,18 +1,74 @@
1
- import React, { type PropsWithChildren } from 'react'
1
+ import React from 'react'
2
2
  import classNames from 'classnames'
3
- import { ListBoxItem as RACListBoxItem, type ListBoxItemProps } from 'react-aria-components'
4
- import { type SelectItem } from '../../types'
3
+ import { useOption } from 'react-aria'
4
+ import { Icon } from '~components/Icon'
5
+ import { Radio } from '~components/Radio'
6
+ import { type ListItemProps, type SelectItem } from '../../types'
5
7
  import styles from './ListItem.module.css'
6
8
 
7
- export const ListItem = ({
8
- children,
9
+ export const ListItem = <T extends SelectItem>({
10
+ item,
11
+ state,
12
+ selectedIcon = 'check',
13
+ selectedPosition = 'end',
9
14
  className,
10
- ...props
11
- }: ListBoxItemProps<SelectItem> & PropsWithChildren): React.ReactElement => {
15
+ }: ListItemProps<T>): JSX.Element => {
16
+ const ref = React.useRef(null)
17
+ const { optionProps, isSelected, isDisabled, isFocused } = useOption(
18
+ { key: item.key },
19
+ state,
20
+ ref,
21
+ )
22
+
23
+ const isStart = selectedPosition === 'start'
24
+ const isEnd = selectedPosition === 'end'
25
+ const isCheck = selectedIcon === 'check'
26
+ const isRadio = selectedIcon === 'radio'
27
+
28
+ const renderSelectionIcon = (): JSX.Element | null => {
29
+ if (isCheck) {
30
+ if (isSelected) {
31
+ return (
32
+ <div className={styles.selectedIconWrapper}>
33
+ <Icon name="check" isPresentational className={styles.iconChecked} />
34
+ </div>
35
+ )
36
+ }
37
+
38
+ return isStart ? <div className={styles.emptySpacer} /> : null
39
+ }
40
+
41
+ if (isRadio) {
42
+ return (
43
+ <div className={styles.selectedIconWrapper}>
44
+ <Radio id={item.textValue} name={item.textValue} selectedStatus={isSelected} />
45
+ </div>
46
+ )
47
+ }
48
+
49
+ return null
50
+ }
51
+
12
52
  return (
13
- <RACListBoxItem className={classNames(styles.listItem, className)} {...props}>
14
- {children}
15
- </RACListBoxItem>
53
+ <li
54
+ key={item.key}
55
+ {...optionProps}
56
+ ref={ref}
57
+ className={classNames(styles.listItem, {
58
+ [styles.focused]: isFocused,
59
+ [styles.disabled]: isDisabled,
60
+ [styles.selected]: isSelected,
61
+ [styles.selectedStartPosition]: isStart,
62
+ })}
63
+ >
64
+ {isStart && renderSelectionIcon()}
65
+ {typeof item.rendered === 'string' ? (
66
+ <span className={classNames(styles.itemText, className)}>{item.rendered}</span>
67
+ ) : (
68
+ <span className={classNames(styles.itemContent, className)}>{item.rendered}</span>
69
+ )}
70
+
71
+ {isEnd && renderSelectionIcon()}
72
+ </li>
16
73
  )
17
74
  }
18
- ListItem.displayName = 'SingleSelect.ListItem'
@@ -1,9 +1,24 @@
1
1
  @layer kz-components {
2
+ .sectionWrapper:not(.firstSectionHeader) {
3
+ padding-top: var(--spacing-16);
4
+ }
5
+
6
+ .firstSectionHeader {
7
+ padding-top: var(--spacing-8);
8
+ }
9
+
2
10
  .listSectionHeader {
3
- font-family: var(--typography-heading-5-font-family);
4
- font-weight: var(--typography-heading-5-font-weight);
5
- font-size: var(--typography-heading-5-font-size);
6
- line-height: var(--typography-heading-5-line-height);
7
- letter-spacing: var(--typography-heading-5-letter-spacing);
11
+ font-family: var(--typography-paragraph-small-font-family);
12
+ font-weight: var(--typography-paragraph-bold-font-weight);
13
+ font-size: var(--typography-paragraph-small-font-size);
14
+ line-height: var(--typography-paragraph-small-line-height);
15
+ letter-spacing: var(--typography-paragraph-small-letter-spacing);
16
+ padding-bottom: var(--spacing-4);
17
+ padding-left: var(--spacing-16);
18
+ }
19
+
20
+ .listSectionGroup {
21
+ padding: 0;
22
+ list-style: none;
8
23
  }
9
24
  }
@@ -1,25 +1,52 @@
1
- import React, { type PropsWithChildren } from 'react'
1
+ import React from 'react'
2
2
  import classNames from 'classnames'
3
- import {
4
- Header as RACHeader,
5
- ListBoxSection as RACListBoxSection,
6
- type ListBoxSectionProps,
7
- } from 'react-aria-components'
8
- import { type SelectSection } from '../../types'
3
+ import { useListBoxSection } from 'react-aria'
4
+ import { Divider } from '~components/Divider'
5
+ import { type ListSectionProps, type SelectItem } from '../../types'
6
+ import { ListItem } from '../ListItem'
9
7
  import styles from './ListSection.module.css'
10
8
 
11
- export const ListSection = ({
12
- name,
13
- className,
14
- children,
15
- ...props
16
- }: ListBoxSectionProps<SelectSection> &
17
- PropsWithChildren & { name: string }): React.ReactElement => {
9
+ export const ListSection = <T extends SelectItem>({
10
+ section,
11
+ state,
12
+ }: ListSectionProps<T>): JSX.Element => {
13
+ const { headingProps, itemProps, groupProps } = useListBoxSection({
14
+ 'heading': section.rendered,
15
+ 'aria-label': section['aria-label'],
16
+ })
17
+
18
+ const firstSectionHeader = section.key === state.collection.getFirstKey()
19
+
18
20
  return (
19
- <RACListBoxSection {...props}>
20
- <RACHeader className={classNames(styles.listSectionHeader, className)}>{name}</RACHeader>
21
- {children}
22
- </RACListBoxSection>
21
+ <React.Fragment key={section.key}>
22
+ {!firstSectionHeader && <li role="presentation" aria-hidden />}
23
+ {!firstSectionHeader && <Divider variant="content" />}
24
+ <li
25
+ {...itemProps}
26
+ className={classNames(styles.sectionWrapper, {
27
+ [styles.firstSectionHeader]: firstSectionHeader,
28
+ })}
29
+ >
30
+ {section.rendered && (
31
+ <span
32
+ {...headingProps}
33
+ role="presentation"
34
+ aria-hidden
35
+ className={styles.listSectionHeader}
36
+ >
37
+ {section.rendered}
38
+ </span>
39
+ )}
40
+ <ul
41
+ key={`${section.key}-group-contents`}
42
+ {...groupProps}
43
+ className={styles.listSectionGroup}
44
+ >
45
+ {Array.from(section.childNodes).map((node) => (
46
+ <ListItem key={node.key} item={node} state={state} />
47
+ ))}
48
+ </ul>
49
+ </li>
50
+ </React.Fragment>
23
51
  )
24
52
  }
25
- ListSection.displayName = 'SingleSelect.ListSection'
@@ -1,17 +1,14 @@
1
1
  @layer kz-components {
2
2
  .popover {
3
3
  position: absolute;
4
- height: auto;
5
4
  background-color: var(--color-white);
6
5
  border-radius: var(--spacing-8);
7
6
  padding: 0;
8
7
  box-shadow: var(--shadow-small-box-shadow);
9
- overflow: hidden auto;
10
8
  margin: 0;
11
9
  box-sizing: border-box;
12
-
13
- /* TODO: update width based on design */
14
- width: 200px;
10
+ display: flex;
11
+ flex-direction: column;
15
12
 
16
13
  @supports (anchor-name: --anchor) {
17
14
  position-anchor: var(--position-anchor);
@@ -21,4 +18,9 @@
21
18
  width: anchor-size(width);
22
19
  }
23
20
  }
21
+
22
+ .offsetSpacing {
23
+ margin-top: var(--spacing-4);
24
+ margin-bottom: var(--spacing-4);
25
+ }
24
26
  }
@@ -1,54 +1,107 @@
1
- import React, { useLayoutEffect, useMemo, type PropsWithChildren } from 'react'
2
-
3
- import { Popover as RACPopover } from 'react-aria-components'
1
+ import React, { useLayoutEffect } from 'react'
2
+ import classNames from 'classnames'
3
+ import { DismissButton, Overlay, usePopover } from 'react-aria'
4
4
  import { useSingleSelectContext } from '../../context'
5
- import { type PopoverProps } from '../../types'
6
- import { usePositioningStyles } from './utils/usePositioningStyles'
5
+ import { type PopoverProps, type SelectItem } from '../../types'
6
+ import { usePositioningStyles, useSupportsAnchorPositioning } from './utils'
7
+
7
8
  import styles from './Popover.module.css'
8
9
 
9
- export const Popover = ({
10
- buttonRef,
10
+ export const Popover = <T extends SelectItem>({
11
+ state,
11
12
  popoverRef,
12
- racPopoverRef,
13
13
  children,
14
- }: PopoverProps & PropsWithChildren): React.ReactElement => {
15
- const { isOpen, setOpen, anchorName } = useSingleSelectContext()
14
+ clearButtonRef,
15
+ ...restProps
16
+ }: PopoverProps<T>): React.ReactElement => {
17
+ const { anchorName } = useSingleSelectContext()
18
+ const manualPopoverRef = React.useRef<HTMLDivElement>(null)
16
19
 
17
- const { popoverStyle, isPositioned } = usePositioningStyles(buttonRef, popoverRef, anchorName)
20
+ const { popoverProps } = usePopover(
21
+ {
22
+ ...restProps,
23
+ popoverRef,
24
+ shouldCloseOnInteractOutside: (element) => {
25
+ if (clearButtonRef?.current?.contains(element)) {
26
+ return false
27
+ }
28
+ return true
29
+ },
30
+ },
31
+ state,
32
+ )
18
33
 
19
- const shouldShowPopover = useMemo(() => isOpen && isPositioned, [isOpen, isPositioned])
34
+ const supportsAnchorPositioning = useSupportsAnchorPositioning()
35
+ const { popoverStyle, isPositioned, updatePosition } = usePositioningStyles(
36
+ restProps.triggerRef as React.RefObject<HTMLElement>,
37
+ manualPopoverRef,
38
+ anchorName,
39
+ )
20
40
 
21
41
  useLayoutEffect(() => {
22
- const popover = popoverRef.current
23
- if (!popover?.showPopover || !popover?.hidePopover) return
42
+ if (!supportsAnchorPositioning || !state.isOpen) return
43
+
44
+ updatePosition()
24
45
 
25
- if (shouldShowPopover) {
46
+ const popover = manualPopoverRef?.current
47
+
48
+ if (popover?.showPopover) {
26
49
  popover.showPopover()
27
- } else {
50
+ }
51
+
52
+ return () => {
53
+ if (popover?.hidePopover) {
54
+ popover.hidePopover()
55
+ }
56
+ }
57
+ }, [state.isOpen, supportsAnchorPositioning, updatePosition, isPositioned])
58
+
59
+ useLayoutEffect(() => {
60
+ if (!supportsAnchorPositioning || state.isOpen) return
61
+
62
+ const popover = manualPopoverRef?.current
63
+
64
+ if (popover?.hidePopover) {
28
65
  popover.hidePopover()
29
66
  }
30
- // eslint-disable-next-line react-hooks/exhaustive-deps
31
- }, [shouldShowPopover])
67
+ }, [state.isOpen, supportsAnchorPositioning])
32
68
 
33
- return (
34
- <RACPopover
35
- shouldUpdatePosition={false}
36
- trigger="manual"
37
- isOpen={isOpen}
38
- onOpenChange={setOpen}
39
- ref={racPopoverRef}
69
+ const manualPopover = (
70
+ <div
71
+ // @ts-expect-error - popover attribute is not included in current ts version, ignore type error
72
+ popover="manual"
73
+ ref={manualPopoverRef}
74
+ className={styles.popover}
75
+ style={popoverStyle}
40
76
  >
41
- <div
42
- // @ts-expect-error - popover attribute is not included in current ts version, ignore type error
43
- popover="manual"
44
- ref={popoverRef}
45
- className={styles.popover}
46
- style={popoverStyle}
47
- >
48
- {children}
49
- </div>
50
- </RACPopover>
77
+ {children}
78
+ </div>
51
79
  )
52
- }
53
80
 
54
- Popover.displayName = 'SingleSelect.Popover'
81
+ return (
82
+ <>
83
+ {state.isOpen && (
84
+ <Overlay>
85
+ <div
86
+ id="popover-id"
87
+ {...popoverProps}
88
+ ref={popoverRef}
89
+ style={{
90
+ ...popoverProps.style,
91
+ ...(!supportsAnchorPositioning && {
92
+ width: restProps.triggerRef.current?.getBoundingClientRect().width,
93
+ }),
94
+ }}
95
+ className={classNames(styles.popover, {
96
+ [styles.offsetSpacing]: !supportsAnchorPositioning,
97
+ })}
98
+ >
99
+ <DismissButton onDismiss={state.close} />
100
+ {supportsAnchorPositioning ? manualPopover : children}
101
+ <DismissButton onDismiss={state.close} />
102
+ </div>
103
+ </Overlay>
104
+ )}
105
+ </>
106
+ )
107
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './usePopoverPositioning'
2
2
  export * from './useSupportsAnchorPositioning'
3
+ export * from './usePositioningStyles'
@@ -11,7 +11,7 @@ export function usePopoverPositioning({
11
11
  direction = 'ltr',
12
12
  offset = 4,
13
13
  preferredPlacement = 'bottom',
14
- }: UsePopoverPositioningProps): Position & { isPositioned: boolean } {
14
+ }: UsePopoverPositioningProps): Position & { isPositioned: boolean; updatePosition: () => void } {
15
15
  const [position, setPosition] = useState<Position>({
16
16
  top: preferredPlacement === 'bottom' ? offset : 'auto',
17
17
  bottom: preferredPlacement === 'top' ? offset : 'auto',
@@ -104,5 +104,5 @@ export function usePopoverPositioning({
104
104
  }
105
105
  }, [updatePosition, triggerRef])
106
106
 
107
- return { ...position, isPositioned }
107
+ return { ...position, isPositioned, updatePosition }
108
108
  }
@@ -42,16 +42,17 @@ export const usePositioningStyles = (
42
42
  buttonRef: React.RefObject<HTMLElement>,
43
43
  popoverRef: React.RefObject<HTMLDivElement>,
44
44
  anchorName: string,
45
- ): { popoverStyle: React.CSSProperties; isPositioned: boolean } => {
45
+ ): { popoverStyle: React.CSSProperties; isPositioned: boolean; updatePosition: () => void } => {
46
46
  const { direction } = useLocale()
47
47
  const hasAnchorSupport = useSupportsAnchorPositioning()
48
48
 
49
- const { top, bottom, insetInlineStart, maxHeight, isPositioned } = usePopoverPositioning({
50
- triggerRef: buttonRef,
51
- popoverRef,
52
- direction,
53
- preferredPlacement: 'bottom',
54
- })
49
+ const { top, bottom, insetInlineStart, maxHeight, isPositioned, updatePosition } =
50
+ usePopoverPositioning({
51
+ triggerRef: buttonRef,
52
+ popoverRef,
53
+ direction,
54
+ preferredPlacement: 'bottom',
55
+ })
55
56
 
56
57
  const positionData = useMemo(
57
58
  () => ({
@@ -71,5 +72,5 @@ export const usePositioningStyles = (
71
72
  return getAnchorPositioningStyles(anchorName, positionData)
72
73
  }, [hasAnchorSupport, anchorName, positionData])
73
74
 
74
- return { popoverStyle, isPositioned }
75
+ return { popoverStyle, isPositioned, updatePosition }
75
76
  }
@@ -0,0 +1,35 @@
1
+ @layer kz-components {
2
+ .topLabel {
3
+ display: flex;
4
+ flex-direction: column;
5
+ align-items: flex-start;
6
+ }
7
+
8
+ .sideLabel {
9
+ display: grid;
10
+ grid-template-columns: auto 1fr;
11
+ grid-template-rows: auto auto;
12
+ column-gap: var(--spacing-12);
13
+ align-items: center;
14
+ }
15
+
16
+ .label {
17
+ grid-column: 1;
18
+ grid-row: 1;
19
+ }
20
+
21
+ .labelTop {
22
+ margin-bottom: var(--spacing-6);
23
+ }
24
+
25
+ .selectTrigger {
26
+ grid-column: 2;
27
+ grid-row: 1;
28
+ width: 100%;
29
+ }
30
+
31
+ .description {
32
+ grid-column: 2;
33
+ grid-row: 2;
34
+ }
35
+ }
@@ -0,0 +1,84 @@
1
+ import React, { useId, useRef } from 'react'
2
+ import { useSelectState } from '@react-stately/select'
3
+ import classNames from 'classnames'
4
+ import { useSelect } from 'react-aria'
5
+ import { FieldMessage, Label } from '~components/index'
6
+ import { SingleSelectContext } from '../../context'
7
+ import { type SelectItem, type SelectProps } from '../../types'
8
+ import { List } from '../List'
9
+ import { Popover } from '../Popover'
10
+ import { SelectTrigger } from '../SelectTrigger'
11
+ import styles from './Select.module.css'
12
+
13
+ export const Select = <T extends SelectItem>(props: SelectProps<T>): JSX.Element => {
14
+ const {
15
+ label,
16
+ description,
17
+ labelHidden,
18
+ labelPosition = 'top',
19
+ isDisabled,
20
+ isReadOnly,
21
+ size = 'medium',
22
+ variant = 'primary',
23
+ } = props
24
+
25
+ const state = useSelectState({
26
+ ...props,
27
+ items: props.items,
28
+ children: props.children,
29
+ })
30
+
31
+ const popoverRef = useRef<HTMLDivElement>(null)
32
+ const listBoxRef = useRef<HTMLUListElement>(null)
33
+ const triggerRef = useRef<HTMLButtonElement>(null)
34
+
35
+ const { labelProps, descriptionProps, menuProps, triggerProps, valueProps } = useSelect(
36
+ {
37
+ ...props,
38
+ 'aria-label': labelHidden ? label : undefined,
39
+ },
40
+ state,
41
+ triggerRef,
42
+ )
43
+ const uniqueId = useId()
44
+ const anchorName = `--trigger-${uniqueId}`
45
+
46
+ return (
47
+ <SingleSelectContext.Provider
48
+ value={{
49
+ anchorName,
50
+ state,
51
+ isComboBox: false,
52
+ isDisabled: isDisabled ?? false,
53
+ isReadOnly: isReadOnly ?? false,
54
+ secondary: variant === 'secondary',
55
+ size,
56
+ fieldLabel: label,
57
+ }}
58
+ >
59
+ <div className={labelPosition === 'top' ? styles.topLabel : styles.sideLabel}>
60
+ {!labelHidden && (
61
+ <div className={classNames(styles.label, { [styles.labelTop]: labelPosition === 'top' })}>
62
+ <Label {...labelProps}>{label}</Label>
63
+ </div>
64
+ )}
65
+ <div className={styles.selectTrigger}>
66
+ <SelectTrigger
67
+ triggerProps={triggerProps}
68
+ valueProps={valueProps}
69
+ buttonRef={triggerRef}
70
+ />
71
+ </div>
72
+ {description && (
73
+ <div className={styles.description}>
74
+ <FieldMessage message={description} {...descriptionProps} />
75
+ </div>
76
+ )}
77
+ </div>
78
+
79
+ <Popover state={state} triggerRef={triggerRef} popoverRef={popoverRef}>
80
+ <List listBoxOptions={menuProps} state={state} listBoxRef={listBoxRef} />
81
+ </Popover>
82
+ </SingleSelectContext.Provider>
83
+ )
84
+ }
@@ -0,0 +1 @@
1
+ export * from './Select'