@koobiq/react-components 0.23.0 → 0.25.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/dist/components/ContenPanel/ContentPanel.d.ts +3 -2
- package/dist/components/ContenPanel/ContentPanel.js +9 -9
- package/dist/components/Divider/Divider.d.ts +14 -4
- package/dist/components/Divider/Divider.js +23 -8
- package/dist/components/Divider/types.d.ts +4 -3
- package/dist/components/Navbar/Navbar.d.ts +15 -0
- package/dist/components/Navbar/Navbar.js +80 -0
- package/dist/components/Navbar/Navbar.module.css.js +41 -0
- package/dist/components/Navbar/NavbarContext.d.ts +5 -0
- package/dist/components/Navbar/NavbarContext.js +8 -0
- package/dist/components/Navbar/components/NavbarAppItem.d.ts +7 -0
- package/dist/components/Navbar/components/NavbarAppItem.js +10 -0
- package/dist/components/Navbar/components/NavbarBody.d.ts +6 -0
- package/dist/components/Navbar/components/NavbarBody.js +15 -0
- package/dist/components/Navbar/components/NavbarFooter.d.ts +6 -0
- package/dist/components/Navbar/components/NavbarFooter.js +15 -0
- package/dist/components/Navbar/components/NavbarHeader.d.ts +6 -0
- package/dist/components/Navbar/components/NavbarHeader.js +15 -0
- package/dist/components/Navbar/components/NavbarItem.d.ts +29 -0
- package/dist/components/Navbar/components/NavbarItem.js +65 -0
- package/dist/components/Navbar/components/index.d.ts +5 -0
- package/dist/components/Navbar/index.d.ts +2 -0
- package/dist/components/Navbar/intl.json.js +7 -0
- package/dist/components/Navbar/types.d.ts +26 -0
- package/dist/components/Navbar/types.js +4 -0
- package/dist/components/SearchInput/SearchInput.d.ts +1 -1
- package/dist/components/Select/Select.d.ts +4 -0
- package/dist/components/SelectNext/Select.d.ts +13 -0
- package/dist/components/SelectNext/Select.js +258 -0
- package/dist/components/SelectNext/Select.module.css.js +23 -0
- package/dist/components/SelectNext/SelectContext.d.ts +2 -0
- package/dist/components/SelectNext/SelectContext.js +5 -0
- package/dist/components/SelectNext/components/SelectList/SelectList.d.ts +37 -0
- package/dist/components/SelectNext/components/SelectList/SelectList.js +131 -0
- package/dist/components/SelectNext/components/SelectList/SelectList.module.css.js +11 -0
- package/dist/components/SelectNext/components/SelectList/index.d.ts +1 -0
- package/dist/components/SelectNext/components/SelectOption/SelectOption.d.ts +37 -0
- package/dist/components/SelectNext/components/SelectOption/SelectOption.js +48 -0
- package/dist/components/SelectNext/components/SelectOption/index.d.ts +1 -0
- package/dist/components/SelectNext/components/SelectSection/SelectSection.d.ts +9 -0
- package/dist/components/SelectNext/components/SelectSection/SelectSection.js +51 -0
- package/dist/components/SelectNext/components/SelectSection/index.d.ts +1 -0
- package/dist/components/SelectNext/components/Tag/Tag.d.ts +18 -0
- package/dist/components/SelectNext/components/Tag/Tag.js +67 -0
- package/dist/components/SelectNext/components/Tag/index.d.ts +1 -0
- package/dist/components/SelectNext/components/Tag/intl.json.js +7 -0
- package/dist/components/SelectNext/components/Tag/utils.d.ts +3 -0
- package/dist/components/SelectNext/components/Tag/utils.js +9 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroup.d.ts +13 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroup.js +25 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroup.module.css.js +20 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroupMultiline.d.ts +3 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroupMultiline.js +44 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroupResponsive.d.ts +3 -0
- package/dist/components/SelectNext/components/TagGroup/TagGroupResponsive.js +65 -0
- package/dist/components/SelectNext/components/TagGroup/index.d.ts +1 -0
- package/dist/components/SelectNext/components/TagGroup/utils.d.ts +1 -0
- package/dist/components/SelectNext/components/TagGroup/utils.js +4 -0
- package/dist/components/SelectNext/components/index.d.ts +5 -0
- package/dist/components/SelectNext/index.d.ts +2 -0
- package/dist/components/SelectNext/intl.d.ts +2 -0
- package/dist/components/SelectNext/intl.js +21 -0
- package/dist/components/SelectNext/types.d.ts +99 -0
- package/dist/components/SelectNext/types.js +12 -0
- package/dist/components/SelectNext/utils.d.ts +9 -0
- package/dist/components/SelectNext/utils.js +26 -0
- package/dist/components/index.d.ts +3 -1
- package/dist/index.js +13 -1
- package/dist/style.css +328 -43
- package/package.json +5 -5
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useCallback } from "react";
|
|
4
|
+
import { useDOMRef, useElementSize, mergeProps } from "@koobiq/react-core";
|
|
5
|
+
import { IconChevronDownS16 } from "@koobiq/react-icons";
|
|
6
|
+
import { CollectionBuilder, Collection, useSelectState, removeDataAttributes, useSlottedContext, FormContext, useSelect, FieldErrorContext } from "@koobiq/react-primitives";
|
|
7
|
+
import { PopoverInner } from "../Popover/PopoverInner.js";
|
|
8
|
+
import s from "./Select.module.css.js";
|
|
9
|
+
import { SelectList } from "./components/SelectList/SelectList.js";
|
|
10
|
+
import { TagGroup } from "./components/TagGroup/TagGroup.js";
|
|
11
|
+
import { SelectOption } from "./components/SelectOption/SelectOption.js";
|
|
12
|
+
import { SelectSection } from "./components/SelectSection/SelectSection.js";
|
|
13
|
+
import { useForm } from "../Form/FormContext.js";
|
|
14
|
+
import { FormFieldClearButton } from "../FormField/FormFieldClearButton/FormFieldClearButton.js";
|
|
15
|
+
import { FormField } from "../FormField/FormField.js";
|
|
16
|
+
import { Divider } from "../Divider/Divider.js";
|
|
17
|
+
import { List } from "../List/List.js";
|
|
18
|
+
function SelectInner({
|
|
19
|
+
state: inState,
|
|
20
|
+
props,
|
|
21
|
+
listBoxRef
|
|
22
|
+
}) {
|
|
23
|
+
const {
|
|
24
|
+
selectedTagsOverflow = "responsive",
|
|
25
|
+
renderValue: renderValueProp,
|
|
26
|
+
"data-testid": testId,
|
|
27
|
+
defaultInputValue,
|
|
28
|
+
labelPlacement,
|
|
29
|
+
onInputChange,
|
|
30
|
+
selectionMode,
|
|
31
|
+
defaultFilter,
|
|
32
|
+
isLabelHidden,
|
|
33
|
+
isSearchable,
|
|
34
|
+
errorMessage,
|
|
35
|
+
placeholder,
|
|
36
|
+
loadingText,
|
|
37
|
+
isClearable,
|
|
38
|
+
noItemsText,
|
|
39
|
+
inputValue,
|
|
40
|
+
labelAlign,
|
|
41
|
+
startAddon,
|
|
42
|
+
isRequired,
|
|
43
|
+
onLoadMore,
|
|
44
|
+
isDisabled,
|
|
45
|
+
fullWidth,
|
|
46
|
+
className,
|
|
47
|
+
isLoading,
|
|
48
|
+
slotProps,
|
|
49
|
+
endAddon,
|
|
50
|
+
caption,
|
|
51
|
+
onClear,
|
|
52
|
+
style,
|
|
53
|
+
label
|
|
54
|
+
} = props;
|
|
55
|
+
const { validationBehavior: formValidationBehavior } = useSlottedContext(FormContext) || {};
|
|
56
|
+
const validationBehavior = props.validationBehavior ?? formValidationBehavior ?? "aria";
|
|
57
|
+
const clearButtonIsHidden = isDisabled || !inState.selectedItems.length;
|
|
58
|
+
const handleClear = useCallback(() => {
|
|
59
|
+
inState.selectionManager.setSelectedKeys(/* @__PURE__ */ new Set());
|
|
60
|
+
onClear?.();
|
|
61
|
+
}, [onClear, inState]);
|
|
62
|
+
const {
|
|
63
|
+
menuProps,
|
|
64
|
+
valueProps,
|
|
65
|
+
triggerProps,
|
|
66
|
+
descriptionProps,
|
|
67
|
+
errorMessageProps,
|
|
68
|
+
labelProps: labelPropsAria,
|
|
69
|
+
...validation
|
|
70
|
+
} = useSelect(
|
|
71
|
+
removeDataAttributes({
|
|
72
|
+
...props,
|
|
73
|
+
isDisabled,
|
|
74
|
+
selectionMode,
|
|
75
|
+
validationBehavior,
|
|
76
|
+
allowsEmptyCollection: true
|
|
77
|
+
}),
|
|
78
|
+
inState,
|
|
79
|
+
listBoxRef
|
|
80
|
+
);
|
|
81
|
+
const { isInvalid } = validation;
|
|
82
|
+
const { ref: containerRef, width } = useElementSize();
|
|
83
|
+
const rootProps = mergeProps({
|
|
84
|
+
"data-testid": testId,
|
|
85
|
+
"data-invalid": isInvalid || void 0,
|
|
86
|
+
"data-disabled": isDisabled || void 0,
|
|
87
|
+
"data-required": isRequired || void 0,
|
|
88
|
+
className,
|
|
89
|
+
fullWidth,
|
|
90
|
+
labelPlacement,
|
|
91
|
+
labelAlign,
|
|
92
|
+
style
|
|
93
|
+
});
|
|
94
|
+
const listProps = mergeProps(
|
|
95
|
+
{
|
|
96
|
+
isLoading,
|
|
97
|
+
inputValue,
|
|
98
|
+
onLoadMore,
|
|
99
|
+
noItemsText,
|
|
100
|
+
loadingText,
|
|
101
|
+
isSearchable,
|
|
102
|
+
defaultFilter,
|
|
103
|
+
state: inState,
|
|
104
|
+
onInputChange,
|
|
105
|
+
className: s.list,
|
|
106
|
+
defaultInputValue
|
|
107
|
+
},
|
|
108
|
+
slotProps?.list,
|
|
109
|
+
menuProps
|
|
110
|
+
);
|
|
111
|
+
const labelProps = mergeProps(
|
|
112
|
+
{ isHidden: isLabelHidden, children: label, isRequired },
|
|
113
|
+
labelPropsAria,
|
|
114
|
+
slotProps?.label
|
|
115
|
+
);
|
|
116
|
+
const clearButtonProps = mergeProps(
|
|
117
|
+
{
|
|
118
|
+
isClearable,
|
|
119
|
+
onPress: handleClear,
|
|
120
|
+
className: s.clearButton,
|
|
121
|
+
isHidden: clearButtonIsHidden
|
|
122
|
+
},
|
|
123
|
+
slotProps?.clearButton
|
|
124
|
+
);
|
|
125
|
+
const { slotProps: groupSlotProps, ...otherGroup } = slotProps?.group || {};
|
|
126
|
+
const groupProps = mergeProps(
|
|
127
|
+
{
|
|
128
|
+
slotProps: mergeProps(
|
|
129
|
+
{
|
|
130
|
+
endAddon: { className: s.addon },
|
|
131
|
+
startAddon: { className: s.addon }
|
|
132
|
+
},
|
|
133
|
+
groupSlotProps
|
|
134
|
+
),
|
|
135
|
+
startAddon,
|
|
136
|
+
onMouseDown: (e) => {
|
|
137
|
+
if (e.currentTarget !== e.target || isDisabled) return;
|
|
138
|
+
e.preventDefault();
|
|
139
|
+
listBoxRef?.current?.focus();
|
|
140
|
+
inState.open();
|
|
141
|
+
},
|
|
142
|
+
endAddon: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
143
|
+
endAddon,
|
|
144
|
+
/* @__PURE__ */ jsx(FormFieldClearButton, { ...clearButtonProps }),
|
|
145
|
+
/* @__PURE__ */ jsx("span", { className: s.chevron, children: /* @__PURE__ */ jsx(IconChevronDownS16, {}) })
|
|
146
|
+
] }),
|
|
147
|
+
isInvalid,
|
|
148
|
+
isDisabled,
|
|
149
|
+
ref: containerRef
|
|
150
|
+
},
|
|
151
|
+
otherGroup
|
|
152
|
+
);
|
|
153
|
+
const controlProps = mergeProps(
|
|
154
|
+
{
|
|
155
|
+
ref: listBoxRef,
|
|
156
|
+
placeholder
|
|
157
|
+
},
|
|
158
|
+
valueProps,
|
|
159
|
+
triggerProps,
|
|
160
|
+
slotProps?.control
|
|
161
|
+
);
|
|
162
|
+
const popoverProps = mergeProps(
|
|
163
|
+
{
|
|
164
|
+
offset: 4,
|
|
165
|
+
state: inState,
|
|
166
|
+
hideArrow: true,
|
|
167
|
+
type: "listbox",
|
|
168
|
+
maxBlockSize: 256,
|
|
169
|
+
className: s.popover,
|
|
170
|
+
anchorRef: containerRef,
|
|
171
|
+
placement: "bottom start",
|
|
172
|
+
size: Math.max(width, 200)
|
|
173
|
+
},
|
|
174
|
+
slotProps?.popover
|
|
175
|
+
);
|
|
176
|
+
const captionProps = mergeProps(
|
|
177
|
+
{ children: caption },
|
|
178
|
+
descriptionProps,
|
|
179
|
+
slotProps?.caption
|
|
180
|
+
);
|
|
181
|
+
const errorProps = mergeProps(
|
|
182
|
+
{ children: errorMessage },
|
|
183
|
+
errorMessageProps,
|
|
184
|
+
slotProps?.errorMessage
|
|
185
|
+
);
|
|
186
|
+
const renderDefaultValue = (state, states) => {
|
|
187
|
+
if (!state.selectedItems?.length) return null;
|
|
188
|
+
if (selectionMode === "multiple")
|
|
189
|
+
return /* @__PURE__ */ jsx(
|
|
190
|
+
TagGroup,
|
|
191
|
+
{
|
|
192
|
+
state,
|
|
193
|
+
states,
|
|
194
|
+
selectedTagsOverflow
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
return state.selectedItems[0].textValue;
|
|
198
|
+
};
|
|
199
|
+
const renderValue = renderValueProp || renderDefaultValue;
|
|
200
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
201
|
+
/* @__PURE__ */ jsxs(FormField, { ...rootProps, children: [
|
|
202
|
+
/* @__PURE__ */ jsx(FormField.Label, { ...labelProps }),
|
|
203
|
+
/* @__PURE__ */ jsxs("div", { className: s.body, children: [
|
|
204
|
+
/* @__PURE__ */ jsx(FormField.ControlGroup, { ...groupProps, children: /* @__PURE__ */ jsx(FormField.Select, { ...controlProps, children: renderValue(inState, {
|
|
205
|
+
isInvalid,
|
|
206
|
+
isDisabled: props.isDisabled,
|
|
207
|
+
isRequired: props.isRequired
|
|
208
|
+
}) }) }),
|
|
209
|
+
/* @__PURE__ */ jsx(FieldErrorContext.Provider, { value: validation, children: /* @__PURE__ */ jsx(FormField.Error, { ...errorProps }) }),
|
|
210
|
+
/* @__PURE__ */ jsx(FormField.Caption, { ...captionProps })
|
|
211
|
+
] })
|
|
212
|
+
] }),
|
|
213
|
+
/* @__PURE__ */ jsx(PopoverInner, { ...popoverProps, children: /* @__PURE__ */ jsx(SelectList, { ...listProps }) })
|
|
214
|
+
] });
|
|
215
|
+
}
|
|
216
|
+
function StandaloneSelect({
|
|
217
|
+
props: inProps,
|
|
218
|
+
listBoxRef,
|
|
219
|
+
collection
|
|
220
|
+
}) {
|
|
221
|
+
const props = { ...inProps, collection, children: null, items: null };
|
|
222
|
+
const { isDisabled: formIsDisabled } = useForm();
|
|
223
|
+
const isDisabled = inProps?.isDisabled ?? formIsDisabled;
|
|
224
|
+
const state = useSelectState(
|
|
225
|
+
removeDataAttributes({
|
|
226
|
+
...props,
|
|
227
|
+
isDisabled
|
|
228
|
+
})
|
|
229
|
+
);
|
|
230
|
+
return /* @__PURE__ */ jsx(
|
|
231
|
+
SelectInner,
|
|
232
|
+
{
|
|
233
|
+
state,
|
|
234
|
+
listBoxRef,
|
|
235
|
+
props: { ...props, isDisabled }
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
function SelectRender(props, ref) {
|
|
240
|
+
const listBoxRef = useDOMRef(ref);
|
|
241
|
+
return /* @__PURE__ */ jsx(CollectionBuilder, { content: /* @__PURE__ */ jsx(Collection, { ...props }), children: (collection) => /* @__PURE__ */ jsx(
|
|
242
|
+
StandaloneSelect,
|
|
243
|
+
{
|
|
244
|
+
props,
|
|
245
|
+
collection,
|
|
246
|
+
listBoxRef
|
|
247
|
+
}
|
|
248
|
+
) });
|
|
249
|
+
}
|
|
250
|
+
const SelectComponent = forwardRef(SelectRender);
|
|
251
|
+
const SelectNext = SelectComponent;
|
|
252
|
+
SelectNext.Item = SelectOption;
|
|
253
|
+
SelectNext.Section = SelectSection;
|
|
254
|
+
SelectNext.Divider = Divider;
|
|
255
|
+
SelectNext.ItemText = List.ItemText;
|
|
256
|
+
export {
|
|
257
|
+
SelectNext
|
|
258
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const addon = "kbq-select-addon-1077d8";
|
|
2
|
+
const chevron = "kbq-select-chevron-5918a1";
|
|
3
|
+
const body = "kbq-select-body-698617";
|
|
4
|
+
const list = "kbq-select-list-51ca7a";
|
|
5
|
+
const popover = "kbq-select-popover-756d4e";
|
|
6
|
+
const clearButton = "kbq-select-clearButton-8498d2";
|
|
7
|
+
const s = {
|
|
8
|
+
addon,
|
|
9
|
+
chevron,
|
|
10
|
+
body,
|
|
11
|
+
list,
|
|
12
|
+
popover,
|
|
13
|
+
clearButton
|
|
14
|
+
};
|
|
15
|
+
export {
|
|
16
|
+
addon,
|
|
17
|
+
body,
|
|
18
|
+
chevron,
|
|
19
|
+
clearButton,
|
|
20
|
+
s as default,
|
|
21
|
+
list,
|
|
22
|
+
popover
|
|
23
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef, CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import type { SelectState, AriaListBoxProps } from '@koobiq/react-primitives';
|
|
3
|
+
import type { SelectionMode } from '@react-types/select';
|
|
4
|
+
import { type DividerProps } from '../../../Divider';
|
|
5
|
+
import { type SearchInputProps } from '../../../SearchInput';
|
|
6
|
+
export type SelectListProps<T extends object, M extends SelectionMode = 'single'> = {
|
|
7
|
+
state: SelectState<T, M>;
|
|
8
|
+
/** The filter function used to determine if a option should be included in the Select list. */
|
|
9
|
+
defaultFilter?: (textValue: string, inputValue: string) => boolean;
|
|
10
|
+
/** The value of the Select search input (controlled). */
|
|
11
|
+
inputValue?: string;
|
|
12
|
+
/** The default value of the Select search input (uncontrolled). */
|
|
13
|
+
defaultInputValue?: string;
|
|
14
|
+
/** Handler that is called when the Select search input value changes. */
|
|
15
|
+
onInputChange?: (value: string) => void;
|
|
16
|
+
/** Additional CSS-classes. */
|
|
17
|
+
className?: string;
|
|
18
|
+
/** Inline styles. */
|
|
19
|
+
style?: CSSProperties;
|
|
20
|
+
/** The load more spinner to render when loading additional items. */
|
|
21
|
+
isLoading?: boolean;
|
|
22
|
+
/** Handler that is called when more items should be loaded, e.g. while scrolling near the bottom. */
|
|
23
|
+
onLoadMore?: () => void;
|
|
24
|
+
/** Content to display when no items are available. */
|
|
25
|
+
noItemsText?: ReactNode;
|
|
26
|
+
/** Content to display when items are loading. */
|
|
27
|
+
loadingText?: ReactNode;
|
|
28
|
+
/** Enables search input for filtering items in the list. */
|
|
29
|
+
isSearchable?: boolean;
|
|
30
|
+
/** The props used for each slot inside. */
|
|
31
|
+
slotProps?: {
|
|
32
|
+
divider?: DividerProps;
|
|
33
|
+
root?: ComponentPropsWithRef<'div'>;
|
|
34
|
+
'search-input'?: SearchInputProps;
|
|
35
|
+
};
|
|
36
|
+
} & Omit<AriaListBoxProps<T>, 'children'>;
|
|
37
|
+
export declare function SelectList<T extends object, M extends SelectionMode = 'single'>(props: SelectListProps<T, M>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { useLocalizedStringFormatter, useFilter, useControlledState, useMultiRef, mergeProps, clsx } from "@koobiq/react-core";
|
|
5
|
+
import { useAutocompleteState, useAutocomplete, UNSTABLE_useFilteredListState, useListBox } from "@koobiq/react-primitives";
|
|
6
|
+
import { utilClasses } from "../../../../styles/utility.js";
|
|
7
|
+
import intlMessages from "../../intl.js";
|
|
8
|
+
import { SelectContext } from "../../SelectContext.js";
|
|
9
|
+
import { CollectionRoot } from "../../utils.js";
|
|
10
|
+
import s from "./SelectList.module.css.js";
|
|
11
|
+
import { SearchInput } from "../../../SearchInput/SearchInput.js";
|
|
12
|
+
import { Divider } from "../../../Divider/Divider.js";
|
|
13
|
+
import { ListEmptyState } from "../../../List/components/ListEmptyState/ListEmptyState.js";
|
|
14
|
+
import { ListLoadingState } from "../../../List/components/ListLoadingState/ListLoadingState.js";
|
|
15
|
+
const { list } = utilClasses;
|
|
16
|
+
function SelectList(props) {
|
|
17
|
+
const {
|
|
18
|
+
style,
|
|
19
|
+
isLoading,
|
|
20
|
+
className,
|
|
21
|
+
onLoadMore,
|
|
22
|
+
slotProps,
|
|
23
|
+
inputValue,
|
|
24
|
+
isSearchable,
|
|
25
|
+
onInputChange,
|
|
26
|
+
defaultFilter,
|
|
27
|
+
state: inState,
|
|
28
|
+
defaultInputValue,
|
|
29
|
+
noItemsText: noItemsTextProp,
|
|
30
|
+
loadingText: loadingTextProp
|
|
31
|
+
} = props;
|
|
32
|
+
const t = useLocalizedStringFormatter(intlMessages);
|
|
33
|
+
const domRef = useRef(null);
|
|
34
|
+
const inputRef = useRef(null);
|
|
35
|
+
const collectionRef = useRef(null);
|
|
36
|
+
const { contains } = useFilter({ sensitivity: "base" });
|
|
37
|
+
const [filterText, setFilterText] = useControlledState(
|
|
38
|
+
inputValue,
|
|
39
|
+
defaultInputValue ?? "",
|
|
40
|
+
onInputChange
|
|
41
|
+
);
|
|
42
|
+
const noItemsText = (() => {
|
|
43
|
+
if (noItemsTextProp !== void 0) return noItemsTextProp;
|
|
44
|
+
const hasQuery = isSearchable && filterText.trim().length > 0;
|
|
45
|
+
return hasQuery ? t.format("nothing found") : t.format("empty items");
|
|
46
|
+
})();
|
|
47
|
+
const autocompleteState = useAutocompleteState({
|
|
48
|
+
inputValue: isSearchable ? filterText : "",
|
|
49
|
+
onInputChange: isSearchable ? setFilterText : () => {
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const {
|
|
53
|
+
inputProps,
|
|
54
|
+
collectionProps,
|
|
55
|
+
filter: filterFn,
|
|
56
|
+
collectionRef: mergedCollectionRef
|
|
57
|
+
} = useAutocomplete(
|
|
58
|
+
{
|
|
59
|
+
inputRef,
|
|
60
|
+
collectionRef,
|
|
61
|
+
filter: defaultFilter || contains
|
|
62
|
+
},
|
|
63
|
+
autocompleteState
|
|
64
|
+
);
|
|
65
|
+
const listRef = useMultiRef([mergedCollectionRef, domRef]);
|
|
66
|
+
const state = UNSTABLE_useFilteredListState(
|
|
67
|
+
inState,
|
|
68
|
+
isSearchable ? filterFn : null
|
|
69
|
+
);
|
|
70
|
+
const isEmpty = state.collection.size === 0;
|
|
71
|
+
const { listBoxProps } = useListBox(
|
|
72
|
+
mergeProps(props, isSearchable ? collectionProps : null),
|
|
73
|
+
state,
|
|
74
|
+
domRef
|
|
75
|
+
);
|
|
76
|
+
const rootProps = mergeProps({ className: s.base }, slotProps?.root);
|
|
77
|
+
const listProps = mergeProps(
|
|
78
|
+
{
|
|
79
|
+
style,
|
|
80
|
+
ref: listRef,
|
|
81
|
+
"data-padded": true,
|
|
82
|
+
className: clsx(list, className)
|
|
83
|
+
},
|
|
84
|
+
listBoxProps
|
|
85
|
+
);
|
|
86
|
+
const searchInputProps = mergeProps(
|
|
87
|
+
{
|
|
88
|
+
autoFocus: true,
|
|
89
|
+
fullWidth: true,
|
|
90
|
+
isLabelHidden: true,
|
|
91
|
+
className: s.search,
|
|
92
|
+
placeholder: t.format("search"),
|
|
93
|
+
"aria-label": t.format("search"),
|
|
94
|
+
variant: "transparent"
|
|
95
|
+
},
|
|
96
|
+
slotProps?.["search-input"],
|
|
97
|
+
inputProps
|
|
98
|
+
);
|
|
99
|
+
const loadingText = loadingTextProp ?? t.format("loading");
|
|
100
|
+
const { collection } = state;
|
|
101
|
+
return /* @__PURE__ */ jsxs("div", { ...rootProps, children: [
|
|
102
|
+
isSearchable && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103
|
+
/* @__PURE__ */ jsx(SearchInput, { ref: inputRef, ...searchInputProps }),
|
|
104
|
+
/* @__PURE__ */ jsx(Divider, { disablePaddings: true, ...slotProps?.divider })
|
|
105
|
+
] }),
|
|
106
|
+
/* @__PURE__ */ jsxs("ul", { ...listProps, children: [
|
|
107
|
+
/* @__PURE__ */ jsx(SelectContext.Provider, { value: state, children: /* @__PURE__ */ jsx(CollectionRoot, { collection }) }),
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
ListEmptyState,
|
|
110
|
+
{
|
|
111
|
+
isEmpty,
|
|
112
|
+
isLoading,
|
|
113
|
+
noItemsText
|
|
114
|
+
}
|
|
115
|
+
),
|
|
116
|
+
/* @__PURE__ */ jsx(
|
|
117
|
+
ListLoadingState,
|
|
118
|
+
{
|
|
119
|
+
root: domRef.current,
|
|
120
|
+
isLoading,
|
|
121
|
+
onLoadMore,
|
|
122
|
+
loadingText,
|
|
123
|
+
observeDeps: [state.collection]
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
] })
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
export {
|
|
130
|
+
SelectList
|
|
131
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SelectList';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Key, ExtendableComponentPropsWithRef } from '@koobiq/react-core';
|
|
2
|
+
export type SelectOptionProps<T = object> = ExtendableComponentPropsWithRef<{
|
|
3
|
+
className?: string;
|
|
4
|
+
/** The unique id of the item. */
|
|
5
|
+
id?: Key;
|
|
6
|
+
/** The object value that this item represents. When using dynamic collections, this is set automatically. */
|
|
7
|
+
value?: T;
|
|
8
|
+
/** A string representation of the item's contents, used for features like typeahead. */
|
|
9
|
+
textValue?: string;
|
|
10
|
+
/** An accessibility label for this item. */
|
|
11
|
+
'aria-label'?: string;
|
|
12
|
+
/** Whether the item is disabled. */
|
|
13
|
+
isDisabled?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Handler that is called when a user performs an action on the item. The exact user event depends on
|
|
16
|
+
* the collection's `selectionBehavior` prop and the interaction modality.
|
|
17
|
+
*/
|
|
18
|
+
onAction?: () => void;
|
|
19
|
+
}, 'a'>;
|
|
20
|
+
export declare const SelectOption: <T extends object>(props: Omit<import("react").DetailedHTMLProps<import("react").AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, "value" | "className" | "id" | "aria-label" | "isDisabled" | "textValue" | "onAction"> & {
|
|
21
|
+
className?: string;
|
|
22
|
+
/** The unique id of the item. */
|
|
23
|
+
id?: Key;
|
|
24
|
+
/** The object value that this item represents. When using dynamic collections, this is set automatically. */
|
|
25
|
+
value?: object | undefined;
|
|
26
|
+
/** A string representation of the item's contents, used for features like typeahead. */
|
|
27
|
+
textValue?: string;
|
|
28
|
+
/** An accessibility label for this item. */
|
|
29
|
+
'aria-label'?: string;
|
|
30
|
+
/** Whether the item is disabled. */
|
|
31
|
+
isDisabled?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Handler that is called when a user performs an action on the item. The exact user event depends on
|
|
34
|
+
* the collection's `selectionBehavior` prop and the interaction modality.
|
|
35
|
+
*/
|
|
36
|
+
onAction?: () => void;
|
|
37
|
+
} & import("react").RefAttributes<HTMLElement>) => import("react").ReactElement | null;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useContext } from "react";
|
|
4
|
+
import { useMultiRef, useHover, usePress, filterDOMProps, clsx, mergeProps } from "@koobiq/react-core";
|
|
5
|
+
import { createLeafComponent, ItemNode, useOption } from "@koobiq/react-primitives";
|
|
6
|
+
import { utilClasses } from "../../../../styles/utility.js";
|
|
7
|
+
import { SelectContext } from "../../SelectContext.js";
|
|
8
|
+
import { Checkbox } from "../../../Checkbox/Checkbox.js";
|
|
9
|
+
const textVariant = utilClasses.typography;
|
|
10
|
+
const { listItem } = utilClasses;
|
|
11
|
+
const SelectOption = createLeafComponent(ItemNode, function SelectItem(props, forwardedRef, item) {
|
|
12
|
+
const { href, className, style } = props;
|
|
13
|
+
const domRef = useRef(null);
|
|
14
|
+
const ref = useMultiRef([forwardedRef, domRef]);
|
|
15
|
+
const state = useContext(SelectContext);
|
|
16
|
+
const { optionProps, isSelected, isDisabled, isFocusVisible } = useOption(
|
|
17
|
+
{ key: item.key },
|
|
18
|
+
state,
|
|
19
|
+
domRef
|
|
20
|
+
);
|
|
21
|
+
const { hoverProps, isHovered } = useHover({ isDisabled });
|
|
22
|
+
const { isPressed, pressProps } = usePress({ isDisabled });
|
|
23
|
+
const Tag = href ? "a" : "li";
|
|
24
|
+
const DOMProps = filterDOMProps(props, { global: true });
|
|
25
|
+
delete DOMProps.id;
|
|
26
|
+
delete DOMProps.onClick;
|
|
27
|
+
return /* @__PURE__ */ jsxs(
|
|
28
|
+
Tag,
|
|
29
|
+
{
|
|
30
|
+
ref,
|
|
31
|
+
style,
|
|
32
|
+
"data-hovered": isHovered || void 0,
|
|
33
|
+
"data-pressed": isPressed || void 0,
|
|
34
|
+
"data-disabled": isDisabled || void 0,
|
|
35
|
+
"data-selected": isSelected || void 0,
|
|
36
|
+
"data-focus-visible": isFocusVisible || void 0,
|
|
37
|
+
...mergeProps(optionProps, hoverProps, pressProps),
|
|
38
|
+
className: clsx(listItem, textVariant["text-normal"], className),
|
|
39
|
+
children: [
|
|
40
|
+
state.selectionManager.selectionMode === "multiple" && /* @__PURE__ */ jsx(Checkbox, { isDisabled, isSelected, isReadOnly: true }),
|
|
41
|
+
item.rendered
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
export {
|
|
47
|
+
SelectOption
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SelectOption';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Key, SectionProps, ExtendableComponentPropsWithRef } from '@koobiq/react-core';
|
|
2
|
+
export type SelectSectionProps<T> = ExtendableComponentPropsWithRef<SectionProps<T> & {
|
|
3
|
+
/** The unique id of the item. */
|
|
4
|
+
id?: Key;
|
|
5
|
+
}, 'section'>;
|
|
6
|
+
export declare const SelectSection: <T extends object>(props: Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLElement>, HTMLElement>, "id" | keyof SectionProps<T_1>> & SectionProps<T> & {
|
|
7
|
+
/** The unique id of the item. */
|
|
8
|
+
id?: Key;
|
|
9
|
+
} & import("react").RefAttributes<HTMLElement>) => import("react").ReactElement | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useContext } from "react";
|
|
4
|
+
import { filterDOMProps, mergeProps } from "@koobiq/react-core";
|
|
5
|
+
import { createBranchComponent, SectionNode, useListBoxSection } from "@koobiq/react-primitives";
|
|
6
|
+
import { utilClasses } from "../../../../styles/utility.js";
|
|
7
|
+
import { SelectContext } from "../../SelectContext.js";
|
|
8
|
+
import { CollectionBranch } from "../../utils.js";
|
|
9
|
+
import { Typography } from "../../../Typography/Typography.js";
|
|
10
|
+
const { listHeading } = utilClasses;
|
|
11
|
+
function SelectSectionInner(props, ref, section) {
|
|
12
|
+
const state = useContext(SelectContext);
|
|
13
|
+
const { className, style } = props;
|
|
14
|
+
const { headingProps, groupProps } = useListBoxSection({
|
|
15
|
+
heading: section.rendered,
|
|
16
|
+
"aria-label": props["aria-label"] ?? void 0
|
|
17
|
+
});
|
|
18
|
+
const DOMProps = filterDOMProps(props, { global: true });
|
|
19
|
+
delete DOMProps.id;
|
|
20
|
+
return /* @__PURE__ */ jsxs(
|
|
21
|
+
"section",
|
|
22
|
+
{
|
|
23
|
+
...mergeProps(DOMProps, groupProps),
|
|
24
|
+
className,
|
|
25
|
+
style,
|
|
26
|
+
ref,
|
|
27
|
+
children: [
|
|
28
|
+
/* @__PURE__ */ jsx(
|
|
29
|
+
Typography,
|
|
30
|
+
{
|
|
31
|
+
as: "span",
|
|
32
|
+
display: "block",
|
|
33
|
+
variant: "caps-compact-strong",
|
|
34
|
+
color: "contrast-secondary",
|
|
35
|
+
className: listHeading,
|
|
36
|
+
...headingProps,
|
|
37
|
+
children: props.title
|
|
38
|
+
}
|
|
39
|
+
),
|
|
40
|
+
/* @__PURE__ */ jsx(CollectionBranch, { collection: state.collection, parent: section })
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
const SelectSection = createBranchComponent(
|
|
46
|
+
SectionNode,
|
|
47
|
+
SelectSectionInner
|
|
48
|
+
);
|
|
49
|
+
export {
|
|
50
|
+
SelectSection
|
|
51
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SelectSection';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import type { ExtendableComponentPropsWithRef } from '@koobiq/react-core';
|
|
3
|
+
import type { TagGroupPropVariant } from '../../../TagGroup';
|
|
4
|
+
type TagProps = ExtendableComponentPropsWithRef<{
|
|
5
|
+
/**
|
|
6
|
+
* The variant to use.
|
|
7
|
+
* @default 'theme-fade'
|
|
8
|
+
*/
|
|
9
|
+
variant?: TagGroupPropVariant;
|
|
10
|
+
className?: string;
|
|
11
|
+
style?: CSSProperties;
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
icon?: ReactNode;
|
|
14
|
+
isDisabled?: boolean;
|
|
15
|
+
onRemove?: () => void;
|
|
16
|
+
}, 'div'>;
|
|
17
|
+
export declare const Tag: import("react").ForwardRefExoticComponent<Omit<TagProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
18
|
+
export {};
|