@kaizen/components 2.0.0 → 2.0.1
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.
- package/alpha/README.md +28 -0
- package/alpha/package.json +5 -0
- package/dist/cjs/alpha.cjs +1 -0
- package/dist/cjs/src/Notification/InlineNotification/InlineNotification.cjs +1 -1
- package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +35 -74
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.cjs +105 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.cjs +11 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.cjs +112 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.cjs +16 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/List/List.cjs +35 -10
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.cjs +61 -8
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.cjs +10 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.cjs +38 -9
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.cjs +4 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +60 -30
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.cjs +2 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +2 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +4 -2
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.cjs +87 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.cjs +11 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.cjs +52 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.cjs +13 -0
- package/dist/esm/alpha.mjs +1 -1
- package/dist/esm/src/Notification/InlineNotification/InlineNotification.mjs +1 -1
- package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +39 -73
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.mjs +96 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css.mjs +9 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.mjs +103 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css.mjs +14 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/List/List.mjs +37 -14
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.mjs +63 -13
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css.mjs +10 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.mjs +41 -15
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css.mjs +4 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +69 -43
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +2 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +2 -1
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +4 -2
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.mjs +78 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css.mjs +9 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.mjs +43 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css.mjs +11 -0
- package/dist/styles.css +389 -25
- package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +14 -19
- package/dist/types/__alpha__/SingleSelect/_docs/mockData.d.ts +3 -0
- package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +15 -7
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBox/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -7
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -7
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -9
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +3 -6
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Select/Select.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Select/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +4 -1
- package/dist/types/__alpha__/SingleSelect/types.d.ts +68 -11
- package/locales/en.json +9 -1
- package/package.json +9 -2
- package/src/Notification/InlineNotification/InlineNotification.tsx +1 -1
- package/src/__alpha__/SingleSelect/SingleSelect.tsx +35 -88
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +96 -6
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +22 -24
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +389 -33
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +41 -22
- package/src/__alpha__/SingleSelect/_docs/mockData.ts +20 -14
- package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +18 -7
- package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.module.css +35 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBox/ComboBox.tsx +106 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBox/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.module.css +130 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/ComboBoxTrigger.tsx +121 -0
- package/src/__alpha__/SingleSelect/subcomponents/ComboBoxTrigger/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +5 -0
- package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +36 -13
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +84 -3
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +67 -11
- package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.module.css +20 -5
- package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +46 -19
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +7 -5
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +90 -37
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +2 -2
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +9 -8
- package/src/__alpha__/SingleSelect/subcomponents/Select/Select.module.css +35 -0
- package/src/__alpha__/SingleSelect/subcomponents/Select/Select.tsx +84 -0
- package/src/__alpha__/SingleSelect/subcomponents/Select/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.module.css +77 -0
- package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/SelectTrigger.tsx +52 -0
- package/src/__alpha__/SingleSelect/subcomponents/SelectTrigger/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/index.ts +4 -1
- package/src/__alpha__/SingleSelect/types.ts +94 -14
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +0 -57
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.cjs +0 -6
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +0 -49
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css.mjs +0 -4
- package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +0 -2
- package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/index.d.ts +0 -1
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +0 -19
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +0 -35
- 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'
|
|
@@ -1,18 +1,41 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import { type
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}:
|
|
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
|
-
<
|
|
14
|
-
{
|
|
15
|
-
</
|
|
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:
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import classNames from 'classnames'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
|
|
9
|
+
export const ListItem = <T extends SelectItem>({
|
|
10
|
+
item,
|
|
11
|
+
state,
|
|
12
|
+
selectedIcon = 'check',
|
|
13
|
+
selectedPosition = 'end',
|
|
9
14
|
className,
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
<
|
|
14
|
-
{
|
|
15
|
-
|
|
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-
|
|
4
|
-
font-weight: var(--typography-
|
|
5
|
-
font-size: var(--typography-
|
|
6
|
-
line-height: var(--typography-
|
|
7
|
-
letter-spacing: var(--typography-
|
|
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
|
}
|