@crab-dev/rc-select 0.0.3 → 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,59 @@
1
+ # @crab-dev/rc-select
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903) Thanks [@hotlif](https://github.com/hotlif)! - Rebuild the component library around a single package inventory, independent architecture and usage classifications, and three design systems: MD3 Expressive (md3), Apple (apple), and shadcn/ui (shadcn).
8
+
9
+ This is a breaking pre-1.0 migration: unify controlled/uncontrolled values, selection callbacks, component APIs, sizes, and density contracts; replace legacy theme identifiers and remove old API aliases. Only stored browser appearance preferences are migrated automatically.
10
+
11
+ Declare required React/ReactDOM ^19.2.0 peers through runtime dependency chains while retaining development dependencies. Correct runtime token dependencies, public/internal exports, strict declaration consumption, and independent offline PnP package verification.
12
+
13
+ Correct RTL layout and overlay placement, field validation placement, embedded editor composition and focus, native virtual-scroll synchronization, and composed button feedback. Define independent multiline field geometry and preserve all eight configuration axes across controlled previews and PDF Web Components. Built-in error messages follow locale without replacing application-owned error data.
14
+
15
+ Restore compiled focus and disabled styles by using valid CSS template comments, keep Tree focus indicators inside measured containers and above selected row backgrounds, accommodate localized color-channel labels, preserve physical Table column geometry with RTL content, and accept PDF byte sources from iframe realms without treating them as URLs. Respect reduced motion in the property-driven Canvas animation example and let the pill Tabs example inherit the configured size.
16
+
17
+ Follow .website/docs/guides/migration.mdx before upgrading. All affected packages remain experimental; minor bumps represent the coordinated pre-1.0 breaking release and do not declare a stable 1.0 contract.
18
+
19
+ ### Patch Changes
20
+
21
+ - [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da) Thanks [@hotlif](https://github.com/hotlif)! - 补齐选择、导航和浮层的三体系规格、公开示例与包内 Playwright 状态矩阵,覆盖真实键盘、Portal 配置与草稿保留。颜色面板的轨道、宽度与操作间距归入组件令牌,窄屏操作区支持换行,强制颜色下保留清晰的触发器焦点边界。
22
+
23
+ 恢复颜色选择器公开的 showEyeDropper 屏幕取色入口,按所属浏览器能力显示,取消或拒绝不改值,卸载中止原生取色会话。
24
+
25
+ 补充水平与折叠菜单真实展开、Dialog 确认/取消 pending 动态场景,以及 Drawer 包公开的 iframe/Shadow DOM 组合示例;分别记录共享底层回归与真实浏览器验收范围,保留未运行、未复审部分。
26
+
27
+ - [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903) Thanks [@hotlif](https://github.com/hotlif)! - Restore focus after an accepted keyboard tab close, position Select popups outside their own supporting text, preserve design-specific disabled placeholders, and keep menu focus and color trigger feedback visible. Fix the appointment dialog example to retain Form's grid layout.
28
+
29
+ Register dropdown Escape layers when delayed Portal elements actually mount, and preserve explicit close reasons and native events through consumer dispatch.
30
+
31
+ Use the date placeholder for the appointment form's empty state instead of presenting a fake selected value.
32
+ - Updated dependencies [[`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`d11001f`](https://github.com/hotlif/crab-dev/commit/d11001fa35d4af4f53d0571c074de4b3e9806903), [`ec1f759`](https://github.com/hotlif/crab-dev/commit/ec1f759d51a83a456b4eb7f608f2e4908d4d46da)]:
33
+ - @crab-dev/rc-dropdown-container@0.1.0
34
+ - @crab-dev/rc-hooks@0.1.0
35
+ - @crab-dev/rc-tag@0.1.0
36
+ - @crab-dev/rc-button@1.0.0
37
+ - @crab-dev/rc-checkbox@0.1.0
38
+ - @crab-dev/rc-config-provider@0.1.0
39
+ - @crab-dev/rc-line-edit@0.1.0
40
+ - @crab-dev/rc-spin@0.1.0
41
+ - @crab-dev/rc-token-semantic@1.0.0
42
+ - @crab-dev/rc-virtual@0.2.0
43
+
44
+ ## 0.0.5
45
+
46
+ ### Patch Changes
47
+
48
+ - [`3a32293`](https://github.com/hotlif/crab-dev/commit/3a32293c30ac0e77d5f0a6fa0e2173e9293cd462) Thanks [@hotlif](https://github.com/hotlif)! - Use cached axis metrics, binary-search range lookup, configurable overscan, and animation-frame coalescing to reduce virtual scrolling work.
49
+
50
+ - [`01b9bde`](https://github.com/hotlif/crab-dev/commit/01b9bde4201bf08eced59f222a53bb3d018460e3) Thanks [@hotlif](https://github.com/hotlif)! - Remove the extra leading line box before the first virtualized select option.
51
+ - Updated dependencies [[`35d5f5a`](https://github.com/hotlif/crab-dev/commit/35d5f5ab65b8e038d34e1c889de56e1aecad53d0), [`3a32293`](https://github.com/hotlif/crab-dev/commit/3a32293c30ac0e77d5f0a6fa0e2173e9293cd462), [`f991b0e`](https://github.com/hotlif/crab-dev/commit/f991b0eede1ead4d70534302bd51f47b0a4ab143)]:
52
+ - @crab-dev/rc-button@0.0.3
53
+ - @crab-dev/rc-virtual@0.1.4
54
+ - @crab-dev/rc-dropdown-container@0.0.5
55
+ - @crab-dev/rc-line-edit@0.0.5
56
+ - @crab-dev/rc-spin@0.0.4
57
+ - @crab-dev/rc-checkbox@0.0.4
58
+ - @crab-dev/rc-config-provider@0.0.2
59
+ - @crab-dev/rc-tag@0.0.3
package/README.md CHANGED
@@ -1,20 +1,15 @@
1
- 下拉选择器,支持单选、多选和搜索
1
+ # @crab-dev/rc-select
2
2
 
3
- ## ✨ 特性
3
+ 在下拉列表中选择一个或多个值,支持搜索选项。
4
4
 
5
- - 提供下拉选择能力,适用于结构化选项录入
6
- - 支持在有限空间内高效完成选项检索与选择
7
- - 适用于表单字段、筛选面板与配置项编辑
8
- - 当前版本:`0.0.1`
9
- - 示例数量:`10` 个 Demo
10
- - 主题能力:支持 Design Token(token.toml)
5
+ ## 主要功能
11
6
 
12
- ## 🔨 使用示例
7
+ - 支持单选、多选、搜索过滤和禁用选项。
8
+ - 提供受控与非受控用法,适用于表单和筛选面板。
13
9
 
14
- <demos />
10
+ ## 文档
15
11
 
16
- ## API
12
+ - [使用说明与 API](./docs/index.mdx)
13
+ - [示例源码](./docs/demos/)
17
14
 
18
- <api />
19
-
20
- > 其余原生属性按底层实现透传,详见 API。
15
+ 在当前包目录运行 `yarn start` 可打开组件工作台,查看交互示例。
@@ -0,0 +1,40 @@
1
+ Object.defineProperty(exports, "__esModule", { value: true });
2
+ const __wake_namespace_0 = require("react");
3
+ ;
4
+ ;
5
+ const useKeyboardNavigation = ({ filteredOptions, open }) => {
6
+ const [highlightIndex, setHighlightIndex] = (0, __wake_namespace_0["useState"])(-1);
7
+ const findNextEnabledIndex = (startIndex, direction) => {
8
+ const len = filteredOptions.length;
9
+ if (len === 0) {
10
+ return -1;
11
+ }
12
+ let index = startIndex;
13
+ for (let i = 0; i < len; i += 1) {
14
+ index = ((index + direction) % len + len) % len;
15
+ const opt = filteredOptions[index];
16
+ if (!opt.disabled && !opt.isGroupLabel) {
17
+ return index;
18
+ }
19
+ }
20
+ return -1;
21
+ };
22
+ const moveHighlight = (direction) => {
23
+ if (!open) {
24
+ return;
25
+ }
26
+ setHighlightIndex((prev) => {
27
+ const start = prev === -1 ? direction === 1 ? -1 : 0 : prev;
28
+ return findNextEnabledIndex(start, direction);
29
+ });
30
+ };
31
+ const resetHighlight = () => {
32
+ setHighlightIndex(-1);
33
+ };
34
+ const highlightedOption = highlightIndex >= 0 && highlightIndex < filteredOptions.length ? filteredOptions[highlightIndex] : undefined;
35
+ return { highlightIndex, highlightedOption, moveHighlight, resetHighlight, setHighlightIndex };
36
+ };
37
+ const __wake_default_export = useKeyboardNavigation;
38
+ Object.defineProperty(exports, "default", { enumerable: true, get: function() {
39
+ return __wake_default_export;
40
+ } });
@@ -0,0 +1,155 @@
1
+ Object.defineProperty(exports, "__esModule", { value: true });
2
+ const __wake_namespace_0 = require("react/jsx-runtime");
3
+ const __wake_namespace_1 = require("@crab-dev/rc-config-provider");
4
+ const __wake_namespace_2 = require("@crab-dev/rc-dropdown-container");
5
+ const __wake_namespace_3 = require("@crab-dev/rc-dropdown-container/internal");
6
+ require("@crab-dev/rc-token-semantic");
7
+ const __wake_namespace_4 = require("react");
8
+ ;
9
+ const __wake_namespace_5 = require("@crab-dev/rc-hooks");
10
+ const __wake_namespace_6 = require("./hooks/useKeyboardNavigation.cjs");
11
+ const __wake_namespace_7 = require("./selectInput.cjs");
12
+ const __wake_namespace_8 = require("./selectOverlay.cjs");
13
+ require("./token.cjs");
14
+ ;
15
+ const isGroup = (item) => "options" in item && Array.isArray(item.options);
16
+ const asArray = (value) => {
17
+ if (value == null) {
18
+ return [];
19
+ }
20
+ return Array.isArray(value) ? value : [value];
21
+ };
22
+ const flattenOptions = (options) => {
23
+ const result = [];
24
+ options.forEach((item, index) => {
25
+ if (isGroup(item)) {
26
+ result.push({ label: item.label, value: `__group_${index}`, isGroupLabel: true });
27
+ for (const opt of item.options) {
28
+ result.push(opt);
29
+ }
30
+ } else {
31
+ result.push(item);
32
+ }
33
+ });
34
+ return result;
35
+ };
36
+ const flattenPlainOptions = (options) => {
37
+ const result = [];
38
+ for (const item of options) {
39
+ if (isGroup(item)) {
40
+ for (const opt of item.options) {
41
+ result.push(opt);
42
+ }
43
+ } else {
44
+ result.push(item);
45
+ }
46
+ }
47
+ return result;
48
+ };
49
+ const filterOptions = (flatOptions, searchable, searchText) => {
50
+ if (!searchable || searchText.trim() === "") {
51
+ return flatOptions;
52
+ }
53
+ const keyword = searchText.trim().toLowerCase();
54
+ return flatOptions.filter((opt) => {
55
+ if (opt.isGroupLabel) {
56
+ return false;
57
+ }
58
+ if (typeof opt.label === "string") {
59
+ return opt.label.toLowerCase().includes(keyword);
60
+ }
61
+ return opt.value.toLowerCase().includes(keyword);
62
+ });
63
+ };
64
+ const firstEnabledIndex = (flatOptions) => flatOptions.findIndex((opt) => !opt.disabled && !opt.isGroupLabel);
65
+ const fieldStyle = "rc-select-0f3ecf945b98";
66
+ const supportingStyle = "rc-select-3bd2a27273ad";
67
+ function SelectField({ children }) {
68
+ const setPositionReference = (0, __wake_namespace_3["useDropdownPositionReference"])();
69
+ return (0, __wake_namespace_0["jsx"])("div", { className: fieldStyle, ref: setPositionReference, children: children });
70
+ }
71
+ function Select({ ref, id, options, placeholder, disabled = false, searchable = false, multiple = false, size: sizeProp, status, appearance = "outlined", label, supportingText, errorText, required, allowClear = false, loading = false, maxTagCount, autoFocus = false, notFoundContent, popupClassName, popupMatchSelectWidth = true, optionRender, tagRender, dropdownRender, value, defaultValue, onValueChange, open, defaultOpen, onOpenChange, onFocus, onBlur, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...restProps }) {
72
+ const size = (0, __wake_namespace_1["useComponentSize"])(sizeProp);
73
+ const { messages } = (0, __wake_namespace_1["useLocale"])();
74
+ const listboxId = (0, __wake_namespace_4["useId"])();
75
+ const generatedId = (0, __wake_namespace_4["useId"])();
76
+ const inputId = id ?? generatedId;
77
+ const labelId = `${generatedId}-label`;
78
+ const descriptionId = `${generatedId}-description`;
79
+ const fieldStatus = errorText ? "error" : status;
80
+ const description = errorText || supportingText;
81
+ const [searchText, setSearchText] = (0, __wake_namespace_4["useState"])("");
82
+ const [selectedValues, setSelectedValues] = (0, __wake_namespace_5["useControllableValue"])({ value: value !== undefined ? asArray(value) : undefined, defaultValue: asArray(defaultValue) });
83
+ const [triggerWidth, setTriggerWidth] = (0, __wake_namespace_4["useState"])(0);
84
+ const controlRef = (0, __wake_namespace_4["useRef"])(null);
85
+ const allPlainOptions = flattenPlainOptions(options);
86
+ const selectedOptions = allPlainOptions.filter((opt) => selectedValues.includes(opt.value));
87
+ const flatOptions = flattenOptions(options);
88
+ const filteredOptions = filterOptions(flatOptions, searchable, searchText);
89
+ const { highlightIndex, highlightedOption, moveHighlight, resetHighlight, setHighlightIndex } = (0, __wake_namespace_6 && __wake_namespace_6.__esModule ? __wake_namespace_6.default : __wake_namespace_6)({ filteredOptions, open: true });
90
+ const syncOpenState = (nextOpen) => {
91
+ if (nextOpen) {
92
+ if (!multiple && selectedValues.length > 0) {
93
+ const selectedIndex = filteredOptions.findIndex((opt) => opt.value === selectedValues[0]);
94
+ setHighlightIndex(selectedIndex);
95
+ }
96
+ } else {
97
+ setSearchText("");
98
+ resetHighlight();
99
+ }
100
+ };
101
+ const emitOpenChange = (nextOpen, details) => {
102
+ onOpenChange?.(nextOpen, details);
103
+ };
104
+ const openProps = open === undefined ? { defaultOpen } : { open };
105
+ const updateValue = (nextValues, reason, event) => {
106
+ if (disabled) return;
107
+ if (nextValues.length === selectedValues.length && nextValues.every((item, index) => item === selectedValues[index])) return;
108
+ setSelectedValues(nextValues);
109
+ if (multiple) {
110
+ const nextOptions = allPlainOptions.filter((opt) => nextValues.includes(opt.value));
111
+ onValueChange?.(nextValues, { reason, options: nextOptions, event });
112
+ return;
113
+ }
114
+ const nextValue = nextValues[0] ?? null;
115
+ const nextOption = allPlainOptions.find((opt) => opt.value === nextValue);
116
+ onValueChange?.(nextValue, { reason, option: nextOption, options: nextOption ? [nextOption] : [], event });
117
+ };
118
+ const handleOptionSelect = (option, event) => {
119
+ if (disabled || option.disabled || option.isGroupLabel) {
120
+ return;
121
+ }
122
+ if (multiple) {
123
+ const exists = selectedValues.includes(option.value);
124
+ const nextValues = exists ? selectedValues.filter((v) => v !== option.value) : [...selectedValues, option.value];
125
+ updateValue(nextValues, exists ? "deselect" : "select", event);
126
+ return;
127
+ }
128
+ controlRef.current?.focus();
129
+ updateValue([option.value], "select", event);
130
+ };
131
+ const handleClear = (event) => {
132
+ if (disabled) return;
133
+ updateValue([], "clear", event);
134
+ setSearchText("");
135
+ };
136
+ const handleRemoveTag = (val, event) => {
137
+ updateValue(selectedValues.filter((v) => v !== val), "remove", event);
138
+ };
139
+ const handleTriggerWidthChange = (width) => {
140
+ setTriggerWidth(width);
141
+ };
142
+ const handleSearchTextChange = (text) => {
143
+ if (disabled) return;
144
+ setSearchText(text);
145
+ const nextFiltered = filterOptions(flatOptions, searchable, text);
146
+ setHighlightIndex(firstEnabledIndex(nextFiltered));
147
+ };
148
+ const floatingStyle = popupMatchSelectWidth ? { width: triggerWidth } : { minWidth: triggerWidth };
149
+ return (0, __wake_namespace_0["jsx"])(__wake_namespace_2 && __wake_namespace_2.__esModule ? __wake_namespace_2.default : __wake_namespace_2, { ...openProps, onOpenChange: emitOpenChange, size: size, ...restProps, className: className, overlay: (0, __wake_namespace_0["jsx"])(__wake_namespace_8 && __wake_namespace_8.__esModule ? __wake_namespace_8.default : __wake_namespace_8, { size: size, multiple: multiple, disabled: disabled, loading: loading, filteredOptions: filteredOptions, selectedValues: selectedValues, triggerWidth: triggerWidth, highlightIndex: highlightIndex, listboxId: listboxId, notFoundContent: notFoundContent, popupClassName: popupClassName, optionRender: optionRender, dropdownRender: dropdownRender, onOptionSelect: handleOptionSelect, onHighlightChange: setHighlightIndex }), floatingContainerProps: { style: floatingStyle }, children: (0, __wake_namespace_0["jsxs"])(SelectField, { children: [(0, __wake_namespace_0["jsx"])(__wake_namespace_7 && __wake_namespace_7.__esModule ? __wake_namespace_7.default : __wake_namespace_7, { ref: ref, controlRef: controlRef, id: inputId, ariaLabel: ariaLabel, ariaLabelledBy: ariaLabelledBy ?? (label ? labelId : undefined), ariaDescribedBy: [ariaDescribedBy, description ? descriptionId : undefined].filter(Boolean).join(" ") || undefined, disabled: disabled, searchable: searchable, multiple: multiple, size: size, status: fieldStatus, appearance: appearance, label: label, labelId: labelId, required: required, allowClear: allowClear, loading: loading, maxTagCount: maxTagCount, autoFocus: autoFocus, placeholder: placeholder ?? messages.select.placeholder, selectedOptions: selectedOptions, searchText: searchText, highlightIndex: highlightIndex, highlightedOption: highlightedOption, listboxId: listboxId, tagRender: tagRender, onSearchTextChange: handleSearchTextChange, onOpenStateChange: syncOpenState, onWidthChange: handleTriggerWidthChange, onMoveHighlight: moveHighlight, onSelectHighlighted: handleOptionSelect, onClear: handleClear, onRemoveTag: handleRemoveTag, onFocus: onFocus, onBlur: onBlur }), description && (0, __wake_namespace_0["jsx"])("div", { id: descriptionId, className: supportingStyle, "data-status": fieldStatus, role: errorText ? "alert" : undefined, children: description })] }) });
150
+ }
151
+ ;
152
+ const __wake_default_export = Select;
153
+ Object.defineProperty(exports, "default", { enumerable: true, get: function() {
154
+ return __wake_default_export;
155
+ } });
@@ -0,0 +1,205 @@
1
+ Object.defineProperty(exports, "__esModule", { value: true });
2
+ const __wake_namespace_0 = require("react/jsx-runtime");
3
+ const __wake_namespace_1 = require("@crab-dev/rc-dropdown-container");
4
+ const __wake_namespace_2 = require("@crab-dev/rc-line-edit");
5
+ const __wake_namespace_3 = require("@crab-dev/rc-button");
6
+ const __wake_namespace_4 = require("@crab-dev/rc-spin");
7
+ const __wake_namespace_5 = require("@crab-dev/rc-tag");
8
+ const __wake_namespace_6 = require("@crab-dev/css");
9
+ const __wake_namespace_7 = require("@crab-dev/rc-config-provider");
10
+ const __wake_namespace_8 = require("react");
11
+ require("./token.cjs");
12
+ ;
13
+ const __wake_namespace_9 = require("./selectInput.styles.cjs");
14
+ const setRef = (ref, node) => {
15
+ if (typeof ref === "function") {
16
+ ref(node);
17
+ } else if (ref) {
18
+ ref.current = node;
19
+ }
20
+ };
21
+ const sizeMetricsMap = { large: "rc-select-df4a743b55e1", medium: "rc-select-de4a743b542e", small: "rc-select-dd4a743b527b" };
22
+ const sizeHeightFixedMap = { large: "rc-select-b8fc5925a597", medium: "rc-select-b7fc5925a3e4", small: "rc-select-bafc5925a8fd" };
23
+ const sizeHeightFlexibleMap = { large: "rc-select-5d07f79a2f4a", medium: "rc-select-5e07f79a30fd", small: "rc-select-5b07f79a2be4" };
24
+ const CaretIcon = () => (0, __wake_namespace_0["jsx"])("svg", { "aria-hidden": "true", focusable: "false", viewBox: "0 0 24 24", fill: "currentColor", children: (0, __wake_namespace_0["jsx"])("path", { d: "m7 10 5 5 5-5z" }) });
25
+ const ClearIcon = () => (0, __wake_namespace_0["jsx"])("svg", { "aria-hidden": "true", focusable: "false", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: (0, __wake_namespace_0["jsx"])("path", { d: "m18 6-12 12M6 6l12 12" }) });
26
+ ;
27
+ const SelectInput = ({ ref, controlRef, id, ariaLabel, ariaLabelledBy, ariaDescribedBy, disabled, searchable, multiple, size, status, appearance, label, labelId, required, allowClear, loading, maxTagCount, autoFocus, placeholder, selectedOptions, searchText, highlightIndex, highlightedOption, listboxId, tagRender, onSearchTextChange, onOpenStateChange, onWidthChange, onMoveHighlight, onSelectHighlighted, onClear, onRemoveTag, onFocus, onBlur }) => {
28
+ const { state, refs, dispatch } = (0, __wake_namespace_1["useDropdownContext"])();
29
+ const { messages } = (0, __wake_namespace_7["useLocale"])();
30
+ const inputRef = (0, __wake_namespace_8["useRef"])(null);
31
+ const open = state.open;
32
+ const [hovered, setHovered] = (0, __wake_namespace_8["useState"])(false);
33
+ const [actionFocused, setActionFocused] = (0, __wake_namespace_8["useState"])(false);
34
+ const [withoutHover, setWithoutHover] = (0, __wake_namespace_8["useState"])(false);
35
+ (0, __wake_namespace_8["useEffect"])(() => {
36
+ const media = window.matchMedia?.("(hover: none)");
37
+ if (!media) return;
38
+ const update = () => setWithoutHover(media.matches);
39
+ update();
40
+ media.addEventListener("change", update);
41
+ return () => media.removeEventListener("change", update);
42
+ }, []);
43
+ const syncOpen = (0, __wake_namespace_8["useEffectEvent"])(onOpenStateChange);
44
+ const mergeRef = (0, __wake_namespace_8["useCallback"])((node) => {
45
+ controlRef.current = node;
46
+ if (typeof refs.setReference === "function") {
47
+ refs.setReference(node);
48
+ }
49
+ setRef(ref, node);
50
+ }, [refs.setReference, ref, controlRef]);
51
+ (0, __wake_namespace_8["useEffect"])(() => {
52
+ const el = controlRef.current;
53
+ if (!el) {
54
+ return;
55
+ }
56
+ const observer = new ResizeObserver((entries) => {
57
+ for (const entry of entries) {
58
+ onWidthChange(entry.target.getBoundingClientRect().width);
59
+ }
60
+ });
61
+ observer.observe(el);
62
+ return () => observer.disconnect();
63
+ }, [onWidthChange]);
64
+ (0, __wake_namespace_8["useEffect"])(() => {
65
+ if (open && searchable && !disabled) {
66
+ queueMicrotask(() => inputRef.current?.focus());
67
+ }
68
+ }, [disabled, open, searchable]);
69
+ (0, __wake_namespace_8["useEffect"])(() => {
70
+ if (autoFocus && !disabled && controlRef.current) {
71
+ controlRef.current.focus();
72
+ }
73
+ }, [autoFocus, disabled]);
74
+ (0, __wake_namespace_8["useEffect"])(() => {
75
+ syncOpen(open);
76
+ }, [open]);
77
+ const handleClick = () => {
78
+ if (disabled) {
79
+ return;
80
+ }
81
+ dispatch({ type: "setOpen", payload: !open });
82
+ };
83
+ const handleKeyDown = (e) => {
84
+ if (disabled) {
85
+ return;
86
+ }
87
+ switch (e.key) {
88
+ case "ArrowDown":
89
+ e.preventDefault();
90
+ if (!open) {
91
+ dispatch({ type: "setOpen", payload: true });
92
+ }
93
+ onMoveHighlight(1);
94
+ break;
95
+ case "ArrowUp":
96
+ e.preventDefault();
97
+ if (!open) {
98
+ dispatch({ type: "setOpen", payload: true });
99
+ }
100
+ onMoveHighlight(-1);
101
+ break;
102
+ case "Enter":
103
+ e.preventDefault();
104
+ if (open && highlightedOption) {
105
+ onSelectHighlighted(highlightedOption, e);
106
+ if (!multiple) {
107
+ dispatch({ type: "setOpen", payload: false });
108
+ }
109
+ } else if (!open) {
110
+ dispatch({ type: "setOpen", payload: true });
111
+ }
112
+ break;
113
+ case " ":
114
+ if (!searchable || !open) {
115
+ e.preventDefault();
116
+ if (!open) {
117
+ dispatch({ type: "setOpen", payload: true });
118
+ }
119
+ }
120
+ break;
121
+ case "Escape":
122
+ if (open) {
123
+ e.preventDefault();
124
+ controlRef.current?.focus();
125
+ dispatch({ type: "setOpen", payload: false, details: { reason: "escape", event: e.nativeEvent } });
126
+ }
127
+ break;
128
+ case "Backspace":
129
+ if (multiple && searchText === "" && selectedOptions.length > 0) {
130
+ const lastOption = selectedOptions[selectedOptions.length - 1];
131
+ onRemoveTag(lastOption.value, e);
132
+ }
133
+ break;
134
+ default:
135
+ break;
136
+ }
137
+ };
138
+ const triggerClear = (event) => {
139
+ controlRef.current?.focus();
140
+ onClear(event);
141
+ if (open) {
142
+ dispatch({ type: "setOpen", payload: false });
143
+ }
144
+ };
145
+ const handleClearClick = (e) => {
146
+ e.stopPropagation();
147
+ triggerClear(e);
148
+ };
149
+ const handleClearKeyDown = (e) => {
150
+ if (e.key === "Enter" || e.key === " ") {
151
+ e.stopPropagation();
152
+ }
153
+ };
154
+ const canClear = allowClear && !disabled && selectedOptions.length > 0;
155
+ const showClear = canClear && (hovered || actionFocused || withoutHover);
156
+ const renderTag = (opt) => {
157
+ const handleClose = (event) => {
158
+ if (!disabled) onRemoveTag(opt.value, event);
159
+ };
160
+ if (tagRender) {
161
+ return (0, __wake_namespace_0["jsx"])("span", { children: tagRender(opt, handleClose) }, opt.value);
162
+ }
163
+ return (0, __wake_namespace_0["jsx"])(__wake_namespace_5 && __wake_namespace_5.__esModule ? __wake_namespace_5.default : __wake_namespace_5, { size: "small", closable: !disabled, closeAriaLabel: messages.select.removeTag(typeof opt.label === "string" ? opt.label : opt.value), onClose: handleClose, children: (0, __wake_namespace_0["jsx"])("span", { className: __wake_namespace_9["tagLabelStyle"], children: opt.label }) }, opt.value);
164
+ };
165
+ const renderTags = () => {
166
+ if (maxTagCount !== undefined && selectedOptions.length > maxTagCount) {
167
+ const visible = selectedOptions.slice(0, maxTagCount);
168
+ const overflowCount = selectedOptions.length - maxTagCount;
169
+ return (0, __wake_namespace_0["jsxs"])(__wake_namespace_0["Fragment"], { children: [visible.map(renderTag), (0, __wake_namespace_0["jsxs"])(__wake_namespace_5 && __wake_namespace_5.__esModule ? __wake_namespace_5.default : __wake_namespace_5, { size: "small", children: ["+", overflowCount] })] });
170
+ }
171
+ return selectedOptions.map(renderTag);
172
+ };
173
+ const renderContent = () => {
174
+ if (searchable && open) {
175
+ return (0, __wake_namespace_0["jsxs"])(__wake_namespace_0["Fragment"], { children: [multiple && renderTags(), (0, __wake_namespace_0["jsx"])(__wake_namespace_2 && __wake_namespace_2.__esModule ? __wake_namespace_2.default : __wake_namespace_2, { ref: inputRef, "aria-label": ariaLabel ?? label ?? placeholder, bordered: false, disabled: disabled, value: searchText, placeholder: multiple && selectedOptions.length > 0 ? "" : placeholder, onChange: (e) => onSearchTextChange(e.target.value), onClick: (e) => e.stopPropagation(), className: __wake_namespace_9["searchInputStyle"] })] });
176
+ }
177
+ if (multiple) {
178
+ if (selectedOptions.length === 0) {
179
+ return (0, __wake_namespace_0["jsx"])("span", { "data-role": "select-placeholder", className: __wake_namespace_9["placeholderStyle"], children: placeholder });
180
+ }
181
+ return renderTags();
182
+ }
183
+ if (selectedOptions.length === 0) {
184
+ return (0, __wake_namespace_0["jsx"])("span", { "data-role": "select-placeholder", className: __wake_namespace_9["placeholderStyle"], children: placeholder });
185
+ }
186
+ return (0, __wake_namespace_0["jsx"])("span", { className: __wake_namespace_9["singleValueStyle"], children: selectedOptions[0]?.label });
187
+ };
188
+ const activeDescendantId = open && highlightIndex >= 0 ? `${listboxId}-option-${highlightIndex}` : undefined;
189
+ return (0, __wake_namespace_0["jsxs"])("div", { className: __wake_namespace_9["fieldLayoutStyle"], "data-labeled": Boolean(label), "data-appearance": appearance, "data-floating": Boolean(open || selectedOptions.length > 0 || searchText), "data-open": open, "data-status": status, "data-disabled": disabled || undefined, children: [label && (0, __wake_namespace_0["jsxs"])("span", { "data-role": "select-label", children: [(0, __wake_namespace_0["jsx"])("span", { id: labelId, children: label }), required && (0, __wake_namespace_0["jsx"])("span", { "data-required-marker": "", "aria-hidden": "true", children: "*" })] }), (0, __wake_namespace_0["jsxs"])("div", { id: id, role: "combobox", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-required": required || undefined, "aria-expanded": open, "aria-disabled": disabled, "aria-busy": loading || undefined, "aria-haspopup": "listbox", "aria-controls": open ? listboxId : undefined, "aria-activedescendant": activeDescendantId, "aria-invalid": status === "error" ? true : undefined, "data-appearance": appearance, "data-clearable": canClear, "data-labeled": Boolean(label), "data-floating": Boolean(open || selectedOptions.length > 0 || searchText), "data-status": status, tabIndex: disabled ? -1 : 0, ref: mergeRef, className: __wake_namespace_6["cx"].call(undefined, __wake_namespace_9["controlStyle"], sizeMetricsMap[size], multiple ? sizeHeightFlexibleMap[size] : sizeHeightFixedMap[size], disabled && __wake_namespace_9["controlDisabledStyle"], status === "error" && __wake_namespace_9["controlErrorStyle"], status === "warning" && __wake_namespace_9["controlWarningStyle"], __wake_namespace_9["fieldControlStyle"]), onClick: handleClick, onKeyDown: handleKeyDown, onFocus: onFocus, onBlur: onBlur, onPointerOver: (event) => {
190
+ if (event.pointerType !== "touch") setHovered(true);
191
+ }, onPointerOut: (event) => {
192
+ if (!(event.relatedTarget instanceof Node) || !event.currentTarget.contains(event.relatedTarget)) {
193
+ setHovered(false);
194
+ }
195
+ }, children: [label && appearance === "outlined" && (0, __wake_namespace_0["jsx"])("fieldset", { "aria-hidden": "true", children: (0, __wake_namespace_0["jsxs"])("legend", { children: [label, required ? " *" : ""] }) }), (0, __wake_namespace_0["jsx"])("div", { "data-role": "select-value", className: __wake_namespace_9["valueWrapStyle"], children: renderContent() }), (0, __wake_namespace_0["jsxs"])("span", { "data-role": "select-suffix", className: __wake_namespace_9["suffixWrapStyle"], children: [loading ? (0, __wake_namespace_0["jsx"])("span", { "aria-hidden": "true", className: __wake_namespace_9["loadingIconStyle"], children: (0, __wake_namespace_0["jsx"])(__wake_namespace_4["SpinIndicator"], {}) }) : null, (0, __wake_namespace_0["jsx"])(__wake_namespace_3 && __wake_namespace_3.__esModule ? __wake_namespace_3.default : __wake_namespace_3, { "data-role": showClear ? "select-clear" : "select-caret", className: __wake_namespace_9["actionStyle"], type: "button", appearance: "text", shape: "circle", size: size === "small" ? "xs" : size === "large" ? "m" : "s", disabled: disabled, "aria-label": showClear ? messages.common.clear : open ? messages.select.closeOptions : messages.select.openOptions, "aria-expanded": showClear ? undefined : open, icon: showClear ? (0, __wake_namespace_0["jsx"])(ClearIcon, {}) : (0, __wake_namespace_0["jsx"])("span", { "aria-hidden": "true", className: __wake_namespace_6["cx"].call(undefined, __wake_namespace_9["caretStyle"], open && __wake_namespace_9["caretOpenStyle"]), children: (0, __wake_namespace_0["jsx"])(CaretIcon, {}) }), onFocus: () => setActionFocused(true), onBlur: () => setActionFocused(false), onClick: (event) => {
196
+ if (showClear) handleClearClick(event); else {
197
+ event.stopPropagation();
198
+ handleClick();
199
+ }
200
+ }, onKeyDown: handleClearKeyDown })] })] })] });
201
+ };
202
+ const __wake_default_export = SelectInput;
203
+ Object.defineProperty(exports, "default", { enumerable: true, get: function() {
204
+ return __wake_default_export;
205
+ } });
@@ -0,0 +1,69 @@
1
+ Object.defineProperty(exports, "__esModule", { value: true });
2
+ require("@crab-dev/rc-button");
3
+ require("@crab-dev/rc-spin");
4
+ require("@crab-dev/rc-token-semantic");
5
+ require("./token.cjs");
6
+ const controlStyle = "rc-select-5e896678dc89";
7
+ Object.defineProperty(exports, "controlStyle", { enumerable: true, get: function() {
8
+ return controlStyle;
9
+ } });
10
+ const fieldControlStyle = "rc-select-29edc097b42b";
11
+ Object.defineProperty(exports, "fieldControlStyle", { enumerable: true, get: function() {
12
+ return fieldControlStyle;
13
+ } });
14
+ const fieldLayoutStyle = "rc-select-910e459fbbe0";
15
+ Object.defineProperty(exports, "fieldLayoutStyle", { enumerable: true, get: function() {
16
+ return fieldLayoutStyle;
17
+ } });
18
+ const controlDisabledStyle = "rc-select-82fbf3807561";
19
+ Object.defineProperty(exports, "controlDisabledStyle", { enumerable: true, get: function() {
20
+ return controlDisabledStyle;
21
+ } });
22
+ const controlErrorStyle = "rc-select-6d2a034155e7";
23
+ Object.defineProperty(exports, "controlErrorStyle", { enumerable: true, get: function() {
24
+ return controlErrorStyle;
25
+ } });
26
+ const controlWarningStyle = "rc-select-36466465840d";
27
+ Object.defineProperty(exports, "controlWarningStyle", { enumerable: true, get: function() {
28
+ return controlWarningStyle;
29
+ } });
30
+ const valueWrapStyle = "rc-select-f0b8a9c0c369";
31
+ Object.defineProperty(exports, "valueWrapStyle", { enumerable: true, get: function() {
32
+ return valueWrapStyle;
33
+ } });
34
+ const singleValueStyle = "rc-select-bcd33343d38d";
35
+ Object.defineProperty(exports, "singleValueStyle", { enumerable: true, get: function() {
36
+ return singleValueStyle;
37
+ } });
38
+ const placeholderStyle = "rc-select-30719d992205";
39
+ Object.defineProperty(exports, "placeholderStyle", { enumerable: true, get: function() {
40
+ return placeholderStyle;
41
+ } });
42
+ const searchInputStyle = "rc-select-c91210473c12";
43
+ Object.defineProperty(exports, "searchInputStyle", { enumerable: true, get: function() {
44
+ return searchInputStyle;
45
+ } });
46
+ const caretStyle = "rc-select-8ca15a371517";
47
+ Object.defineProperty(exports, "caretStyle", { enumerable: true, get: function() {
48
+ return caretStyle;
49
+ } });
50
+ const caretOpenStyle = "rc-select-72a72768a0ef";
51
+ Object.defineProperty(exports, "caretOpenStyle", { enumerable: true, get: function() {
52
+ return caretOpenStyle;
53
+ } });
54
+ const tagLabelStyle = "rc-select-113a9493f812";
55
+ Object.defineProperty(exports, "tagLabelStyle", { enumerable: true, get: function() {
56
+ return tagLabelStyle;
57
+ } });
58
+ const suffixWrapStyle = "rc-select-5032f5885db7";
59
+ Object.defineProperty(exports, "suffixWrapStyle", { enumerable: true, get: function() {
60
+ return suffixWrapStyle;
61
+ } });
62
+ const actionStyle = "rc-select-aa51540f6a1a";
63
+ Object.defineProperty(exports, "actionStyle", { enumerable: true, get: function() {
64
+ return actionStyle;
65
+ } });
66
+ const loadingIconStyle = "rc-select-14f1c2e4ccd5";
67
+ Object.defineProperty(exports, "loadingIconStyle", { enumerable: true, get: function() {
68
+ return loadingIconStyle;
69
+ } });