@kaizen/components 2.0.0 → 2.0.2

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 (107) 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/Notification/InlineNotification/InlineNotification.cjs +1 -1
  5. package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +35 -74
  6. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.cjs +105 -0
  7. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.cjs +11 -0
  8. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.cjs +112 -0
  9. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.cjs +16 -0
  10. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/List/List.cjs +35 -10
  11. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.cjs +61 -8
  12. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.cjs +10 -1
  13. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.cjs +38 -9
  14. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.cjs +4 -1
  15. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +60 -30
  16. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.cjs +2 -1
  17. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +2 -1
  18. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +4 -2
  19. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.cjs +87 -0
  20. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.cjs +11 -0
  21. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.cjs +52 -0
  22. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.cjs +13 -0
  23. package/dist/esm/alpha.mjs +1 -1
  24. package/dist/esm/src/Notification/InlineNotification/InlineNotification.mjs +1 -1
  25. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +39 -73
  26. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.mjs +96 -0
  27. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.mjs +9 -0
  28. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.mjs +103 -0
  29. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.mjs +14 -0
  30. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/List/List.mjs +37 -14
  31. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.mjs +63 -13
  32. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.mjs +10 -1
  33. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.mjs +41 -15
  34. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.mjs +4 -1
  35. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +69 -43
  36. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +2 -1
  37. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +2 -1
  38. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +4 -2
  39. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.mjs +78 -0
  40. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.mjs +9 -0
  41. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.mjs +43 -0
  42. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.mjs +11 -0
  43. package/dist/styles.css +443 -79
  44. package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +14 -19
  45. package/dist/types/__alpha__/SingleSelect/_docs/mockData.d.ts +3 -0
  46. package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +15 -7
  47. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.d.ts +2 -0
  48. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/index.d.ts +1 -0
  49. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.d.ts +2 -0
  50. package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.d.ts +1 -0
  51. package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -7
  52. package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -7
  53. package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -9
  54. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +3 -6
  55. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +1 -0
  56. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +1 -0
  57. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +1 -0
  58. package/dist/types/__alpha__/SingleSelect/subcomponents/Select/Select.d.ts +2 -0
  59. package/dist/types/__alpha__/SingleSelect/subcomponents/Select/index.d.ts +1 -0
  60. package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.d.ts +2 -0
  61. package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.d.ts +1 -0
  62. package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +4 -1
  63. package/dist/types/__alpha__/SingleSelect/types.d.ts +68 -11
  64. package/locales/en.json +9 -1
  65. package/package.json +9 -2
  66. package/src/Notification/InlineNotification/InlineNotification.tsx +1 -1
  67. package/src/__alpha__/SingleSelect/SingleSelect.tsx +35 -88
  68. package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +96 -6
  69. package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +22 -24
  70. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +389 -33
  71. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +41 -22
  72. package/src/__alpha__/SingleSelect/_docs/mockData.ts +20 -14
  73. package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +18 -7
  74. package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css +35 -0
  75. package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.tsx +106 -0
  76. package/src/__alpha__/SingleSelect/subcomponents/ComboBox/index.ts +1 -0
  77. package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css +130 -0
  78. package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.tsx +121 -0
  79. package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.ts +1 -0
  80. package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +5 -0
  81. package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +36 -13
  82. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +84 -3
  83. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +67 -11
  84. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css +20 -5
  85. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +46 -19
  86. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +7 -5
  87. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +90 -37
  88. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +1 -0
  89. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +2 -2
  90. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +9 -8
  91. package/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css +35 -0
  92. package/src/__alpha__/SingleSelect/subcomponents/Select/Select.tsx +84 -0
  93. package/src/__alpha__/SingleSelect/subcomponents/Select/index.ts +1 -0
  94. package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css +77 -0
  95. package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.tsx +52 -0
  96. package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.ts +1 -0
  97. package/src/__alpha__/SingleSelect/subcomponents/index.ts +4 -1
  98. package/src/__alpha__/SingleSelect/types.ts +94 -14
  99. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +0 -57
  100. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.cjs +0 -6
  101. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +0 -49
  102. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.mjs +0 -4
  103. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +0 -2
  104. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/index.d.ts +0 -1
  105. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +0 -19
  106. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +0 -35
  107. package/src/__alpha__/SingleSelect/subcomponents/Trigger/index.ts +0 -1
