@dt-dds/react-select 1.0.0-beta.46

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/index.mjs ADDED
@@ -0,0 +1,482 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
32
+
33
+ // src/Select.tsx
34
+ import { useClickOutside } from "@dt-dds/react-core";
35
+ import { Icon } from "@dt-dds/react-icon";
36
+ import { IconButton } from "@dt-dds/react-icon-button";
37
+ import { LabelField } from "@dt-dds/react-label-field";
38
+ import { Tooltip } from "@dt-dds/react-tooltip";
39
+ import { Typography } from "@dt-dds/react-typography";
40
+ import { useSelect } from "downshift";
41
+ import {
42
+ useRef,
43
+ useState,
44
+ useEffect,
45
+ Children,
46
+ isValidElement,
47
+ useMemo,
48
+ useCallback
49
+ } from "react";
50
+
51
+ // src/components/SelectOption.tsx
52
+ import { Checkbox } from "@dt-dds/react-checkbox";
53
+
54
+ // src/context/SelectProvider.tsx
55
+ import { createContext, useContext } from "react";
56
+ import { jsx } from "react/jsx-runtime";
57
+ var SelectContext = createContext(void 0);
58
+ var useSelectContext = () => {
59
+ const context = useContext(SelectContext);
60
+ if (!context) {
61
+ throw new Error("useSelectContext must be used within a SelectProvider");
62
+ }
63
+ return context;
64
+ };
65
+ var SelectProvider = ({ children, value }) => {
66
+ const _a = value, { highlightedIndex, selectedItems } = _a, rest = __objRest(_a, ["highlightedIndex", "selectedItems"]);
67
+ const isItemHighlighted = (index) => index === highlightedIndex;
68
+ const isItemSelected = (item) => {
69
+ return !!selectedItems.find(
70
+ (selectedItem) => selectedItem.value === item.value
71
+ );
72
+ };
73
+ return /* @__PURE__ */ jsx(
74
+ SelectContext.Provider,
75
+ {
76
+ value: __spreadValues({
77
+ isItemHighlighted,
78
+ isItemSelected
79
+ }, rest),
80
+ children
81
+ }
82
+ );
83
+ };
84
+
85
+ // src/components/SelectOption.styled.ts
86
+ import styled from "@emotion/styled";
87
+ var SelectOptionStyled = styled.li`
88
+ display: flex;
89
+
90
+ ${({ theme, isMulti, isHighlighted }) => `
91
+ cursor:pointer;
92
+ align-items: center;
93
+ ${!isMulti && `padding: ${theme.spacing["4xs"]} ${theme.spacing["2xs"]}`};
94
+ gap: ${isMulti ? theme.spacing["4xs"] : theme.spacing["5xs"]};
95
+ background-color: ${isHighlighted && !isMulti ? theme.palette.primary.light : theme.palette.surface.contrast};
96
+
97
+ color: ${theme.palette.content.default};
98
+ ${theme.fontStyles.body2}
99
+
100
+ & > label {
101
+ ${isMulti && `padding: ${theme.spacing["4xs"]} ${theme.spacing["2xs"]}`};
102
+ }
103
+
104
+ &:hover,&:focus-within {
105
+ background-color: ${theme.palette.primary.light};
106
+ }
107
+
108
+ &[aria-disabled="true"] {
109
+ cursor: not-allowed;
110
+ color: ${theme.palette.content.light};
111
+ &:hover {
112
+ background-color: ${theme.palette.surface.contrast};
113
+ }
114
+ }
115
+ `}
116
+ `;
117
+ var SelectOptionContentStyled = styled.div`
118
+ text-overflow: ellipsis;
119
+ white-space: nowrap;
120
+ overflow: hidden;
121
+ width: 100%;
122
+
123
+ ${({ theme, isSelected }) => `
124
+ ${theme.fontStyles[isSelected ? "body2Bold" : "body2"]};
125
+ `}
126
+ `;
127
+
128
+ // src/components/SelectOption.tsx
129
+ import { jsx as jsx2 } from "react/jsx-runtime";
130
+ var SelectOption = ({
131
+ dataTestId,
132
+ index,
133
+ children,
134
+ style,
135
+ value,
136
+ label,
137
+ disabled
138
+ }) => {
139
+ const { getItemProps, isItemHighlighted, isItemSelected, isMulti } = useSelectContext();
140
+ const item = { value, label, disabled: !!disabled };
141
+ const isSelected = isItemSelected(item);
142
+ const isHighlighted = isItemHighlighted(index);
143
+ const itemProps = getItemProps({ item, index });
144
+ return /* @__PURE__ */ jsx2(
145
+ SelectOptionStyled,
146
+ __spreadProps(__spreadValues({
147
+ "aria-disabled": itemProps["aria-disabled"],
148
+ "data-testid": dataTestId,
149
+ isHighlighted,
150
+ isMulti,
151
+ style
152
+ }, !isMulti && __spreadProps(__spreadValues({}, itemProps), {
153
+ "aria-selected": isSelected
154
+ })), {
155
+ children: isMulti ? /* @__PURE__ */ jsx2(
156
+ Checkbox,
157
+ __spreadProps(__spreadValues({
158
+ isChecked: isSelected,
159
+ isDisabled: itemProps["aria-disabled"]
160
+ }, itemProps), {
161
+ children: children || label || value
162
+ })
163
+ ) : /* @__PURE__ */ jsx2(SelectOptionContentStyled, { isSelected, children: children || label || value })
164
+ })
165
+ );
166
+ };
167
+
168
+ // src/Select.styled.ts
169
+ import { DROPDOWN_MENU_Z_INDEX } from "@dt-dds/react-core";
170
+ import styled2 from "@emotion/styled";
171
+ var SelectStyled = styled2.div`
172
+ position: relative;
173
+ width: 100%;
174
+ `;
175
+ var getThemedBackgroundFill = (fill, theme) => ({
176
+ default: theme.palette.surface.default,
177
+ contrast: theme.palette.surface.contrast,
178
+ light: theme.palette.surface.light
179
+ })[fill];
180
+ var SelectFieldStyled = styled2.div`
181
+ display: flex;
182
+ height: auto;
183
+ width: 100%;
184
+ position: relative;
185
+ flex-direction: column;
186
+ overflow: hidden;
187
+ `;
188
+ var SelectMenuStyled = styled2.ul`
189
+ list-style-type: none;
190
+ position: absolute;
191
+ z-index: ${DROPDOWN_MENU_Z_INDEX};
192
+ width: 100%;
193
+ max-height: 180px;
194
+ overflow: auto;
195
+
196
+ ${({ isOpen, theme }) => `
197
+ margin-top:${theme.spacing["5xs"]};
198
+ padding: ${theme.spacing["2xs"]} ${theme.spacing.none};
199
+ background-color: ${theme.palette.surface.contrast};
200
+ display: ${isOpen ? "block" : "none"};
201
+ border-radius: ${theme.shape.formField};
202
+ box-shadow: ${theme.shadows.s};
203
+ `}
204
+ `;
205
+ var SelectValueContainerStyled = styled2.div`
206
+ display: flex;
207
+ flex: 1;
208
+ align-self: end;
209
+ overflow: hidden;
210
+ `;
211
+ var SelectValueStyled = styled2.div`
212
+ overflow: hidden;
213
+ text-overflow: ellipsis;
214
+ white-space: nowrap;
215
+
216
+ ${({ theme }) => `
217
+ ${theme.fontStyles.body2}
218
+ color: ${theme.palette.content.default};
219
+ `}
220
+ `;
221
+ var SelectContainerStyled = styled2.div`
222
+ transition: all 0.2s ease-in-out;
223
+ width: 100%;
224
+ height: 54px;
225
+ display: flex;
226
+ align-items: center;
227
+ justify-content: center;
228
+
229
+ ${({
230
+ theme,
231
+ hasError,
232
+ disabled,
233
+ isOpen = false,
234
+ variant = "outlined",
235
+ fill = "default"
236
+ }) => `
237
+ ${theme.fontStyles.body2}
238
+ color: ${disabled ? theme.palette.content.light : theme.palette.content.default};
239
+ padding: ${theme.spacing["4xs"]} ${theme.spacing["3xs"]};
240
+ gap: ${theme.spacing["4xs"]} ;
241
+ background-color: ${getThemedBackgroundFill(fill, theme)};
242
+ border-radius: ${theme.shape.formField};
243
+ cursor: ${disabled ? "not-allowed" : "pointer"};
244
+
245
+ border-width: ${variant === "outlined" ? "1px" : "0 0 1px"};
246
+ border-color: ${isOpen ? theme.palette.content.dark : theme.palette.border.medium};
247
+ border-style: solid;
248
+
249
+ &:focus, &:hover {
250
+ border-color: ${hasError ? theme.palette.error.default : theme.palette.content.dark};
251
+ }
252
+
253
+ ${hasError && `border-color: ${theme.palette.error.default}`};
254
+ `};
255
+ `;
256
+ var SelectActionContainerStyled = styled2.div`
257
+ display: flex;
258
+ `;
259
+ var HelperSelectFieldMessageStyled = styled2.div`
260
+ ${({ theme }) => `
261
+ padding-left: ${theme.spacing["2xs"]};
262
+ `}
263
+ `;
264
+
265
+ // src/Select.tsx
266
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
267
+ var createOptionObject = (element) => {
268
+ return {
269
+ value: element.props.value,
270
+ label: element.props.label,
271
+ disabled: !!element.props.disabled
272
+ };
273
+ };
274
+ var itemToString = (item) => {
275
+ if (item) {
276
+ return item.label || item.value;
277
+ }
278
+ return "";
279
+ };
280
+ var Select = ({
281
+ dataTestId,
282
+ style,
283
+ helperText = "",
284
+ hasError = false,
285
+ initialValue = "",
286
+ label,
287
+ isMulti = false,
288
+ isRequired,
289
+ errorMessage,
290
+ children,
291
+ isDisabled,
292
+ variant = "outlined",
293
+ fill = "default",
294
+ onChange
295
+ }) => {
296
+ const [selectedItems, setSelectedItems] = useState([]);
297
+ const ref = useRef(null);
298
+ const changeSelectedItems = useCallback(
299
+ (newItems, skipOnChangeTrigger = false) => {
300
+ var _a;
301
+ setSelectedItems(newItems);
302
+ if (onChange && !skipOnChangeTrigger) {
303
+ if (isMulti) {
304
+ const values = newItems.map((newSelectedItem) => newSelectedItem.value).filter((value) => Boolean(value));
305
+ onChange(values);
306
+ } else {
307
+ const value = ((_a = newItems[0]) == null ? void 0 : _a.value) || "";
308
+ onChange(value);
309
+ }
310
+ }
311
+ },
312
+ [setSelectedItems, isMulti, onChange]
313
+ );
314
+ const clearSelection = (event) => {
315
+ event.stopPropagation();
316
+ changeSelectedItems([]);
317
+ };
318
+ const helperMessage = hasError && errorMessage ? errorMessage : helperText;
319
+ const options = useMemo(() => {
320
+ const tempOptions = Children.map(children, (child) => {
321
+ if (isValidElement(child) && child.props.value != void 0) {
322
+ return createOptionObject(child);
323
+ }
324
+ return null;
325
+ });
326
+ return tempOptions ? tempOptions.filter(Boolean) : [];
327
+ }, [children]);
328
+ const validSelectedItems = useMemo(() => {
329
+ const isSelectedItemValid = (selectedItem) => {
330
+ return !!options.find((option) => option.value === selectedItem.value);
331
+ };
332
+ return selectedItems.filter(isSelectedItemValid);
333
+ }, [options, selectedItems]);
334
+ useEffect(() => {
335
+ const getInitialOptions = () => {
336
+ if (Array.isArray(initialValue)) {
337
+ return initialValue.map(
338
+ (value) => options.find((option) => option.value === value)
339
+ ).filter((option) => Boolean(option));
340
+ } else {
341
+ return options.find((option) => option.value === initialValue);
342
+ }
343
+ };
344
+ const initalOptions = getInitialOptions();
345
+ if (!initialValue || !initalOptions || Array.isArray(initalOptions) && initalOptions.length === 0) {
346
+ changeSelectedItems([], true);
347
+ } else {
348
+ if (Array.isArray(initalOptions)) {
349
+ changeSelectedItems(isMulti ? initalOptions : [initalOptions[0]], true);
350
+ } else {
351
+ changeSelectedItems([initalOptions], true);
352
+ }
353
+ }
354
+ }, [isMulti, initialValue, options, changeSelectedItems]);
355
+ useEffect(() => {
356
+ if (options.length === 1 && selectedItems.length === 0) {
357
+ changeSelectedItems([options[0]]);
358
+ }
359
+ if (validSelectedItems.length !== selectedItems.length) {
360
+ changeSelectedItems(validSelectedItems);
361
+ }
362
+ }, [options, selectedItems, validSelectedItems, changeSelectedItems]);
363
+ const {
364
+ isOpen,
365
+ closeMenu,
366
+ getToggleButtonProps,
367
+ getLabelProps,
368
+ getMenuProps,
369
+ highlightedIndex,
370
+ getItemProps
371
+ } = useSelect({
372
+ items: options,
373
+ isItemDisabled(item) {
374
+ return !!item.disabled;
375
+ },
376
+ itemToString,
377
+ stateReducer: (state, actionAndChanges) => {
378
+ const { changes, type } = actionAndChanges;
379
+ switch (type) {
380
+ case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:
381
+ case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:
382
+ case useSelect.stateChangeTypes.ItemClick:
383
+ case useSelect.stateChangeTypes.ToggleButtonBlur:
384
+ return __spreadProps(__spreadValues({}, changes), {
385
+ isOpen: isMulti,
386
+ highlightedIndex: state.highlightedIndex
387
+ });
388
+ default:
389
+ return changes;
390
+ }
391
+ },
392
+ selectedItem: null,
393
+ onSelectedItemChange: ({ selectedItem }) => {
394
+ let newSelectedItems = [];
395
+ if (!isMulti) {
396
+ if (selectedItem) {
397
+ newSelectedItems = [selectedItem];
398
+ }
399
+ } else {
400
+ if (selectedItem) {
401
+ if (selectedItems.includes(selectedItem)) {
402
+ newSelectedItems = selectedItems.filter(
403
+ (item) => item !== selectedItem
404
+ );
405
+ } else {
406
+ newSelectedItems = [...selectedItems, selectedItem];
407
+ }
408
+ } else {
409
+ newSelectedItems = selectedItems;
410
+ }
411
+ }
412
+ changeSelectedItems(newSelectedItems);
413
+ }
414
+ });
415
+ const hasSelectedItems = !!selectedItems && !!selectedItems.length;
416
+ const selectedValueContainerText = () => {
417
+ return selectedItems.length > 1 ? `${selectedItems.length} options selected` : selectedItems[0].label || selectedItems[0].value;
418
+ };
419
+ useClickOutside({ ref, handler: () => closeMenu() });
420
+ const disabled = isDisabled || options.length === 1;
421
+ return /* @__PURE__ */ jsx3(
422
+ SelectProvider,
423
+ {
424
+ value: { highlightedIndex, getItemProps, selectedItems, isMulti },
425
+ children: /* @__PURE__ */ jsxs(SelectStyled, { "data-testid": dataTestId, ref, style, children: [
426
+ /* @__PURE__ */ jsx3(SelectFieldStyled, { children: /* @__PURE__ */ jsxs(
427
+ SelectContainerStyled,
428
+ __spreadProps(__spreadValues({}, getToggleButtonProps({ disabled })), {
429
+ "data-testid": "select-container",
430
+ fill,
431
+ hasError,
432
+ isOpen,
433
+ variant,
434
+ children: [
435
+ /* @__PURE__ */ jsxs(SelectValueContainerStyled, { children: [
436
+ /* @__PURE__ */ jsx3(
437
+ LabelField,
438
+ __spreadProps(__spreadValues({}, getLabelProps()), {
439
+ hasError,
440
+ isActive: hasSelectedItems,
441
+ isDisabled: disabled,
442
+ isRequired,
443
+ children: label
444
+ })
445
+ ),
446
+ hasSelectedItems ? /* @__PURE__ */ jsx3(SelectValueStyled, { children: selectedValueContainerText() }) : null
447
+ ] }),
448
+ /* @__PURE__ */ jsxs(SelectActionContainerStyled, { children: [
449
+ isMulti && hasSelectedItems ? /* @__PURE__ */ jsxs(Tooltip, { children: [
450
+ /* @__PURE__ */ jsx3(IconButton, { onClick: clearSelection, children: /* @__PURE__ */ jsx3(Icon, { code: "close", dataTestId: "clear-selection", size: "s" }) }),
451
+ /* @__PURE__ */ jsx3(Tooltip.Content, { children: "Clear all" })
452
+ ] }) : null,
453
+ /* @__PURE__ */ jsx3(Icon, { code: isOpen ? "expand_less" : "expand_more", size: "s" })
454
+ ] })
455
+ ]
456
+ })
457
+ ) }),
458
+ /* @__PURE__ */ jsx3(
459
+ SelectMenuStyled,
460
+ __spreadProps(__spreadValues({}, getMenuProps()), {
461
+ "aria-multiselectable": isMulti,
462
+ isOpen,
463
+ children: isOpen ? children : null
464
+ })
465
+ ),
466
+ helperMessage ? /* @__PURE__ */ jsx3(HelperSelectFieldMessageStyled, { children: /* @__PURE__ */ jsx3(
467
+ Typography,
468
+ {
469
+ color: hasError ? "error.default" : "content.light",
470
+ element: "span",
471
+ fontStyles: "body3",
472
+ children: helperMessage
473
+ }
474
+ ) }) : null
475
+ ] })
476
+ }
477
+ );
478
+ };
479
+ Select.Option = SelectOption;
480
+ export {
481
+ Select
482
+ };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@dt-dds/react-select",
3
+ "version": "1.0.0-beta.46",
4
+ "license": "MIT",
5
+ "exports": {
6
+ ".": "./dist/index.js"
7
+ },
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.mjs",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist/**"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "dev": "tsup --watch",
17
+ "lint": "eslint --cache .",
18
+ "test": "jest",
19
+ "test:report": "open ./jest-coverage/lcov-report/index.html",
20
+ "test:update:snapshot": "jest -u"
21
+ },
22
+ "dependencies": {
23
+ "@dt-dds/react-checkbox": "1.0.0-beta.40",
24
+ "@dt-dds/react-core": "1.0.0-beta.41",
25
+ "@dt-dds/react-icon": "1.0.0-beta.42",
26
+ "@dt-dds/react-icon-button": "1.0.0-beta.9",
27
+ "@dt-dds/react-label-field": "1.0.0-beta.37",
28
+ "@dt-dds/react-tooltip": "1.0.0-beta.49",
29
+ "@dt-dds/react-typography": "1.0.0-beta.32",
30
+ "downshift": "^9.0.4"
31
+ },
32
+ "devDependencies": {
33
+ "@babel/core": "^7.22.9",
34
+ "@babel/preset-env": "^7.22.9",
35
+ "@babel/preset-react": "^7.22.5",
36
+ "@babel/preset-typescript": "^7.23.3",
37
+ "@emotion/babel-plugin": "^11.11.0",
38
+ "@emotion/css": "^11.7.1",
39
+ "@emotion/jest": "^11.10.0",
40
+ "@emotion/react": "^11.8.2",
41
+ "@emotion/styled": "^11.8.1",
42
+ "@types/react": "^18.0.9",
43
+ "@types/react-dom": "^18.0.4",
44
+ "babel-loader": "^8.3.0",
45
+ "eslint-config-custom": "*",
46
+ "eslint-plugin-storybook": "^9.1.0",
47
+ "jest-config": "*",
48
+ "react": "^18.1.0",
49
+ "react-dom": "^18.2.0",
50
+ "tsconfig": "*",
51
+ "tsup": "^6.6.3",
52
+ "typescript": "^4.5.3"
53
+ },
54
+ "peerDependencies": {
55
+ "@emotion/css": "^11.7.1",
56
+ "@emotion/react": "^11.8.2",
57
+ "@emotion/styled": "^11.8.1",
58
+ "react": ">=17.0.2",
59
+ "react-dom": ">=17.0.2"
60
+ }
61
+ }