@codecademy/gamut
71.0.071.0.1-alpha.69ab4c.0
+
Added (1 files)
~
Modified (15 files)
Index: package/dist/Form/SelectDropdown/elements/constants.js
===================================================================
--- package/dist/Form/SelectDropdown/elements/constants.js
+++ package/dist/Form/SelectDropdown/elements/constants.js
@@ -1,5 +1,5 @@
-import { ArrowChevronDownIcon, CloseIcon, MiniChevronDownIcon, MiniDeleteIcon, SearchIcon } from '@codecademy/gamut-icons';
+import { ArrowChevronDownIcon, CloseIcon, MiniChevronDownIcon, MiniDeleteIcon } from '@codecademy/gamut-icons';
export const iconSize = {
small: 12,
medium: 16
};
@@ -15,16 +15,8 @@
mediumChevron: {
size: iconSize.medium,
icon: ArrowChevronDownIcon
},
- smallSearchable: {
- size: iconSize.small,
- icon: SearchIcon
- },
- mediumSearchable: {
- size: iconSize.medium,
- icon: SearchIcon
- },
smallRemove: {
size: iconSize.small,
icon: MiniDeleteIcon
}, Index: package/dist/Form/SelectDropdown/elements/containers.js
===================================================================
--- package/dist/Form/SelectDropdown/elements/containers.js
+++ package/dist/Form/SelectDropdown/elements/containers.js
@@ -1,6 +1,7 @@
import { createContext, useLayoutEffect } from 'react';
import ReactSelect, { components as SelectDropdownElements } from 'react-select';
+import CreatableSelect from 'react-select/creatable';
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* React context for sharing state between SelectDropdown components.
* Provides access to focus state and refs for keyboard navigation.
@@ -115,14 +116,29 @@
};
/**
* Typed wrapper around react-select component.
- * Provides type safety for the underlying react-select implementation.
+ * Renders CreatableSelect when isCreatable is true, ReactSelect otherwise.
+ * Creatable-only props (formatCreateLabel, isValidNewOption) are stripped from
+ * the non-creatable path so they don't reach ReactSelect. `onCreateOption` is
+ * handled in SelectDropdown's changeHandler — do not pass it to CreatableSelect
+ * or react-select will skip onChange on create.
*/
export function TypedReactSelect({
selectRef,
+ isCreatable,
+ formatCreateLabel,
+ isValidNewOption,
...props
}) {
+ if (isCreatable) {
+ return /*#__PURE__*/_jsx(CreatableSelect, {
+ ...props,
+ formatCreateLabel: formatCreateLabel,
+ isValidNewOption: isValidNewOption,
+ ref: selectRef
+ });
+ }
return /*#__PURE__*/_jsx(ReactSelect, {
...props,
ref: selectRef
}); Index: package/dist/Form/SelectDropdown/elements/controls.js
===================================================================
--- package/dist/Form/SelectDropdown/elements/controls.js
+++ package/dist/Form/SelectDropdown/elements/controls.js
@@ -35,17 +35,15 @@
* The icon type depends on whether the select is searchable or not.
*/
export const DropdownButton = props => {
const {
- size,
- isSearchable
+ size
} = props.selectProps;
const color = props.isDisabled ? 'text-disabled' : 'text';
const iconSize = size ?? 'medium';
- const iconType = isSearchable ? 'Searchable' : 'Chevron';
const {
...iconProps
- } = indicatorIcons[`${iconSize}${iconType}`];
+ } = indicatorIcons[`${iconSize}Chevron`];
const {
icon: IndicatorIcon
} = iconProps;
return /*#__PURE__*/_jsx(DropdownIndicator, {
@@ -65,9 +63,9 @@
},
'&:focus-visible': {
outline: `2px solid ${theme.colors.primary}`
}
-}), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9Gb3JtL1NlbGVjdERyb3Bkb3duL2VsZW1lbnRzL2NvbnRyb2xzLnRzeCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFxRGlDIiwiZmlsZSI6Ii4uLy4uLy4uLy4uL3NyYy9Gb3JtL1NlbGVjdERyb3Bkb3duL2VsZW1lbnRzL2NvbnRyb2xzLnRzeCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcywgdGhlbWUgfSBmcm9tICdAY29kZWNhZGVteS9nYW11dC1zdHlsZXMnO1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuaW1wb3J0IHsgS2V5Ym9hcmRFdmVudCwgdXNlQ29udGV4dCB9IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7XG4gIEFyaWFPbkZvY3VzLFxuICBjb21wb25lbnRzIGFzIFNlbGVjdERyb3Bkb3duRWxlbWVudHMsXG59IGZyb20gJ3JlYWN0LXNlbGVjdCc7XG5cbmltcG9ydCB7IEV4dGVuZGVkT3B0aW9uLCBTaXplZEluZGljYXRvclByb3BzIH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgaW5kaWNhdG9ySWNvbnMgfSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQgeyBTZWxlY3REcm9wZG93bkNvbnRleHQgfSBmcm9tICcuL2NvbnRhaW5lcnMnO1xuXG5jb25zdCB7IERyb3Bkb3duSW5kaWNhdG9yIH0gPSBTZWxlY3REcm9wZG93bkVsZW1lbnRzO1xuXG4vKipcbiAqIEdlbmVyYXRlcyBhY2Nlc3NpYmxlIGZvY3VzIG1lc3NhZ2VzIGZvciBzY3JlZW4gcmVhZGVycy5cbiAqIFByb3ZpZGVzIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50bHkgZm9jdXNlZCBvcHRpb24uXG4gKlxuICogQHBhcmFtIHBhcmFtcyAtIE9iamVjdCBjb250YWluaW5nIHRoZSBmb2N1c2VkIG9wdGlvbiBkZXRhaWxzXG4gKiBAcmV0dXJucyBGb3JtYXR0ZWQgYWNjZXNzaWJpbGl0eSBtZXNzYWdlXG4gKi9cbmV4cG9ydCBjb25zdCBvbkZvY3VzOiBBcmlhT25Gb2N1czxFeHRlbmRlZE9wdGlvbj4gPSAoe1xuICBmb2N1c2VkOiB7IGxhYmVsLCBzdWJ0aXRsZSwgcmlnaHRMYWJlbCwgZGlzYWJsZWQgfSxcbn0pID0+IHtcbiAgY29uc3QgZm9ybWF0dGVkU3VidGl0bGUgPSBgLCAke3N1YnRpdGxlfWA7XG4gIGNvbnN0IGZvcm1hdHRlZFJpZ2h0TGFiZWwgPSBgLCAke3JpZ2h0TGFiZWx9YDtcblxuICBjb25zdCBtc2cgPSBgWW91IGFyZSBjdXJyZW50bHkgZm9jdXNlZCBvbiBvcHRpb24gJHtsYWJlbH0ke1xuICAgIHN1YnRpdGxlID8gZm9ybWF0dGVkU3VidGl0bGUgOiAnJ1xuICB9ICR7cmlnaHRMYWJlbCA/IGZvcm1hdHRlZFJpZ2h0TGFiZWwgOiAnJ30ke2Rpc2FibGVkID8gJywgZGlzYWJsZWQnIDogJyd9YDtcblxuICByZXR1cm4gbXNnO1xufTtcblxuLyoqXG4gKiBDdXN0b20gZHJvcGRvd24gaW5kaWNhdG9yIHRoYXQgc2hvd3MgZWl0aGVyIGEgY2hldnJvbiBvciBzZWFyY2ggaWNvbi5cbiAqIFRoZSBpY29uIHR5cGUgZGVwZW5kcyBvbiB3aGV0aGVyIHRoZSBzZWxlY3QgaXMgc2VhcmNoYWJsZSBvciBub3QuXG4gKi9cbmV4cG9ydCBjb25zdCBEcm9wZG93bkJ1dHRvbiA9IChwcm9wczogU2l6ZWRJbmRpY2F0b3JQcm9wcykgPT4ge1xuICBjb25zdCB7IHNpemUsIGlzU2VhcmNoYWJsZSB9ID0gcHJvcHMuc2VsZWN0UHJvcHM7XG4gIGNvbnN0IGNvbG9yID0gcHJvcHMuaXNEaXNhYmxlZCA/ICd0ZXh0LWRpc2FibGVkJyA6ICd0ZXh0JztcbiAgY29uc3QgaWNvblNpemUgPSBzaXplID8/ICdtZWRpdW0nO1xuICBjb25zdCBpY29uVHlwZSA9IGlzU2VhcmNoYWJsZSA/ICdTZWFyY2hhYmxlJyA6ICdDaGV2cm9uJztcbiAgY29uc3QgeyAuLi5pY29uUHJvcHMgfSA9IGluZGljYXRvckljb25zW2Ake2ljb25TaXplfSR7aWNvblR5cGV9YF07XG4gIGNvbnN0IHsgaWNvbjogSW5kaWNhdG9ySWNvbiB9ID0gaWNvblByb3BzO1xuXG4gIHJldHVybiAoXG4gICAgPERyb3Bkb3duSW5kaWNhdG9yIHsuLi5wcm9wc30+XG4gICAgICA8SW5kaWNhdG9ySWNvbiB7Li4uaWNvblByb3BzfSBjb2xvcj17Y29sb3J9IC8+XG4gICAgPC9Ecm9wZG93bkluZGljYXRvcj5cbiAgKTtcbn07XG5cbmNvbnN0IEN1c3RvbVN0eWxlZFJlbW92ZUFsbERpdiA9IHN0eWxlZCgnZGl2JykoXG4gIGNzcyh7XG4gICAgJyY6Zm9jdXMnOiB7XG4gICAgICBvdXRsaW5lOiBgMnB4IHNvbGlkICR7dGhlbWUuY29sb3JzLnByaW1hcnl9YCxcbiAgICB9LFxuICAgICcmOmZvY3VzLXZpc2libGUnOiB7XG4gICAgICBvdXRsaW5lOiBgMnB4IHNvbGlkICR7dGhlbWUuY29sb3JzLnByaW1hcnl9YCxcbiAgICB9LFxuICB9KVxuKTtcblxuLyoqXG4gKiBDdXN0b20gcmVtb3ZlIGFsbCBidXR0b24gZm9yIG11bHRpLXNlbGVjdCBtb2RlLlxuICogUHJvdmlkZXMga2V5Ym9hcmQgbmF2aWdhdGlvbiBhbmQgYWNjZXNzaWJsZSByZW1vdmFsIG9mIGFsbCBzZWxlY3RlZCB2YWx1ZXMuXG4gKi9cbmV4cG9ydCBjb25zdCBSZW1vdmVBbGxCdXR0b24gPSAocHJvcHM6IFNpemVkSW5kaWNhdG9yUHJvcHMpID0+IHtcbiAgY29uc3Qge1xuICAgIGdldFN0eWxlcyxcbiAgICBpbm5lclByb3BzOiB7IC4uLnJlc3RJbm5lclByb3BzIH0sXG4gICAgc2VsZWN0UHJvcHM6IHsgc2l6ZSB9LFxuICB9ID0gcHJvcHM7XG5cbiAgY29uc3QgeyByZW1vdmVBbGxCdXR0b25SZWYsIHNlbGVjdElucHV0UmVmIH0gPSB1c2VDb250ZXh0KFxuICAgIFNlbGVjdERyb3Bkb3duQ29udGV4dFxuICApO1xuXG4gIGNvbnN0IGljb25TaXplID0gc2l6ZSA/PyAnbWVkaXVtJztcbiAgY29uc3QgeyAuLi5pY29uUHJvcHMgfSA9IGluZGljYXRvckljb25zW2Ake2ljb25TaXplfVJlbW92ZWBdO1xuICBjb25zdCB7IGljb246IEluZGljYXRvckljb24gfSA9IGljb25Qcm9wcztcblxuICBjb25zdCBvbktleVByZXNzID0gKGU6IEtleWJvYXJkRXZlbnQ8SFRNTERpdkVsZW1lbnQ+KSA9PiB7XG4gICAgaWYgKGUua2V5ID09PSAnRW50ZXInICYmIHJlc3RJbm5lclByb3BzLm9uTW91c2VEb3duKSB7XG4gICAgICByZXN0SW5uZXJQcm9wcy5vbk1vdXNlRG93bihlIGFzIGFueSk7XG4gICAgfVxuXG4gICAgaWYgKFxuICAgICAgc2VsZWN0SW5wdXRSZWY/LmN1cnJlbnQgJiZcbiAgICAgIChlLmtleSA9PT0gJ0Fycm93UmlnaHQnIHx8IGUua2V5ID09PSAnQXJyb3dMZWZ0JyB8fCBlLmtleSA9PT0gJ0Fycm93RG93bicpXG4gICAgKSB7XG4gICAgICBzZWxlY3RJbnB1dFJlZj8uY3VycmVudC5mb2N1cygpO1xuICAgIH1cbiAgfTtcblxuICBjb25zdCBzdHlsZSA9IGdldFN0eWxlcygnY2xlYXJJbmRpY2F0b3InLCBwcm9wcykgYXMgUmVhY3QuQ1NTUHJvcGVydGllcztcblxuICByZXR1cm4gKFxuICAgIDxDdXN0b21TdHlsZWRSZW1vdmVBbGxEaXZcbiAgICAgIGFyaWEtbGFiZWw9XCJSZW1vdmUgYWxsIHNlbGVjdGVkXCJcbiAgICAgIHJvbGU9XCJidXR0b25cIlxuICAgICAgdGFiSW5kZXg9ezB9XG4gICAgICB7Li4ucmVzdElubmVyUHJvcHN9XG4gICAgICByZWY9e3JlbW92ZUFsbEJ1dHRvblJlZn1cbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBnYW11dC9uby1pbmxpbmUtc3R5bGVcbiAgICAgIHN0eWxlPXtzdHlsZX1cbiAgICAgIG9uS2V5RG93bj17b25LZXlQcmVzc31cbiAgICA+XG4gICAgICA8SW5kaWNhdG9ySWNvbiB7Li4uaWNvblByb3BzfSBjb2xvcj1cInRleHRcIiAvPlxuICAgIDwvQ3VzdG9tU3R5bGVkUmVtb3ZlQWxsRGl2PlxuICApO1xufTtcbiJdfQ== */");
+}), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9Gb3JtL1NlbGVjdERyb3Bkb3duL2VsZW1lbnRzL2NvbnRyb2xzLnRzeCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFvRGlDIiwiZmlsZSI6Ii4uLy4uLy4uLy4uL3NyYy9Gb3JtL1NlbGVjdERyb3Bkb3duL2VsZW1lbnRzL2NvbnRyb2xzLnRzeCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGNzcywgdGhlbWUgfSBmcm9tICdAY29kZWNhZGVteS9nYW11dC1zdHlsZXMnO1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuaW1wb3J0IHsgS2V5Ym9hcmRFdmVudCwgdXNlQ29udGV4dCB9IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7XG4gIEFyaWFPbkZvY3VzLFxuICBjb21wb25lbnRzIGFzIFNlbGVjdERyb3Bkb3duRWxlbWVudHMsXG59IGZyb20gJ3JlYWN0LXNlbGVjdCc7XG5cbmltcG9ydCB7IEV4dGVuZGVkT3B0aW9uLCBTaXplZEluZGljYXRvclByb3BzIH0gZnJvbSAnLi4vdHlwZXMnO1xuaW1wb3J0IHsgaW5kaWNhdG9ySWNvbnMgfSBmcm9tICcuL2NvbnN0YW50cyc7XG5pbXBvcnQgeyBTZWxlY3REcm9wZG93bkNvbnRleHQgfSBmcm9tICcuL2NvbnRhaW5lcnMnO1xuXG5jb25zdCB7IERyb3Bkb3duSW5kaWNhdG9yIH0gPSBTZWxlY3REcm9wZG93bkVsZW1lbnRzO1xuXG4vKipcbiAqIEdlbmVyYXRlcyBhY2Nlc3NpYmxlIGZvY3VzIG1lc3NhZ2VzIGZvciBzY3JlZW4gcmVhZGVycy5cbiAqIFByb3ZpZGVzIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjdXJyZW50bHkgZm9jdXNlZCBvcHRpb24uXG4gKlxuICogQHBhcmFtIHBhcmFtcyAtIE9iamVjdCBjb250YWluaW5nIHRoZSBmb2N1c2VkIG9wdGlvbiBkZXRhaWxzXG4gKiBAcmV0dXJucyBGb3JtYXR0ZWQgYWNjZXNzaWJpbGl0eSBtZXNzYWdlXG4gKi9cbmV4cG9ydCBjb25zdCBvbkZvY3VzOiBBcmlhT25Gb2N1czxFeHRlbmRlZE9wdGlvbj4gPSAoe1xuICBmb2N1c2VkOiB7IGxhYmVsLCBzdWJ0aXRsZSwgcmlnaHRMYWJlbCwgZGlzYWJsZWQgfSxcbn0pID0+IHtcbiAgY29uc3QgZm9ybWF0dGVkU3VidGl0bGUgPSBgLCAke3N1YnRpdGxlfWA7XG4gIGNvbnN0IGZvcm1hdHRlZFJpZ2h0TGFiZWwgPSBgLCAke3JpZ2h0TGFiZWx9YDtcblxuICBjb25zdCBtc2cgPSBgWW91IGFyZSBjdXJyZW50bHkgZm9jdXNlZCBvbiBvcHRpb24gJHtsYWJlbH0ke1xuICAgIHN1YnRpdGxlID8gZm9ybWF0dGVkU3VidGl0bGUgOiAnJ1xuICB9ICR7cmlnaHRMYWJlbCA/IGZvcm1hdHRlZFJpZ2h0TGFiZWwgOiAnJ30ke2Rpc2FibGVkID8gJywgZGlzYWJsZWQnIDogJyd9YDtcblxuICByZXR1cm4gbXNnO1xufTtcblxuLyoqXG4gKiBDdXN0b20gZHJvcGRvd24gaW5kaWNhdG9yIHRoYXQgc2hvd3MgZWl0aGVyIGEgY2hldnJvbiBvciBzZWFyY2ggaWNvbi5cbiAqIFRoZSBpY29uIHR5cGUgZGVwZW5kcyBvbiB3aGV0aGVyIHRoZSBzZWxlY3QgaXMgc2VhcmNoYWJsZSBvciBub3QuXG4gKi9cbmV4cG9ydCBjb25zdCBEcm9wZG93bkJ1dHRvbiA9IChwcm9wczogU2l6ZWRJbmRpY2F0b3JQcm9wcykgPT4ge1xuICBjb25zdCB7IHNpemUgfSA9IHByb3BzLnNlbGVjdFByb3BzO1xuICBjb25zdCBjb2xvciA9IHByb3BzLmlzRGlzYWJsZWQgPyAndGV4dC1kaXNhYmxlZCcgOiAndGV4dCc7XG4gIGNvbnN0IGljb25TaXplID0gc2l6ZSA/PyAnbWVkaXVtJztcbiAgY29uc3QgeyAuLi5pY29uUHJvcHMgfSA9IGluZGljYXRvckljb25zW2Ake2ljb25TaXplfUNoZXZyb25gXTtcbiAgY29uc3QgeyBpY29uOiBJbmRpY2F0b3JJY29uIH0gPSBpY29uUHJvcHM7XG5cbiAgcmV0dXJuIChcbiAgICA8RHJvcGRvd25JbmRpY2F0b3Igey4uLnByb3BzfT5cbiAgICAgIDxJbmRpY2F0b3JJY29uIHsuLi5pY29uUHJvcHN9IGNvbG9yPXtjb2xvcn0gLz5cbiAgICA8L0Ryb3Bkb3duSW5kaWNhdG9yPlxuICApO1xufTtcblxuY29uc3QgQ3VzdG9tU3R5bGVkUmVtb3ZlQWxsRGl2ID0gc3R5bGVkKCdkaXYnKShcbiAgY3NzKHtcbiAgICAnJjpmb2N1cyc6IHtcbiAgICAgIG91dGxpbmU6IGAycHggc29saWQgJHt0aGVtZS5jb2xvcnMucHJpbWFyeX1gLFxuICAgIH0sXG4gICAgJyY6Zm9jdXMtdmlzaWJsZSc6IHtcbiAgICAgIG91dGxpbmU6IGAycHggc29saWQgJHt0aGVtZS5jb2xvcnMucHJpbWFyeX1gLFxuICAgIH0sXG4gIH0pXG4pO1xuXG4vKipcbiAqIEN1c3RvbSByZW1vdmUgYWxsIGJ1dHRvbiBmb3IgbXVsdGktc2VsZWN0IG1vZGUuXG4gKiBQcm92aWRlcyBrZXlib2FyZCBuYXZpZ2F0aW9uIGFuZCBhY2Nlc3NpYmxlIHJlbW92YWwgb2YgYWxsIHNlbGVjdGVkIHZhbHVlcy5cbiAqL1xuZXhwb3J0IGNvbnN0IFJlbW92ZUFsbEJ1dHRvbiA9IChwcm9wczogU2l6ZWRJbmRpY2F0b3JQcm9wcykgPT4ge1xuICBjb25zdCB7XG4gICAgZ2V0U3R5bGVzLFxuICAgIGlubmVyUHJvcHM6IHsgLi4ucmVzdElubmVyUHJvcHMgfSxcbiAgICBzZWxlY3RQcm9wczogeyBzaXplIH0sXG4gIH0gPSBwcm9wcztcblxuICBjb25zdCB7IHJlbW92ZUFsbEJ1dHRvblJlZiwgc2VsZWN0SW5wdXRSZWYgfSA9IHVzZUNvbnRleHQoXG4gICAgU2VsZWN0RHJvcGRvd25Db250ZXh0XG4gICk7XG5cbiAgY29uc3QgaWNvblNpemUgPSBzaXplID8/ICdtZWRpdW0nO1xuICBjb25zdCB7IC4uLmljb25Qcm9wcyB9ID0gaW5kaWNhdG9ySWNvbnNbYCR7aWNvblNpemV9UmVtb3ZlYF07XG4gIGNvbnN0IHsgaWNvbjogSW5kaWNhdG9ySWNvbiB9ID0gaWNvblByb3BzO1xuXG4gIGNvbnN0IG9uS2V5UHJlc3MgPSAoZTogS2V5Ym9hcmRFdmVudDxIVE1MRGl2RWxlbWVudD4pID0+IHtcbiAgICBpZiAoZS5rZXkgPT09ICdFbnRlcicgJiYgcmVzdElubmVyUHJvcHMub25Nb3VzZURvd24pIHtcbiAgICAgIHJlc3RJbm5lclByb3BzLm9uTW91c2VEb3duKGUgYXMgYW55KTtcbiAgICB9XG5cbiAgICBpZiAoXG4gICAgICBzZWxlY3RJbnB1dFJlZj8uY3VycmVudCAmJlxuICAgICAgKGUua2V5ID09PSAnQXJyb3dSaWdodCcgfHwgZS5rZXkgPT09ICdBcnJvd0xlZnQnIHx8IGUua2V5ID09PSAnQXJyb3dEb3duJylcbiAgICApIHtcbiAgICAgIHNlbGVjdElucHV0UmVmPy5jdXJyZW50LmZvY3VzKCk7XG4gICAgfVxuICB9O1xuXG4gIGNvbnN0IHN0eWxlID0gZ2V0U3R5bGVzKCdjbGVhckluZGljYXRvcicsIHByb3BzKSBhcyBSZWFjdC5DU1NQcm9wZXJ0aWVzO1xuXG4gIHJldHVybiAoXG4gICAgPEN1c3RvbVN0eWxlZFJlbW92ZUFsbERpdlxuICAgICAgYXJpYS1sYWJlbD1cIlJlbW92ZSBhbGwgc2VsZWN0ZWRcIlxuICAgICAgcm9sZT1cImJ1dHRvblwiXG4gICAgICB0YWJJbmRleD17MH1cbiAgICAgIHsuLi5yZXN0SW5uZXJQcm9wc31cbiAgICAgIHJlZj17cmVtb3ZlQWxsQnV0dG9uUmVmfVxuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGdhbXV0L25vLWlubGluZS1zdHlsZVxuICAgICAgc3R5bGU9e3N0eWxlfVxuICAgICAgb25LZXlEb3duPXtvbktleVByZXNzfVxuICAgID5cbiAgICAgIDxJbmRpY2F0b3JJY29uIHsuLi5pY29uUHJvcHN9IGNvbG9yPVwidGV4dFwiIC8+XG4gICAgPC9DdXN0b21TdHlsZWRSZW1vdmVBbGxEaXY+XG4gICk7XG59O1xuIl19 */");
/**
* Custom remove all button for multi-select mode.
* Provides keyboard navigation and accessible removal of all selected values. Index: package/dist/Form/SelectDropdown/elements/options.js
===================================================================
--- package/dist/Form/SelectDropdown/elements/options.js
+++ package/dist/Form/SelectDropdown/elements/options.js
@@ -43,8 +43,9 @@
/**
* Custom option component that displays a check icon for selected items.
* Also manages ARIA attributes for accessibility.
+ * Skips the check icon for react-select/creatable's "Add" row (__isNew__).
*/
export const IconOption = ({
children,
...rest
@@ -53,17 +54,19 @@
size
} = rest.selectProps;
const {
isFocused,
- innerProps
+ innerProps,
+ data
} = rest;
+ const isNew = data?.__isNew__;
return /*#__PURE__*/_jsxs(SelectDropdownElements.Option, {
...rest,
innerProps: {
...innerProps,
'aria-selected': isFocused
},
- children: [children, rest?.isSelected && /*#__PURE__*/_jsx(CheckIcon, {
+ children: [children, !isNew && rest?.isSelected && /*#__PURE__*/_jsx(CheckIcon, {
size: selectedIconSize[size ?? 'medium']
})]
});
}; Index: package/dist/Form/SelectDropdown/SelectDropdown.js
===================================================================
--- package/dist/Form/SelectDropdown/SelectDropdown.js
+++ package/dist/Form/SelectDropdown/SelectDropdown.js
@@ -3,9 +3,9 @@
import * as React from 'react';
import { parseOptions } from '../utils';
import { AbbreviatedSingleValue, CustomContainer, CustomInput, CustomValueContainer, DropdownButton, formatGroupLabel, formatOptionLabel, IconOption, MultiValueRemoveButton, MultiValueWithColorMode, onFocus, RemoveAllButton, SelectDropdownContext, TypedReactSelect } from './elements';
import { getMemoizedStyles } from './styles';
-import { filterValueFromOptions, isMultipleSelectProps, isOptionsGrouped, isSingleSelectProps, removeValueFromSelectedOptions } from './utils';
+import { filterValueFromOptions, getCreatedOptionValue, isMultipleSelectProps, isOptionsGrouped, isSingleSelectProps, removeValueFromSelectedOptions } from './utils';
import { jsx as _jsx } from "react/jsx-runtime";
const defaultProps = {
name: undefined,
components: {
@@ -72,24 +72,32 @@
export const SelectDropdown = ({
disabled,
dropdownWidth,
error,
+ formatCreateLabel = inputValue => `Add "${inputValue}"`,
id,
inputProps,
inputWidth,
- isSearchable = false,
+ isCreatable = false,
+ isSearchable: isSearchableProp = false,
+ isValidNewOption,
menuAlignment = 'left',
multiple,
name,
onChange,
+ onCreateOption,
+ onInputChange,
options,
placeholder = 'Select an option',
shownOptionsLimit = 6,
size,
+ validationMessage,
value,
zIndex,
...rest
}) => {
+ // isSearchable is forced true when isCreatable is true (CreatableSelect requires a text input)
+ const isSearchable = isCreatable || isSearchableProp;
const rawInputId = useId();
const inputId = name ?? `${id}-select-dropdown-${rawInputId}`;
const [activated, setActivated] = useState(false);
const [currentFocusedValue, setCurrentFocusedValue] = useState(undefined);
@@ -125,41 +133,43 @@
const [multiValues, setMultiValues] = useState(multiple &&
// To keep this efficient for non-multiSelect
filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions)));
- // If the caller changes the initial value, let's update our value to match.
+ // Sync multi-select value from props when controlled (`value` is a string[]).
+ // Uncontrolled multi (`value` undefined or '') keeps selection in local state.
useEffect(() => {
+ if (!multiple || !Array.isArray(value)) return;
const newMultiValues = filterValueFromOptions(selectOptions, value, isOptionsGrouped(selectOptions));
if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
- //
// We only update this when our passed in options or value changes, not multiValues.
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [options, value]);
- const changeHandler = useCallback(optionEvent => {
+ }, [options, value, multiple]);
+ const changeHandler = useCallback((optionEvent, actionMeta) => {
setActivated(true);
-
- // 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!
- // Should probably come with: https://codecademy.atlassian.net/browse/GM-354
+ if (actionMeta.action === 'create-option') {
+ const createdValue = getCreatedOptionValue(optionEvent, actionMeta, multiple);
+ if (createdValue) {
+ onCreateOption?.(createdValue);
+ }
+ }
const onChangeProps = {
onChange,
multiple
};
+ const forwardedMeta = actionMeta.action === 'create-option' ? actionMeta : {
+ action: onChangeAction,
+ option: isMultipleSelectProps(onChangeProps) ? undefined : optionEvent
+ };
if (isSingleSelectProps(onChangeProps)) {
const singleOptionEvent = optionEvent;
- onChangeProps.onChange?.(singleOptionEvent, {
- action: onChangeAction,
- option: singleOptionEvent
- });
+ onChangeProps.onChange?.(singleOptionEvent, forwardedMeta);
}
if (isMultipleSelectProps(onChangeProps)) {
setMultiValues(optionEvent);
- onChangeProps.onChange?.(optionEvent, {
- action: onChangeAction,
- 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)
- });
+ onChangeProps.onChange?.(optionEvent, forwardedMeta);
}
- }, [onChange, multiple]);
+ }, [onChange, multiple, onCreateOption]);
const keyPressHandler = e => {
if (multiple && e.key === 'Enter' && currentFocusedValue && multiValues) {
const newMultiValues = removeValueFromSelectedOptions(multiValues, currentFocusedValue);
if (newMultiValues !== multiValues) setMultiValues(newMultiValues);
@@ -167,8 +177,10 @@
if (removeAllButtonRef.current !== null && e.key === 'ArrowRight' && multiValues && currentFocusedValue === multiValues[multiValues.length - 1].value) {
removeAllButtonRef.current.focus();
}
};
+ const noOptionsMessage = validationMessage === undefined ? undefined // fall back to react-select default ("No options")
+ : typeof validationMessage === 'function' ? validationMessage : () => validationMessage;
const theme = useTheme();
const memoizedStyles = useMemo(() => {
return getMemoizedStyles(theme, zIndex);
}, [theme, zIndex]);
@@ -187,30 +199,35 @@
onFocus
},
dropdownWidth: dropdownWidth,
error: Boolean(error),
+ formatCreateLabel: formatCreateLabel,
formatGroupLabel: formatGroupLabel,
formatOptionLabel: formatOptionLabel,
id: id || rest.htmlFor || rawInputId,
inputId: inputId,
inputProps: {
...inputProps
},
inputWidth: inputWidth,
+ isCreatable: isCreatable,
isDisabled: disabled,
isMulti: multiple,
isOptionDisabled: option => option.disabled,
isSearchable: isSearchable,
+ isValidNewOption: isValidNewOption,
menuAlignment: menuAlignment,
name: name,
+ noOptionsMessage: noOptionsMessage,
options: selectOptions,
placeholder: placeholder,
selectRef: selectInputRef,
shownOptionsLimit: shownOptionsLimit,
size: size,
styles: memoizedStyles,
value: multiple ? multiValues : parsedValue,
onChange: changeHandler,
+ onInputChange: onInputChange,
onKeyDown: multiple ? e => keyPressHandler(e) : undefined,
...rest
})
}); Index: package/dist/Form/SelectDropdown/styles.js
===================================================================
--- package/dist/Form/SelectDropdown/styles.js
+++ package/dist/Form/SelectDropdown/styles.js
@@ -136,8 +136,10 @@
...dropdownBorderStates({
error: state.selectProps.error,
theme
}),
+ // Drop react-select's default menu drop shadow; the border above defines the edge.
+ boxShadow: 'none',
...(dropdownWidth ? {
minWidth: dropdownWidth,
width: dropdownWidth
} : {}),
@@ -193,18 +195,34 @@
':hover': {
backgroundColor: theme.colors['secondary-hover']
}
}),
- option: (provided, state) => ({
- ...getOptionBackground(state.isSelected, state.isFocused)({
- theme
- }),
- alignItems: 'center',
- color: state.isDisabled ? 'text-disabled' : 'default',
- cursor: state.isDisabled ? 'not-allowed' : 'pointer',
- display: 'flex',
- padding: state.selectProps.size === 'small' ? '3px 14px' : '11px 14px'
+ noOptionsMessage: provided => ({
+ ...provided,
+ color: theme.colors['text-secondary']
}),
+ option: (provided, state) => {
+ const isNew = state.data?.__isNew__;
+ const isSmall = state.selectProps.size === 'small';
+ return {
+ ...getOptionBackground(state.isSelected, state.isFocused)({
+ theme
+ }),
+ alignItems: 'center',
+ color: isNew ? state.isDisabled ? theme.colors['text-disabled'] : theme.colors.primary : state.isDisabled ? theme.colors['text-disabled'] : theme.colors.text,
+ cursor: state.isDisabled ? 'not-allowed' : 'pointer',
+ display: 'flex',
+ padding: isSmall ? '3px 14px' : '11px 14px',
+ ...(isNew && {
+ // Gradient creates the 1px divider line centred in the 16px spacer above the option text
+ backgroundImage: `linear-gradient(${theme.colors['text-disabled']} 1px, transparent 1px)`,
+ backgroundPosition: '0 8px',
+ backgroundRepeat: 'no-repeat',
+ backgroundSize: '100% 1px',
+ paddingTop: isSmall ? '19px' : '27px'
+ })
+ };
+ },
placeholder: provided => ({
...provided,
...placeholderColor({
theme Index: package/dist/Form/SelectDropdown/utils.js
===================================================================
--- package/dist/Form/SelectDropdown/utils.js
+++ package/dist/Form/SelectDropdown/utils.js
@@ -1,6 +1,20 @@
export const isMultipleSelectProps = props => !!props.multiple;
export const isSingleSelectProps = props => !props.multiple;
+/**
+ * Resolves the value for a newly created option from react-select action metadata
+ * or the onChange option payload. Returns undefined when no reliable value exists.
+ */
+export const getCreatedOptionValue = (optionEvent, actionMeta, multiple) => {
+ const metaValue = actionMeta.option?.value;
+ if (metaValue) return metaValue;
+ if (!multiple) {
+ const value = optionEvent.value;
+ return value || undefined;
+ }
+ const newOption = optionEvent.find(option => option.__isNew__);
+ return newOption?.value || undefined;
+};
export const isOptionGroup = obj => obj != null && typeof obj === 'object' && 'options' in obj && obj.options !== undefined;
export const isOptionsGrouped = options => Array.isArray(options) && options.some(option => isOptionGroup(option));
/** Index: package/package.json
===================================================================
--- package/package.json
+++ package/package.json
@@ -1,16 +1,16 @@
{
"name": "@codecademy/gamut",
"description": "Styleguide & Component library for Codecademy",
- "version": "71.0.0",
+ "version": "71.0.1-alpha.69ab4c.0",
"author": "Codecademy Engineering <[email protected]>",
"bin": "./bin/gamut.mjs",
"dependencies": {
- "@codecademy/gamut-icons": "9.57.7",
- "@codecademy/gamut-illustrations": "0.58.13",
- "@codecademy/gamut-patterns": "0.10.32",
- "@codecademy/gamut-styles": "20.0.0",
- "@codecademy/variance": "0.26.1",
+ "@codecademy/gamut-icons": "9.57.8-alpha.69ab4c.0",
+ "@codecademy/gamut-illustrations": "0.58.14-alpha.69ab4c.0",
+ "@codecademy/gamut-patterns": "0.10.33-alpha.69ab4c.0",
+ "@codecademy/gamut-styles": "20.0.1-alpha.69ab4c.0",
+ "@codecademy/variance": "0.26.2-alpha.69ab4c.0",
"@formatjs/intl-locale": "5.3.1",
"@react-aria/interactions": "3.25.0",
"@types/marked": "^4.0.8",
"@vidstack/react": "^1.12.12", Index: package/agent-tools/skills/gamut-forms/SKILL.md
===================================================================
--- package/agent-tools/skills/gamut-forms/SKILL.md
+++ package/agent-tools/skills/gamut-forms/SKILL.md
@@ -25,8 +25,14 @@
- Checkbox, Radio, Select: same pairing; checkbox/radio use the visually hidden input pattern from `@codecademy/gamut-styles` where applicable.
---
+## SelectDropdown
+
+For `SelectDropdown` — single vs multi value, controlled vs uncontrolled patterns, creatable options, and react-select action metadata — use [`gamut-select-dropdown`](../gamut-select-dropdown/SKILL.md). Generic `FormGroup` wiring (labels, errors, live regions) still applies as documented below; SelectDropdown-specific state contracts live in that skill.
+
+---
+
## `FormGroup` (baseline)
[`FormGroup.tsx`](https://github.com/Codecademy/gamut/blob/main/packages/gamut/src/Form/elements/FormGroup.tsx) Index: package/dist/Form/SelectDropdown/types/component-props.d.ts
===================================================================
--- package/dist/Form/SelectDropdown/types/component-props.d.ts
+++ package/dist/Form/SelectDropdown/types/component-props.d.ts
@@ -1,6 +1,6 @@
import { Ref, SelectHTMLAttributes } from 'react';
-import { Props as NamedProps } from 'react-select';
+import { Options as OptionsType, Props as NamedProps } from 'react-select';
import { SelectComponentProps } from '../../inputs/Select';
import { OptionStrict, SelectDropdownGroup, SelectDropdownOptions } from './options';
import { ReactSelectAdditionalProps, SelectDropdownSizes, SharedProps } from './styles';
/**
@@ -27,9 +27,9 @@
/**
* Core props interface that defines the essential properties for SelectDropdown.
* This interface combines base props with react-select props and HTML select attributes.
*/
-export interface SelectDropdownCoreProps extends SelectDropdownBaseProps, Omit<NamedProps<OptionStrict, boolean>, 'formatOptionLabel' | 'isDisabled' | 'value' | 'options' | 'components' | 'styles' | 'theme' | 'onChange' | 'multiple'>, Pick<SelectHTMLAttributes<HTMLSelectElement>, 'value' | 'disabled' | 'onClick'>, SharedProps {
+export interface SelectDropdownCoreProps extends SelectDropdownBaseProps, Omit<NamedProps<OptionStrict, boolean>, 'formatOptionLabel' | 'isDisabled' | 'value' | 'options' | 'components' | 'styles' | 'theme' | 'onChange' | 'multiple' | 'isSearchable'>, Pick<SelectHTMLAttributes<HTMLSelectElement>, 'value' | 'disabled' | 'onClick'>, SharedProps {
/** Required name attribute for the select input */
name: string;
/** Placeholder text shown when no option is selected.
* Placeholder text is not recommended for accessibility. If you need to use placeholder text,
@@ -37,8 +37,41 @@
* I.e - if the placeholder text describes an action you'd like the user to take, please use a label instead. */
placeholder?: string;
/** Array of options or option groups to display in the dropdown */
options?: SelectDropdownOptions | SelectDropdownGroup[];
+ /**
+ * Allows users to create new options by typing a value not in the options list.
+ * When true, isSearchable is automatically set to true.
+ * Pair with onCreateOption to persist new options.
+ */
+ isCreatable?: boolean;
+ /**
+ * Called when the user confirms a new option via the "Add" row.
+ * Convenience callback for persisting the new value to your `options` list.
+ * Selection updates are delivered through `onChange` with `action: 'create-option'`.
+ */
+ onCreateOption?: (inputValue: string) => void;
+ /**
+ * Customises the label shown in the "Add" row.
+ * Defaults to: (inputValue) => `Add "${inputValue}"`.
+ */
+ formatCreateLabel?: (inputValue: string) => React.ReactNode;
+ /**
+ * Controls when the "Add" row is visible.
+ * Receives the current input, selected values, and all options.
+ * Defaults to react-select's built-in logic (hidden when input matches an existing option label).
+ * Use cases: minimum-length gating, pattern validation, case-insensitive dedup, max-items cap.
+ */
+ isValidNewOption?: (inputValue: string, value: OptionsType<OptionStrict>, options: OptionsType<OptionStrict>) => boolean;
+ /**
+ * Customizes the message shown inside the dropdown menu when no option matches
+ * the current input (react-select's "No options" state). Useful for surfacing
+ * validation/error text directly in the dropdown. Accepts a node, or a function
+ * receiving the current input value.
+ */
+ validationMessage?: React.ReactNode | ((obj: {
+ inputValue: string;
+ }) => React.ReactNode);
}
/**
* Props for single-select mode.
* When multiple is false or undefined, only one option can be selected.
@@ -59,12 +92,24 @@
/** Callback fired when the selected values change */
onChange?: NamedProps<OptionStrict, true>['onChange'];
}
/**
+ * Enforces that isSearchable cannot be false when isCreatable is true.
+ * Creatable mode requires the search input so users can type new option values.
+ */
+type CreatableConstraint = {
+ isCreatable?: false | undefined;
+ isSearchable?: boolean;
+} | {
+ isCreatable: true;
+ isSearchable?: true;
+};
+/**
* Union type for all SelectDropdown prop variants.
- * Supports both single and multi-select modes through discriminated union.
+ * Supports both single and multi-select modes through discriminated union,
+ * intersected with CreatableConstraint to enforce isSearchable compatibility.
*/
-export type SelectDropdownProps = SingleSelectDropdownProps | MultiSelectDropdownProps;
+export type SelectDropdownProps = (SingleSelectDropdownProps | MultiSelectDropdownProps) & CreatableConstraint;
/**
* Base interface for onChange-related props.
* Used internally for type checking and prop validation.
*/
@@ -75,10 +120,17 @@
onChange?: SingleSelectDropdownProps['onChange'] | MultiSelectDropdownProps['onChange'];
}
/**
* Props for the typed React Select component wrapper.
- * Extends ReactSelectAdditionalProps with an optional ref.
+ * Extends ReactSelectAdditionalProps with an optional ref and creatable flag.
*/
export interface TypedReactSelectProps extends ReactSelectAdditionalProps {
/** Optional ref to the underlying react-select component */
selectRef?: Ref<any>;
+ /** When true, renders CreatableSelect instead of ReactSelect */
+ isCreatable?: boolean;
+ /** Customises the "Add" row label */
+ formatCreateLabel?: (inputValue: string) => React.ReactNode;
+ /** Controls visibility of the "Add" row */
+ isValidNewOption?: (inputValue: string, value: OptionsType<OptionStrict>, options: OptionsType<OptionStrict>) => boolean;
}
+export {}; Index: package/dist/Form/SelectDropdown/elements/constants.d.ts
===================================================================
--- package/dist/Form/SelectDropdown/elements/constants.d.ts
+++ package/dist/Form/SelectDropdown/elements/constants.d.ts
@@ -14,16 +14,8 @@
mediumChevron: {
size: number;
icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
};
- smallSearchable: {
- size: number;
- icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
- };
- mediumSearchable: {
- size: number;
- icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
- };
smallRemove: {
size: number;
icon: import("react").ForwardRefExoticComponent<import("@codecademy/gamut-icons").GamutIconProps & import("react").RefAttributes<SVGSVGElement>>;
}; Index: package/dist/Form/SelectDropdown/elements/containers.d.ts
===================================================================
--- package/dist/Form/SelectDropdown/elements/containers.d.ts
+++ package/dist/Form/SelectDropdown/elements/containers.d.ts
@@ -23,7 +23,11 @@
*/
export declare const CustomInput: ({ ...rest }: CustomSelectComponentProps<typeof SelectDropdownElements.Input>) => import("react/jsx-runtime").JSX.Element;
/**
* Typed wrapper around react-select component.
- * Provides type safety for the underlying react-select implementation.
+ * Renders CreatableSelect when isCreatable is true, ReactSelect otherwise.
+ * Creatable-only props (formatCreateLabel, isValidNewOption) are stripped from
+ * the non-creatable path so they don't reach ReactSelect. `onCreateOption` is
+ * handled in SelectDropdown's changeHandler — do not pass it to CreatableSelect
+ * or react-select will skip onChange on create.
*/
-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;
+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; Index: package/dist/Form/SelectDropdown/elements/options.d.ts
===================================================================
--- package/dist/Form/SelectDropdown/elements/options.d.ts
+++ package/dist/Form/SelectDropdown/elements/options.d.ts
@@ -2,8 +2,9 @@
import { CustomSelectComponentProps, ExtendedOption, SelectDropdownGroup } from '../types';
/**
* Custom option component that displays a check icon for selected items.
* Also manages ARIA attributes for accessibility.
+ * Skips the check icon for react-select/creatable's "Add" row (__isNew__).
*/
export declare const IconOption: ({ children, ...rest }: CustomSelectComponentProps<typeof SelectDropdownElements.Option>) => import("react/jsx-runtime").JSX.Element;
/**
* Custom single value component that displays abbreviated text when available. Index: package/dist/Form/SelectDropdown/types/styles.d.ts
===================================================================
--- package/dist/Form/SelectDropdown/types/styles.d.ts
+++ package/dist/Form/SelectDropdown/types/styles.d.ts
@@ -68,6 +68,10 @@
*/
export type OptionState = BaseSelectComponentProps & InteractionStates & {
/** Whether the option is selected */
isSelected: boolean;
+ /** Option data — includes __isNew__ for react-select/creatable's "Add" row */
+ data?: {
+ __isNew__?: boolean;
+ };
};
export {}; Index: package/dist/Form/SelectDropdown/utils.d.ts
===================================================================
--- package/dist/Form/SelectDropdown/utils.d.ts
+++ package/dist/Form/SelectDropdown/utils.d.ts
@@ -1,8 +1,14 @@
+import { ActionMeta, Options as OptionsType } from 'react-select';
import { SelectOptionBase } from '../utils';
-import { BaseOnChangeProps, ExtendedOption, MultiSelectDropdownProps, SelectDropdownGroup, SelectDropdownOptions, SelectDropdownProps, SingleSelectDropdownProps } from './types';
+import { BaseOnChangeProps, ExtendedOption, MultiSelectDropdownProps, OptionStrict, SelectDropdownGroup, SelectDropdownOptions, SelectDropdownProps, SingleSelectDropdownProps } from './types';
export declare const isMultipleSelectProps: (props: BaseOnChangeProps) => props is MultiSelectDropdownProps;
export declare const isSingleSelectProps: (props: BaseOnChangeProps) => props is SingleSelectDropdownProps;
+/**
+ * Resolves the value for a newly created option from react-select action metadata
+ * or the onChange option payload. Returns undefined when no reliable value exists.
+ */
+export declare const getCreatedOptionValue: (optionEvent: OptionStrict | OptionsType<OptionStrict>, actionMeta: ActionMeta<OptionStrict>, multiple?: boolean) => string | undefined;
export declare const isOptionGroup: (obj: unknown) => obj is SelectDropdownGroup;
export declare const isOptionsGrouped: (options: SelectDropdownOptions) => options is SelectDropdownGroup[];
/**
* Filters options based on the selected value(s).