@codecademy/gamut 72.2.4-alpha.503c2e.0 → 72.2.4-alpha.787061.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/agent-tools/skills/gamut-forms/SKILL.md +16 -0
- package/agent-tools/skills/gamut-select-dropdown/SKILL.md +236 -0
- package/dist/Alert/elements.d.ts +2 -2
- package/dist/Anchor/index.d.ts +9 -19
- package/dist/Anchor/index.js +6 -9
- package/dist/BarChart/BarRow/elements.d.ts +45 -47
- package/dist/BarChart/utils/hooks.d.ts +2 -2
- package/dist/BarChart/utils/hooks.js +1 -3
- package/dist/Box/GridBox.d.ts +0 -1
- package/dist/Box/GridBox.js +1 -1
- package/dist/Box/props.d.ts +1 -1
- package/dist/Breadcrumbs/index.d.ts +5 -5
- package/dist/Breadcrumbs/index.js +2 -2
- package/dist/Button/CTAButton.d.ts +2 -2
- package/dist/Button/FillButton.d.ts +4 -4
- package/dist/Button/IconButton.d.ts +4 -4
- package/dist/Button/StrokeButton.d.ts +4 -4
- package/dist/Button/TextButton.d.ts +4 -4
- package/dist/Button/shared/InlineIconButton.d.ts +2 -2
- package/dist/Button/shared/styles.d.ts +3 -3
- package/dist/Button/shared/types.d.ts +1 -1
- package/dist/ButtonBase/ButtonBase.d.ts +4 -9
- package/dist/ButtonBase/ButtonBase.js +4 -11
- package/dist/Card/elements.d.ts +103 -109
- package/dist/Card/styles.d.ts +8 -8
- package/dist/Coachmark/index.d.ts +1 -1
- package/dist/ConnectedForm/ConnectedForm.d.ts +1 -1
- package/dist/ConnectedForm/ConnectedFormGroup.js +2 -3
- package/dist/ConnectedForm/utils.d.ts +1 -1
- package/dist/ConnectedForm/utils.js +1 -1
- package/dist/DatePicker/DatePickerInput/index.d.ts +1 -1
- package/dist/Disclosure/elements.d.ts +12 -18
- package/dist/FeatureShimmer/index.js +1 -1
- package/dist/Form/SelectDropdown/SelectDropdown.js +49 -101
- package/dist/Form/SelectDropdown/core/accessibility.d.ts +3 -0
- package/dist/Form/SelectDropdown/core/accessibility.js +12 -0
- package/dist/Form/SelectDropdown/core/constants.d.ts +13 -0
- package/dist/Form/SelectDropdown/core/constants.js +14 -0
- package/dist/Form/SelectDropdown/{styles.js → core/styles.js} +30 -12
- package/dist/Form/SelectDropdown/{utils.d.ts → core/utils.d.ts} +12 -2
- package/dist/Form/SelectDropdown/{utils.js → core/utils.js} +24 -2
- package/dist/Form/SelectDropdown/elements/constants.d.ts +0 -8
- package/dist/Form/SelectDropdown/elements/constants.js +1 -9
- package/dist/Form/SelectDropdown/elements/containers.d.ts +6 -2
- package/dist/Form/SelectDropdown/elements/containers.js +18 -2
- package/dist/Form/SelectDropdown/elements/controls.d.ts +1 -15
- package/dist/Form/SelectDropdown/elements/controls.js +2 -91
- package/dist/Form/SelectDropdown/elements/index.d.ts +2 -1
- package/dist/Form/SelectDropdown/elements/index.js +2 -1
- package/dist/Form/SelectDropdown/elements/multi-value.js +2 -2
- package/dist/Form/SelectDropdown/elements/options.d.ts +1 -0
- package/dist/Form/SelectDropdown/elements/options.js +5 -2
- package/dist/Form/SelectDropdown/hooks/useSelectHandlers.d.ts +22 -0
- package/dist/Form/SelectDropdown/hooks/useSelectHandlers.js +62 -0
- package/dist/Form/SelectDropdown/hooks/useSelectOptions.d.ts +14 -0
- package/dist/Form/SelectDropdown/hooks/useSelectOptions.js +39 -0
- package/dist/Form/SelectDropdown/types/component-props.d.ts +54 -6
- package/dist/Form/SelectDropdown/types/internal.d.ts +4 -4
- package/dist/Form/SelectDropdown/types/styles.d.ts +5 -1
- package/dist/Form/elements/Form.d.ts +15 -15
- package/dist/Form/elements/FormGroup.d.ts +1 -1
- package/dist/Form/styles/Checkbox-styles.d.ts +1 -1
- package/dist/Form/styles/index.d.ts +1 -1
- package/dist/Form/styles/index.js +1 -1
- package/dist/GridForm/GridFormButtons/index.d.ts +4 -4
- package/dist/List/ListProvider.d.ts +1 -1
- package/dist/List/elements.d.ts +42 -44
- package/dist/Menu/MenuItem.js +6 -10
- package/dist/Menu/elements.d.ts +2 -2
- package/dist/Modals/Dialog.js +2 -6
- package/dist/Modals/Modal.js +2 -5
- package/dist/Modals/elements.d.ts +1 -1
- package/dist/Pagination/AnimatedPaginationButtons.d.ts +29 -31
- package/dist/Pagination/EllipsisButton.d.ts +2 -2
- package/dist/Pagination/PaginationButton.d.ts +6 -6
- package/dist/Pagination/utils.d.ts +29 -31
- package/dist/Pagination/utils.js +11 -14
- package/dist/Popover/Popover.js +6 -6
- package/dist/Popover/types.d.ts +3 -4
- package/dist/PopoverContainer/PopoverContainer.js +9 -9
- package/dist/PopoverContainer/hooks.d.ts +4 -16
- package/dist/PopoverContainer/hooks.js +27 -50
- package/dist/PopoverContainer/types.d.ts +1 -2
- package/dist/Tabs/TabButton.d.ts +2 -2
- package/dist/Tabs/TabNavLink.d.ts +2 -2
- package/dist/Tag/elements.d.ts +8 -14
- package/dist/Tag/index.js +1 -1
- package/dist/Tip/InfoTip/InfoTipButton.d.ts +4 -4
- package/dist/Tip/PreviewTip/elements.d.ts +6 -12
- package/dist/Tip/__tests__/helpers.d.ts +1 -1
- package/dist/Tip/shared/FloatingTip.js +2 -2
- package/dist/Tip/shared/types.d.ts +2 -2
- package/dist/Tip/shared/utils.js +1 -1
- package/dist/utils/react.js +2 -4
- package/package.json +11 -11
- package/dist/utils/nullish.d.ts +0 -10
- package/dist/utils/nullish.js +0 -11
- /package/dist/Form/SelectDropdown/{styles.d.ts → core/styles.d.ts} +0 -0
|
@@ -17,7 +17,7 @@ const Shimmer = /*#__PURE__*/_styled(BaseContainer, {
|
|
|
17
17
|
left: '-100%',
|
|
18
18
|
top: 0,
|
|
19
19
|
bg: 'white-400'
|
|
20
|
-
}), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,
|
|
20
|
+
}), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9GZWF0dXJlU2hpbW1lci9pbmRleC50c3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBT2dCIiwiZmlsZSI6Ii4uLy4uL3NyYy9GZWF0dXJlU2hpbW1lci9pbmRleC50c3giLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjc3MgfSBmcm9tICdAY29kZWNhZGVteS9nYW11dC1zdHlsZXMnO1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuaW1wb3J0IHsgbW90aW9uLCB1c2VSZWR1Y2VkTW90aW9uIH0gZnJvbSAnZnJhbWVyLW1vdGlvbic7XG5cbmltcG9ydCB7IEJveCwgQm94UHJvcHMgfSBmcm9tICcuLi9Cb3gnO1xuXG5jb25zdCBCYXNlQ29udGFpbmVyID0gbW90aW9uLmNyZWF0ZShCb3gpO1xuY29uc3QgU2hpbW1lciA9IHN0eWxlZChCYXNlQ29udGFpbmVyKShcbiAgY3NzKHtcbiAgICBoZWlnaHQ6ICcxNTAlJyxcbiAgICB3aWR0aDogJ2NhbGMoMTAwJSAvIDkpJyxcbiAgICBwb3NpdGlvbjogJ2Fic29sdXRlJyxcbiAgICB0cmFuc2Zvcm06ICdyb3RhdGUoMzBkZWcpJyxcbiAgICB0cmFuc2Zvcm1PcmlnaW46ICd0b3AgcmlnaHQnLFxuICAgIGZpbHRlcjogJ2JsdXIoMjVweCknLFxuICAgIGxlZnQ6ICctMTAwJScsXG4gICAgdG9wOiAwLFxuICAgIGJnOiAnd2hpdGUtNDAwJyxcbiAgfSlcbik7XG5cbmNvbnN0IGJveFZhcmlhbnRzID0ge1xuICBpblZpZXc6IHtcbiAgICBiYWNrZ3JvdW5kQ29sb3I6ICdyZ2JhKDAsIDAsIDAsIDApJyxcbiAgICBib3JkZXJDb2xvcjogJ3JnYmEoMCwgMCwgMCwgMCknLFxuICAgIHRyYW5zaXRpb246IHtcbiAgICAgIGVhc2U6ICdlYXNlT3V0JyxcbiAgICAgIGR1cmF0aW9uOiAwLjMsXG4gICAgICBkZWxheTogNCxcbiAgICB9LFxuICB9LFxufTtcblxuY29uc3Qgc2hpbW1lclZhcmlhbnRzID0ge1xuICBpblZpZXc6IHtcbiAgICBsZWZ0OiAnMTIwJScsIC8vIGV4dHJhICUgdG8gYWNjb3VudCBmb3Igcm90YXRpb25cbiAgICBiYWNrZ3JvdW5kQ29sb3I6ICdyZ2JhKDAsIDAsIDAsIDApJyxcbiAgICB0cmFuc2l0aW9uOiB7XG4gICAgICBsZWZ0OiB7XG4gICAgICAgIGVhc2U6ICdlYXNlSW5PdXQnLFxuICAgICAgICBkdXJhdGlvbjogMixcbiAgICAgICAgZGVsYXk6IDIsXG4gICAgICB9LFxuICAgICAgYmFja2dyb3VuZENvbG9yOiB7XG4gICAgICAgIGVhc2U6ICdlYXNlT3V0JyxcbiAgICAgICAgZHVyYXRpb246IDEsXG4gICAgICAgIGRlbGF5OiA0LFxuICAgICAgfSxcbiAgICB9LFxuICB9LFxufTtcblxuZXhwb3J0IGNvbnN0IEZlYXR1cmVTaGltbWVyOiBSZWFjdC5GQzxPbWl0PEJveFByb3BzLCAncmVmJz4+ID0gKHtcbiAgY2hpbGRyZW4sXG4gIC4uLnJlc3Rcbn0pID0+IHtcbiAgY29uc3Qgc2hvdWxkUmVkdWNlTW90aW9uID0gdXNlUmVkdWNlZE1vdGlvbigpO1xuICByZXR1cm4gKFxuICAgIDxCb3ggey4uLnJlc3R9PlxuICAgICAge3Nob3VsZFJlZHVjZU1vdGlvbiA/IChcbiAgICAgICAgY2hpbGRyZW5cbiAgICAgICkgOiAoXG4gICAgICAgIDxCYXNlQ29udGFpbmVyXG4gICAgICAgICAgYmc9XCJiYWNrZ3JvdW5kLXNlbGVjdGVkXCJcbiAgICAgICAgICBib3JkZXI9ezF9XG4gICAgICAgICAgYm9yZGVyQ29sb3I9XCJib3JkZXItdGVydGlhcnlcIlxuICAgICAgICAgIGJvcmRlclJhZGl1cz17cmVzdD8uYm9yZGVyUmFkaXVzfVxuICAgICAgICAgIGhlaWdodD17MX1cbiAgICAgICAgICBvdmVyZmxvdz1cImhpZGRlblwiXG4gICAgICAgICAgcG9zaXRpb249XCJyZWxhdGl2ZVwiXG4gICAgICAgICAgdmFyaWFudHM9e2JveFZhcmlhbnRzfVxuICAgICAgICAgIHZpZXdwb3J0PXt7IG9uY2U6IHRydWUgfX1cbiAgICAgICAgICB3aGlsZUluVmlldz1cImluVmlld1wiXG4gICAgICAgICAgd2lkdGg9ezF9XG4gICAgICAgID5cbiAgICAgICAgICA8U2hpbW1lclxuICAgICAgICAgICAgZGF0YS10ZXN0aWQ9XCJmZWF0dXJlLXNoaW1tZXJcIlxuICAgICAgICAgICAgdmFyaWFudHM9e3NoaW1tZXJWYXJpYW50c31cbiAgICAgICAgICAgIHZpZXdwb3J0PXt7IG9uY2U6IHRydWUgfX1cbiAgICAgICAgICAvPlxuICAgICAgICAgIHtjaGlsZHJlbn1cbiAgICAgICAgPC9CYXNlQ29udGFpbmVyPlxuICAgICAgKX1cbiAgICA8L0JveD5cbiAgKTtcbn07XG4iXX0= */");
|
|
21
21
|
const boxVariants = {
|
|
22
22
|
inView: {
|
|
23
23
|
backgroundColor: 'rgba(0, 0, 0, 0)',
|
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
import { useTheme } from '@emotion/react';
|
|
2
|
-
import {
|
|
2
|
+
import { useId, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import * as React from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { getMemoizedStyles } from './styles';
|
|
7
|
-
import {
|
|
4
|
+
import { onFocus } from './core/accessibility';
|
|
5
|
+
import { defaultComponents } from './core/constants';
|
|
6
|
+
import { getMemoizedStyles } from './core/styles';
|
|
7
|
+
import { resolveNoOptionsMessage } from './core/utils';
|
|
8
|
+
import { formatGroupLabel, formatOptionLabel, SelectDropdownContext, TypedReactSelect } from './elements';
|
|
9
|
+
import { useSelectHandlers } from './hooks/useSelectHandlers';
|
|
10
|
+
import { useSelectOptions } from './hooks/useSelectOptions';
|
|
8
11
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
const defaultProps = {
|
|
10
|
-
name: undefined,
|
|
11
|
-
components: {
|
|
12
|
-
DropdownIndicator: DropdownButton,
|
|
13
|
-
IndicatorSeparator: () => null,
|
|
14
|
-
ClearIndicator: RemoveAllButton,
|
|
15
|
-
SelectContainer: CustomContainer,
|
|
16
|
-
ValueContainer: CustomValueContainer,
|
|
17
|
-
MultiValue: MultiValueWithColorMode,
|
|
18
|
-
MultiValueRemove: MultiValueRemoveButton,
|
|
19
|
-
Option: IconOption,
|
|
20
|
-
SingleValue: AbbreviatedSingleValue,
|
|
21
|
-
Input: CustomInput
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
const onChangeAction = 'select-option';
|
|
25
|
-
|
|
26
12
|
/**
|
|
27
13
|
* A flexible dropdown select component built on top of react-select.
|
|
28
14
|
*
|
|
@@ -73,101 +59,58 @@ export const SelectDropdown = ({
|
|
|
73
59
|
disabled,
|
|
74
60
|
dropdownWidth,
|
|
75
61
|
error,
|
|
62
|
+
formatCreateLabel = inputValue => `Add "${inputValue}"`,
|
|
76
63
|
id,
|
|
77
64
|
inputProps,
|
|
78
65
|
inputWidth,
|
|
79
|
-
|
|
66
|
+
isCreatable = false,
|
|
67
|
+
isSearchable: isSearchableProp = false,
|
|
68
|
+
isValidNewOption,
|
|
80
69
|
menuAlignment = 'left',
|
|
81
70
|
multiple,
|
|
82
71
|
name,
|
|
83
72
|
onChange,
|
|
73
|
+
onCreateOption,
|
|
74
|
+
onInputChange,
|
|
84
75
|
options,
|
|
85
76
|
placeholder = 'Select an option',
|
|
86
77
|
shownOptionsLimit = 6,
|
|
87
78
|
size,
|
|
79
|
+
validationMessage,
|
|
88
80
|
value,
|
|
89
81
|
zIndex,
|
|
90
82
|
...rest
|
|
91
83
|
}) => {
|
|
84
|
+
// isSearchable is forced true when isCreatable is true (CreatableSelect requires a text input)
|
|
85
|
+
const isSearchable = isCreatable || isSearchableProp;
|
|
92
86
|
const rawInputId = useId();
|
|
93
87
|
const inputId = name ?? `${id}-select-dropdown-${rawInputId}`;
|
|
94
|
-
const [activated, setActivated] = useState(false);
|
|
95
|
-
const [currentFocusedValue, setCurrentFocusedValue] = useState(undefined);
|
|
96
|
-
|
|
97
|
-
// these are used to programatically manage the focus state of our multi-select options + 'Remove all' button
|
|
98
88
|
const removeAllButtonRef = useRef(null);
|
|
99
89
|
const selectInputRef = useRef(null);
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}, [selectOptions, value]);
|
|
125
|
-
const [multiValues, setMultiValues] = useState(multiple &&
|
|
126
|
-
// To keep this efficient for non-multiSelect
|
|
127
|
-
filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions)));
|
|
128
|
-
|
|
129
|
-
// If the caller changes the initial value, let's update our value to match.
|
|
130
|
-
useEffect(() => {
|
|
131
|
-
const newMultiValues = filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions));
|
|
132
|
-
if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
// We only update this when our passed in options or value changes, not multiValues.
|
|
136
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
137
|
-
}, [options, value]);
|
|
138
|
-
const changeHandler = useCallback(optionEvent => {
|
|
139
|
-
setActivated(true);
|
|
140
|
-
|
|
141
|
-
// We have to do this because the version of typescript we have doesn't have the transitivity of these type guards yet. But, we will soon!
|
|
142
|
-
// Should probably come with: https://codecademy.atlassian.net/browse/GM-354
|
|
143
|
-
const onChangeProps = {
|
|
144
|
-
onChange,
|
|
145
|
-
multiple
|
|
146
|
-
};
|
|
147
|
-
if (isSingleSelectProps(onChangeProps)) {
|
|
148
|
-
const singleOptionEvent = optionEvent;
|
|
149
|
-
onChangeProps.onChange?.(singleOptionEvent, {
|
|
150
|
-
action: onChangeAction,
|
|
151
|
-
option: singleOptionEvent
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
if (isMultipleSelectProps(onChangeProps)) {
|
|
155
|
-
setMultiValues(optionEvent);
|
|
156
|
-
onChangeProps.onChange?.(optionEvent, {
|
|
157
|
-
action: onChangeAction,
|
|
158
|
-
option: undefined // At the moment this isn't used, but when multi select is built for real, boom (https://codecademy.atlassian.net/browse/GM-354)
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}, [onChange, multiple]);
|
|
162
|
-
const keyPressHandler = e => {
|
|
163
|
-
if (multiple && e.key === 'Enter' && currentFocusedValue && multiValues) {
|
|
164
|
-
const newMultiValues = removeValueFromSelectedOptions(multiValues, currentFocusedValue);
|
|
165
|
-
if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
|
|
166
|
-
}
|
|
167
|
-
if (removeAllButtonRef.current !== null && e.key === 'ArrowRight' && multiValues && currentFocusedValue === multiValues[multiValues.length - 1].value) {
|
|
168
|
-
removeAllButtonRef.current.focus();
|
|
169
|
-
}
|
|
170
|
-
};
|
|
90
|
+
const [currentFocusedValue, setCurrentFocusedValue] = useState(undefined);
|
|
91
|
+
const {
|
|
92
|
+
selectOptions,
|
|
93
|
+
parsedValue
|
|
94
|
+
} = useSelectOptions({
|
|
95
|
+
options,
|
|
96
|
+
id,
|
|
97
|
+
size,
|
|
98
|
+
value: value
|
|
99
|
+
});
|
|
100
|
+
const {
|
|
101
|
+
activated,
|
|
102
|
+
multiValues,
|
|
103
|
+
changeHandler,
|
|
104
|
+
keyPressHandler
|
|
105
|
+
} = useSelectHandlers({
|
|
106
|
+
onChange,
|
|
107
|
+
multiple,
|
|
108
|
+
onCreateOption,
|
|
109
|
+
selectOptions,
|
|
110
|
+
value,
|
|
111
|
+
currentFocusedValue,
|
|
112
|
+
removeAllButtonRef
|
|
113
|
+
});
|
|
171
114
|
const theme = useTheme();
|
|
172
115
|
const memoizedStyles = useMemo(() => {
|
|
173
116
|
return getMemoizedStyles(theme, zIndex);
|
|
@@ -180,14 +123,15 @@ export const SelectDropdown = ({
|
|
|
180
123
|
selectInputRef
|
|
181
124
|
},
|
|
182
125
|
children: /*#__PURE__*/_jsx(TypedReactSelect, {
|
|
183
|
-
...defaultProps,
|
|
184
126
|
activated: activated,
|
|
185
127
|
"aria-live": "assertive",
|
|
186
128
|
ariaLiveMessages: {
|
|
187
129
|
onFocus
|
|
188
130
|
},
|
|
131
|
+
components: defaultComponents,
|
|
189
132
|
dropdownWidth: dropdownWidth,
|
|
190
133
|
error: Boolean(error),
|
|
134
|
+
formatCreateLabel: formatCreateLabel,
|
|
191
135
|
formatGroupLabel: formatGroupLabel,
|
|
192
136
|
formatOptionLabel: formatOptionLabel,
|
|
193
137
|
id: id || rest.htmlFor || rawInputId,
|
|
@@ -196,12 +140,15 @@ export const SelectDropdown = ({
|
|
|
196
140
|
...inputProps
|
|
197
141
|
},
|
|
198
142
|
inputWidth: inputWidth,
|
|
143
|
+
isCreatable: isCreatable,
|
|
199
144
|
isDisabled: disabled,
|
|
200
145
|
isMulti: multiple,
|
|
201
|
-
isOptionDisabled: option => option.disabled
|
|
146
|
+
isOptionDisabled: option => option.disabled,
|
|
202
147
|
isSearchable: isSearchable,
|
|
148
|
+
isValidNewOption: isValidNewOption,
|
|
203
149
|
menuAlignment: menuAlignment,
|
|
204
150
|
name: name,
|
|
151
|
+
noOptionsMessage: resolveNoOptionsMessage(validationMessage),
|
|
205
152
|
options: selectOptions,
|
|
206
153
|
placeholder: placeholder,
|
|
207
154
|
selectRef: selectInputRef,
|
|
@@ -210,7 +157,8 @@ export const SelectDropdown = ({
|
|
|
210
157
|
styles: memoizedStyles,
|
|
211
158
|
value: multiple ? multiValues : parsedValue,
|
|
212
159
|
onChange: changeHandler,
|
|
213
|
-
|
|
160
|
+
onInputChange: onInputChange,
|
|
161
|
+
onKeyDown: multiple ? keyPressHandler : undefined,
|
|
214
162
|
...rest
|
|
215
163
|
})
|
|
216
164
|
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const onFocus = ({
|
|
2
|
+
focused: {
|
|
3
|
+
label,
|
|
4
|
+
subtitle,
|
|
5
|
+
rightLabel,
|
|
6
|
+
disabled
|
|
7
|
+
}
|
|
8
|
+
}) => {
|
|
9
|
+
const formattedSubtitle = `, ${subtitle}`;
|
|
10
|
+
const formattedRightLabel = `, ${rightLabel}`;
|
|
11
|
+
return `You are currently focused on option ${label}${subtitle ? formattedSubtitle : ''} ${rightLabel ? formattedRightLabel : ''}${disabled ? ', disabled' : ''}`;
|
|
12
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const defaultComponents: {
|
|
2
|
+
DropdownIndicator: (props: import("../types").SizedIndicatorProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
IndicatorSeparator: () => null;
|
|
4
|
+
ClearIndicator: (props: import("../types").SizedIndicatorProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
SelectContainer: ({ children, ...rest }: import("../types").CustomSelectComponentProps<(<Option_18, IsMulti_18 extends boolean, Group_18 extends import("react-select").GroupBase<Option_18>>(props: import("react-select").ContainerProps<Option_18, IsMulti_18, Group_18>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
ValueContainer: ({ ...rest }: import("../types").CustomSelectComponentProps<(<Option_20, IsMulti_20 extends boolean, Group_20 extends import("react-select").GroupBase<Option_20>>(props: import("react-select").ValueContainerProps<Option_20, IsMulti_20, Group_20>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
MultiValue: (props: import("react-select").MultiValueProps<import("..").ExtendedOption, true, import("react-select").GroupBase<import("..").ExtendedOption>>) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
MultiValueRemove: (props: import("react-select").MultiValueRemoveProps<import("..").ExtendedOption, true, import("react-select").GroupBase<import("..").ExtendedOption>>) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
Option: ({ children, ...rest }: import("../types").CustomSelectComponentProps<(<Option_16, IsMulti_16 extends boolean, Group_16 extends import("react-select").GroupBase<Option_16>>(props: import("react-select").OptionProps<Option_16, IsMulti_16, Group_16>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
SingleValue: (props: import("react-select").SingleValueProps<import("..").ExtendedOption, false>) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
Input: ({ ...rest }: import("../types").CustomSelectComponentProps<(<Option_7, IsMulti_7 extends boolean, Group_7 extends import("react-select").GroupBase<Option_7>>(props: import("react-select").InputProps<Option_7, IsMulti_7, Group_7>) => import("@emotion/react").jsx.JSX.Element)>) => import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
};
|
|
13
|
+
export declare const ON_CHANGE_ACTION: "select-option";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AbbreviatedSingleValue, CustomContainer, CustomInput, CustomValueContainer, DropdownButton, IconOption, MultiValueRemoveButton, MultiValueWithColorMode, RemoveAllButton } from '../elements';
|
|
2
|
+
export const defaultComponents = {
|
|
3
|
+
DropdownIndicator: DropdownButton,
|
|
4
|
+
IndicatorSeparator: () => null,
|
|
5
|
+
ClearIndicator: RemoveAllButton,
|
|
6
|
+
SelectContainer: CustomContainer,
|
|
7
|
+
ValueContainer: CustomValueContainer,
|
|
8
|
+
MultiValue: MultiValueWithColorMode,
|
|
9
|
+
MultiValueRemove: MultiValueRemoveButton,
|
|
10
|
+
Option: IconOption,
|
|
11
|
+
SingleValue: AbbreviatedSingleValue,
|
|
12
|
+
Input: CustomInput
|
|
13
|
+
};
|
|
14
|
+
export const ON_CHANGE_ACTION = 'select-option';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { css, states, variant } from '@codecademy/gamut-styles';
|
|
2
|
-
import { dismissSharedStyles, tagBaseStyles, tagLabelFontSize, tagLabelPadding } from '
|
|
3
|
-
import { formBaseComponentStyles, formBaseFieldStylesObject, formFieldDisabledStyles, formFieldPaddingStyles, InputSelectors } from '
|
|
2
|
+
import { dismissSharedStyles, tagBaseStyles, tagLabelFontSize, tagLabelPadding } from '../../../Tag/styles';
|
|
3
|
+
import { formBaseComponentStyles, formBaseFieldStylesObject, formFieldDisabledStyles, formFieldPaddingStyles, InputSelectors } from '../../styles';
|
|
4
4
|
const selectDropdownStyles = css({
|
|
5
5
|
...formBaseFieldStylesObject,
|
|
6
6
|
display: 'flex',
|
|
@@ -62,7 +62,7 @@ const textColor = css({
|
|
|
62
62
|
color: 'text'
|
|
63
63
|
});
|
|
64
64
|
const placeholderColor = css({
|
|
65
|
-
color: 'text-
|
|
65
|
+
color: 'text-secondary'
|
|
66
66
|
});
|
|
67
67
|
export const getMemoizedStyles = (theme, zIndex) => {
|
|
68
68
|
return {
|
|
@@ -137,6 +137,8 @@ export const getMemoizedStyles = (theme, zIndex) => {
|
|
|
137
137
|
error: state.selectProps.error,
|
|
138
138
|
theme
|
|
139
139
|
}),
|
|
140
|
+
// Drop react-select's default menu drop shadow; the border above defines the edge.
|
|
141
|
+
boxShadow: 'none',
|
|
140
142
|
...(dropdownWidth ? {
|
|
141
143
|
minWidth: dropdownWidth,
|
|
142
144
|
width: dropdownWidth
|
|
@@ -194,16 +196,32 @@ export const getMemoizedStyles = (theme, zIndex) => {
|
|
|
194
196
|
backgroundColor: theme.colors['secondary-hover']
|
|
195
197
|
}
|
|
196
198
|
}),
|
|
197
|
-
|
|
198
|
-
...
|
|
199
|
-
|
|
200
|
-
}),
|
|
201
|
-
alignItems: 'center',
|
|
202
|
-
color: state.isDisabled ? 'text-disabled' : 'default',
|
|
203
|
-
cursor: state.isDisabled ? 'not-allowed' : 'pointer',
|
|
204
|
-
display: 'flex',
|
|
205
|
-
padding: state.selectProps.size === 'small' ? '3px 14px' : '11px 14px'
|
|
199
|
+
noOptionsMessage: provided => ({
|
|
200
|
+
...provided,
|
|
201
|
+
color: theme.colors['text-secondary']
|
|
206
202
|
}),
|
|
203
|
+
option: (provided, state) => {
|
|
204
|
+
const isNew = state.data?.__isNew__;
|
|
205
|
+
const isSmall = state.selectProps.size === 'small';
|
|
206
|
+
return {
|
|
207
|
+
...getOptionBackground(state.isSelected, state.isFocused)({
|
|
208
|
+
theme
|
|
209
|
+
}),
|
|
210
|
+
alignItems: 'center',
|
|
211
|
+
color: state.isDisabled ? theme.colors['text-disabled'] : isNew ? theme.colors.primary : theme.colors.text,
|
|
212
|
+
cursor: state.isDisabled ? 'not-allowed' : 'pointer',
|
|
213
|
+
display: 'flex',
|
|
214
|
+
padding: isSmall ? '3px 14px' : '11px 14px',
|
|
215
|
+
...(isNew && {
|
|
216
|
+
// Gradient creates the 1px divider line at the top edge of the option background
|
|
217
|
+
backgroundImage: `linear-gradient(${theme.colors['text-disabled']} 1px, transparent 1px)`,
|
|
218
|
+
backgroundPosition: '0 0',
|
|
219
|
+
backgroundRepeat: 'no-repeat',
|
|
220
|
+
backgroundSize: '100% 1px',
|
|
221
|
+
paddingTop: isSmall ? '11px' : '19px'
|
|
222
|
+
})
|
|
223
|
+
};
|
|
224
|
+
},
|
|
207
225
|
placeholder: provided => ({
|
|
208
226
|
...provided,
|
|
209
227
|
...placeholderColor({
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ActionMeta, Options as OptionsType } from 'react-select';
|
|
3
|
+
import { SelectOptionBase } from '../../utils';
|
|
4
|
+
import { BaseOnChangeProps, ExtendedOption, MultiSelectDropdownProps, OptionStrict, SelectDropdownGroup, SelectDropdownOptions, SelectDropdownProps, SingleSelectDropdownProps } from '../types';
|
|
3
5
|
export declare const isMultipleSelectProps: (props: BaseOnChangeProps) => props is MultiSelectDropdownProps;
|
|
4
6
|
export declare const isSingleSelectProps: (props: BaseOnChangeProps) => props is SingleSelectDropdownProps;
|
|
7
|
+
/**
|
|
8
|
+
* Resolves the value for a newly created option from react-select action metadata
|
|
9
|
+
* or the onChange option payload. Returns undefined when no reliable value exists.
|
|
10
|
+
*/
|
|
11
|
+
export declare const getCreatedOptionValue: (optionEvent: OptionStrict | OptionsType<OptionStrict>, actionMeta: ActionMeta<OptionStrict>, multiple?: boolean) => string | undefined;
|
|
5
12
|
export declare const isOptionGroup: (obj: unknown) => obj is SelectDropdownGroup;
|
|
6
13
|
export declare const isOptionsGrouped: (options: SelectDropdownOptions) => options is SelectDropdownGroup[];
|
|
7
14
|
/**
|
|
@@ -23,4 +30,7 @@ export declare const filterValueFromOptions: (options: SelectOptionBase[] | Sele
|
|
|
23
30
|
* @param value - The value or values to remove
|
|
24
31
|
* @returns New array with the specified values removed
|
|
25
32
|
*/
|
|
33
|
+
export declare const resolveNoOptionsMessage: (validationMessage: SelectDropdownProps["validationMessage"]) => ((obj: {
|
|
34
|
+
inputValue: string;
|
|
35
|
+
}) => React.ReactNode) | undefined;
|
|
26
36
|
export declare const removeValueFromSelectedOptions: (selectedOptions: ExtendedOption[] | SelectOptionBase[], value: SelectDropdownProps["value"]) => SelectOptionBase[];
|
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { isDefined } from '../../utils/nullish';
|
|
2
1
|
export const isMultipleSelectProps = props => !!props.multiple;
|
|
3
2
|
export const isSingleSelectProps = props => !props.multiple;
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the value for a newly created option from react-select action metadata
|
|
5
|
+
* or the onChange option payload. Returns undefined when no reliable value exists.
|
|
6
|
+
*/
|
|
7
|
+
export const getCreatedOptionValue = (optionEvent, actionMeta, multiple) => {
|
|
8
|
+
const metaValue = actionMeta.option?.value;
|
|
9
|
+
if (metaValue) return metaValue;
|
|
10
|
+
if (!multiple) {
|
|
11
|
+
const {
|
|
12
|
+
value
|
|
13
|
+
} = optionEvent;
|
|
14
|
+
return value || undefined;
|
|
15
|
+
}
|
|
16
|
+
const newOption = optionEvent.find(option => option.__isNew__);
|
|
17
|
+
return newOption?.value || undefined;
|
|
18
|
+
};
|
|
19
|
+
export const isOptionGroup = obj => obj != null && typeof obj === 'object' && 'options' in obj && obj.options !== undefined;
|
|
5
20
|
export const isOptionsGrouped = options => Array.isArray(options) && options.some(option => isOptionGroup(option));
|
|
6
21
|
|
|
7
22
|
/**
|
|
@@ -29,6 +44,13 @@ export const filterValueFromOptions = (options, value, optionsAreGrouped) => {
|
|
|
29
44
|
* @param value - The value or values to remove
|
|
30
45
|
* @returns New array with the specified values removed
|
|
31
46
|
*/
|
|
47
|
+
export const resolveNoOptionsMessage = validationMessage => {
|
|
48
|
+
if (validationMessage === undefined) return undefined;
|
|
49
|
+
if (typeof validationMessage === 'function') {
|
|
50
|
+
return validationMessage;
|
|
51
|
+
}
|
|
52
|
+
return () => validationMessage;
|
|
53
|
+
};
|
|
32
54
|
export const removeValueFromSelectedOptions = (selectedOptions, value) => {
|
|
33
55
|
return selectedOptions.filter(option => {
|
|
34
56
|
if (Array.isArray(value)) {
|
|
@@ -15,14 +15,6 @@ export declare const indicatorIcons: {
|
|
|
15
15
|
size: number;
|
|
16
16
|
icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
17
17
|
};
|
|
18
|
-
smallSearchable: {
|
|
19
|
-
size: number;
|
|
20
|
-
icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
21
|
-
};
|
|
22
|
-
mediumSearchable: {
|
|
23
|
-
size: number;
|
|
24
|
-
icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
25
|
-
};
|
|
26
18
|
smallRemove: {
|
|
27
19
|
size: number;
|
|
28
20
|
icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ArrowChevronDownIcon, CloseIcon, MiniChevronDownIcon, MiniDeleteIcon
|
|
1
|
+
import { ArrowChevronDownIcon, CloseIcon, MiniChevronDownIcon, MiniDeleteIcon } from '@codecademy/gamut-icons';
|
|
2
2
|
export const iconSize = {
|
|
3
3
|
small: 12,
|
|
4
4
|
medium: 16
|
|
@@ -16,14 +16,6 @@ export const indicatorIcons = {
|
|
|
16
16
|
size: iconSize.medium,
|
|
17
17
|
icon: ArrowChevronDownIcon
|
|
18
18
|
},
|
|
19
|
-
smallSearchable: {
|
|
20
|
-
size: iconSize.small,
|
|
21
|
-
icon: SearchIcon
|
|
22
|
-
},
|
|
23
|
-
mediumSearchable: {
|
|
24
|
-
size: iconSize.medium,
|
|
25
|
-
icon: SearchIcon
|
|
26
|
-
},
|
|
27
19
|
smallRemove: {
|
|
28
20
|
size: iconSize.small,
|
|
29
21
|
icon: MiniDeleteIcon
|
|
@@ -24,6 +24,10 @@ export declare const CustomValueContainer: ({ ...rest }: CustomSelectComponentPr
|
|
|
24
24
|
export declare const CustomInput: ({ ...rest }: CustomSelectComponentProps<typeof SelectDropdownElements.Input>) => import("react/jsx-runtime").JSX.Element;
|
|
25
25
|
/**
|
|
26
26
|
* Typed wrapper around react-select component.
|
|
27
|
-
*
|
|
27
|
+
* Renders CreatableSelect when isCreatable is true, ReactSelect otherwise.
|
|
28
|
+
* Creatable-only props (formatCreateLabel, isValidNewOption) are stripped from
|
|
29
|
+
* the non-creatable path so they don't reach ReactSelect. `onCreateOption` is
|
|
30
|
+
* handled in SelectDropdown's changeHandler — do not pass it to CreatableSelect
|
|
31
|
+
* or react-select will skip onChange on create.
|
|
28
32
|
*/
|
|
29
|
-
export declare function TypedReactSelect<OptionType, IsMulti extends boolean = false, GroupType extends GroupBase<OptionType> = GroupBase<OptionType>>({ selectRef, ...props }: Props<OptionType, IsMulti, GroupType> & TypedReactSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
33
|
+
export declare function TypedReactSelect<OptionType, IsMulti extends boolean = false, GroupType extends GroupBase<OptionType> = GroupBase<OptionType>>({ selectRef, isCreatable, formatCreateLabel, isValidNewOption, ...props }: Props<OptionType, IsMulti, GroupType> & TypedReactSelectProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createContext, useLayoutEffect } from 'react';
|
|
2
2
|
import ReactSelect, { components as SelectDropdownElements } from 'react-select';
|
|
3
|
+
import CreatableSelect from 'react-select/creatable';
|
|
3
4
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
5
|
/**
|
|
5
6
|
* React context for sharing state between SelectDropdown components.
|
|
@@ -61,7 +62,7 @@ export const CustomValueContainer = ({
|
|
|
61
62
|
} = rest.selectProps;
|
|
62
63
|
if (inputId) {
|
|
63
64
|
const inputElement = document.getElementById(inputId);
|
|
64
|
-
if (inputElement
|
|
65
|
+
if (inputElement && inputElement.getAttribute('role') === 'combobox') {
|
|
65
66
|
Object.entries(comboboxProps).forEach(([key, value]) => {
|
|
66
67
|
inputElement.setAttribute(key, value);
|
|
67
68
|
});
|
|
@@ -116,12 +117,27 @@ export const CustomInput = ({
|
|
|
116
117
|
|
|
117
118
|
/**
|
|
118
119
|
* Typed wrapper around react-select component.
|
|
119
|
-
*
|
|
120
|
+
* Renders CreatableSelect when isCreatable is true, ReactSelect otherwise.
|
|
121
|
+
* Creatable-only props (formatCreateLabel, isValidNewOption) are stripped from
|
|
122
|
+
* the non-creatable path so they don't reach ReactSelect. `onCreateOption` is
|
|
123
|
+
* handled in SelectDropdown's changeHandler — do not pass it to CreatableSelect
|
|
124
|
+
* or react-select will skip onChange on create.
|
|
120
125
|
*/
|
|
121
126
|
export function TypedReactSelect({
|
|
122
127
|
selectRef,
|
|
128
|
+
isCreatable,
|
|
129
|
+
formatCreateLabel,
|
|
130
|
+
isValidNewOption,
|
|
123
131
|
...props
|
|
124
132
|
}) {
|
|
133
|
+
if (isCreatable) {
|
|
134
|
+
return /*#__PURE__*/_jsx(CreatableSelect, {
|
|
135
|
+
...props,
|
|
136
|
+
formatCreateLabel: formatCreateLabel,
|
|
137
|
+
isValidNewOption: isValidNewOption,
|
|
138
|
+
ref: selectRef
|
|
139
|
+
});
|
|
140
|
+
}
|
|
125
141
|
return /*#__PURE__*/_jsx(ReactSelect, {
|
|
126
142
|
...props,
|
|
127
143
|
ref: selectRef
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ExtendedOption, SizedIndicatorProps } from '../types';
|
|
3
|
-
/**
|
|
4
|
-
* Generates accessible focus messages for screen readers.
|
|
5
|
-
* Provides detailed information about the currently focused option.
|
|
6
|
-
*
|
|
7
|
-
* @param params - Object containing the focused option details
|
|
8
|
-
* @returns Formatted accessibility message
|
|
9
|
-
*/
|
|
10
|
-
export declare const onFocus: AriaOnFocus<ExtendedOption>;
|
|
1
|
+
import { SizedIndicatorProps } from '../types';
|
|
11
2
|
/**
|
|
12
3
|
* Custom dropdown indicator that shows either a chevron or search icon.
|
|
13
4
|
* The icon type depends on whether the select is searchable or not.
|
|
14
5
|
*/
|
|
15
6
|
export declare const DropdownButton: (props: SizedIndicatorProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
|
-
/**
|
|
17
|
-
* Custom remove all button for multi-select mode.
|
|
18
|
-
* Provides keyboard navigation and accessible removal of all selected values.
|
|
19
|
-
*/
|
|
20
|
-
export declare const RemoveAllButton: (props: SizedIndicatorProps) => import("react/jsx-runtime").JSX.Element;
|