@@ -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'
@@ -0,0 +1,77 @@
1
+ @layer kz-components {
2
+ .trigger {
3
+ anchor-name: var(--anchor-name);
4
+ display: flex;
5
+ align-items: center;
6
+ justify-content: space-between;
7
+ font-family: var(--typography-paragraph-body-font-family);
8
+ font-weight: var(--typography-paragraph-body-font-weight);
9
+ font-size: var(--typography-paragraph-body-font-size);
10
+ line-height: var(--typography-paragraph-body-line-height);
11
+ letter-spacing: var(--typography-paragraph-body-letter-spacing);
12
+ padding: var(--spacing-12);
13
+ height: var(--spacing-40);
14
+ min-width: var(--spacing-200);
15
+ width: 100%;
16
+ background-color: var(--color-white);
17
+ border-radius: var(--spacing-8);
18
+ border: var(--spacing-2) solid var(--color-gray-500);
19
+ }
20
+
21
+ .trigger:focus-visible {
22
+ outline-offset: var(--spacing-2);
23
+ outline: var(--spacing-2) solid var(--color-blue-500);
24
+ background-color: var(--color-white);
25
+ }
26
+
27
+ .trigger:hover:not(.disabled) {
28
+ border-color: var(--color-gray-600);
29
+ background-color: var(--color-gray-200);
30
+ }
31
+
32
+ .secondary:not(.disabled, .readOnly) {
33
+ border: 2px solid transparent;
34
+ border-bottom: var(--spacing-2) solid var(--color-gray-400);
35
+ border-radius: 0.4375rem 0.4375rem 0 0;
36
+ }
37
+
38
+ .secondary:not(.disabled, .readOnly):hover {
39
+ border: var(--spacing-2) solid var(--color-gray-600);
40
+ border-radius: 0.4375rem;
41
+ }
42
+
43
+ .secondary:not(.disabled, .readOnly):focus-within {
44
+ border: var(--spacing-2) solid var(--color-gray-500);
45
+ border-radius: 0.4375rem;
46
+ }
47
+
48
+ .disabled {
49
+ pointer-events: none;
50
+ border: var(--spacing-2) solid var(--color-gray-300);
51
+ background-color: var(--color-gray-100);
52
+ }
53
+
54
+ .disabledText {
55
+ color: var(--color-gray-500);
56
+ }
57
+
58
+ .readOnly {
59
+ pointer-events: none;
60
+ background-color: var(--color-gray-200);
61
+ border: var(--spacing-2) solid var(--color-gray-200);
62
+ }
63
+
64
+ .small {
65
+ height: var(--spacing-32);
66
+ }
67
+
68
+ .large {
69
+ height: var(--spacing-48);
70
+ }
71
+
72
+ .chevronButton {
73
+ background: none;
74
+ padding: 0;
75
+ display: flex;
76
+ }
77
+ }
@@ -0,0 +1,52 @@
1
+ import React from 'react'
2
+ import classNames from 'classnames'
3
+ import { useButton } from 'react-aria'
4
+ import { Icon } from '~components/Icon'
5
+ import { Text } from '~components/Text'
6
+ import { useSingleSelectContext } from '../../context'
7
+ import { type SelectTriggerProps } from '../../types'
8
+ import styles from './SelectTrigger.module.css'
9
+
10
+ const ChevronButton = (): JSX.Element => {
11
+ const { state } = useSingleSelectContext()
12
+
13
+ return (
14
+ <div className={styles.chevronButton}>
15
+ <Icon isPresentational name={state.isOpen ? 'keyboard_arrow_up' : 'keyboard_arrow_down'} />
16
+ </div>
17
+ )
18
+ }
19
+
20
+ export const SelectTrigger = ({
21
+ triggerProps,
22
+ valueProps,
23
+ buttonRef,
24
+ }: SelectTriggerProps): JSX.Element => {
25
+ const { state, anchorName, isDisabled, isReadOnly, secondary, size } = useSingleSelectContext()
26
+ const { buttonProps } = useButton(triggerProps, buttonRef)
27
+
28
+ return (
29
+ <button
30
+ {...buttonProps}
31
+ type="button"
32
+ style={{ '--anchor-name': anchorName } as React.CSSProperties}
33
+ className={classNames(styles.trigger, {
34
+ [styles.disabled]: isDisabled,
35
+ [styles.readOnly]: isReadOnly,
36
+ [styles.secondary]: secondary,
37
+ [styles.small]: size === 'small',
38
+ [styles.large]: size === 'large',
39
+ })}
40
+ ref={buttonRef}
41
+ >
42
+ <Text
43
+ variant={size === 'small' ? 'small' : 'body'}
44
+ classNameOverride={isDisabled ? styles.disabledText : undefined}
45
+ >
46
+ <span {...valueProps}>{state.selectedItem ? state.selectedItem.textValue : ''}</span>
47
+ </Text>
48
+
49
+ {!isReadOnly && <ChevronButton />}
50
+ </button>
51
+ )
52
+ }
@@ -0,0 +1 @@
1
+ export * from './SelectTrigger'
@@ -1,5 +1,8 @@
1
1
  export * from './List'
2
2
  export * from './ListSection'
3
3
  export * from './ListItem'
4
- export * from './Trigger'
4
+ export * from './SelectTrigger'
5
+ export * from './ComboBoxTrigger'
5
6
  export * from './Popover'
7
+ export * from './Select'
8
+ export * from './ComboBox'