@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
@@ -0,0 +1,106 @@
1
+ import React, { useId, useRef } from 'react'
2
+ import { useComboBoxState } from '@react-stately/combobox'
3
+ import classNames from 'classnames'
4
+ import { useComboBox, useFilter } from 'react-aria'
5
+ import { FieldMessage } from '~components/FieldMessage'
6
+ import { Label } from '~components/Label'
7
+ import { SingleSelectContext } from '../../context'
8
+ import { type ComboBoxProps, type SelectItem } from '../../types'
9
+ import { ComboBoxTrigger } from '../ComboBoxTrigger'
10
+ import { List } from '../List'
11
+ import { Popover } from '../Popover'
12
+ import styles from './ComboBox.module.css'
13
+
14
+ export const ComboBox = <T extends SelectItem>(props: ComboBoxProps<T>): JSX.Element => {
15
+ const {
16
+ items,
17
+ children,
18
+ label,
19
+ description,
20
+ labelHidden,
21
+ labelPosition = 'top',
22
+ isReadOnly,
23
+ isDisabled,
24
+ size = 'medium',
25
+ variant = 'primary',
26
+ } = props
27
+
28
+ const { contains } = useFilter({ sensitivity: 'base' })
29
+
30
+ const state = useComboBoxState({
31
+ ...props,
32
+ items: items,
33
+ defaultFilter: contains,
34
+ children: children,
35
+ menuTrigger: 'focus',
36
+ })
37
+
38
+ const inputRef = useRef<HTMLInputElement>(null)
39
+ const popoverRef = useRef<HTMLDivElement>(null)
40
+ const listBoxRef = useRef<HTMLUListElement>(null)
41
+ const buttonRef = useRef<HTMLButtonElement>(null)
42
+ const clearButtonRef = useRef<HTMLButtonElement>(null)
43
+ const triggerWrapperRef = useRef<HTMLDivElement>(null)
44
+
45
+ const { labelProps, descriptionProps, inputProps, listBoxProps, buttonProps } = useComboBox(
46
+ {
47
+ ...props,
48
+ 'aria-label': labelHidden ? label : undefined,
49
+ inputRef,
50
+ buttonRef,
51
+ popoverRef,
52
+ listBoxRef,
53
+ },
54
+ state,
55
+ )
56
+ const uniqueId = useId()
57
+ const anchorName = `--trigger-${uniqueId}`
58
+
59
+ return (
60
+ <SingleSelectContext.Provider
61
+ value={{
62
+ anchorName,
63
+ state,
64
+ isComboBox: true,
65
+ isDisabled: isDisabled ?? false,
66
+ isReadOnly: isReadOnly ?? false,
67
+ secondary: variant === 'secondary',
68
+ size,
69
+ fieldLabel: label,
70
+ }}
71
+ >
72
+ <div className={labelPosition === 'top' ? styles.topLabel : styles.sideLabel}>
73
+ {!labelHidden && (
74
+ <div className={classNames(styles.label, { [styles.labelTop]: labelPosition === 'top' })}>
75
+ <Label {...labelProps}>{label}</Label>
76
+ </div>
77
+ )}
78
+ <div className={styles.comboBoxTrigger}>
79
+ <ComboBoxTrigger
80
+ inputProps={inputProps}
81
+ inputRef={inputRef}
82
+ buttonRef={buttonRef}
83
+ buttonProps={buttonProps}
84
+ clearButtonRef={clearButtonRef}
85
+ triggerWrapperRef={triggerWrapperRef}
86
+ />
87
+ </div>
88
+
89
+ {description && (
90
+ <div className={styles.description}>
91
+ <FieldMessage message={description} {...descriptionProps} />
92
+ </div>
93
+ )}
94
+ </div>
95
+
96
+ <Popover
97
+ state={state}
98
+ triggerRef={triggerWrapperRef}
99
+ popoverRef={popoverRef}
100
+ clearButtonRef={clearButtonRef}
101
+ >
102
+ <List listBoxOptions={listBoxProps} state={state} listBoxRef={listBoxRef} />
103
+ </Popover>
104
+ </SingleSelectContext.Provider>
105
+ )
106
+ }
@@ -0,0 +1 @@
1
+ export * from './ComboBox'
@@ -0,0 +1,130 @@
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
+ height: var(--spacing-40);
13
+ min-width: var(--spacing-200);
14
+ width: 100%;
15
+ background-color: var(--color-white);
16
+ border-radius: 0.4375rem;
17
+ border: var(--spacing-2) solid var(--color-gray-500);
18
+ box-sizing: border-box;
19
+ }
20
+
21
+ .smallText {
22
+ font-family: var(--typography-paragraph-small-font-family);
23
+ font-weight: var(--typography-paragraph-small-font-weight);
24
+ font-size: var(--typography-paragraph-small-font-size);
25
+ line-height: var(--typography-paragraph-small-line-height);
26
+ letter-spacing: var(--typography-paragraph-small-letter-spacing);
27
+ }
28
+
29
+ .secondary:not(.disabled, .readOnly) {
30
+ border-radius: 0.4375rem 0.4375rem 0 0;
31
+ border: 2px solid transparent;
32
+ border-bottom: var(--spacing-2) solid var(--color-gray-400);
33
+ }
34
+
35
+ .secondary:not(.disabled, .readOnly):hover {
36
+ border: var(--spacing-2) solid var(--color-gray-600);
37
+ border-radius: 0.4375rem;
38
+ }
39
+
40
+ .secondary:not(.disabled, .readOnly):focus-within {
41
+ border: var(--spacing-2) solid var(--color-gray-600);
42
+ border-radius: 0.4375rem;
43
+ }
44
+
45
+ .input {
46
+ outline: none;
47
+ text-overflow: ellipsis;
48
+ background-color: var(--color-white);
49
+ padding: var(--spacing-8) 0;
50
+ margin-inline-start: var(--spacing-16);
51
+ height: 2.25rem;
52
+ box-sizing: border-box;
53
+ width: inherit;
54
+ }
55
+
56
+ .small .input {
57
+ height: 1.75rem;
58
+ }
59
+
60
+ .disabled {
61
+ pointer-events: none;
62
+ border: var(--spacing-2) solid var(--color-gray-300);
63
+ background-color: var(--color-gray-100);
64
+
65
+ .input {
66
+ background-color: var(--color-gray-100);
67
+ color: var(--color-gray-500);
68
+ }
69
+ }
70
+
71
+ .readOnly {
72
+ pointer-events: none;
73
+ background-color: var(--color-gray-200);
74
+ border: var(--spacing-2) solid var(--color-gray-200);
75
+
76
+ .input {
77
+ background-color: var(--color-gray-200);
78
+ }
79
+ }
80
+
81
+ .trigger:focus-within {
82
+ outline-offset: var(--spacing-2);
83
+ outline: var(--spacing-2) solid var(--color-blue-500);
84
+ background-color: var(--color-white);
85
+ border-radius: 0.4375rem;
86
+
87
+ .input {
88
+ background-color: var(--color-white);
89
+ }
90
+ }
91
+
92
+ .trigger:hover:not(:focus-within) {
93
+ border-color: var(--color-gray-600);
94
+ background-color: var(--color-gray-200);
95
+
96
+ .input {
97
+ background-color: var(--color-gray-200);
98
+ }
99
+ }
100
+
101
+ .small {
102
+ height: var(--spacing-32);
103
+ }
104
+
105
+ .large {
106
+ height: var(--spacing-48);
107
+ }
108
+
109
+ .clearButton {
110
+ background: none;
111
+ padding: 0;
112
+ display: flex;
113
+ }
114
+
115
+ .clearButton:focus-visible {
116
+ outline: var(--spacing-2) solid var(--color-blue-500);
117
+ border-radius: 50%;
118
+ }
119
+
120
+ .chevronButton {
121
+ background: none;
122
+ padding-block: var(--spacing-8);
123
+ padding-inline: var(--spacing-6) var(--spacing-16);
124
+ display: flex;
125
+ }
126
+
127
+ .hidden {
128
+ visibility: hidden;
129
+ }
130
+ }
@@ -0,0 +1,121 @@
1
+ import React from 'react'
2
+ import { useIntl } from '@cultureamp/i18n-react-intl'
3
+ import classNames from 'classnames'
4
+ import { useButton } from 'react-aria'
5
+ import { Icon } from '~components/Icon'
6
+ import { VisuallyHidden } from '~components/VisuallyHidden'
7
+ import { useSingleSelectContext } from '../../context'
8
+ import {
9
+ type ChevronButtonProps,
10
+ type ClearButtonProps,
11
+ type ComboBoxTriggerProps,
12
+ } from '../../types'
13
+ import styles from './ComboBoxTrigger.module.css'
14
+
15
+ const ClearButton = ({ clearButtonRef, inputRef }: ClearButtonProps): JSX.Element => {
16
+ const { state, isComboBox, fieldLabel } = useSingleSelectContext()
17
+
18
+ const { formatMessage } = useIntl()
19
+
20
+ const clearButtonAlt = formatMessage(
21
+ {
22
+ id: 'singleSelect.clearButtonAlt',
23
+ defaultMessage: 'Clear {field} selection',
24
+ description: 'Alt text for the clear selection button',
25
+ },
26
+ { field: fieldLabel },
27
+ )
28
+
29
+ const { buttonProps } = useButton(
30
+ {
31
+ onPress: () => {
32
+ if (isComboBox) {
33
+ state.setSelectedKey(null)
34
+ }
35
+ inputRef.current?.focus()
36
+ },
37
+ },
38
+ clearButtonRef,
39
+ )
40
+
41
+ return (
42
+ <button
43
+ {...buttonProps}
44
+ ref={clearButtonRef}
45
+ type="button"
46
+ className={classNames(styles.clearButton, { [styles.hidden]: !state.selectedKey })}
47
+ tabIndex={0}
48
+ >
49
+ <Icon name="cancel" isPresentational isFilled />
50
+ <VisuallyHidden>{clearButtonAlt}</VisuallyHidden>
51
+ </button>
52
+ )
53
+ }
54
+
55
+ const ChevronButton = (props: ChevronButtonProps): JSX.Element => {
56
+ const { state, fieldLabel } = useSingleSelectContext()
57
+ const { formatMessage } = useIntl()
58
+
59
+ const chevronButton = formatMessage(
60
+ {
61
+ id: 'singleSelect.chevronButton',
62
+ defaultMessage: 'Show suggestions for {field}',
63
+ description: 'Aria label text for the SingleSelect button to open and close suggestions list',
64
+ },
65
+ { field: fieldLabel },
66
+ )
67
+
68
+ const { buttonProps } = useButton(
69
+ { ...props, 'aria-label': String(chevronButton), 'aria-labelledby': undefined },
70
+ props.buttonRef,
71
+ )
72
+
73
+ return (
74
+ <button
75
+ type="button"
76
+ {...buttonProps}
77
+ ref={props.buttonRef}
78
+ className={styles.chevronButton}
79
+ tabIndex={-1}
80
+ >
81
+ <Icon isPresentational name={state.isOpen ? 'keyboard_arrow_up' : 'keyboard_arrow_down'} />
82
+ </button>
83
+ )
84
+ }
85
+
86
+ export const ComboBoxTrigger = ({
87
+ inputProps,
88
+ inputRef,
89
+ buttonProps,
90
+ buttonRef,
91
+ triggerWrapperRef,
92
+ clearButtonRef,
93
+ }: ComboBoxTriggerProps): JSX.Element => {
94
+ const { anchorName, isDisabled, isReadOnly, secondary, size } = useSingleSelectContext()
95
+
96
+ return (
97
+ <>
98
+ <div
99
+ style={{ '--anchor-name': anchorName } as React.CSSProperties}
100
+ ref={triggerWrapperRef}
101
+ className={classNames(styles.trigger, {
102
+ [styles.disabled]: isDisabled,
103
+ [styles.readOnly]: isReadOnly,
104
+ [styles.secondary]: secondary,
105
+ [styles.small]: size === 'small',
106
+ [styles.large]: size === 'large',
107
+ })}
108
+ >
109
+ <input
110
+ {...inputProps}
111
+ ref={inputRef}
112
+ className={classNames(styles.input, { [styles.smallText]: size === 'small' })}
113
+ />
114
+ {!isDisabled && !isReadOnly && (
115
+ <ClearButton clearButtonRef={clearButtonRef} inputRef={inputRef} />
116
+ )}
117
+ {!isReadOnly && <ChevronButton {...buttonProps} buttonRef={buttonRef} />}
118
+ </div>
119
+ </>
120
+ )
121
+ }
@@ -0,0 +1 @@
1
+ export * from './ComboBoxTrigger'
@@ -2,5 +2,10 @@
2
2
  .list {
3
3
  display: flex;
4
4
  flex-direction: column;
5
+ flex: 1 1 auto;
6
+ list-style: none;
7
+ overflow-y: auto;
8
+ padding: 0.875rem;
9
+ margin: 0;
5
10
  }
