@impulse-ui-native/select 2.0.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.
@@ -0,0 +1,49 @@
1
+ import { ReactElement } from 'react';
2
+ import { FlyoutProps } from '@impulse-ui-native/flyout';
3
+ import { ControlComponentProps, ControlComponentCommonProps, PressableCoreProps } from '@impulse-ui-native/primitives';
4
+
5
+ type PrimitiveValue = string | number | null | undefined;
6
+ type SelectFn<Value extends PrimitiveValue> = (option: SelectOption<Value>) => void;
7
+ type SelectOption<Value> = {
8
+ label: string;
9
+ value: Value;
10
+ disabled?: boolean;
11
+ };
12
+ interface CommonSelectProps<Value> extends Partial<ControlComponentProps>, ControlComponentCommonProps {
13
+ loading?: boolean;
14
+ onChange?: (value: Value | undefined) => void;
15
+ }
16
+ interface SelectControlProps<Value> extends CommonSelectProps<Value> {
17
+ onPress: PressableCoreProps["onPress"];
18
+ }
19
+ interface SelectProps<Value extends PrimitiveValue> extends CommonSelectProps<Value> {
20
+ options: SelectOption<Value>[];
21
+ value?: Value;
22
+ defaultValue?: Value;
23
+ }
24
+ interface MultiSelectProps<Value extends PrimitiveValue> extends Partial<ControlComponentProps>, CommonSelectProps<Value[]> {
25
+ options: SelectOption<Value>[];
26
+ value?: Value[];
27
+ defaultValue?: Value[];
28
+ }
29
+ interface SelectFlyoutProps<Value extends PrimitiveValue> extends FlyoutProps {
30
+ options: SelectOption<Value>[];
31
+ isSelected: (value: Value) => boolean;
32
+ onSelect: (value: Value) => void;
33
+ }
34
+ interface OptionProps extends PressableCoreProps {
35
+ label: string;
36
+ selected: boolean;
37
+ }
38
+ interface UseSelectProps<Value> {
39
+ options: SelectOption<PrimitiveValue>[];
40
+ value?: Value;
41
+ defaultValue?: Value;
42
+ onChange?: (value: Value | undefined) => void;
43
+ }
44
+
45
+ declare const Select: <Value extends PrimitiveValue>(props: SelectProps<Value>) => ReactElement | null;
46
+
47
+ declare const MultiSelect: <Value extends PrimitiveValue>(props: MultiSelectProps<Value>) => ReactElement | null;
48
+
49
+ export { type CommonSelectProps, MultiSelect, type MultiSelectProps, type OptionProps, type PrimitiveValue, Select, type SelectControlProps, type SelectFlyoutProps, type SelectFn, type SelectOption, type SelectProps, type UseSelectProps };
package/dist/index.js ADDED
@@ -0,0 +1,337 @@
1
+ // src/components/select.tsx
2
+ import { memo as memo4, useCallback as useCallback3, useId } from "react";
3
+ import { useIsOpen } from "@impulse-ui-native/core";
4
+ import { Control as Control2 } from "@impulse-ui-native/primitives";
5
+
6
+ // src/hooks/use-select.hook.ts
7
+ import { useCallback, useMemo } from "react";
8
+ import { useControllableState } from "@impulse-ui-native/core";
9
+
10
+ // src/utils/option.utils.ts
11
+ function buildLabelMap(options) {
12
+ return options.reduce((acc, current) => {
13
+ if (current.value !== null && current.value !== void 0) {
14
+ acc[String(current.value)] = current.label;
15
+ return acc;
16
+ }
17
+ return acc;
18
+ }, {});
19
+ }
20
+
21
+ // src/hooks/use-select.hook.ts
22
+ function useSelect(props) {
23
+ const { value, defaultValue, onChange } = props;
24
+ const [selected, setSelected] = useControllableState({
25
+ prop: value,
26
+ defaultProp: defaultValue,
27
+ onChange
28
+ });
29
+ const labelsMap = useMemo(
30
+ () => buildLabelMap(props.options),
31
+ [props.options]
32
+ );
33
+ const getLabel = useCallback(
34
+ (value2) => labelsMap[String(value2)],
35
+ [labelsMap]
36
+ );
37
+ const select = useCallback(
38
+ (value2) => {
39
+ setSelected((prev) => {
40
+ if (prev === value2) {
41
+ return null;
42
+ }
43
+ return value2;
44
+ });
45
+ },
46
+ [setSelected]
47
+ );
48
+ const isSelected = useCallback(
49
+ (value2) => value2 === selected,
50
+ [selected]
51
+ );
52
+ return {
53
+ selected,
54
+ hasSelected: selected !== null && selected !== void 0,
55
+ select,
56
+ getLabel,
57
+ isSelected
58
+ };
59
+ }
60
+
61
+ // src/components/select-control.tsx
62
+ import { memo } from "react";
63
+ import { Control, Pressable } from "@impulse-ui-native/primitives";
64
+ import { jsx, jsxs } from "react/jsx-runtime";
65
+ function SelectControlComponent(props) {
66
+ const {
67
+ size = "medium",
68
+ variant = "outlined",
69
+ label,
70
+ error,
71
+ disabled,
72
+ loading,
73
+ PrefixIcon,
74
+ Prefix,
75
+ onPressPrefix,
76
+ SuffixIcon,
77
+ Suffix,
78
+ onPressSuffix,
79
+ onPress,
80
+ children
81
+ } = props;
82
+ return /* @__PURE__ */ jsx(
83
+ Control.Provider,
84
+ {
85
+ size,
86
+ variant,
87
+ error,
88
+ disabled,
89
+ children: /* @__PURE__ */ jsxs(Control.Root, { children: [
90
+ /* @__PURE__ */ jsx(Control.Label, { children: label }),
91
+ /* @__PURE__ */ jsx(Pressable, { onPress, children: /* @__PURE__ */ jsxs(Control.Container, { children: [
92
+ /* @__PURE__ */ jsx(
93
+ Control.Addon,
94
+ {
95
+ icon: PrefixIcon,
96
+ Content: Prefix,
97
+ onPress: onPressPrefix
98
+ }
99
+ ),
100
+ children,
101
+ /* @__PURE__ */ jsx(
102
+ Control.Addon,
103
+ {
104
+ icon: SuffixIcon,
105
+ Content: loading ? Control.Loader : Suffix,
106
+ onPress: onPressSuffix
107
+ }
108
+ )
109
+ ] }) }),
110
+ /* @__PURE__ */ jsx(Control.Error, {})
111
+ ] })
112
+ }
113
+ );
114
+ }
115
+ var SelectControl = memo(SelectControlComponent);
116
+
117
+ // src/components/select.flyout.tsx
118
+ import { memo as memo3, useCallback as useCallback2 } from "react";
119
+ import { FlatList } from "react-native";
120
+ import { Flyout } from "@impulse-ui-native/flyout";
121
+ import { Portal } from "@impulse-ui-native/portal";
122
+ import { View as View2 } from "@impulse-ui-native/primitives";
123
+
124
+ // src/components/option.tsx
125
+ import { memo as memo2 } from "react";
126
+ import { CheckIcon, Icon } from "@impulse-ui-native/icon";
127
+ import { Pressable as Pressable2, Typography, View } from "@impulse-ui-native/primitives";
128
+ import { useComponentsTokens } from "@impulse-ui-native/theme";
129
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
130
+ var Option = memo2(function Option2(props) {
131
+ const { label, selected, ...rest } = props;
132
+ const tokens = useComponentsTokens();
133
+ const optionTokens = tokens.select.option;
134
+ return /* @__PURE__ */ jsx2(Pressable2, { ...rest, children: /* @__PURE__ */ jsxs2(
135
+ View,
136
+ {
137
+ padding: optionTokens.padding,
138
+ flexDirection: "row",
139
+ alignItems: "center",
140
+ justifyContent: "space-between",
141
+ children: [
142
+ /* @__PURE__ */ jsx2(Typography.Body, { children: label }),
143
+ /* @__PURE__ */ jsx2(
144
+ Icon,
145
+ {
146
+ icon: CheckIcon,
147
+ color: optionTokens.selectedIconColor,
148
+ opacity: selected ? optionTokens.selectedIconVisibleOpacity : optionTokens.selectedIconHiddenOpacity
149
+ }
150
+ )
151
+ ]
152
+ }
153
+ ) });
154
+ });
155
+
156
+ // src/components/select.flyout.tsx
157
+ import { jsx as jsx3 } from "react/jsx-runtime";
158
+ var SelectFlyoutComponent = function SelectFlyoutContent(props) {
159
+ const { options, isSelected, onSelect, ...rest } = props;
160
+ const renderItem = useCallback2(
161
+ ({ item }) => {
162
+ return /* @__PURE__ */ jsx3(
163
+ Option,
164
+ {
165
+ label: item.label,
166
+ onPress: () => onSelect(item.value),
167
+ disabled: item.disabled,
168
+ selected: isSelected(item.value)
169
+ }
170
+ );
171
+ },
172
+ [isSelected, onSelect]
173
+ );
174
+ return /* @__PURE__ */ jsx3(Portal, { children: /* @__PURE__ */ jsx3(Flyout, { ...rest, children: /* @__PURE__ */ jsx3(View2, { height: 300, children: /* @__PURE__ */ jsx3(
175
+ FlatList,
176
+ {
177
+ data: options,
178
+ renderItem,
179
+ keyExtractor: getOptionId2,
180
+ showsHorizontalScrollIndicator: false,
181
+ showsVerticalScrollIndicator: false,
182
+ keyboardShouldPersistTaps: "handled",
183
+ removeClippedSubviews: true,
184
+ windowSize: 5,
185
+ initialNumToRender: 12,
186
+ maxToRenderPerBatch: 12,
187
+ updateCellsBatchingPeriod: 32
188
+ }
189
+ ) }) }) });
190
+ };
191
+ var SelectFlyout = memo3(SelectFlyoutComponent);
192
+
193
+ // src/components/select.tsx
194
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
195
+ function SelectComponent(props) {
196
+ const { options, value, defaultValue, onChange, placeholder, ...rest } = props;
197
+ const { isOpen, open, close } = useIsOpen();
198
+ const id = useId();
199
+ const { selected, hasSelected, select, isSelected, getLabel } = useSelect({
200
+ options,
201
+ value,
202
+ defaultValue,
203
+ onChange
204
+ });
205
+ const handleOnSelect = useCallback3(
206
+ (value2) => {
207
+ select(value2);
208
+ close();
209
+ },
210
+ [select, close]
211
+ );
212
+ return /* @__PURE__ */ jsxs3(SelectControl, { ...rest, onPress: open, children: [
213
+ !hasSelected ? /* @__PURE__ */ jsx4(Control2.Placeholder, { children: placeholder }) : null,
214
+ selected !== null && selected !== void 0 ? /* @__PURE__ */ jsx4(Control2.Value, { children: getLabel(selected) }) : null,
215
+ /* @__PURE__ */ jsx4(
216
+ SelectFlyout,
217
+ {
218
+ id,
219
+ title: "Options",
220
+ open: isOpen,
221
+ options,
222
+ isSelected,
223
+ onCloseFinished: close,
224
+ onSelect: handleOnSelect
225
+ }
226
+ )
227
+ ] });
228
+ }
229
+ var Select = memo4(SelectComponent);
230
+
231
+ // src/components/multi-select.tsx
232
+ import { memo as memo5, useId as useId2 } from "react";
233
+ import { useIsOpen as useIsOpen2 } from "@impulse-ui-native/core";
234
+ import { Control as Control3, Tag, View as View3 } from "@impulse-ui-native/primitives";
235
+
236
+ // src/hooks/use-multi-select.hook.ts
237
+ import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
238
+ import { useControllableState as useControllableState2 } from "@impulse-ui-native/core";
239
+ function useMultiSelect(props) {
240
+ const { value, defaultValue, onChange } = props;
241
+ const [selected, setSelected] = useControllableState2({
242
+ prop: value,
243
+ defaultProp: defaultValue,
244
+ onChange
245
+ });
246
+ const selectedValuesSet = useMemo2(() => new Set(selected), [selected]);
247
+ const labelsMap = useMemo2(
248
+ () => buildLabelMap(props.options),
249
+ [props.options]
250
+ );
251
+ const getLabel = useCallback4(
252
+ (value2) => labelsMap[String(value2)],
253
+ [labelsMap]
254
+ );
255
+ const select = useCallback4(
256
+ (value2) => {
257
+ setSelected((prev) => {
258
+ if (!prev) {
259
+ return [value2];
260
+ }
261
+ const exists = prev.includes(value2);
262
+ if (exists) {
263
+ return prev.filter((prevValue) => prevValue !== value2);
264
+ }
265
+ return [...prev, value2];
266
+ });
267
+ },
268
+ [setSelected]
269
+ );
270
+ const isSelected = useCallback4(
271
+ (value2) => selectedValuesSet.has(value2),
272
+ [selectedValuesSet]
273
+ );
274
+ return {
275
+ selected,
276
+ hasSelected: selected && selected.length > 0,
277
+ select,
278
+ isSelected,
279
+ getLabel
280
+ };
281
+ }
282
+
283
+ // src/components/multi-select.tsx
284
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
285
+ function MultiSelectComponent(props) {
286
+ const { options, value, defaultValue, onChange, placeholder, ...rest } = props;
287
+ const { isOpen, open, close } = useIsOpen2();
288
+ const id = useId2();
289
+ const { selected, hasSelected, select, isSelected, getLabel } = useMultiSelect({
290
+ options,
291
+ value,
292
+ defaultValue,
293
+ onChange
294
+ });
295
+ return /* @__PURE__ */ jsxs4(SelectControl, { ...rest, onPress: open, children: [
296
+ !hasSelected ? /* @__PURE__ */ jsx5(Control3.Placeholder, { children: placeholder }) : null,
297
+ hasSelected ? /* @__PURE__ */ jsx5(
298
+ View3,
299
+ {
300
+ flex: 1,
301
+ flexDirection: "row",
302
+ gap: 4,
303
+ marginHorizontal: 4,
304
+ overflow: "hidden",
305
+ children: selected ? selected.map((value2) => /* @__PURE__ */ jsx5(
306
+ Tag,
307
+ {
308
+ variant: "filled",
309
+ size: props.size,
310
+ closable: true,
311
+ onClose: () => select(value2),
312
+ label: String(getLabel(value2))
313
+ },
314
+ value2
315
+ )) : null
316
+ }
317
+ ) : null,
318
+ /* @__PURE__ */ jsx5(
319
+ SelectFlyout,
320
+ {
321
+ id,
322
+ title: "Options",
323
+ open: isOpen,
324
+ options,
325
+ isSelected,
326
+ onCloseFinished: close,
327
+ onSelect: select
328
+ }
329
+ )
330
+ ] });
331
+ }
332
+ var MultiSelect = memo5(MultiSelectComponent);
333
+ export {
334
+ MultiSelect,
335
+ Select
336
+ };
337
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/select.tsx","../src/hooks/use-select.hook.ts","../src/utils/option.utils.ts","../src/components/select-control.tsx","../src/components/select.flyout.tsx","../src/components/option.tsx","../src/components/multi-select.tsx","../src/hooks/use-multi-select.hook.ts"],"sourcesContent":["import { memo, ReactElement, useCallback, useId } from \"react\";\r\n\r\nimport { useIsOpen } from \"@impulse-ui-native/core\";\r\nimport { Control } from \"@impulse-ui-native/primitives\";\r\n\r\nimport { useSelect } from \"../hooks/use-select.hook\";\r\nimport { PrimitiveValue, SelectProps } from \"../types\";\r\nimport { SelectControl } from \"./select-control\";\r\nimport { SelectFlyout } from \"./select.flyout\";\r\n\r\nfunction SelectComponent<Value extends PrimitiveValue>(\r\n props: SelectProps<Value>,\r\n) {\r\n const { options, value, defaultValue, onChange, placeholder, ...rest } =\r\n props;\r\n const { isOpen, open, close } = useIsOpen();\r\n\r\n const id = useId();\r\n\r\n const { selected, hasSelected, select, isSelected, getLabel } = useSelect({\r\n options,\r\n value,\r\n defaultValue,\r\n onChange,\r\n });\r\n\r\n const handleOnSelect = useCallback(\r\n (value: Value) => {\r\n select(value);\r\n close();\r\n },\r\n [select, close],\r\n );\r\n\r\n return (\r\n <SelectControl {...rest} onPress={open}>\r\n {!hasSelected ? (\r\n <Control.Placeholder>{placeholder}</Control.Placeholder>\r\n ) : null}\r\n {selected !== null && selected !== undefined ? (\r\n <Control.Value>{getLabel(selected)}</Control.Value>\r\n ) : null}\r\n <SelectFlyout\r\n id={id}\r\n title=\"Options\"\r\n open={isOpen}\r\n options={options}\r\n isSelected={isSelected}\r\n onCloseFinished={close}\r\n onSelect={handleOnSelect}\r\n />\r\n </SelectControl>\r\n );\r\n}\r\n\r\nexport const Select = memo(SelectComponent) as <Value extends PrimitiveValue>(\r\n props: SelectProps<Value>,\r\n) => ReactElement | null;\r\n","import { useCallback, useMemo } from \"react\";\r\n\r\nimport { useControllableState } from \"@impulse-ui-native/core\";\r\n\r\nimport { PrimitiveValue, UseSelectProps } from \"../types\";\r\nimport { buildLabelMap } from \"../utils/option.utils\";\r\n\r\nexport function useSelect<Value extends PrimitiveValue>(\r\n props: UseSelectProps<Value>,\r\n) {\r\n const { value, defaultValue, onChange } = props;\r\n\r\n const [selected, setSelected] = useControllableState({\r\n prop: value,\r\n defaultProp: defaultValue,\r\n onChange,\r\n });\r\n\r\n const labelsMap = useMemo(\r\n () => buildLabelMap(props.options),\r\n [props.options],\r\n );\r\n\r\n const getLabel = useCallback(\r\n (value: Value) => labelsMap[String(value)],\r\n [labelsMap],\r\n );\r\n\r\n const select = useCallback(\r\n (value: Value) => {\r\n setSelected((prev) => {\r\n if (prev === value) {\r\n return null as Value;\r\n }\r\n\r\n return value;\r\n });\r\n },\r\n [setSelected],\r\n );\r\n\r\n const isSelected = useCallback(\r\n (value: Value) => value === selected,\r\n [selected],\r\n );\r\n\r\n return {\r\n selected,\r\n hasSelected: selected !== null && selected !== undefined,\r\n select,\r\n getLabel,\r\n isSelected,\r\n };\r\n}\r\n","import { PrimitiveValue, SelectOption } from \"../../dist/index.mjs\";\r\n\r\nexport function buildLabelMap<Value extends PrimitiveValue>(\r\n options: SelectOption<Value>[],\r\n) {\r\n return options.reduce<Record<string, string>>((acc, current) => {\r\n if (current.value !== null && current.value !== undefined) {\r\n acc[String(current.value)] = current.label;\r\n\r\n return acc;\r\n }\r\n\r\n return acc;\r\n }, {});\r\n}\r\n","import { memo, PropsWithChildren, ReactElement } from \"react\";\r\n\r\nimport { Control, Pressable } from \"@impulse-ui-native/primitives\";\r\n\r\nimport { PrimitiveValue, SelectControlProps } from \"../types\";\r\n\r\nfunction SelectControlComponent<Value extends PrimitiveValue>(\r\n props: PropsWithChildren<SelectControlProps<Value>>,\r\n) {\r\n const {\r\n size = \"medium\",\r\n variant = \"outlined\",\r\n label,\r\n error,\r\n disabled,\r\n loading,\r\n PrefixIcon,\r\n Prefix,\r\n onPressPrefix,\r\n SuffixIcon,\r\n Suffix,\r\n onPressSuffix,\r\n onPress,\r\n children,\r\n } = props;\r\n\r\n return (\r\n <Control.Provider\r\n size={size}\r\n variant={variant}\r\n error={error}\r\n disabled={disabled}\r\n >\r\n <Control.Root>\r\n <Control.Label>{label}</Control.Label>\r\n <Pressable onPress={onPress}>\r\n <Control.Container>\r\n <Control.Addon\r\n icon={PrefixIcon}\r\n Content={Prefix}\r\n onPress={onPressPrefix}\r\n />\r\n {children}\r\n <Control.Addon\r\n icon={SuffixIcon}\r\n Content={loading ? Control.Loader : Suffix}\r\n onPress={onPressSuffix}\r\n />\r\n </Control.Container>\r\n </Pressable>\r\n <Control.Error />\r\n </Control.Root>\r\n </Control.Provider>\r\n );\r\n}\r\n\r\nexport const SelectControl = memo(SelectControlComponent) as <\r\n Value extends PrimitiveValue,\r\n>(\r\n props: PropsWithChildren<SelectControlProps<Value>>,\r\n) => ReactElement | null;\r\n","import { memo, ReactElement, useCallback } from \"react\";\r\nimport { FlatList, ListRenderItemInfo } from \"react-native\";\r\n\r\nimport { Flyout } from \"@impulse-ui-native/flyout\";\r\nimport { Portal } from \"@impulse-ui-native/portal\";\r\nimport { View } from \"@impulse-ui-native/primitives\";\r\n\r\nimport { PrimitiveValue, SelectFlyoutProps, SelectOption } from \"../types\";\r\nimport { getOptionId } from \"../utils\";\r\nimport { Option } from \"./option\";\r\n\r\nconst SelectFlyoutComponent = function SelectFlyoutContent<\r\n Value extends PrimitiveValue,\r\n>(props: SelectFlyoutProps<Value>) {\r\n const { options, isSelected, onSelect, ...rest } = props;\r\n\r\n const renderItem = useCallback(\r\n ({ item }: ListRenderItemInfo<SelectOption<Value>>) => {\r\n return (\r\n <Option\r\n label={item.label}\r\n onPress={() => onSelect(item.value)}\r\n disabled={item.disabled}\r\n selected={isSelected(item.value)}\r\n />\r\n );\r\n },\r\n [isSelected, onSelect],\r\n );\r\n\r\n return (\r\n <Portal>\r\n <Flyout {...rest}>\r\n <View height={300}>\r\n <FlatList\r\n data={options}\r\n renderItem={renderItem}\r\n keyExtractor={getOptionId}\r\n showsHorizontalScrollIndicator={false}\r\n showsVerticalScrollIndicator={false}\r\n keyboardShouldPersistTaps=\"handled\"\r\n removeClippedSubviews\r\n windowSize={5}\r\n initialNumToRender={12}\r\n maxToRenderPerBatch={12}\r\n updateCellsBatchingPeriod={32}\r\n />\r\n </View>\r\n </Flyout>\r\n </Portal>\r\n );\r\n};\r\n\r\nexport const SelectFlyout = memo(SelectFlyoutComponent) as <\r\n Value extends PrimitiveValue,\r\n>(\r\n props: SelectFlyoutProps<Value>,\r\n) => ReactElement | null;\r\n","import { memo } from \"react\";\r\n\r\nimport { CheckIcon, Icon } from \"@impulse-ui-native/icon\";\r\nimport { Pressable, Typography, View } from \"@impulse-ui-native/primitives\";\r\nimport { useComponentsTokens } from \"@impulse-ui-native/theme\";\r\n\r\nimport { OptionProps } from \"../types\";\r\n\r\nexport const Option = memo(function Option(props: OptionProps) {\r\n const { label, selected, ...rest } = props;\r\n\r\n const tokens = useComponentsTokens();\r\n const optionTokens = tokens.select.option;\r\n\r\n return (\r\n <Pressable {...rest}>\r\n <View\r\n padding={optionTokens.padding}\r\n flexDirection=\"row\"\r\n alignItems=\"center\"\r\n justifyContent=\"space-between\"\r\n >\r\n <Typography.Body>{label}</Typography.Body>\r\n\r\n <Icon\r\n icon={CheckIcon}\r\n color={optionTokens.selectedIconColor}\r\n opacity={\r\n selected\r\n ? optionTokens.selectedIconVisibleOpacity\r\n : optionTokens.selectedIconHiddenOpacity\r\n }\r\n />\r\n </View>\r\n </Pressable>\r\n );\r\n});\r\n","import { memo, ReactElement, useId } from \"react\";\r\n\r\nimport { useIsOpen } from \"@impulse-ui-native/core\";\r\nimport { Control, Tag, View } from \"@impulse-ui-native/primitives\";\r\n\r\nimport { useMultiSelect } from \"../hooks/use-multi-select.hook\";\r\nimport { MultiSelectProps, PrimitiveValue } from \"../types\";\r\nimport { SelectControl } from \"./select-control\";\r\nimport { SelectFlyout } from \"./select.flyout\";\r\n\r\nfunction MultiSelectComponent<Value extends PrimitiveValue>(\r\n props: MultiSelectProps<Value>,\r\n) {\r\n const { options, value, defaultValue, onChange, placeholder, ...rest } =\r\n props;\r\n const { isOpen, open, close } = useIsOpen();\r\n\r\n const id = useId();\r\n\r\n const { selected, hasSelected, select, isSelected, getLabel } =\r\n useMultiSelect({\r\n options,\r\n value,\r\n defaultValue,\r\n onChange,\r\n });\r\n\r\n return (\r\n <SelectControl {...rest} onPress={open}>\r\n {!hasSelected ? (\r\n <Control.Placeholder>{placeholder}</Control.Placeholder>\r\n ) : null}\r\n {hasSelected ? (\r\n <View\r\n flex={1}\r\n flexDirection=\"row\"\r\n gap={4}\r\n marginHorizontal={4}\r\n overflow=\"hidden\"\r\n >\r\n {selected\r\n ? selected.map((value) => (\r\n <Tag\r\n variant=\"filled\"\r\n size={props.size}\r\n key={value}\r\n closable\r\n onClose={() => select(value)}\r\n label={String(getLabel(value))}\r\n />\r\n ))\r\n : null}\r\n </View>\r\n ) : null}\r\n <SelectFlyout\r\n id={id}\r\n title=\"Options\"\r\n open={isOpen}\r\n options={options}\r\n isSelected={isSelected}\r\n onCloseFinished={close}\r\n onSelect={select}\r\n />\r\n </SelectControl>\r\n );\r\n}\r\n\r\nexport const MultiSelect = memo(MultiSelectComponent) as <\r\n Value extends PrimitiveValue,\r\n>(\r\n props: MultiSelectProps<Value>,\r\n) => ReactElement | null;\r\n","import { useCallback, useMemo } from \"react\";\r\n\r\nimport { useControllableState } from \"@impulse-ui-native/core\";\r\n\r\nimport { PrimitiveValue, UseSelectProps } from \"../types\";\r\nimport { buildLabelMap } from \"../utils/option.utils\";\r\n\r\nexport function useMultiSelect<Value extends PrimitiveValue>(\r\n props: UseSelectProps<Value[]>,\r\n) {\r\n const { value, defaultValue, onChange } = props;\r\n\r\n const [selected, setSelected] = useControllableState({\r\n prop: value,\r\n defaultProp: defaultValue,\r\n onChange,\r\n });\r\n\r\n const selectedValuesSet = useMemo(() => new Set(selected), [selected]);\r\n\r\n const labelsMap = useMemo(\r\n () => buildLabelMap(props.options),\r\n [props.options],\r\n );\r\n\r\n const getLabel = useCallback(\r\n (value: Value) => labelsMap[String(value)],\r\n [labelsMap],\r\n );\r\n\r\n const select = useCallback(\r\n (value: Value) => {\r\n setSelected((prev) => {\r\n if (!prev) {\r\n return [value];\r\n }\r\n\r\n const exists = prev.includes(value);\r\n\r\n if (exists) {\r\n return prev.filter((prevValue) => prevValue !== value);\r\n }\r\n\r\n return [...prev, value];\r\n });\r\n },\r\n [setSelected],\r\n );\r\n\r\n const isSelected = useCallback(\r\n (value: Value) => selectedValuesSet.has(value),\r\n [selectedValuesSet],\r\n );\r\n\r\n return {\r\n selected,\r\n hasSelected: selected && selected.length > 0,\r\n select,\r\n isSelected,\r\n getLabel,\r\n };\r\n}\r\n"],"mappings":";AAAA,SAAS,QAAAA,OAAoB,eAAAC,cAAa,aAAa;AAEvD,SAAS,iBAAiB;AAC1B,SAAS,WAAAC,gBAAe;;;ACHxB,SAAS,aAAa,eAAe;AAErC,SAAS,4BAA4B;;;ACA9B,SAAS,cACd,SACA;AACA,SAAO,QAAQ,OAA+B,CAAC,KAAK,YAAY;AAC9D,QAAI,QAAQ,UAAU,QAAQ,QAAQ,UAAU,QAAW;AACzD,UAAI,OAAO,QAAQ,KAAK,CAAC,IAAI,QAAQ;AAErC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;;;ADPO,SAAS,UACd,OACA;AACA,QAAM,EAAE,OAAO,cAAc,SAAS,IAAI;AAE1C,QAAM,CAAC,UAAU,WAAW,IAAI,qBAAqB;AAAA,IACnD,MAAM;AAAA,IACN,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AAED,QAAM,YAAY;AAAA,IAChB,MAAM,cAAc,MAAM,OAAO;AAAA,IACjC,CAAC,MAAM,OAAO;AAAA,EAChB;AAEA,QAAM,WAAW;AAAA,IACf,CAACC,WAAiB,UAAU,OAAOA,MAAK,CAAC;AAAA,IACzC,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,SAAS;AAAA,IACb,CAACA,WAAiB;AAChB,kBAAY,CAAC,SAAS;AACpB,YAAI,SAASA,QAAO;AAClB,iBAAO;AAAA,QACT;AAEA,eAAOA;AAAA,MACT,CAAC;AAAA,IACH;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,aAAa;AAAA,IACjB,CAACA,WAAiBA,WAAU;AAAA,IAC5B,CAAC,QAAQ;AAAA,EACX;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,aAAa,QAAQ,aAAa;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AErDA,SAAS,YAA6C;AAEtD,SAAS,SAAS,iBAAiB;AAgC3B,cAEE,YAFF;AA5BR,SAAS,uBACP,OACA;AACA,QAAM;AAAA,IACJ,OAAO;AAAA,IACP,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,SACE;AAAA,IAAC,QAAQ;AAAA,IAAR;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEA,+BAAC,QAAQ,MAAR,EACC;AAAA,4BAAC,QAAQ,OAAR,EAAe,iBAAM;AAAA,QACtB,oBAAC,aAAU,SACT,+BAAC,QAAQ,WAAR,EACC;AAAA;AAAA,YAAC,QAAQ;AAAA,YAAR;AAAA,cACC,MAAM;AAAA,cACN,SAAS;AAAA,cACT,SAAS;AAAA;AAAA,UACX;AAAA,UACC;AAAA,UACD;AAAA,YAAC,QAAQ;AAAA,YAAR;AAAA,cACC,MAAM;AAAA,cACN,SAAS,UAAU,QAAQ,SAAS;AAAA,cACpC,SAAS;AAAA;AAAA,UACX;AAAA,WACF,GACF;AAAA,QACA,oBAAC,QAAQ,OAAR,EAAc;AAAA,SACjB;AAAA;AAAA,EACF;AAEJ;AAEO,IAAM,gBAAgB,KAAK,sBAAsB;;;ACxDxD,SAAS,QAAAC,OAAoB,eAAAC,oBAAmB;AAChD,SAAS,gBAAoC;AAE7C,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,QAAAC,aAAY;;;ACLrB,SAAS,QAAAC,aAAY;AAErB,SAAS,WAAW,YAAY;AAChC,SAAS,aAAAC,YAAW,YAAY,YAAY;AAC5C,SAAS,2BAA2B;AAY9B,SAME,OAAAC,MANF,QAAAC,aAAA;AARC,IAAM,SAASH,MAAK,SAASI,QAAO,OAAoB;AAC7D,QAAM,EAAE,OAAO,UAAU,GAAG,KAAK,IAAI;AAErC,QAAM,SAAS,oBAAoB;AACnC,QAAM,eAAe,OAAO,OAAO;AAEnC,SACE,gBAAAF,KAACD,YAAA,EAAW,GAAG,MACb,0BAAAE;AAAA,IAAC;AAAA;AAAA,MACC,SAAS,aAAa;AAAA,MACtB,eAAc;AAAA,MACd,YAAW;AAAA,MACX,gBAAe;AAAA,MAEf;AAAA,wBAAAD,KAAC,WAAW,MAAX,EAAiB,iBAAM;AAAA,QAExB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,OAAO,aAAa;AAAA,YACpB,SACE,WACI,aAAa,6BACb,aAAa;AAAA;AAAA,QAErB;AAAA;AAAA;AAAA,EACF,GACF;AAEJ,CAAC;;;ADjBO,gBAAAG,YAAA;AARR,IAAM,wBAAwB,SAAS,oBAErC,OAAiC;AACjC,QAAM,EAAE,SAAS,YAAY,UAAU,GAAG,KAAK,IAAI;AAEnD,QAAM,aAAaC;AAAA,IACjB,CAAC,EAAE,KAAK,MAA+C;AACrD,aACE,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,KAAK;AAAA,UACZ,SAAS,MAAM,SAAS,KAAK,KAAK;AAAA,UAClC,UAAU,KAAK;AAAA,UACf,UAAU,WAAW,KAAK,KAAK;AAAA;AAAA,MACjC;AAAA,IAEJ;AAAA,IACA,CAAC,YAAY,QAAQ;AAAA,EACvB;AAEA,SACE,gBAAAA,KAAC,UACC,0BAAAA,KAAC,UAAQ,GAAG,MACV,0BAAAA,KAACE,OAAA,EAAK,QAAQ,KACZ,0BAAAF;AAAA,IAAC;AAAA;AAAA,MACC,MAAM;AAAA,MACN;AAAA,MACA,cAAcG;AAAA,MACd,gCAAgC;AAAA,MAChC,8BAA8B;AAAA,MAC9B,2BAA0B;AAAA,MAC1B,uBAAqB;AAAA,MACrB,YAAY;AAAA,MACZ,oBAAoB;AAAA,MACpB,qBAAqB;AAAA,MACrB,2BAA2B;AAAA;AAAA,EAC7B,GACF,GACF,GACF;AAEJ;AAEO,IAAM,eAAeC,MAAK,qBAAqB;;;AJlBlD,SAEI,OAAAC,MAFJ,QAAAC,aAAA;AAzBJ,SAAS,gBACP,OACA;AACA,QAAM,EAAE,SAAS,OAAO,cAAc,UAAU,aAAa,GAAG,KAAK,IACnE;AACF,QAAM,EAAE,QAAQ,MAAM,MAAM,IAAI,UAAU;AAE1C,QAAM,KAAK,MAAM;AAEjB,QAAM,EAAE,UAAU,aAAa,QAAQ,YAAY,SAAS,IAAI,UAAU;AAAA,IACxE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,iBAAiBC;AAAA,IACrB,CAACC,WAAiB;AAChB,aAAOA,MAAK;AACZ,YAAM;AAAA,IACR;AAAA,IACA,CAAC,QAAQ,KAAK;AAAA,EAChB;AAEA,SACE,gBAAAF,MAAC,iBAAe,GAAG,MAAM,SAAS,MAC/B;AAAA,KAAC,cACA,gBAAAD,KAACI,SAAQ,aAAR,EAAqB,uBAAY,IAChC;AAAA,IACH,aAAa,QAAQ,aAAa,SACjC,gBAAAJ,KAACI,SAAQ,OAAR,EAAe,mBAAS,QAAQ,GAAE,IACjC;AAAA,IACJ,gBAAAJ;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,OAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,iBAAiB;AAAA,QACjB,UAAU;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;AAEO,IAAM,SAASK,MAAK,eAAe;;;AMvD1C,SAAS,QAAAC,OAAoB,SAAAC,cAAa;AAE1C,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,WAAAC,UAAS,KAAK,QAAAC,aAAY;;;ACHnC,SAAS,eAAAC,cAAa,WAAAC,gBAAe;AAErC,SAAS,wBAAAC,6BAA4B;AAK9B,SAAS,eACd,OACA;AACA,QAAM,EAAE,OAAO,cAAc,SAAS,IAAI;AAE1C,QAAM,CAAC,UAAU,WAAW,IAAIC,sBAAqB;AAAA,IACnD,MAAM;AAAA,IACN,aAAa;AAAA,IACb;AAAA,EACF,CAAC;AAED,QAAM,oBAAoBC,SAAQ,MAAM,IAAI,IAAI,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAErE,QAAM,YAAYA;AAAA,IAChB,MAAM,cAAc,MAAM,OAAO;AAAA,IACjC,CAAC,MAAM,OAAO;AAAA,EAChB;AAEA,QAAM,WAAWC;AAAA,IACf,CAACC,WAAiB,UAAU,OAAOA,MAAK,CAAC;AAAA,IACzC,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,SAASD;AAAA,IACb,CAACC,WAAiB;AAChB,kBAAY,CAAC,SAAS;AACpB,YAAI,CAAC,MAAM;AACT,iBAAO,CAACA,MAAK;AAAA,QACf;AAEA,cAAM,SAAS,KAAK,SAASA,MAAK;AAElC,YAAI,QAAQ;AACV,iBAAO,KAAK,OAAO,CAAC,cAAc,cAAcA,MAAK;AAAA,QACvD;AAEA,eAAO,CAAC,GAAG,MAAMA,MAAK;AAAA,MACxB,CAAC;AAAA,IACH;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,aAAaD;AAAA,IACjB,CAACC,WAAiB,kBAAkB,IAAIA,MAAK;AAAA,IAC7C,CAAC,iBAAiB;AAAA,EACpB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,aAAa,YAAY,SAAS,SAAS;AAAA,IAC3C;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ADjCI,SAEI,OAAAC,MAFJ,QAAAC,aAAA;AAlBJ,SAAS,qBACP,OACA;AACA,QAAM,EAAE,SAAS,OAAO,cAAc,UAAU,aAAa,GAAG,KAAK,IACnE;AACF,QAAM,EAAE,QAAQ,MAAM,MAAM,IAAIC,WAAU;AAE1C,QAAM,KAAKC,OAAM;AAEjB,QAAM,EAAE,UAAU,aAAa,QAAQ,YAAY,SAAS,IAC1D,eAAe;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAEH,SACE,gBAAAF,MAAC,iBAAe,GAAG,MAAM,SAAS,MAC/B;AAAA,KAAC,cACA,gBAAAD,KAACI,SAAQ,aAAR,EAAqB,uBAAY,IAChC;AAAA,IACH,cACC,gBAAAJ;AAAA,MAACK;AAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,eAAc;AAAA,QACd,KAAK;AAAA,QACL,kBAAkB;AAAA,QAClB,UAAS;AAAA,QAER,qBACG,SAAS,IAAI,CAACC,WACZ,gBAAAN;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,MAAM,MAAM;AAAA,YAEZ,UAAQ;AAAA,YACR,SAAS,MAAM,OAAOM,MAAK;AAAA,YAC3B,OAAO,OAAO,SAASA,MAAK,CAAC;AAAA;AAAA,UAHxBA;AAAA,QAIP,CACD,IACD;AAAA;AAAA,IACN,IACE;AAAA,IACJ,gBAAAN;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,OAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,iBAAiB;AAAA,QACjB,UAAU;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;AAEO,IAAM,cAAcO,MAAK,oBAAoB;","names":["memo","useCallback","Control","value","memo","useCallback","View","memo","Pressable","jsx","jsxs","Option","jsx","useCallback","View","getOptionId","memo","jsx","jsxs","useCallback","value","Control","memo","memo","useId","useIsOpen","Control","View","useCallback","useMemo","useControllableState","useControllableState","useMemo","useCallback","value","jsx","jsxs","useIsOpen","useId","Control","View","value","memo"]}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@impulse-ui-native/select",
3
+ "version": "2.0.0",
4
+ "source": "src/index.ts",
5
+ "module": "dist/index.js",
6
+ "types": "dist/index.d.mts",
7
+ "react-native": "src/index.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.mts",
11
+ "react-native": "./src/index.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "dependencies": {
21
+ "@impulse-ui-native/theme": "2.0.0",
22
+ "@impulse-ui-native/input": "2.0.0",
23
+ "@impulse-ui-native/core": "2.0.0",
24
+ "@impulse-ui-native/icon": "2.0.0",
25
+ "@impulse-ui-native/flyout": "2.0.0",
26
+ "@impulse-ui-native/types": "2.0.0",
27
+ "@impulse-ui-native/layers": "2.0.0",
28
+ "@impulse-ui-native/portal": "2.0.0",
29
+ "@impulse-ui-native/primitives": "2.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@shopify/flash-list": "2.0.2",
33
+ "@types/react": "~19.2.14",
34
+ "eslint": "^9.38.0",
35
+ "prettier": "^3.6.2",
36
+ "react": "19.2.0",
37
+ "react-native": "0.83.6",
38
+ "typescript": "~5.9.2",
39
+ "@impulse-ui-native/eslint-config": "1.0.0",
40
+ "@impulse-ui-native/prettier-config": "1.0.0",
41
+ "@impulse-ui-native/tsconfig": "1.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "react": ">=18",
45
+ "react-native": ">=0.73",
46
+ "@shopify/flash-list": ">=2"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "prettier": "@impulse-ui-native/prettier-config",
52
+ "scripts": {
53
+ "build": "tsup --config tsup.config.ts",
54
+ "typecheck": "tsc -p tsconfig.json --noEmit",
55
+ "lint": "pnpm exec eslint . --fix",
56
+ "format": "prettier --write \"./**/*.{ts,tsx,js,jsx,json,md}\" --ignore-path ../../.prettierignore"
57
+ }
58
+ }