@stack-spot/citric-react 0.41.2 → 0.42.0-beta.0
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/CHANGELOG.md +13 -13
- package/dist/citric.css +3090 -2846
- package/dist/components/Accordion.d.ts +1 -1
- package/dist/components/Accordion.js +1 -1
- package/dist/components/Alert.d.ts +1 -1
- package/dist/components/Alert.js +1 -1
- package/dist/components/AsyncContent.d.ts +1 -1
- package/dist/components/AsyncContent.js +1 -1
- package/dist/components/Autocomplete/Autocomplete.d.ts +211 -0
- package/dist/components/Autocomplete/Autocomplete.d.ts.map +1 -0
- package/dist/components/Autocomplete/Autocomplete.js +409 -0
- package/dist/components/Autocomplete/Autocomplete.js.map +1 -0
- package/dist/components/Autocomplete/index.d.ts +3 -0
- package/dist/components/Autocomplete/index.d.ts.map +1 -0
- package/dist/components/Autocomplete/index.js +2 -0
- package/dist/components/Autocomplete/index.js.map +1 -0
- package/dist/components/Avatar.d.ts +1 -1
- package/dist/components/Avatar.js +1 -1
- package/dist/components/AvatarGroup.d.ts +1 -1
- package/dist/components/AvatarGroup.js +1 -1
- package/dist/components/Badge.d.ts +1 -1
- package/dist/components/Badge.js +1 -1
- package/dist/components/Blockquote.d.ts +1 -1
- package/dist/components/Blockquote.js +1 -1
- package/dist/components/Breadcrumb.d.ts +1 -1
- package/dist/components/Breadcrumb.js +1 -1
- package/dist/components/Button.d.ts +1 -1
- package/dist/components/Button.js +1 -1
- package/dist/components/ButtonLink.d.ts +1 -1
- package/dist/components/ButtonLink.js +1 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +1 -1
- package/dist/components/Checkbox.d.ts +1 -1
- package/dist/components/Checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox.js +2 -2
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/CheckboxGroup.d.ts +1 -1
- package/dist/components/CheckboxGroup.js +1 -1
- package/dist/components/Circle.d.ts +1 -1
- package/dist/components/Circle.js +1 -1
- package/dist/components/CitricComponent.d.ts +1 -1
- package/dist/components/CitricComponent.d.ts.map +1 -1
- package/dist/components/Divider.d.ts +1 -1
- package/dist/components/Divider.js +1 -1
- package/dist/components/ErrorBoundary.d.ts +1 -1
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/ErrorMessage.d.ts +1 -1
- package/dist/components/ErrorMessage.js +1 -1
- package/dist/components/FallbackBoundary.d.ts +1 -1
- package/dist/components/FallbackBoundary.js +1 -1
- package/dist/components/Favorite.d.ts +1 -1
- package/dist/components/Favorite.js +1 -1
- package/dist/components/FieldGroup.d.ts +1 -1
- package/dist/components/FieldGroup.js +1 -1
- package/dist/components/Form.d.ts +2 -2
- package/dist/components/Form.js +1 -1
- package/dist/components/FormGroup.d.ts +1 -1
- package/dist/components/FormGroup.js +1 -1
- package/dist/components/Icon.d.ts +1 -1
- package/dist/components/Icon.js +1 -1
- package/dist/components/IconBox.d.ts +3 -3
- package/dist/components/IconBox.js +1 -1
- package/dist/components/ImageBox.d.ts +3 -3
- package/dist/components/ImageBox.js +1 -1
- package/dist/components/ImageWithFallback.d.ts +1 -1
- package/dist/components/ImageWithFallback.js +1 -1
- package/dist/components/Input.d.ts +1 -1
- package/dist/components/Input.js +1 -1
- package/dist/components/Link.d.ts +1 -1
- package/dist/components/Link.js +1 -1
- package/dist/components/LoadingPanel.d.ts +1 -1
- package/dist/components/LoadingPanel.js +1 -1
- package/dist/components/MenuOverlay/Menu.d.ts +1 -1
- package/dist/components/MenuOverlay/Menu.js +1 -1
- package/dist/components/MenuOverlay/index.d.ts +1 -1
- package/dist/components/MenuOverlay/index.js +1 -1
- package/dist/components/Overlay/index.d.ts +1 -1
- package/dist/components/Overlay/index.js +1 -1
- package/dist/components/Pagination.d.ts +1 -1
- package/dist/components/Pagination.js +1 -1
- package/dist/components/ProgressBar.d.ts +1 -1
- package/dist/components/ProgressBar.js +1 -1
- package/dist/components/ProgressCircular.d.ts +1 -1
- package/dist/components/ProgressCircular.js +1 -1
- package/dist/components/RadioGroup.d.ts +1 -1
- package/dist/components/RadioGroup.js +1 -1
- package/dist/components/Rating.d.ts +1 -1
- package/dist/components/Rating.js +1 -1
- package/dist/components/Select/MultiSelect.d.ts +1 -1
- package/dist/components/Select/MultiSelect.js +1 -1
- package/dist/components/Select/RichSelect.d.ts +1 -1
- package/dist/components/Select/RichSelect.js +1 -1
- package/dist/components/Select/SimpleSelect.d.ts +1 -1
- package/dist/components/Select/SimpleSelect.js +1 -1
- package/dist/components/Select/index.d.ts +1 -1
- package/dist/components/Select/index.js +1 -1
- package/dist/components/SelectBox.d.ts +1 -1
- package/dist/components/SelectBox.js +1 -1
- package/dist/components/Skeleton.d.ts +1 -1
- package/dist/components/Skeleton.js +1 -1
- package/dist/components/Slider.d.ts +1 -1
- package/dist/components/Slider.js +1 -1
- package/dist/components/SmartTable.d.ts +1 -1
- package/dist/components/SmartTable.js +1 -1
- package/dist/components/Stepper.d.ts +1 -1
- package/dist/components/Stepper.js +1 -1
- package/dist/components/Table.d.ts +3 -3
- package/dist/components/Table.js +1 -1
- package/dist/components/Tabs/index.d.ts +1 -1
- package/dist/components/Tabs/index.js +1 -1
- package/dist/components/Textarea.d.ts +1 -1
- package/dist/components/Textarea.js +1 -1
- package/dist/components/Tooltip.d.ts +1 -1
- package/dist/components/Tooltip.js +1 -1
- package/dist/context/CitricProvider.d.ts +1 -1
- package/dist/context/CitricProvider.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/overlay.js +1 -1
- package/dist/theme.css +415 -415
- package/package.json +1 -1
- package/scripts/build-css.ts +49 -49
- package/src/components/Accordion.tsx +130 -130
- package/src/components/Alert.tsx +24 -24
- package/src/components/AsyncContent.tsx +75 -75
- package/src/components/Autocomplete/Autocomplete.tsx +794 -0
- package/src/components/Autocomplete/index.ts +3 -0
- package/src/components/Avatar.tsx +45 -45
- package/src/components/AvatarGroup.tsx +49 -49
- package/src/components/Badge.tsx +47 -47
- package/src/components/Blockquote.tsx +18 -18
- package/src/components/Breadcrumb.tsx +33 -33
- package/src/components/Button.tsx +105 -105
- package/src/components/ButtonLink.tsx +45 -45
- package/src/components/Card.tsx +68 -68
- package/src/components/Checkbox.tsx +52 -51
- package/src/components/CheckboxGroup.tsx +153 -153
- package/src/components/Circle.tsx +43 -43
- package/src/components/CitricComponent.ts +47 -47
- package/src/components/Divider.tsx +24 -24
- package/src/components/ErrorBoundary.tsx +75 -75
- package/src/components/ErrorMessage.tsx +11 -11
- package/src/components/FallbackBoundary.tsx +40 -40
- package/src/components/Favorite.tsx +57 -57
- package/src/components/FieldGroup.tsx +46 -46
- package/src/components/Form.tsx +36 -36
- package/src/components/FormGroup.tsx +57 -57
- package/src/components/Icon.tsx +35 -35
- package/src/components/IconBox.tsx +134 -134
- package/src/components/ImageBox.tsx +125 -125
- package/src/components/ImageWithFallback.tsx +65 -65
- package/src/components/Input.tsx +49 -49
- package/src/components/Link.tsx +55 -55
- package/src/components/LoadingPanel.tsx +12 -12
- package/src/components/MenuOverlay/Menu.tsx +158 -158
- package/src/components/MenuOverlay/context.ts +20 -20
- package/src/components/MenuOverlay/index.tsx +55 -55
- package/src/components/MenuOverlay/keyboard.ts +60 -60
- package/src/components/MenuOverlay/types.ts +171 -171
- package/src/components/Overlay/context.ts +10 -10
- package/src/components/Overlay/index.tsx +182 -182
- package/src/components/Overlay/types.ts +75 -75
- package/src/components/Pagination.tsx +133 -133
- package/src/components/ProgressBar.tsx +45 -45
- package/src/components/ProgressCircular.tsx +45 -45
- package/src/components/RadioGroup.tsx +147 -147
- package/src/components/Rating.tsx +98 -98
- package/src/components/Select/MultiSelect.tsx +217 -217
- package/src/components/Select/RichSelect.tsx +128 -128
- package/src/components/Select/SimpleSelect.tsx +73 -73
- package/src/components/Select/hooks.ts +133 -133
- package/src/components/Select/index.tsx +35 -35
- package/src/components/Select/types.ts +134 -134
- package/src/components/SelectBox.tsx +167 -167
- package/src/components/Skeleton.tsx +53 -53
- package/src/components/Slider.tsx +89 -89
- package/src/components/SmartTable.tsx +227 -227
- package/src/components/Stepper.tsx +163 -163
- package/src/components/Table.tsx +234 -234
- package/src/components/Tabs/TabController.ts +54 -54
- package/src/components/Tabs/index.tsx +106 -106
- package/src/components/Tabs/types.ts +67 -67
- package/src/components/Tabs/utils.ts +6 -6
- package/src/components/Text.ts +111 -111
- package/src/components/Textarea.tsx +27 -27
- package/src/components/Tooltip.tsx +83 -83
- package/src/components/layout.tsx +101 -101
- package/src/context/CitricContext.tsx +4 -4
- package/src/context/CitricProvider.tsx +14 -14
- package/src/context/hooks.ts +6 -6
- package/src/index.ts +59 -58
- package/src/overlay.ts +348 -348
- package/src/types.ts +235 -235
- package/src/utils/ValueController.ts +28 -28
- package/src/utils/acessibility.ts +92 -92
- package/src/utils/checkbox.ts +121 -121
- package/src/utils/css.ts +119 -119
- package/src/utils/options.ts +9 -9
- package/src/utils/radio.ts +93 -93
- package/src/utils/react.ts +6 -6
- package/src/utils/time.ts +5 -5
- package/tsconfig.json +10 -10
|
@@ -67,5 +67,5 @@ export type AccordionProps = Omit<React.JSX.IntrinsicElements['div'], 'onChange'
|
|
|
67
67
|
* )
|
|
68
68
|
* ```
|
|
69
69
|
*/
|
|
70
|
-
export declare const Accordion: ({ id, appearance, expanded, onChange, header, maxHeight, startExpanded, className, style, children, ...props }: AccordionProps) => import("react/jsx-runtime").JSX.Element;
|
|
70
|
+
export declare const Accordion: ({ id, appearance, expanded, onChange, header, maxHeight, startExpanded, className, style, children, ...props }: AccordionProps) => import("react/jsx-runtime.js").JSX.Element;
|
|
71
71
|
//# sourceMappingURL=Accordion.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime.js";
|
|
2
2
|
import { listToClass } from '@stack-spot/portal-theme';
|
|
3
3
|
import { useTranslate } from '@stack-spot/portal-translate';
|
|
4
4
|
import { useEffect, useMemo, useState } from 'react';
|
|
@@ -14,5 +14,5 @@ export type AlertProps = React.JSX.IntrinsicElements['div'] & BaseAlertProps;
|
|
|
14
14
|
* <Alert type="warning">My warning!</Alert>
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
|
-
export declare const Alert: ({ type, className, children, ...props }: AlertProps) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare const Alert: ({ type, className, children, ...props }: AlertProps) => import("react/jsx-runtime.js").JSX.Element;
|
|
18
18
|
//# sourceMappingURL=Alert.d.ts.map
|
package/dist/components/Alert.js
CHANGED
|
@@ -45,6 +45,6 @@ interface Props {
|
|
|
45
45
|
* )
|
|
46
46
|
* ```
|
|
47
47
|
*/
|
|
48
|
-
export declare const AsyncContent: ({ loading, error, autofocus, children, loadingStyle }: Props) => string | number | boolean | Iterable<import("react").ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
|
|
48
|
+
export declare const AsyncContent: ({ loading, error, autofocus, children, loadingStyle }: Props) => string | number | boolean | Iterable<import("react").ReactNode> | import("react/jsx-runtime.js").JSX.Element | null | undefined;
|
|
49
49
|
export {};
|
|
50
50
|
//# sourceMappingURL=AsyncContent.d.ts.map
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { ColorPaletteName, ColorSchemeName } from '@stack-spot/portal-theme';
|
|
2
|
+
export interface BaseAutocompleteProps<T, Multiple extends boolean = false> {
|
|
3
|
+
/**
|
|
4
|
+
* The list of options available for selection.
|
|
5
|
+
*/
|
|
6
|
+
options: T[];
|
|
7
|
+
/**
|
|
8
|
+
* The current value(s) selected.
|
|
9
|
+
* - Single selection: T | null
|
|
10
|
+
* - Multiple selection: T[]
|
|
11
|
+
*/
|
|
12
|
+
value: Multiple extends true ? T[] : (T | null);
|
|
13
|
+
/**
|
|
14
|
+
* Callback fired when the value changes.
|
|
15
|
+
*/
|
|
16
|
+
onChange: Multiple extends true ? (value: T[]) => void : (value: T | null) => void;
|
|
17
|
+
/**
|
|
18
|
+
* If true, enables multiple selection mode.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
multiple?: Multiple;
|
|
22
|
+
/**
|
|
23
|
+
* If true, allows the user to enter values that are not in the options list.
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
freeSolo?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* If true, allows creating new options when no match is found.
|
|
29
|
+
* Shows an "Add [value]" option at the top of the list.
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
creatable?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Callback fired when a new option is created.
|
|
35
|
+
* Required when creatable is true and you want manual control.
|
|
36
|
+
*/
|
|
37
|
+
onCreate?: (inputValue: string) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Function to create a new option object from the input value.
|
|
40
|
+
* Required when creatable is true without onCreate and working with objects.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* getOptionFromInput={(inputValue) => ({ id: Date.now(), name: inputValue })}
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
getOptionFromInput?: (inputValue: string) => T;
|
|
48
|
+
/**
|
|
49
|
+
* The input value (controlled).
|
|
50
|
+
*/
|
|
51
|
+
inputValue?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Callback fired when the input value changes.
|
|
54
|
+
*/
|
|
55
|
+
onInputChange?: (value: string) => void;
|
|
56
|
+
/**
|
|
57
|
+
* A function to render the item label.
|
|
58
|
+
* @default "the item's toString() result."
|
|
59
|
+
*/
|
|
60
|
+
renderLabel?: (option: T) => string;
|
|
61
|
+
/**
|
|
62
|
+
* A function to generate a unique key for each option.
|
|
63
|
+
*/
|
|
64
|
+
renderKey?: (option: T) => string | number;
|
|
65
|
+
/**
|
|
66
|
+
* A function to render an option in the dropdown.
|
|
67
|
+
*/
|
|
68
|
+
renderOption?: (option: T) => React.ReactNode;
|
|
69
|
+
/**
|
|
70
|
+
* A function to render a tag in multiple mode.
|
|
71
|
+
*/
|
|
72
|
+
renderTag?: (option: T, onRemove: () => void) => React.ReactNode;
|
|
73
|
+
/**
|
|
74
|
+
* Custom filter function for options.
|
|
75
|
+
* @default filters by label includes input (case insensitive)
|
|
76
|
+
*/
|
|
77
|
+
filterOptions?: (options: T[], inputValue: string) => T[];
|
|
78
|
+
/**
|
|
79
|
+
* If true, shows a loading indicator.
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
82
|
+
loading?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* If true, the component is disabled.
|
|
85
|
+
* @default false
|
|
86
|
+
*/
|
|
87
|
+
disabled?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Placeholder text for the input.
|
|
90
|
+
*/
|
|
91
|
+
placeholder?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Maximum height for the dropdown panel in pixels.
|
|
94
|
+
*/
|
|
95
|
+
maxHeight?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Maximum number of tags to show before truncating.
|
|
98
|
+
* Only applies when multiple is true.
|
|
99
|
+
*/
|
|
100
|
+
maxTagsToShow?: number;
|
|
101
|
+
/**
|
|
102
|
+
* If true, automatically highlights the first option.
|
|
103
|
+
* @default false
|
|
104
|
+
*/
|
|
105
|
+
autoHighlight?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* If true, clears the input value when an option is selected.
|
|
108
|
+
* Only applies when multiple is true.
|
|
109
|
+
* @default true (for multiple)
|
|
110
|
+
*/
|
|
111
|
+
clearOnSelect?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* If true, the popup will open on input focus.
|
|
114
|
+
* @default true
|
|
115
|
+
*/
|
|
116
|
+
openOnFocus?: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Callback fired when an option is selected (before onChange).
|
|
119
|
+
*/
|
|
120
|
+
onSelect?: (option: T | null) => void;
|
|
121
|
+
/**
|
|
122
|
+
* Text to display when no options are available.
|
|
123
|
+
*/
|
|
124
|
+
noOptionsText?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Text to display when loading.
|
|
127
|
+
*/
|
|
128
|
+
loadingText?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Callback fired when the user scrolls to the end of the options list.
|
|
131
|
+
* Useful for implementing infinite scroll/pagination.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* <Autocomplete
|
|
136
|
+
* options={options}
|
|
137
|
+
* onScrollEnd={() => fetchMoreOptions()}
|
|
138
|
+
* loading={isFetchingMore}
|
|
139
|
+
* />
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
onScrollEnd?: () => void;
|
|
143
|
+
/**
|
|
144
|
+
* Margin in pixels before the end of the list to trigger onScrollEnd.
|
|
145
|
+
* @default 200
|
|
146
|
+
*/
|
|
147
|
+
scrollEndMargin?: number;
|
|
148
|
+
/**
|
|
149
|
+
* Color scheme for the tags (badges) in multiple mode.
|
|
150
|
+
* @example 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info'
|
|
151
|
+
*/
|
|
152
|
+
tagColorScheme?: ColorSchemeName;
|
|
153
|
+
/**
|
|
154
|
+
* Color palette for the tags (badges) in multiple mode.
|
|
155
|
+
* @example 'blue' | 'green' | 'red' | 'yellow' | 'purple'
|
|
156
|
+
*/
|
|
157
|
+
tagColorPalette?: ColorPaletteName;
|
|
158
|
+
/**
|
|
159
|
+
* Appearance of the tags (badges) in multiple mode.
|
|
160
|
+
* @default 'circle'
|
|
161
|
+
*/
|
|
162
|
+
tagAppearance?: 'square' | 'circle';
|
|
163
|
+
}
|
|
164
|
+
export type AutocompleteProps<T, Multiple extends boolean = false> = Omit<React.JSX.IntrinsicElements['div'], 'ref' | 'onChange' | 'onSelect'> & BaseAutocompleteProps<T, Multiple>;
|
|
165
|
+
/**
|
|
166
|
+
* A combination of a text input and a dropdown that suggests options as the user types.
|
|
167
|
+
* Supports both single and multiple selection modes, similar to Material-UI Autocomplete.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* Basic usage (single selection):
|
|
171
|
+
* ```tsx
|
|
172
|
+
* const [value, setValue] = useState<Option | null>(null)
|
|
173
|
+
*
|
|
174
|
+
* <Autocomplete
|
|
175
|
+
* options={options}
|
|
176
|
+
* value={value}
|
|
177
|
+
* onChange={setValue}
|
|
178
|
+
* renderLabel={o => o.name}
|
|
179
|
+
* renderKey={o => o.id}
|
|
180
|
+
* />
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* Multiple selection with tags:
|
|
185
|
+
* ```tsx
|
|
186
|
+
* const [value, setValue] = useState<Option[]>([])
|
|
187
|
+
*
|
|
188
|
+
* <Autocomplete
|
|
189
|
+
* multiple
|
|
190
|
+
* options={options}
|
|
191
|
+
* value={value}
|
|
192
|
+
* onChange={setValue}
|
|
193
|
+
* renderLabel={o => o.name}
|
|
194
|
+
* renderKey={o => o.id}
|
|
195
|
+
* />
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* Free solo (allow custom values):
|
|
200
|
+
* ```tsx
|
|
201
|
+
* <Autocomplete
|
|
202
|
+
* freeSolo
|
|
203
|
+
* options={options}
|
|
204
|
+
* value={value}
|
|
205
|
+
* onChange={setValue}
|
|
206
|
+
* renderLabel={o => o.name}
|
|
207
|
+
* />
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
export declare const Autocomplete: <T, Multiple extends boolean = false>(props: AutocompleteProps<T, Multiple>) => React.ReactElement;
|
|
211
|
+
//# sourceMappingURL=Autocomplete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Autocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/Autocomplete/Autocomplete.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAe,MAAM,0BAA0B,CAAA;AAczF,MAAM,WAAW,qBAAqB,CAAC,CAAC,EAAE,QAAQ,SAAS,OAAO,GAAG,KAAK;IACxE;;OAEG;IACH,OAAO,EAAE,CAAC,EAAE,CAAC;IACb;;;;OAIG;IACH,KAAK,EAAE,QAAQ,SAAS,IAAI,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAChD;;OAEG;IACH,QAAQ,EAAE,QAAQ,SAAS,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACnF;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,CAAC,CAAC;IAC/C;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,CAAC;IACpC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,GAAG,MAAM,CAAC;IAC3C;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC9C;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,IAAI,KAAK,KAAK,CAAC,SAAS,CAAC;IACjE;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;IAC1D;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACtC;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC;;;OAGG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC;;;OAGG;IACH,aAAa,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CACrC;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,EAAE,QAAQ,SAAS,OAAO,GAAG,KAAK,IAC/D,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,UAAU,CAAC,GACzE,qBAAqB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,YAAY,EAgiBpB,CAAC,CAAC,EAAE,QAAQ,SAAS,OAAO,GAAG,KAAK,EACvC,KAAK,EAAE,iBAAiB,CAAC,CAAC,EAAE,QAAQ,CAAC,KAClC,KAAK,CAAC,YAAY,CAAA"}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime.js";
|
|
2
|
+
import { listToClass } from '@stack-spot/portal-theme';
|
|
3
|
+
import { useTranslate } from '@stack-spot/portal-translate';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { applyCSSVariable } from '../../utils/css.js';
|
|
6
|
+
import { defaultRenderKey, defaultRenderLabel } from '../../utils/options.js';
|
|
7
|
+
import { withRef } from '../../utils/react.js';
|
|
8
|
+
import { Badge } from '../Badge.js';
|
|
9
|
+
import { Checkbox } from '../Checkbox.js';
|
|
10
|
+
import { CitricComponent } from '../CitricComponent.js';
|
|
11
|
+
import { IconButton } from '../IconBox.js';
|
|
12
|
+
import { ProgressCircular } from '../ProgressCircular.js';
|
|
13
|
+
import { useDisabledEffect, useFocusEffect } from '../Select/hooks.js';
|
|
14
|
+
import { Row } from '../layout.js';
|
|
15
|
+
/**
|
|
16
|
+
* A combination of a text input and a dropdown that suggests options as the user types.
|
|
17
|
+
* Supports both single and multiple selection modes, similar to Material-UI Autocomplete.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* Basic usage (single selection):
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const [value, setValue] = useState<Option | null>(null)
|
|
23
|
+
*
|
|
24
|
+
* <Autocomplete
|
|
25
|
+
* options={options}
|
|
26
|
+
* value={value}
|
|
27
|
+
* onChange={setValue}
|
|
28
|
+
* renderLabel={o => o.name}
|
|
29
|
+
* renderKey={o => o.id}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* Multiple selection with tags:
|
|
35
|
+
* ```tsx
|
|
36
|
+
* const [value, setValue] = useState<Option[]>([])
|
|
37
|
+
*
|
|
38
|
+
* <Autocomplete
|
|
39
|
+
* multiple
|
|
40
|
+
* options={options}
|
|
41
|
+
* value={value}
|
|
42
|
+
* onChange={setValue}
|
|
43
|
+
* renderLabel={o => o.name}
|
|
44
|
+
* renderKey={o => o.id}
|
|
45
|
+
* />
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* Free solo (allow custom values):
|
|
50
|
+
* ```tsx
|
|
51
|
+
* <Autocomplete
|
|
52
|
+
* freeSolo
|
|
53
|
+
* options={options}
|
|
54
|
+
* value={value}
|
|
55
|
+
* onChange={setValue}
|
|
56
|
+
* renderLabel={o => o.name}
|
|
57
|
+
* />
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export const Autocomplete = withRef(function Autocomplete({ options, value, onChange, multiple = false, freeSolo = false, creatable = false, onCreate, getOptionFromInput, inputValue: controlledInputValue, onInputChange, renderLabel = defaultRenderLabel, renderKey = defaultRenderKey, renderOption, renderTag, filterOptions, loading = false, disabled = false, placeholder, maxHeight, maxTagsToShow, autoHighlight = false, clearOnSelect = multiple, openOnFocus = true, onSelect, noOptionsText, loadingText, onScrollEnd, scrollEndMargin = 200, tagColorScheme, tagColorPalette, tagAppearance = 'circle', style, className, ...props }, ref) {
|
|
61
|
+
const t = useTranslate(dictionary);
|
|
62
|
+
const _element = useRef(null);
|
|
63
|
+
const inputRef = useRef(null);
|
|
64
|
+
const dropdownRef = useRef(null);
|
|
65
|
+
const element = ref ?? _element;
|
|
66
|
+
const [open, setOpen] = useState(false);
|
|
67
|
+
const [focused, setFocused] = useState(false);
|
|
68
|
+
const [internalInputValue, setInternalInputValue] = useState('');
|
|
69
|
+
const [highlightedIndex, setHighlightedIndex] = useState(-1);
|
|
70
|
+
useFocusEffect({ element, focused, setFocused, setOpen });
|
|
71
|
+
useDisabledEffect({ disabled, setOpen, setFocused });
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (!open)
|
|
74
|
+
return;
|
|
75
|
+
const handleClickOutside = (event) => {
|
|
76
|
+
if (element.current && !element.current.contains(event.target)) {
|
|
77
|
+
setOpen(false);
|
|
78
|
+
setFocused(false);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
document.addEventListener('click', handleClickOutside);
|
|
83
|
+
}, 10);
|
|
84
|
+
return () => {
|
|
85
|
+
document.removeEventListener('click', handleClickOutside);
|
|
86
|
+
};
|
|
87
|
+
}, [open, element]);
|
|
88
|
+
const inputValue = controlledInputValue ?? internalInputValue;
|
|
89
|
+
const setInputValue = useCallback((newValue) => {
|
|
90
|
+
if (onInputChange) {
|
|
91
|
+
onInputChange(newValue);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
setInternalInputValue(newValue);
|
|
95
|
+
}
|
|
96
|
+
}, [onInputChange]);
|
|
97
|
+
const defaultFilter = useCallback((opts, input) => {
|
|
98
|
+
if (!input)
|
|
99
|
+
return opts;
|
|
100
|
+
return opts.filter(option => renderLabel(option).toLowerCase().includes(input.toLowerCase()));
|
|
101
|
+
}, [renderLabel]);
|
|
102
|
+
const filter = filterOptions ?? defaultFilter;
|
|
103
|
+
const filteredOptions = useMemo(() => filter(options, inputValue), [options, inputValue, filter]);
|
|
104
|
+
const showCreateOption = useMemo(() => {
|
|
105
|
+
if (!creatable || !onCreate || !inputValue.trim())
|
|
106
|
+
return false;
|
|
107
|
+
const hasExactMatch = filteredOptions.some(option => renderLabel(option).toLowerCase() === inputValue.toLowerCase());
|
|
108
|
+
return !hasExactMatch;
|
|
109
|
+
}, [creatable, onCreate, inputValue, filteredOptions, renderLabel]);
|
|
110
|
+
const handleCreate = useCallback(() => {
|
|
111
|
+
if (!onCreate || !inputValue.trim())
|
|
112
|
+
return;
|
|
113
|
+
onCreate(inputValue.trim());
|
|
114
|
+
setInputValue('');
|
|
115
|
+
if (inputRef.current) {
|
|
116
|
+
inputRef.current.focus();
|
|
117
|
+
}
|
|
118
|
+
}, [onCreate, inputValue, setInputValue]);
|
|
119
|
+
const isSelected = useCallback((option) => {
|
|
120
|
+
if (multiple) {
|
|
121
|
+
return value.some(v => renderKey(v) === renderKey(option));
|
|
122
|
+
}
|
|
123
|
+
return value !== null && renderKey(value) === renderKey(option);
|
|
124
|
+
}, [value, multiple, renderKey]);
|
|
125
|
+
const handleSelect = useCallback((option) => {
|
|
126
|
+
if (onSelect)
|
|
127
|
+
onSelect(option);
|
|
128
|
+
if (multiple) {
|
|
129
|
+
const currentValue = value;
|
|
130
|
+
const isAlreadySelected = currentValue.some(v => renderKey(v) === renderKey(option));
|
|
131
|
+
if (isAlreadySelected) {
|
|
132
|
+
const newValue = currentValue.filter(v => renderKey(v) !== renderKey(option));
|
|
133
|
+
onChange(newValue);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
onChange([...currentValue, option]);
|
|
137
|
+
}
|
|
138
|
+
if (clearOnSelect) {
|
|
139
|
+
setInputValue('');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
onChange(option);
|
|
144
|
+
setInputValue(renderLabel(option));
|
|
145
|
+
setOpen(false);
|
|
146
|
+
}
|
|
147
|
+
}, [multiple, value, onChange, renderKey, renderLabel, clearOnSelect, setInputValue, onSelect]);
|
|
148
|
+
const handleRemoveTag = useCallback((optionToRemove) => {
|
|
149
|
+
if (!multiple)
|
|
150
|
+
return;
|
|
151
|
+
const newValue = value.filter(v => renderKey(v) !== renderKey(optionToRemove));
|
|
152
|
+
onChange(newValue);
|
|
153
|
+
}, [multiple, value, onChange, renderKey]);
|
|
154
|
+
const handleInputChange = (newValue) => {
|
|
155
|
+
setInputValue(newValue);
|
|
156
|
+
if (!open && newValue) {
|
|
157
|
+
setOpen(true);
|
|
158
|
+
}
|
|
159
|
+
setHighlightedIndex(autoHighlight ? 0 : -1);
|
|
160
|
+
};
|
|
161
|
+
const handleFocus = () => {
|
|
162
|
+
setFocused(true);
|
|
163
|
+
if (openOnFocus) {
|
|
164
|
+
setOpen(true);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const handleBlur = (e) => {
|
|
168
|
+
if (element.current?.contains(e.relatedTarget)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
setFocused(false);
|
|
172
|
+
setOpen(false);
|
|
173
|
+
if (freeSolo && inputValue && !multiple) {
|
|
174
|
+
if (creatable && !onCreate) {
|
|
175
|
+
if (getOptionFromInput) {
|
|
176
|
+
const newOption = getOptionFromInput(inputValue.trim());
|
|
177
|
+
onChange(newOption);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
onChange(inputValue);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const exactMatch = options.find(o => renderLabel(o).toLowerCase() === inputValue.toLowerCase());
|
|
185
|
+
if (exactMatch) {
|
|
186
|
+
handleSelect(exactMatch);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const handleKeyDown = (e) => {
|
|
192
|
+
if (disabled)
|
|
193
|
+
return;
|
|
194
|
+
switch (e.key) {
|
|
195
|
+
case 'ArrowDown':
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
if (!open) {
|
|
198
|
+
setOpen(true);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
setHighlightedIndex(prev => prev < filteredOptions.length - 1 ? prev + 1 : prev);
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
case 'ArrowUp':
|
|
205
|
+
e.preventDefault();
|
|
206
|
+
if (open) {
|
|
207
|
+
setHighlightedIndex(prev => prev > 0 ? prev - 1 : 0);
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
case 'Enter':
|
|
211
|
+
e.preventDefault();
|
|
212
|
+
if (open && highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
|
|
213
|
+
handleSelect(filteredOptions[highlightedIndex]);
|
|
214
|
+
}
|
|
215
|
+
else if (creatable && inputValue.trim()) {
|
|
216
|
+
if (onCreate) {
|
|
217
|
+
handleCreate();
|
|
218
|
+
}
|
|
219
|
+
else if (freeSolo && getOptionFromInput) {
|
|
220
|
+
const newOption = getOptionFromInput(inputValue.trim());
|
|
221
|
+
if (multiple) {
|
|
222
|
+
const currentValue = value;
|
|
223
|
+
const isDuplicate = currentValue.some(v => renderKey(v) === renderKey(newOption));
|
|
224
|
+
if (!isDuplicate) {
|
|
225
|
+
onChange([...currentValue, newOption]);
|
|
226
|
+
}
|
|
227
|
+
setInputValue('');
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
onChange(newOption);
|
|
231
|
+
setInputValue(renderLabel(newOption));
|
|
232
|
+
setOpen(false);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else if (freeSolo) {
|
|
236
|
+
if (multiple) {
|
|
237
|
+
const currentValue = value;
|
|
238
|
+
const inputAsOption = inputValue;
|
|
239
|
+
const isDuplicate = currentValue.some(v => renderLabel(v).toLowerCase() === inputValue.toLowerCase());
|
|
240
|
+
if (!isDuplicate) {
|
|
241
|
+
onChange([...currentValue, inputAsOption]);
|
|
242
|
+
}
|
|
243
|
+
setInputValue('');
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
onChange(inputValue);
|
|
247
|
+
setOpen(false);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else if (freeSolo && inputValue && !multiple) {
|
|
252
|
+
const exactMatch = options.find(o => renderLabel(o).toLowerCase() === inputValue.toLowerCase());
|
|
253
|
+
if (exactMatch) {
|
|
254
|
+
handleSelect(exactMatch);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
case 'Escape':
|
|
259
|
+
e.preventDefault();
|
|
260
|
+
setOpen(false);
|
|
261
|
+
if (inputRef.current) {
|
|
262
|
+
inputRef.current.blur();
|
|
263
|
+
}
|
|
264
|
+
break;
|
|
265
|
+
case 'Backspace':
|
|
266
|
+
if (multiple && !inputValue && value.length > 0) {
|
|
267
|
+
const lastTag = value[value.length - 1];
|
|
268
|
+
handleRemoveTag(lastTag);
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
default:
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const handleClear = () => {
|
|
276
|
+
if (multiple) {
|
|
277
|
+
onChange([]);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
onChange(null);
|
|
281
|
+
}
|
|
282
|
+
setInputValue('');
|
|
283
|
+
if (inputRef.current) {
|
|
284
|
+
inputRef.current.focus();
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
if (!multiple && value && !focused) {
|
|
289
|
+
setInternalInputValue(renderLabel(value));
|
|
290
|
+
}
|
|
291
|
+
}, [value, multiple, renderLabel, focused]);
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
if (autoHighlight && open && filteredOptions.length > 0) {
|
|
294
|
+
setHighlightedIndex(0);
|
|
295
|
+
}
|
|
296
|
+
}, [autoHighlight, open, filteredOptions.length]);
|
|
297
|
+
useEffect(() => {
|
|
298
|
+
if (highlightedIndex < 0 || !open)
|
|
299
|
+
return;
|
|
300
|
+
const optionsContainer = dropdownRef.current?.querySelector('.options');
|
|
301
|
+
if (!optionsContainer)
|
|
302
|
+
return;
|
|
303
|
+
const highlightedOption = optionsContainer.children[highlightedIndex];
|
|
304
|
+
if (!highlightedOption)
|
|
305
|
+
return;
|
|
306
|
+
const containerRect = optionsContainer.getBoundingClientRect();
|
|
307
|
+
const optionRect = highlightedOption.getBoundingClientRect();
|
|
308
|
+
if (optionRect.bottom > containerRect.bottom) {
|
|
309
|
+
highlightedOption.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
310
|
+
}
|
|
311
|
+
else if (optionRect.top < containerRect.top) {
|
|
312
|
+
highlightedOption.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
313
|
+
}
|
|
314
|
+
}, [highlightedIndex, open]);
|
|
315
|
+
useEffect(() => {
|
|
316
|
+
if (!onScrollEnd || !open)
|
|
317
|
+
return;
|
|
318
|
+
const optionsContainer = dropdownRef.current?.querySelector('.options');
|
|
319
|
+
if (!optionsContainer)
|
|
320
|
+
return;
|
|
321
|
+
const handleScroll = () => {
|
|
322
|
+
if (loading)
|
|
323
|
+
return;
|
|
324
|
+
const { scrollTop, scrollHeight, clientHeight } = optionsContainer;
|
|
325
|
+
const scrollBottom = scrollHeight - scrollTop - clientHeight;
|
|
326
|
+
if (scrollBottom <= scrollEndMargin) {
|
|
327
|
+
onScrollEnd();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
optionsContainer.addEventListener('scroll', handleScroll);
|
|
331
|
+
handleScroll();
|
|
332
|
+
return () => {
|
|
333
|
+
optionsContainer.removeEventListener('scroll', handleScroll);
|
|
334
|
+
};
|
|
335
|
+
}, [onScrollEnd, open, filteredOptions.length, loading, scrollEndMargin]);
|
|
336
|
+
const renderTags = () => {
|
|
337
|
+
if (!multiple || value.length === 0)
|
|
338
|
+
return null;
|
|
339
|
+
const tags = value;
|
|
340
|
+
const visibleTags = maxTagsToShow && tags.length > maxTagsToShow
|
|
341
|
+
? tags.slice(0, maxTagsToShow)
|
|
342
|
+
: tags;
|
|
343
|
+
const remainingCount = maxTagsToShow && tags.length > maxTagsToShow
|
|
344
|
+
? tags.length - maxTagsToShow
|
|
345
|
+
: 0;
|
|
346
|
+
return (_jsxs(_Fragment, { children: [visibleTags.map(tag => {
|
|
347
|
+
if (renderTag) {
|
|
348
|
+
return renderTag(tag, () => handleRemoveTag(tag));
|
|
349
|
+
}
|
|
350
|
+
return (_jsxs(Badge, { colorScheme: tagColorScheme, colorPalette: tagColorPalette, appearance: tagAppearance, children: [renderLabel(tag), !disabled && (_jsx(IconButton, { icon: "Times", type: "button", appearance: "none", size: "xs", style: { color: 'inherit' }, onClick: (e) => {
|
|
351
|
+
e.stopPropagation();
|
|
352
|
+
if (!disabled)
|
|
353
|
+
handleRemoveTag(tag);
|
|
354
|
+
}, "aria-label": `${t.removeTag} ${renderLabel(tag)}`, disabled: disabled, tabIndex: 0 }))] }, renderKey(tag)));
|
|
355
|
+
}), remainingCount > 0 && (_jsxs(Badge, { colorScheme: tagColorScheme, colorPalette: tagColorPalette, appearance: tagAppearance, children: ["+", remainingCount] }))] }));
|
|
356
|
+
};
|
|
357
|
+
const showClearButton = !disabled && ((!multiple && value !== null) ||
|
|
358
|
+
(multiple && value.length > 0));
|
|
359
|
+
return (_jsxs(CitricComponent, { tag: "div", component: "autocomplete", "data-citric": "autocomplete", style: maxHeight ? applyCSSVariable(style, 'max-height', `${maxHeight}px`) : style, className: listToClass([
|
|
360
|
+
className,
|
|
361
|
+
open && 'open',
|
|
362
|
+
focused && 'focused',
|
|
363
|
+
disabled && 'disabled',
|
|
364
|
+
multiple && 'multiple',
|
|
365
|
+
]), ref: element, "aria-busy": loading, ...props, children: [_jsxs("header", { onClick: () => {
|
|
366
|
+
if (disabled)
|
|
367
|
+
return;
|
|
368
|
+
setFocused(true);
|
|
369
|
+
setOpen(true);
|
|
370
|
+
if (inputRef.current) {
|
|
371
|
+
inputRef.current.focus();
|
|
372
|
+
}
|
|
373
|
+
}, onFocus: () => setFocused(true), tabIndex: disabled ? undefined : 0, children: [_jsxs(Row, { gap: "4px", className: "input-container", children: [multiple && renderTags(), _jsx("input", { ref: inputRef, type: "text", value: inputValue, onChange: (e) => handleInputChange(e.target.value), onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, disabled: disabled, placeholder: multiple && value.length > 0 ? '' : placeholder, autoComplete: "off", "aria-autocomplete": "list", "aria-expanded": open, "aria-controls": "autocomplete-listbox" })] }), _jsxs("div", { className: "end-adornment", children: [loading && _jsx(ProgressCircular, { size: "xs", className: "loader" }), showClearButton && (_jsx(IconButton, { icon: "Times", appearance: "none", size: "sm", type: "button", onClick: (e) => {
|
|
374
|
+
e.stopPropagation();
|
|
375
|
+
handleClear();
|
|
376
|
+
}, disabled: disabled, "aria-label": t.clear, tabIndex: 1, style: { width: '12px', height: '12px' } })), _jsx(IconButton, { icon: open ? 'ChevronUp' : 'ChevronDown', appearance: "none", size: "md", type: "button", onClick: (e) => {
|
|
377
|
+
e.stopPropagation();
|
|
378
|
+
setOpen((prev) => !prev);
|
|
379
|
+
}, disabled: disabled, "aria-label": open ? t.collapse : t.expand, tabIndex: 1, style: { width: '12px', height: '12px' } })] })] }), _jsx("div", { className: "dropdown-panel", ref: dropdownRef, id: "autocomplete-listbox", role: "listbox", "aria-hidden": !open, ...(open ? {} : { inert: 'true' }), children: loading && !filteredOptions.length ? (_jsx("div", { className: "message", children: loadingText || t.loading })) : filteredOptions.length === 0 && !showCreateOption ? (_jsx("div", { className: "message", children: noOptionsText || t.noOptions })) : (_jsxs("div", { className: "options", children: [showCreateOption && (_jsxs("div", { role: "option", className: "option create-option", onMouseDown: (e) => {
|
|
380
|
+
e.preventDefault();
|
|
381
|
+
}, onClick: handleCreate, onMouseEnter: () => setHighlightedIndex(-1), children: [_jsx("i", { "data-citric": "icon", className: "citric-icon outline Plus" }), t.addOption.replace('{value}', inputValue)] }, "create-option")), filteredOptions.map((option, index) => (_jsxs("div", { role: "option", "aria-selected": isSelected(option), className: listToClass([
|
|
382
|
+
'option',
|
|
383
|
+
isSelected(option) && 'selected',
|
|
384
|
+
highlightedIndex === index && 'highlighted',
|
|
385
|
+
]), onMouseDown: (e) => {
|
|
386
|
+
e.preventDefault();
|
|
387
|
+
}, onClick: () => handleSelect(option), onMouseEnter: () => setHighlightedIndex(index), children: [multiple && _jsx(Checkbox, { value: isSelected(option), readOnly: true }), renderOption ? renderOption(option) : renderLabel(option)] }, renderKey(option))))] })) })] }));
|
|
388
|
+
});
|
|
389
|
+
const dictionary = {
|
|
390
|
+
en: {
|
|
391
|
+
removeTag: 'Remove',
|
|
392
|
+
clear: 'Clear',
|
|
393
|
+
loading: 'Loading...',
|
|
394
|
+
noOptions: 'No options',
|
|
395
|
+
collapse: 'Collapse',
|
|
396
|
+
expand: 'Expand',
|
|
397
|
+
addOption: 'Add "{value}"',
|
|
398
|
+
},
|
|
399
|
+
pt: {
|
|
400
|
+
removeTag: 'Remover',
|
|
401
|
+
clear: 'Limpar',
|
|
402
|
+
loading: 'Carregando...',
|
|
403
|
+
noOptions: 'Sem opções',
|
|
404
|
+
collapse: 'Recolher',
|
|
405
|
+
expand: 'Expandir',
|
|
406
|
+
addOption: 'Adicionar "{value}"',
|
|
407
|
+
},
|
|
408
|
+
};
|
|
409
|
+
//# sourceMappingURL=Autocomplete.js.map
|