6
11
  }
@@ -1,18 +1,41 @@
1
- import React, { type PropsWithChildren } from 'react'
2
- import classNames from 'classnames'
3
- import { ListBox as RACListBox, type ListBoxProps } from 'react-aria-components'
4
- import { type SelectItem, type SelectSection } from '../../types'
1
+ import React from 'react'
2
+ import type { Node } from '@react-types/shared'
3
+ import { useListBox } from 'react-aria'
4
+ import { type ListProps, type SelectItem } from '../../types'
5
+ import { ListItem } from '../ListItem'
6
+ import { ListSection } from '../ListSection'
5
7
  import styles from './List.module.css'
6
8
 
7
- export const List = ({
8
- children,
9
- className,
10
- ...props
11
- }: ListBoxProps<SelectItem | SelectSection> & PropsWithChildren): React.ReactElement => {
9
+ export const List = <T extends SelectItem>({
10
+ state,
11
+ listBoxOptions,
12
+ listBoxRef,
13
+ }: ListProps<T>): JSX.Element => {
14
+ const { listBoxProps } = useListBox({ ...listBoxOptions, autoFocus: 'first' }, state, listBoxRef)
15
+
16
+ const renderNode = (node: Node<T>): JSX.Element | null => {
17
+ if (node.type === 'section') {
18
+ return node.rendered ? (
19
+ <ListSection key={String(node.key)} section={node} state={state} />
20
+ ) : null
21
+ } else {
22
+ const { selectedIcon, selectedPosition, className } = node.props
23
+ return (
24
+ <ListItem
25
+ key={String(node.key)}
26
+ item={node}
27
+ state={state}
28
+ selectedIcon={selectedIcon}
29
+ selectedPosition={selectedPosition}
30
+ className={className}
31
+ />
32
+ )
33
+ }
34
+ }
35
+
12
36
  return (
13
- <RACListBox className={classNames(styles.list, className)} {...props}>
14
- {children}
15
- </RACListBox>
37
+ <ul {...listBoxProps} ref={listBoxRef} className={styles.list}>
38
+ {Array.from(state.collection).map(renderNode)}
39
+ </ul>
16
40
  )
17
41
  }
18
- List.displayName = 'SingleSelect.List'
@@ -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
  }