@coinbase/cds-web 8.19.1 → 8.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dts/alpha/select/DefaultSelectAllOption.d.ts +12 -0
  3. package/dts/alpha/select/DefaultSelectAllOption.d.ts.map +1 -0
  4. package/dts/alpha/select/DefaultSelectControl.d.ts +13 -0
  5. package/dts/alpha/select/DefaultSelectControl.d.ts.map +1 -0
  6. package/dts/alpha/select/DefaultSelectDropdown.d.ts +12 -0
  7. package/dts/alpha/select/DefaultSelectDropdown.d.ts.map +1 -0
  8. package/dts/alpha/select/DefaultSelectEmptyDropdownContents.d.ts +3 -0
  9. package/dts/alpha/select/DefaultSelectEmptyDropdownContents.d.ts.map +1 -0
  10. package/dts/alpha/select/DefaultSelectOption.d.ts +9 -0
  11. package/dts/alpha/select/DefaultSelectOption.d.ts.map +1 -0
  12. package/dts/alpha/select/Select.d.ts +470 -0
  13. package/dts/alpha/select/Select.d.ts.map +1 -0
  14. package/dts/alpha/select/index.d.ts +7 -0
  15. package/dts/alpha/select/index.d.ts.map +1 -0
  16. package/dts/cells/Cell.d.ts.map +1 -1
  17. package/dts/chips/Chip.d.ts.map +1 -1
  18. package/dts/chips/ChipProps.d.ts +15 -4
  19. package/dts/chips/ChipProps.d.ts.map +1 -1
  20. package/dts/chips/InputChip.d.ts.map +1 -1
  21. package/dts/controls/InputStack.d.ts +65 -63
  22. package/dts/controls/InputStack.d.ts.map +1 -1
  23. package/dts/controls/SearchInput.d.ts +1 -1
  24. package/dts/controls/Select.d.ts +4 -1
  25. package/dts/controls/Select.d.ts.map +1 -1
  26. package/dts/controls/SelectStack.d.ts +1 -1
  27. package/dts/controls/TextInput.d.ts +1 -1
  28. package/dts/hooks/useClickOutside.d.ts +22 -0
  29. package/dts/hooks/useClickOutside.d.ts.map +1 -0
  30. package/dts/overlays/FocusTrap.d.ts +4 -0
  31. package/dts/overlays/FocusTrap.d.ts.map +1 -1
  32. package/dts/overlays/modal/FullscreenModalLayout.d.ts +1 -1
  33. package/dts/system/Interactable.d.ts +45 -38
  34. package/dts/system/Interactable.d.ts.map +1 -1
  35. package/dts/utils/findClosestNonDisabledNodeIndex.d.ts +11 -0
  36. package/dts/utils/findClosestNonDisabledNodeIndex.d.ts.map +1 -0
  37. package/esm/alpha/select/DefaultSelectAllOption.js +47 -0
  38. package/esm/alpha/select/DefaultSelectControl.css +1 -0
  39. package/esm/alpha/select/DefaultSelectControl.js +234 -0
  40. package/esm/alpha/select/DefaultSelectDropdown.js +233 -0
  41. package/esm/alpha/select/DefaultSelectEmptyDropdownContents.js +23 -0
  42. package/esm/alpha/select/DefaultSelectOption.css +2 -0
  43. package/esm/alpha/select/DefaultSelectOption.js +103 -0
  44. package/esm/alpha/select/Select.js +222 -0
  45. package/esm/alpha/select/index.js +6 -0
  46. package/esm/cells/Cell.js +5 -4
  47. package/esm/chips/Chip.js +3 -2
  48. package/esm/chips/InputChip.js +7 -5
  49. package/esm/chips/__figma__/InputChip.figma.js +1 -1
  50. package/esm/controls/InputStack.js +4 -2
  51. package/esm/controls/Select.js +3 -0
  52. package/esm/hooks/useClickOutside.js +37 -0
  53. package/esm/overlays/FocusTrap.js +23 -14
  54. package/esm/system/Interactable.js +34 -11
  55. package/esm/utils/findClosestNonDisabledNodeIndex.js +24 -0
  56. package/package.json +3 -3
