@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,23 +1,12 @@
1
1
  import React from 'react'
2
2
  import { type Meta, type StoryObj } from '@storybook/react'
3
3
  import { SingleSelect } from '../index'
4
+ import { type SelectItem, type SingleSelectProps } from '../types'
4
5
  import { singleMockItems } from './mockData'
5
6
 
6
- const meta = {
7
+ const meta: Meta = {
7
8
  title: 'Components/SingleSelect/SingleSelect (alpha)',
8
9
  component: SingleSelect,
9
- args: {
10
- items: singleMockItems,
11
- children: (
12
- <SingleSelect.List>
13
- {singleMockItems.map((item) => (
14
- <SingleSelect.ListItem key={item.value} id={item.value}>
15
- {item.label}
16
- </SingleSelect.ListItem>
17
- ))}
18
- </SingleSelect.List>
19
- ),
20
- },
21
10
  decorators: [
22
11
  (Story) => (
23
12
  <div className="h-200 justify-center items-center position-relative flex">
@@ -25,18 +14,48 @@ const meta = {
25
14
  </div>
26
15
  ),
27
16
  ],
28
- } satisfies Meta<typeof SingleSelect>
17
+ argTypes: {
18
+ label: { control: 'text', description: 'The label for the select/combobox input' },
19
+ labelHidden: { control: 'boolean', description: 'Visually hide the label' },
20
+ labelPosition: {
21
+ control: { type: 'radio' },
22
+ options: ['top', 'side'],
23
+ description: 'Position of the label relative to the input',
24
+ },
25
+ variant: {
26
+ control: { type: 'radio' },
27
+ options: ['primary', 'secondary'],
28
+ description: 'Visual variant',
29
+ },
30
+ size: {
31
+ control: { type: 'radio' },
32
+ options: ['small', 'medium', 'large'],
33
+ description: 'Size of the input',
34
+ },
35
+ isReadOnly: { control: 'boolean', description: 'Whether the input is read-only' },
36
+ isDisabled: { control: 'boolean', description: 'Whether the input is disabled' },
37
+ isComboBox: {
38
+ control: 'boolean',
39
+ description: 'Whether to render as a combobox (filterable input) or select',
40
+ },
41
+ items: { control: 'object', description: 'List of options to display' },
42
+ },
43
+ }
29
44
 
30
45
  export default meta
31
46
 
32
- type Story = StoryObj<typeof meta>
33
-
34
- export const Playground: Story = {
47
+ export const Playground: StoryObj<SingleSelectProps<SelectItem>> = {
48
+ args: {
49
+ label: 'Choose an item',
50
+ isComboBox: false,
51
+ items: singleMockItems,
52
+ },
53
+ render: (args: SingleSelectProps<SelectItem>) => (
54
+ <SingleSelect {...args}>
55
+ {(item) => <SingleSelect.Item key={item.key}>{item.label}</SingleSelect.Item>}
56
+ </SingleSelect>
57
+ ),
35
58
  parameters: {
36
- docs: {
37
- canvas: {
38
- sourceState: 'shown',
39
- },
40
- },
59
+ docs: { canvas: { sourceState: 'shown' } },
41
60
  },
42
61
  }
@@ -1,29 +1,35 @@
1
1
  export const singleMockItems = [
2
- { label: 'Short black', value: 'short-black' },
3
- { label: 'Long black', value: 'long-black' },
4
- { label: 'Batch brew', value: 'batch-brew' },
5
- { label: 'Latte', value: 'latte' },
6
- { label: 'Flat white', value: 'flat-white' },
7
- { label: 'Mocha', value: 'mocha' },
8
- { label: 'Cappuccino', value: 'cappuccino' },
9
- { label: 'Magic', value: 'magic' },
2
+ {
3
+ key: 'short-black',
4
+ label: 'Short black',
5
+ value: 'short-black',
6
+ },
7
+ { key: 'long-black', label: 'Long black', value: 'long-black' },
8
+ { key: 'batch-brew', label: 'Batch brew', value: 'batch-brew' },
9
+ { key: 'latte', label: 'Latte', value: 'latte' },
10
+ { key: 'flat-white', label: 'Flat white', value: 'flat-white' },
11
+ { key: 'mocha', label: 'Mocha', value: 'mocha' },
12
+ { key: 'cappuccino', label: 'Cappuccino', value: 'cappuccino' },
13
+ { key: 'magic', label: 'Magic', value: 'magic' },
10
14
  ]
11
15
 
12
16
  export const groupedMockItems = [
13
17
  {
18
+ type: 'section',
14
19
  label: 'Colours',
15
20
  options: [
16
- { label: 'Blue', value: 'blue' },
17
- { label: 'Red', value: 'red' },
18
- { label: 'Green', value: 'green' },
21
+ { key: 'blue', label: 'Blue', value: 'blue' },
22
+ { key: 'red', label: 'Red', value: 'red' },
23
+ { key: 'green', label: 'Green', value: 'green' },
19
24
  ],
20
25
  },
21
26
  {
27
+ type: 'section',
22
28
  label: 'Flavours',
23
29
  options: [
24
- { label: 'Vanilla', value: 'vanilla' },
25
- { label: 'Chocolate', value: 'chocolate' },
26
- { label: 'Strawberry', value: 'strawberry' },
30
+ { key: 'vanilla', label: 'Vanilla', value: 'vanilla' },
31
+ { key: 'chocolate', label: 'Chocolate', value: 'chocolate' },
32
+ { key: 'strawberry', label: 'Strawberry', value: 'strawberry' },
27
33
  ],
28
34
  },
29
35
  ]
@@ -1,15 +1,26 @@
1
1
  import { createContext, useContext } from 'react'
2
- import { type Key } from '@react-types/shared'
3
- import { type SelectItem, type SelectSection } from '../types'
2
+ import { type ComboBoxState } from '@react-stately/combobox'
3
+ import { type SelectState } from '@react-stately/select'
4
4
 
5
- type SingleSelectContextType = {
6
- isOpen: boolean
7
- setOpen: (open: boolean) => void
8
- selectedKey: Key | null
9
- items: (SelectItem | SelectSection)[]
5
+ type BaseSingleSelectContextType = {
10
6
  anchorName: string
7
+ isDisabled: boolean
8
+ isReadOnly: boolean
9
+ secondary: boolean
10
+ size?: 'small' | 'medium' | 'large'
11
+ fieldLabel: React.ReactNode
11
12
  }
12
13
 
14
+ type SingleSelectContextType =
15
+ | (BaseSingleSelectContextType & {
16
+ state: ComboBoxState<object>
17
+ isComboBox: true
18
+ })
19
+ | (BaseSingleSelectContextType & {
20
+ state: SelectState<object>
21
+ isComboBox: false
22
+ })
23
+
13
24
  export const SingleSelectContext = createContext<SingleSelectContextType | undefined>(undefined)
14
25
 
15
26
  export const useSingleSelectContext = (): SingleSelectContextType => {
@@ -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
+ gap: var(--spacing-4) var(--spacing-6);
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
+ .comboBoxTrigger {
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,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'