@@ -0,0 +1,234 @@
1
+ const _excluded = ["type", "options", "value", "onChange", "open", "placeholder", "disabled", "setOpen", "variant", "helperText", "label", "labelVariant", "startNode", "endNode", "compact", "blendStyles", "maxSelectedOptionsToShow", "hiddenSelectedOptionsLabel", "removeSelectedOptionAccessibilityLabel", "accessibilityLabel", "ariaHaspopup", "styles", "classNames"];
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
8
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
+ import React, { forwardRef, memo, useCallback, useMemo, useRef } from 'react';
10
+ import { Chip } from '../../chips/Chip';
11
+ import { InputChip } from '../../chips/InputChip';
12
+ import { HelperText } from '../../controls/HelperText';
13
+ import { InputLabel } from '../../controls/InputLabel';
14
+ import { InputStack } from '../../controls/InputStack';
15
+ import { cx } from '../../cx';
16
+ import { HStack } from '../../layout/HStack';
17
+ import { AnimatedCaret } from '../../motion/AnimatedCaret';
18
+ import { Pressable } from '../../system/Pressable';
19
+ import { Text } from '../../typography/Text';
20
+ import { findClosestNonDisabledNodeIndex } from '../../utils/findClosestNonDisabledNodeIndex';
21
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
+ const noFocusOutlineCss = "noFocusOutlineCss-n1gl6kcn";
23
+ const variantColor = {
24
+ foreground: 'fg',
25
+ positive: 'fgPositive',
26
+ negative: 'fgNegative',
27
+ primary: 'fgPrimary',
28
+ foregroundMuted: 'fgMuted',
29
+ secondary: 'fgMuted'
30
+ };
31
+ const DefaultSelectControlComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
32
+ let {
33
+ type,
34
+ options,
35
+ value,
36
+ onChange,
37
+ open,
38
+ placeholder,
39
+ disabled,
40
+ setOpen,
41
+ variant,
42
+ helperText,
43
+ label,
44
+ labelVariant,
45
+ startNode,
46
+ endNode: customEndNode,
47
+ compact,
48
+ blendStyles,
49
+ maxSelectedOptionsToShow = 6,
50
+ hiddenSelectedOptionsLabel = 'more',
51
+ removeSelectedOptionAccessibilityLabel = 'Remove',
52
+ accessibilityLabel,
53
+ ariaHaspopup,
54
+ styles,
55
+ classNames
56
+ } = _ref,
57
+ props = _objectWithoutProperties(_ref, _excluded);
58
+ const shouldShowCompactLabel = compact && label;
59
+ const isMultiSelect = type === 'multi';
60
+ const hasValue = value !== null && !(Array.isArray(value) && value.length === 0);
61
+ const controlPressableRef = useRef(null);
62
+ const valueNodeContainerRef = useRef(null);
63
+ const handleUnselectValue = useCallback((event, index) => {
64
+ var _controlPressableRef$, _controlPressableRef$2, _valueNodes$focusInde;
65
+ // Unselect the value
66
+ event.stopPropagation();
67
+ const currentValue = [...value];
68
+ const changedValue = currentValue[index];
69
+ onChange === null || onChange === void 0 || onChange(changedValue);
70
+
71
+ // Shift focus from the valueNode that will be removed
72
+ // If there will be no values left after removing, focus the control
73
+ if (currentValue.length === 1) return (_controlPressableRef$ = controlPressableRef.current) === null || _controlPressableRef$ === void 0 ? void 0 : _controlPressableRef$.focus();
74
+ if (!valueNodeContainerRef.current) return;
75
+ // Otherwise focus the next value
76
+ const valueNodes = Array.from(valueNodeContainerRef.current.querySelectorAll('[data-selected-value]'));
77
+ const focusIndex = findClosestNonDisabledNodeIndex(valueNodes, index);
78
+ if (focusIndex === null) return (_controlPressableRef$2 = controlPressableRef.current) === null || _controlPressableRef$2 === void 0 ? void 0 : _controlPressableRef$2.focus();
79
+ (_valueNodes$focusInde = valueNodes[focusIndex]) === null || _valueNodes$focusInde === void 0 || _valueNodes$focusInde.focus();
80
+ }, [onChange, value]);
81
+ const interactableBlendStyles = useMemo(() => isMultiSelect ? _objectSpread({
82
+ hoveredBackground: 'rgba(0, 0, 0, 0)',
83
+ hoveredOpacity: 1,
84
+ pressedBackground: 'rgba(0, 0, 0, 0)'
85
+ }, blendStyles) : blendStyles, [isMultiSelect, blendStyles]);
86
+ const helperTextNode = useMemo(() => typeof helperText === 'string' ? /*#__PURE__*/_jsx(HelperText, {
87
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.controlHelperTextNode,
88
+ color: variant ? variantColor[variant] : 'fgMuted',
89
+ style: styles === null || styles === void 0 ? void 0 : styles.controlHelperTextNode,
90
+ children: helperText
91
+ }) : helperText, [helperText, variant, classNames === null || classNames === void 0 ? void 0 : classNames.controlHelperTextNode, styles === null || styles === void 0 ? void 0 : styles.controlHelperTextNode]);
92
+ const labelNode = useMemo(() => typeof label === 'string' && labelVariant === 'inside' ? /*#__PURE__*/_jsx(Pressable, {
93
+ noScaleOnPress: true,
94
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.controlLabelNode,
95
+ disabled: disabled,
96
+ onClick: () => setOpen(s => !s),
97
+ style: styles === null || styles === void 0 ? void 0 : styles.controlLabelNode,
98
+ tabIndex: -1,
99
+ children: /*#__PURE__*/_jsx(InputLabel, {
100
+ color: "fg",
101
+ paddingBottom: 0,
102
+ paddingTop: 1,
103
+ paddingX: 2,
104
+ children: label
105
+ })
106
+ }) : label, [label, labelVariant, disabled, setOpen, classNames === null || classNames === void 0 ? void 0 : classNames.controlLabelNode, styles === null || styles === void 0 ? void 0 : styles.controlLabelNode]);
107
+ const valueNode = useMemo(() => {
108
+ var _ref4, _ref5, _option$label2;
109
+ if (hasValue && isMultiSelect) {
110
+ const valuesToShow = value.length <= maxSelectedOptionsToShow ? value : value.slice(0, maxSelectedOptionsToShow);
111
+ const optionsToShow = valuesToShow.map(value => options.find(option => option.value === value)).filter(Boolean);
112
+ return /*#__PURE__*/_jsxs(HStack, {
113
+ flexWrap: "wrap",
114
+ gap: 1,
115
+ children: [optionsToShow.map((option, index) => {
116
+ var _option$value, _ref2, _ref3, _option$label;
117
+ const accessibilityLabel = typeof option.label === 'string' ? option.label : typeof option.description === 'string' ? option.description : (_option$value = option.value) !== null && _option$value !== void 0 ? _option$value : '';
118
+ return /*#__PURE__*/_jsx(InputChip, {
119
+ "data-selected-value": true,
120
+ accessibilityLabel: "".concat(removeSelectedOptionAccessibilityLabel, " ").concat(accessibilityLabel),
121
+ disabled: option.disabled,
122
+ invertColorScheme: false,
123
+ maxWidth: 200,
124
+ onClick: event => handleUnselectValue(event, index),
125
+ children: (_ref2 = (_ref3 = (_option$label = option.label) !== null && _option$label !== void 0 ? _option$label : option.description) !== null && _ref3 !== void 0 ? _ref3 : option.value) !== null && _ref2 !== void 0 ? _ref2 : ''
126
+ }, option.value);
127
+ }), value.length - maxSelectedOptionsToShow > 0 && /*#__PURE__*/_jsx(Chip, {
128
+ children: "+".concat(value.length - maxSelectedOptionsToShow, " ").concat(hiddenSelectedOptionsLabel)
129
+ })]
130
+ });
131
+ }
132
+ const option = options.find(option => option.value === value);
133
+ const label = (_ref4 = (_ref5 = (_option$label2 = option === null || option === void 0 ? void 0 : option.label) !== null && _option$label2 !== void 0 ? _option$label2 : option === null || option === void 0 ? void 0 : option.description) !== null && _ref5 !== void 0 ? _ref5 : option === null || option === void 0 ? void 0 : option.value) !== null && _ref4 !== void 0 ? _ref4 : placeholder;
134
+ const content = hasValue ? label : placeholder;
135
+ return typeof content === 'string' ? /*#__PURE__*/_jsx(Text, {
136
+ as: "p",
137
+ color: hasValue ? 'fg' : 'fgMuted',
138
+ display: "block",
139
+ font: "body",
140
+ overflow: "truncate",
141
+ children: content
142
+ }) : content;
143
+ }, [hasValue, isMultiSelect, options, placeholder, value, maxSelectedOptionsToShow, hiddenSelectedOptionsLabel, removeSelectedOptionAccessibilityLabel, handleUnselectValue]);
144
+ const inputNode = useMemo(() =>
145
+ /*#__PURE__*/
146
+ // We don't offer control over setting the role since this must always be a button
147
+ _jsxs(Pressable, {
148
+ ref: controlPressableRef,
149
+ noScaleOnPress: true,
150
+ accessibilityLabel: accessibilityLabel,
151
+ "aria-haspopup": ariaHaspopup,
152
+ background: "transparent",
153
+ blendStyles: interactableBlendStyles,
154
+ className: cx(noFocusOutlineCss, classNames === null || classNames === void 0 ? void 0 : classNames.controlInputNode),
155
+ disabled: disabled,
156
+ focusable: false,
157
+ minHeight: isMultiSelect ? 76 : undefined,
158
+ onClick: () => setOpen(s => !s),
159
+ paddingStart: 1,
160
+ style: styles === null || styles === void 0 ? void 0 : styles.controlInputNode,
161
+ width: "100%",
162
+ children: [!!startNode && /*#__PURE__*/_jsx(HStack, {
163
+ alignItems: "center",
164
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.controlStartNode,
165
+ height: "100%",
166
+ justifyContent: "center",
167
+ minWidth: 0,
168
+ paddingX: 1,
169
+ style: styles === null || styles === void 0 ? void 0 : styles.controlStartNode,
170
+ children: startNode
171
+ }), shouldShowCompactLabel ? /*#__PURE__*/_jsx(HStack, {
172
+ alignItems: "center",
173
+ height: "100%",
174
+ maxWidth: "40%",
175
+ padding: 1,
176
+ children: /*#__PURE__*/_jsx(InputLabel, {
177
+ color: "fg",
178
+ overflow: "truncate",
179
+ children: label
180
+ })
181
+ }) : null, /*#__PURE__*/_jsx(HStack, {
182
+ alignItems: "center",
183
+ borderRadius: 200,
184
+ height: "100%",
185
+ justifyContent: "space-between",
186
+ width: "100%",
187
+ children: /*#__PURE__*/_jsx(HStack, {
188
+ ref: valueNodeContainerRef,
189
+ alignItems: "center",
190
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.controlValueNode,
191
+ flexGrow: 1,
192
+ flexShrink: 1,
193
+ flexWrap: "wrap",
194
+ gap: 1,
195
+ height: "100%",
196
+ justifyContent: shouldShowCompactLabel ? 'flex-end' : 'flex-start',
197
+ overflow: "auto",
198
+ paddingTop: labelVariant === 'inside' ? 0 : compact ? 1 : 2,
199
+ paddingX: 1,
200
+ paddingY: labelVariant === 'inside' || compact ? 1 : 2,
201
+ style: styles === null || styles === void 0 ? void 0 : styles.controlValueNode,
202
+ children: valueNode
203
+ })
204
+ })]
205
+ }), [accessibilityLabel, ariaHaspopup, interactableBlendStyles, classNames === null || classNames === void 0 ? void 0 : classNames.controlInputNode, classNames === null || classNames === void 0 ? void 0 : classNames.controlStartNode, classNames === null || classNames === void 0 ? void 0 : classNames.controlValueNode, disabled, isMultiSelect, styles === null || styles === void 0 ? void 0 : styles.controlInputNode, styles === null || styles === void 0 ? void 0 : styles.controlStartNode, styles === null || styles === void 0 ? void 0 : styles.controlValueNode, startNode, shouldShowCompactLabel, label, labelVariant, compact, valueNode, setOpen]);
206
+ const endNode = useMemo(() => /*#__PURE__*/_jsx(HStack, {
207
+ alignItems: "center",
208
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.controlEndNode,
209
+ paddingX: 2,
210
+ style: styles === null || styles === void 0 ? void 0 : styles.controlEndNode,
211
+ children: /*#__PURE__*/_jsx(Pressable, {
212
+ "aria-hidden": true,
213
+ onClick: () => setOpen(s => !s),
214
+ tabIndex: -1,
215
+ children: customEndNode ? customEndNode : /*#__PURE__*/_jsx(AnimatedCaret, {
216
+ color: !open ? 'fg' : variant ? variantColor[variant] : 'fgPrimary',
217
+ rotate: open ? 0 : 180
218
+ })
219
+ })
220
+ }), [open, variant, setOpen, customEndNode, classNames === null || classNames === void 0 ? void 0 : classNames.controlEndNode, styles === null || styles === void 0 ? void 0 : styles.controlEndNode]);
221
+ return /*#__PURE__*/_jsx(InputStack, _objectSpread({
222
+ ref: ref,
223
+ blendStyles: interactableBlendStyles,
224
+ disabled: disabled,
225
+ endNode: endNode,
226
+ helperTextNode: helperTextNode,
227
+ inputNode: inputNode,
228
+ labelNode: shouldShowCompactLabel ? null : labelNode,
229
+ labelVariant: labelVariant,
230
+ variant: variant
231
+ }, props));
232
+ }));
233
+ export const DefaultSelectControl = DefaultSelectControlComponent;
234
+ import "./DefaultSelectControl.css";
@@ -0,0 +1,233 @@
1
+ const _excluded = ["type", "options", "value", "onChange", "open", "setOpen", "controlRef", "disabled", "style", "styles", "className", "classNames", "compact", "label", "end", "selectAllLabel", "emptyOptionsLabel", "clearAllLabel", "hideSelectAll", "accessory", "media", "SelectOptionComponent", "SelectAllOptionComponent", "SelectEmptyDropdownContentsComponent", "accessibilityLabel", "accessibilityRoles"],
2
+ _excluded2 = ["Component", "media", "accessory", "end"];
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
6
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
7
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
8
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
9
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
10
+ import { forwardRef, memo, useCallback, useEffect, useMemo, useState } from 'react';
11
+ import { AnimatePresence, m as motion } from 'framer-motion';
12
+ import { Button } from '../../buttons';
13
+ import { Checkbox } from '../../controls/Checkbox';
14
+ import { Radio } from '../../controls/Radio';
15
+ import { cx } from '../../cx';
16
+ import { Box } from '../../layout/Box';
17
+ import { FocusTrap } from '../../overlays/FocusTrap';
18
+ import { DefaultSelectAllOption } from './DefaultSelectAllOption';
19
+ import { DefaultSelectEmptyDropdownContents } from './DefaultSelectEmptyDropdownContents';
20
+ import { DefaultSelectOption } from './DefaultSelectOption';
21
+ import { defaultAccessibilityRoles } from './Select';
22
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
+ const initialStyle = {
24
+ opacity: 0,
25
+ y: 0
26
+ };
27
+ const animateStyle = {
28
+ opacity: 1,
29
+ y: 4
30
+ };
31
+ const DefaultSelectDropdownComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
32
+ let {
33
+ type,
34
+ options,
35
+ value,
36
+ onChange,
37
+ open,
38
+ setOpen,
39
+ controlRef,
40
+ disabled,
41
+ style,
42
+ styles,
43
+ className,
44
+ classNames,
45
+ compact,
46
+ label,
47
+ end,
48
+ selectAllLabel = 'Select all',
49
+ emptyOptionsLabel = 'No options available',
50
+ clearAllLabel = 'Clear all',
51
+ hideSelectAll,
52
+ accessory,
53
+ media,
54
+ SelectOptionComponent = DefaultSelectOption,
55
+ SelectAllOptionComponent = DefaultSelectAllOption,
56
+ SelectEmptyDropdownContentsComponent = DefaultSelectEmptyDropdownContents,
57
+ accessibilityLabel = 'Select dropdown',
58
+ accessibilityRoles = defaultAccessibilityRoles
59
+ } = _ref,
60
+ props = _objectWithoutProperties(_ref, _excluded);
61
+ const [containerWidth, setContainerWidth] = useState(null);
62
+ const dropdownStyles = useMemo(() => {
63
+ var _controlRef$current;
64
+ return _objectSpread(_objectSpread({
65
+ width: containerWidth !== null ? containerWidth : (_controlRef$current = controlRef.current) === null || _controlRef$current === void 0 ? void 0 : _controlRef$current.getBoundingClientRect().width
66
+ }, style), styles === null || styles === void 0 ? void 0 : styles.root);
67
+ }, [styles === null || styles === void 0 ? void 0 : styles.root, containerWidth, controlRef, style]);
68
+ const optionStyles = useMemo(() => ({
69
+ optionCell: styles === null || styles === void 0 ? void 0 : styles.optionCell,
70
+ optionContent: styles === null || styles === void 0 ? void 0 : styles.optionContent,
71
+ optionLabel: styles === null || styles === void 0 ? void 0 : styles.optionLabel,
72
+ optionDescription: styles === null || styles === void 0 ? void 0 : styles.optionDescription,
73
+ selectAllDivider: styles === null || styles === void 0 ? void 0 : styles.selectAllDivider
74
+ }), [styles === null || styles === void 0 ? void 0 : styles.optionCell, styles === null || styles === void 0 ? void 0 : styles.optionContent, styles === null || styles === void 0 ? void 0 : styles.optionLabel, styles === null || styles === void 0 ? void 0 : styles.optionDescription, styles === null || styles === void 0 ? void 0 : styles.selectAllDivider]);
75
+ const optionClassNames = useMemo(() => ({
76
+ optionCell: classNames === null || classNames === void 0 ? void 0 : classNames.optionCell,
77
+ optionContent: classNames === null || classNames === void 0 ? void 0 : classNames.optionContent,
78
+ optionLabel: classNames === null || classNames === void 0 ? void 0 : classNames.optionLabel,
79
+ optionDescription: classNames === null || classNames === void 0 ? void 0 : classNames.optionDescription,
80
+ selectAllDivider: classNames === null || classNames === void 0 ? void 0 : classNames.selectAllDivider
81
+ }), [classNames === null || classNames === void 0 ? void 0 : classNames.optionCell, classNames === null || classNames === void 0 ? void 0 : classNames.optionContent, classNames === null || classNames === void 0 ? void 0 : classNames.optionLabel, classNames === null || classNames === void 0 ? void 0 : classNames.optionDescription, classNames === null || classNames === void 0 ? void 0 : classNames.selectAllDivider]);
82
+ const emptyDropdownContentsStyles = useMemo(() => ({
83
+ emptyContentsContainer: styles === null || styles === void 0 ? void 0 : styles.emptyContentsContainer,
84
+ emptyContentsText: styles === null || styles === void 0 ? void 0 : styles.emptyContentsText
85
+ }), [styles === null || styles === void 0 ? void 0 : styles.emptyContentsContainer, styles === null || styles === void 0 ? void 0 : styles.emptyContentsText]);
86
+ const emptyDropdownContentsClassNames = useMemo(() => ({
87
+ emptyContentsContainer: classNames === null || classNames === void 0 ? void 0 : classNames.emptyContentsContainer,
88
+ emptyContentsText: classNames === null || classNames === void 0 ? void 0 : classNames.emptyContentsText
89
+ }), [classNames === null || classNames === void 0 ? void 0 : classNames.emptyContentsContainer, classNames === null || classNames === void 0 ? void 0 : classNames.emptyContentsText]);
90
+ const isMultiSelect = type === 'multi';
91
+ const isSomeOptionsSelected = isMultiSelect ? value.length > 0 : false;
92
+ const isAllOptionsSelected = isMultiSelect ? value.length === options.filter(o => o.value !== null).length : false;
93
+ const toggleSelectAll = useCallback(() => {
94
+ if (isAllOptionsSelected) onChange(null);else onChange(options.map(o => o.value).filter(o => o !== null && !(value !== null && value !== void 0 && value.includes(o))));
95
+ }, [isAllOptionsSelected, onChange, options, value]);
96
+ const handleClearAll = useCallback(event => {
97
+ event.stopPropagation();
98
+ onChange(null);
99
+ }, [onChange]);
100
+ const handleEscPress = useCallback(() => setOpen(false), [setOpen]);
101
+ useEffect(() => {
102
+ if (!controlRef.current) return;
103
+ const resizeObserver = new ResizeObserver(entries => {
104
+ setContainerWidth(entries[0].contentRect.width);
105
+ });
106
+ resizeObserver.observe(controlRef.current);
107
+ return () => resizeObserver.disconnect();
108
+ }, [controlRef]);
109
+ const indeterminate = !isAllOptionsSelected && isSomeOptionsSelected ? true : false;
110
+ const SelectAllOption = useMemo(() => /*#__PURE__*/_jsx(SelectAllOptionComponent, {
111
+ accessibilityRole: accessibilityRoles === null || accessibilityRoles === void 0 ? void 0 : accessibilityRoles.option,
112
+ accessory: accessory,
113
+ blendStyles: styles === null || styles === void 0 ? void 0 : styles.optionBlendStyles,
114
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.option,
115
+ classNames: optionClassNames,
116
+ compact: compact,
117
+ disabled: disabled,
118
+ end: end !== null && end !== void 0 ? end : /*#__PURE__*/_jsx(Button, {
119
+ compact: true,
120
+ transparent: true,
121
+ onClick: handleClearAll,
122
+ role: "option",
123
+ style: {
124
+ margin: 'var(--space-0_5)'
125
+ },
126
+ width: "fit-content",
127
+ children: clearAllLabel
128
+ }),
129
+ indeterminate: indeterminate,
130
+ label: "".concat(selectAllLabel, " (").concat(options.filter(o => o.value !== null).length, ")"),
131
+ media: media !== null && media !== void 0 ? media : /*#__PURE__*/_jsx(Checkbox, {
132
+ readOnly: true,
133
+ checked: isAllOptionsSelected,
134
+ iconStyle: {
135
+ opacity: 1
136
+ },
137
+ indeterminate: indeterminate,
138
+ tabIndex: -1
139
+ }),
140
+ onClick: toggleSelectAll,
141
+ selected: isAllOptionsSelected || isSomeOptionsSelected,
142
+ style: styles === null || styles === void 0 ? void 0 : styles.option,
143
+ styles: optionStyles,
144
+ type: type,
145
+ value: 'select-all'
146
+ }, "select-all"), [SelectAllOptionComponent, accessibilityRoles === null || accessibilityRoles === void 0 ? void 0 : accessibilityRoles.option, accessory, styles === null || styles === void 0 ? void 0 : styles.optionBlendStyles, styles === null || styles === void 0 ? void 0 : styles.option, classNames === null || classNames === void 0 ? void 0 : classNames.option, optionClassNames, compact, disabled, end, handleClearAll, clearAllLabel, indeterminate, selectAllLabel, options, media, isAllOptionsSelected, toggleSelectAll, isSomeOptionsSelected, optionStyles, type]);
147
+ return /*#__PURE__*/_jsx(AnimatePresence, {
148
+ children: open && /*#__PURE__*/_jsx(Box, _objectSpread(_objectSpread({
149
+ ref: ref,
150
+ "aria-label": accessibilityLabel,
151
+ "aria-multiselectable": isMultiSelect,
152
+ className: cx(classNames === null || classNames === void 0 ? void 0 : classNames.root, className),
153
+ display: "block",
154
+ role: accessibilityRoles === null || accessibilityRoles === void 0 ? void 0 : accessibilityRoles.dropdown,
155
+ style: dropdownStyles
156
+ }, props), {}, {
157
+ children: /*#__PURE__*/_jsx(FocusTrap, {
158
+ disableAutoFocus: true,
159
+ focusTabIndexElements: true,
160
+ includeTriggerInFocusTrap: true,
161
+ respectNegativeTabIndex: true,
162
+ restoreFocusOnUnmount: true,
163
+ onEscPress: handleEscPress,
164
+ children: /*#__PURE__*/_jsx(motion.div, {
165
+ animate: animateStyle,
166
+ exit: initialStyle,
167
+ initial: initialStyle,
168
+ children: /*#__PURE__*/_jsxs(Box, {
169
+ bordered: true,
170
+ borderRadius: 400,
171
+ elevation: 2,
172
+ flexDirection: "column",
173
+ maxHeight: 252,
174
+ overflow: "auto",
175
+ children: [!hideSelectAll && isMultiSelect && options.length > 0 && SelectAllOption, options.length > 0 ? options.map(_ref2 => {
176
+ var _ref3;
177
+ let {
178
+ Component,
179
+ media: optionMedia,
180
+ accessory: optionAccessory,
181
+ end: optionEnd
182
+ } = _ref2,
183
+ option = _objectWithoutProperties(_ref2, _excluded2);
184
+ const RenderedSelectOption = Component !== null && Component !== void 0 ? Component : SelectOptionComponent;
185
+ const selected = option.value !== null && isMultiSelect ? value.includes(option.value) : value === option.value;
186
+ const defaultMedia = isMultiSelect ? /*#__PURE__*/_jsx(Checkbox, {
187
+ "aria-hidden": true,
188
+ readOnly: true,
189
+ checked: selected,
190
+ iconStyle: {
191
+ opacity: 1
192
+ },
193
+ tabIndex: -1
194
+ }) : /*#__PURE__*/_jsx(Radio, {
195
+ "aria-hidden": true,
196
+ readOnly: true,
197
+ checked: selected,
198
+ iconStyle: {
199
+ opacity: 1
200
+ },
201
+ tabIndex: -1
202
+ });
203
+ return /*#__PURE__*/_jsx(RenderedSelectOption, _objectSpread({
204
+ accessibilityRole: accessibilityRoles === null || accessibilityRoles === void 0 ? void 0 : accessibilityRoles.option,
205
+ accessory: optionAccessory !== null && optionAccessory !== void 0 ? optionAccessory : accessory,
206
+ blendStyles: styles === null || styles === void 0 ? void 0 : styles.optionBlendStyles,
207
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.option,
208
+ classNames: optionClassNames,
209
+ compact: compact,
210
+ disabled: option.disabled || disabled,
211
+ end: optionEnd !== null && optionEnd !== void 0 ? optionEnd : end,
212
+ media: (_ref3 = optionMedia !== null && optionMedia !== void 0 ? optionMedia : media) !== null && _ref3 !== void 0 ? _ref3 : defaultMedia,
213
+ onClick: newValue => {
214
+ onChange(newValue);
215
+ if (!isMultiSelect) setOpen(false);
216
+ },
217
+ selected: selected,
218
+ style: styles === null || styles === void 0 ? void 0 : styles.option,
219
+ styles: optionStyles,
220
+ type: type
221
+ }, option), option.value);
222
+ }) : /*#__PURE__*/_jsx(SelectEmptyDropdownContentsComponent, {
223
+ classNames: emptyDropdownContentsClassNames,
224
+ label: emptyOptionsLabel,
225
+ styles: emptyDropdownContentsStyles
226
+ })]
227
+ })
228
+ })
229
+ })
230
+ }))
231
+ });
232
+ }));
233
+ export const DefaultSelectDropdown = DefaultSelectDropdownComponent;
@@ -0,0 +1,23 @@
1
+ import { forwardRef, memo } from 'react';
2
+ import { Box } from '../../layout/Box';
3
+ import { Text } from '../../typography/Text';
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ export const DefaultSelectEmptyDropdownContents = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
6
+ let {
7
+ label,
8
+ styles,
9
+ classNames
10
+ } = _ref;
11
+ return /*#__PURE__*/_jsx(Box, {
12
+ ref: ref,
13
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.emptyContentsContainer,
14
+ padding: 2,
15
+ style: styles === null || styles === void 0 ? void 0 : styles.emptyContentsContainer,
16
+ children: /*#__PURE__*/_jsx(Text, {
17
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.emptyContentsText,
18
+ font: "body",
19
+ style: styles === null || styles === void 0 ? void 0 : styles.emptyContentsText,
20
+ children: label
21
+ })
22
+ });
23
+ }));
@@ -0,0 +1,2 @@
1
+ @layer cds{.selectOptionCss-s1n0butf{--bookendRadius:var(--borderRadius-400);position:relative;padding:0;margin:0;border:none;}.selectOptionCss-s1n0butf:first-child{border-top-right-radius:var(--bookendRadius);border-top-left-radius:var(--bookendRadius);}.selectOptionCss-s1n0butf:last-child{border-bottom-right-radius:var(--bookendRadius);border-bottom-left-radius:var(--bookendRadius);}.selectOptionCss-s1n0butf:focus{outline:none;}.selectOptionCss-s1n0butf:focus-visible{outline:none;}.selectOptionCss-s1n0butf:focus-visible::after{content:'';position:absolute;inset:0;border-radius:var(--bookendRadius);border:2px solid var(--color-bgLinePrimary);}.selectOptionCss-s1n0butf:focus-visible:first-child::after{border-top-right-radius:var(--bookendRadius);border-top-left-radius:var(--bookendRadius);}.selectOptionCss-s1n0butf:focus-visible:last-child::after{border-bottom-right-radius:var(--bookendRadius);border-bottom-left-radius:var(--bookendRadius);}
2
+ .multilineTextCss-m1dfwwjg{overflow:auto;text-overflow:unset;white-space:normal;}}
@@ -0,0 +1,103 @@
1
+ const _excluded = ["value", "label", "onClick", "disabled", "selected", "compact", "description", "multiline", "styles", "className", "classNames", "accessory", "media", "end", "type", "accessibilityRole", "background"];
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
5
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
6
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
7
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
8
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
9
+ import { forwardRef, memo, useCallback, useMemo } from 'react';
10
+ import { selectCellSpacingConfig } from '@coinbase/cds-common/tokens/select';
11
+ import { Cell } from '../../cells/Cell';
12
+ import { cx } from '../../cx';
13
+ import { VStack } from '../../layout/VStack';
14
+ import { Pressable } from '../../system/Pressable';
15
+ import { Text } from '../../typography/Text';
16
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
17
+ const selectOptionCss = "selectOptionCss-s1n0butf";
18
+ const multilineTextCss = "multilineTextCss-m1dfwwjg";
19
+ const DefaultSelectOptionComponent = /*#__PURE__*/memo(/*#__PURE__*/forwardRef((_ref, ref) => {
20
+ let {
21
+ value,
22
+ label,
23
+ onClick,
24
+ disabled,
25
+ selected,
26
+ compact,
27
+ description,
28
+ multiline,
29
+ styles,
30
+ className,
31
+ classNames,
32
+ accessory,
33
+ media,
34
+ end,
35
+ type,
36
+ accessibilityRole = 'option',
37
+ background = type === 'single' && selected && value !== null ? 'bgAlternate' : 'bg'
38
+ } = _ref,
39
+ props = _objectWithoutProperties(_ref, _excluded);
40
+ const labelNode = useMemo(() => typeof label === 'string' ? /*#__PURE__*/_jsx(Text, {
41
+ as: "div",
42
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.optionLabel,
43
+ font: "headline",
44
+ overflow: "truncate",
45
+ style: styles === null || styles === void 0 ? void 0 : styles.optionLabel,
46
+ children: label
47
+ }) : label, [label, classNames === null || classNames === void 0 ? void 0 : classNames.optionLabel, styles === null || styles === void 0 ? void 0 : styles.optionLabel]);
48
+ const descriptionNode = useMemo(() => typeof description === 'string' ? /*#__PURE__*/_jsx(Text, {
49
+ as: "div",
50
+ className: cx(multiline ? multilineTextCss : undefined, classNames === null || classNames === void 0 ? void 0 : classNames.optionDescription),
51
+ color: "fgMuted",
52
+ font: "body",
53
+ overflow: multiline ? undefined : 'truncate',
54
+ style: styles === null || styles === void 0 ? void 0 : styles.optionDescription,
55
+ children: description
56
+ }) : description, [description, multiline, classNames === null || classNames === void 0 ? void 0 : classNames.optionDescription, styles === null || styles === void 0 ? void 0 : styles.optionDescription]);
57
+ const handleClick = useCallback(() => onClick === null || onClick === void 0 ? void 0 : onClick(value), [onClick, value]);
58
+
59
+ // Since Cell's ref prop is type HTMLDivElement, we need to wrap it in a Pressable to get ref forwarding.
60
+ // On web, the option role doesn't work well with ara-checked and screen readers
61
+ // so we use aria-selected regardless of the option type.
62
+ return /*#__PURE__*/_jsx(Pressable, _objectSpread(_objectSpread({
63
+ ref: ref,
64
+ noScaleOnPress: true,
65
+ "aria-selected": selected,
66
+ background: background,
67
+ className: cx(selectOptionCss, className),
68
+ disabled: disabled,
69
+ onClick: handleClick,
70
+ role: accessibilityRole
71
+ }, props), {}, {
72
+ children: /*#__PURE__*/_jsx(Cell, {
73
+ accessory: accessory,
74
+ background: type === 'multi' || disabled || value === null ? 'transparent' : undefined,
75
+ borderRadius: 0,
76
+ className: cx(multiline ? multilineTextCss : undefined, classNames === null || classNames === void 0 ? void 0 : classNames.optionCell),
77
+ end: end,
78
+ innerSpacing: selectCellSpacingConfig.innerSpacing,
79
+ maxHeight: compact ? 56 : 64,
80
+ media: media,
81
+ minHeight: compact ? 40 : 56,
82
+ outerSpacing: selectCellSpacingConfig.outerSpacing,
83
+ priority: "end",
84
+ selected: selected,
85
+ style: styles === null || styles === void 0 ? void 0 : styles.optionCell
86
+ // This is a workaround to ensure the end element is displayed correctly
87
+ ,
88
+ styles: {
89
+ end: {
90
+ width: 'fit-content'
91
+ }
92
+ },
93
+ children: /*#__PURE__*/_jsxs(VStack, {
94
+ className: classNames === null || classNames === void 0 ? void 0 : classNames.optionContent,
95
+ justifyContent: "center",
96
+ style: styles === null || styles === void 0 ? void 0 : styles.optionContent,
97
+ children: [labelNode, descriptionNode]
98
+ })
99
+ })
100
+ }));
101
+ }));
102
+ export const DefaultSelectOption = DefaultSelectOptionComponent;
103
+ import "./DefaultSelectOption.css";