xmlui 0.10.14 → 0.10.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/lib/{index-779mp2Bm.mjs → index-D4RYJasT.mjs} +2952 -937
  2. package/dist/lib/index.css +1 -1
  3. package/dist/lib/{initMock-CAXdczCj.mjs → initMock-qzTZlH-6.mjs} +1 -1
  4. package/dist/lib/language-server-web-worker.mjs +1 -1
  5. package/dist/lib/language-server.mjs +1 -1
  6. package/dist/lib/{metadata-utils-D90qqMGc.mjs → metadata-utils-CtY0QcvH.mjs} +2 -1
  7. package/dist/lib/{server-common-lmBDLpUh.mjs → server-common-Cine5nRR.mjs} +1 -1
  8. package/dist/lib/xmlui-parser.d.ts +1 -1
  9. package/dist/lib/xmlui.d.ts +87 -11
  10. package/dist/lib/xmlui.mjs +72 -37
  11. package/dist/metadata/{collectedComponentMetadata-7DFXlw-J.mjs → collectedComponentMetadata-BQaefK3f.mjs} +3189 -1242
  12. package/dist/metadata/{initMock-AFWEftc6.mjs → initMock-Cz6QssI3.mjs} +1 -1
  13. package/dist/metadata/style.css +1 -1
  14. package/dist/metadata/xmlui-metadata.mjs +1 -1
  15. package/dist/metadata/xmlui-metadata.umd.js +3 -3
  16. package/dist/scripts/package.json +2 -3
  17. package/dist/scripts/src/components/Animation/AnimationNative.js +5 -1
  18. package/dist/scripts/src/components/AppState/AppState.js +32 -2
  19. package/dist/scripts/src/components/AppState/AppStateNative.js +27 -3
  20. package/dist/scripts/src/components/AutoComplete/AutoComplete.js +1 -5
  21. package/dist/scripts/src/components/AutoComplete/AutoCompleteContext.js +2 -0
  22. package/dist/scripts/src/components/AutoComplete/AutoCompleteNative.js +263 -82
  23. package/dist/scripts/src/components/Button/Button.js +5 -1
  24. package/dist/scripts/src/components/Charts/PieChart/PieChartNative.js +41 -2
  25. package/dist/scripts/src/components/DropdownMenu/DropdownMenuNative.js +7 -9
  26. package/dist/scripts/src/components/Form/FormNative.js +33 -25
  27. package/dist/scripts/src/components/FormItem/ItemWithLabel.js +3 -3
  28. package/dist/scripts/src/components/Icon/IconNative.js +18 -15
  29. package/dist/scripts/src/components/NavGroup/NavGroup.spec.js +182 -123
  30. package/dist/scripts/src/components/NavGroup/NavGroupNative.js +14 -6
  31. package/dist/scripts/src/components/NestedApp/AppWithCodeViewNative.js +1 -1
  32. package/dist/scripts/src/components/NumberBox/NumberBox.js +4 -4
  33. package/dist/scripts/src/components/NumberBox/NumberBox.spec.js +112 -423
  34. package/dist/scripts/src/components/NumberBox/NumberBoxNative.js +18 -4
  35. package/dist/scripts/src/components/Option/Option.js +3 -1
  36. package/dist/scripts/src/components/Select/HiddenOption.js +1 -1
  37. package/dist/scripts/src/components/SelectionStore/SelectionStoreNative.js +3 -1
  38. package/dist/scripts/src/components/Slider/Slider.spec.js +46 -13
  39. package/dist/scripts/src/components/Slider/SliderNative.js +19 -9
  40. package/dist/scripts/src/components/Table/Table.js +7 -1
  41. package/dist/scripts/src/components/Table/TableNative.js +4 -1
  42. package/dist/scripts/src/components/Table/useRowSelection.js +215 -1
  43. package/dist/scripts/src/components/TextBox/TextBox.js +1 -5
  44. package/dist/scripts/src/components/TextBox/TextBox.spec.js +368 -324
  45. package/dist/scripts/src/components/TextBox/TextBoxNative.js +10 -15
  46. package/dist/scripts/src/components/Theme/ThemeNative.js +2 -6
  47. package/dist/scripts/src/components/TimeInput/TimeInput.js +1 -5
  48. package/dist/scripts/src/components/TimeInput/TimeInputNative.js +2 -9
  49. package/dist/scripts/src/components/Tree/Tree-dynamic.spec.js +2894 -0
  50. package/dist/scripts/src/components/Tree/Tree.spec.js +2932 -0
  51. package/dist/scripts/src/components/Tree/TreeComponent.js +266 -10
  52. package/dist/scripts/src/components/Tree/TreeNative.js +1048 -23
  53. package/dist/scripts/src/components/Tree/testData.js +272 -0
  54. package/dist/scripts/src/components-core/ApiBoundComponent.js +38 -24
  55. package/dist/scripts/src/components-core/RestApiProxy.js +0 -1
  56. package/dist/scripts/src/components-core/behaviors/BehaviorContext.js +54 -0
  57. package/dist/scripts/src/components-core/behaviors/CoreBehaviors.js +81 -0
  58. package/dist/scripts/src/components-core/descriptorHelper.js +1 -0
  59. package/dist/scripts/src/components-core/parts.js +4 -1
  60. package/dist/scripts/src/components-core/rendering/AppRoot.js +2 -1
  61. package/dist/scripts/src/components-core/rendering/ComponentAdapter.js +32 -48
  62. package/dist/scripts/src/components-core/rendering/nodeUtils.js +6 -0
  63. package/dist/scripts/src/components-core/theming/layout-resolver.js +2 -0
  64. package/dist/scripts/src/components-core/utils/treeUtils.js +187 -12
  65. package/dist/scripts/src/index.js +38 -1
  66. package/dist/scripts/src/testing/ComponentDrivers.js +77 -31
  67. package/dist/scripts/src/testing/drivers/NumberBoxDriver.js +44 -0
  68. package/dist/scripts/src/testing/drivers/TextBoxDriver.js +20 -0
  69. package/dist/scripts/src/testing/drivers/TreeDriver.js +13 -0
  70. package/dist/scripts/src/testing/fixtures.js +40 -9
  71. package/dist/standalone/xmlui-standalone.es.d.ts +158 -15
  72. package/dist/standalone/xmlui-standalone.umd.js +36 -36
  73. package/package.json +2 -3
  74. package/dist/scripts/src/components/Animation/Animation.js +0 -50
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xmlui",
3
- "version": "0.10.14",
3
+ "version": "0.10.16",
4
4
  "sideEffects": false,
5
5
  "scripts": {
6
6
  "start-test-bed": "cd src/testing/infrastructure && xmlui start",
@@ -34,7 +34,7 @@
34
34
  "@radix-ui/react-accordion": "^1.2.0",
35
35
  "@radix-ui/react-alert-dialog": "^1.1.2",
36
36
  "@radix-ui/react-dialog": "1.0.5",
37
- "@radix-ui/react-dropdown-menu": "2.0.6",
37
+ "@radix-ui/react-dropdown-menu": "2.1.16",
38
38
  "@radix-ui/react-focus-scope": "1.0.4",
39
39
  "@radix-ui/react-hover-card": "1.0.7",
40
40
  "@radix-ui/react-popover": "^1.1.2",
@@ -131,7 +131,6 @@
131
131
  "@types/react-datepicker": "4.19.5",
132
132
  "@types/react-dom": "18.2.8",
133
133
  "@types/react-measure": "^2.0.8",
134
- "@types/react-pdf": "5.7.2",
135
134
  "@types/react-window": "1.8.8",
136
135
  "@types/yargs": "17.0.31",
137
136
  "@typescript-eslint/eslint-plugin": "8.15.0",
@@ -193,6 +193,7 @@ exports.Animation = (0, react_1.forwardRef)(function Animation(_a, forwardedRef)
193
193
  const [reset, setReset] = (0, react_1.useState)(false);
194
194
  const [count, setCount] = (0, react_1.useState)(0);
195
195
  const times = 1;
196
+ const animationId = (0, react_1.useId)();
196
197
  const animationSettings = (0, react_1.useMemo)(() => ({
197
198
  from: _animation.from,
198
199
  to: _animation.to,
@@ -217,7 +218,9 @@ exports.Animation = (0, react_1.forwardRef)(function Animation(_a, forwardedRef)
217
218
  }
218
219
  }
219
220
  },
220
- onStart: () => onStart === null || onStart === void 0 ? void 0 : onStart(),
221
+ onStart: () => {
222
+ onStart === null || onStart === void 0 ? void 0 : onStart();
223
+ },
221
224
  }), [
222
225
  _animation.config,
223
226
  _animation.from,
@@ -231,6 +234,7 @@ exports.Animation = (0, react_1.forwardRef)(function Animation(_a, forwardedRef)
231
234
  reset,
232
235
  reverse,
233
236
  toggle,
237
+ animationId,
234
238
  ]);
235
239
  const [springs, api] = (0, web_1.useSpring)(() => (Object.assign({}, animationSettings)), [animationSettings]);
236
240
  const [ref, animationStyles] = (0, web_1.useInView)(() => animationSettings, {
@@ -12,6 +12,10 @@ exports.AppStateMd = (0, metadata_helpers_1.createMetadata)({
12
12
  "across your entire application. Unlike component variables that are scoped " +
13
13
  "locally, AppState allows any component to access and update shared state " +
14
14
  "without prop drilling.",
15
+ events: {
16
+ didUpdate: (0, metadata_helpers_1.d)("This event is fired when the AppState value is updated. The event provides " +
17
+ "the new state value as its parameter."),
18
+ },
15
19
  props: {
16
20
  bucket: {
17
21
  description: `This property is the identifier of the bucket to which the \`${COMP}\` instance is bound. ` +
@@ -36,9 +40,35 @@ exports.AppStateMd = (0, metadata_helpers_1.createMetadata)({
36
40
  newState: "An object that specifies the new state value.",
37
41
  },
38
42
  },
43
+ appendToList: {
44
+ signature: "appendToList(key: string, id: any)",
45
+ description: "This method appends an item to an array in the application state object bound to the" +
46
+ " `AppState` instance.",
47
+ parameters: {
48
+ key: "The key of the array in the state object.",
49
+ id: "The item to append to the array.",
50
+ },
51
+ },
52
+ removeFromList: {
53
+ signature: "removeFromList(key: string, id: any)",
54
+ description: "This method removes an item from an array in the application state object bound to the" +
55
+ " `AppState` instance.",
56
+ parameters: {
57
+ key: "The key of the array in the state object.",
58
+ id: "The item to remove from the array.",
59
+ },
60
+ },
61
+ listIncludes: {
62
+ signature: "listIncludes(key: string, id: any)",
63
+ description: "This method checks if an array in the application state object contains a specific item.",
64
+ parameters: {
65
+ key: "The key of the array in the state object.",
66
+ id: "The item to check for in the array.",
67
+ },
68
+ },
39
69
  },
40
70
  nonVisual: true,
41
71
  });
42
- exports.appStateComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.AppStateMd, ({ node, extractValue, updateState, registerComponentApi }) => {
43
- return ((0, jsx_runtime_1.jsx)(AppStateNative_1.AppState, { bucket: extractValue(node.props.bucket), initialValue: extractValue(node.props.initialValue), updateState: updateState, registerComponentApi: registerComponentApi }));
72
+ exports.appStateComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.AppStateMd, ({ node, extractValue, updateState, registerComponentApi, lookupEventHandler }) => {
73
+ return ((0, jsx_runtime_1.jsx)(AppStateNative_1.AppState, { bucket: extractValue(node.props.bucket), initialValue: extractValue(node.props.initialValue), updateState: updateState, registerComponentApi: registerComponentApi, onDidUpdate: lookupEventHandler("didUpdate") }));
44
74
  });
@@ -7,7 +7,7 @@ const AppStateContext_1 = require("../../components/App/AppStateContext");
7
7
  exports.defaultProps = {
8
8
  bucket: "default",
9
9
  };
10
- function AppState({ bucket = exports.defaultProps.bucket, updateState, initialValue, registerComponentApi, }) {
10
+ function AppState({ bucket = exports.defaultProps.bucket, updateState, initialValue, registerComponentApi, onDidUpdate, }) {
11
11
  const registerAppState = (0, AppStateContext_1.useAppStateContextPart)((value) => value.registerAppState);
12
12
  const update = (0, AppStateContext_1.useAppStateContextPart)((value) => value.update);
13
13
  (0, hooks_1.useIsomorphicLayoutEffect)(() => {
@@ -18,11 +18,35 @@ function AppState({ bucket = exports.defaultProps.bucket, updateState, initialVa
18
18
  const value = (0, AppStateContext_1.useAppStateContextPart)((value) => value.appState[bucket]);
19
19
  (0, hooks_1.useIsomorphicLayoutEffect)(() => {
20
20
  updateState({ value });
21
- }, [updateState, value]);
21
+ // Fire the didUpdate event when value changes
22
+ if (onDidUpdate) {
23
+ onDidUpdate({ bucket, value, previousValue: undefined }); // Note: previousValue tracking could be added if needed
24
+ }
25
+ }, [updateState, value, onDidUpdate, bucket]);
22
26
  (0, hooks_1.useIsomorphicLayoutEffect)(() => {
23
27
  registerComponentApi({
24
28
  update: (patch) => update(bucket, patch),
29
+ appendToList: (key, id) => {
30
+ const currentState = value || {};
31
+ const currentArray = currentState[key] || [];
32
+ // Only add if the id doesn't already exist in the array
33
+ if (!currentArray.includes(id)) {
34
+ const newArray = [...currentArray, id];
35
+ update(bucket, { [key]: newArray });
36
+ }
37
+ },
38
+ removeFromList: (key, id) => {
39
+ const currentState = value || {};
40
+ const currentArray = currentState[key] || [];
41
+ const newArray = currentArray.filter((item) => item !== id);
42
+ update(bucket, { [key]: newArray });
43
+ },
44
+ listIncludes: (key, id) => {
45
+ const currentState = value || {};
46
+ const currentArray = currentState[key] || [];
47
+ return currentArray.includes(id);
48
+ },
25
49
  });
26
- }, [bucket, registerComponentApi, update]);
50
+ }, [bucket, registerComponentApi, update, value]);
27
51
  return null;
28
52
  }
@@ -29,10 +29,6 @@ exports.AutoCompleteMd = (0, metadata_helpers_1.createMetadata)({
29
29
  initiallyOpen: (0, metadata_helpers_1.d)(`This property determines whether the dropdown list is open when the component is first rendered.`, null, "boolean", AutoCompleteNative_1.defaultProps.initiallyOpen),
30
30
  creatable: (0, metadata_helpers_1.d)(`This property allows the user to create new items that are not present in the list of options.`, null, "boolean", AutoCompleteNative_1.defaultProps.creatable),
31
31
  validationStatus: Object.assign(Object.assign({}, (0, metadata_helpers_1.dValidationStatus)()), { defaultValue: AutoCompleteNative_1.defaultProps.validationStatus }),
32
- label: (0, metadata_helpers_1.dLabel)(),
33
- labelPosition: Object.assign(Object.assign({}, (0, metadata_helpers_1.dLabelPosition)()), { defaultValue: AutoCompleteNative_1.defaultProps.labelPosition }),
34
- labelWidth: (0, metadata_helpers_1.dLabelWidth)(COMP),
35
- labelBreak: (0, metadata_helpers_1.dLabelBreak)(COMP),
36
32
  dropdownHeight: (0, metadata_helpers_1.d)("This property sets the height of the dropdown list."),
37
33
  multi: Object.assign(Object.assign({}, (0, metadata_helpers_1.dMulti)()), { defaultValue: AutoCompleteNative_1.defaultProps.multi }),
38
34
  optionTemplate: (0, metadata_helpers_1.dComponent)(`This property enables the customization of list items. To access the attributes of ` +
@@ -97,7 +93,7 @@ exports.AutoCompleteMd = (0, metadata_helpers_1.createMetadata)({
97
93
  },
98
94
  });
99
95
  exports.autoCompleteComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.AutoCompleteMd, ({ node, state, updateState, extractValue, renderChild, lookupEventHandler, registerComponentApi, className, }) => {
100
- return ((0, jsx_runtime_1.jsx)(AutoCompleteNative_1.AutoComplete, { multi: extractValue.asOptionalBoolean(node.props.multi), className: className, updateState: updateState, initialValue: extractValue(node.props.initialValue), value: state === null || state === void 0 ? void 0 : state.value, creatable: extractValue.asOptionalBoolean(node.props.creatable), label: extractValue(node.props.label), labelPosition: extractValue(node.props.labelPosition), labelWidth: extractValue(node.props.labelWidth), labelBreak: extractValue.asOptionalBoolean(node.props.labelBreak), autoFocus: extractValue.asOptionalBoolean(node.props.autoFocus), enabled: extractValue.asOptionalBoolean(node.props.enabled), placeholder: extractValue.asOptionalString(node.props.placeholder), validationStatus: extractValue(node.props.validationStatus), onDidChange: lookupEventHandler("didChange"), onFocus: lookupEventHandler("gotFocus"), onBlur: lookupEventHandler("lostFocus"), onItemCreated: lookupEventHandler("itemCreated"), registerComponentApi: registerComponentApi, emptyListTemplate: renderChild(node.props.emptyListTemplate), dropdownHeight: extractValue(node.props.dropdownHeight), readOnly: extractValue.asOptionalBoolean(node.props.readOnly), initiallyOpen: extractValue.asOptionalBoolean(node.props.initiallyOpen), optionRenderer: node.props.optionTemplate
96
+ return ((0, jsx_runtime_1.jsx)(AutoCompleteNative_1.AutoComplete, { multi: extractValue.asOptionalBoolean(node.props.multi), className: className, updateState: updateState, initialValue: extractValue(node.props.initialValue), value: state === null || state === void 0 ? void 0 : state.value, creatable: extractValue.asOptionalBoolean(node.props.creatable), autoFocus: extractValue.asOptionalBoolean(node.props.autoFocus), enabled: extractValue.asOptionalBoolean(node.props.enabled), placeholder: extractValue.asOptionalString(node.props.placeholder), validationStatus: extractValue(node.props.validationStatus), onDidChange: lookupEventHandler("didChange"), onFocus: lookupEventHandler("gotFocus"), onBlur: lookupEventHandler("lostFocus"), onItemCreated: lookupEventHandler("itemCreated"), registerComponentApi: registerComponentApi, emptyListTemplate: renderChild(node.props.emptyListTemplate), dropdownHeight: extractValue(node.props.dropdownHeight), readOnly: extractValue.asOptionalBoolean(node.props.readOnly), initiallyOpen: extractValue.asOptionalBoolean(node.props.initiallyOpen), optionRenderer: node.props.optionTemplate
101
97
  ? (item, val, inTrigger) => {
102
98
  return ((0, jsx_runtime_1.jsx)(container_helpers_1.MemoizedItem, { node: node.props.optionTemplate, item: item, context: {
103
99
  $selectedValue: val,
@@ -7,9 +7,11 @@ exports.AutoCompleteContext = (0, react_1.createContext)({
7
7
  value: null,
8
8
  onChange: (selectedValue) => { },
9
9
  inputValue: "",
10
+ searchTerm: "",
10
11
  options: new Set(),
11
12
  open: false,
12
13
  setOpen: (open) => { },
14
+ setSelectedIndex: (index) => { },
13
15
  optionRenderer: (option, selectedValue, inTrigger) => null,
14
16
  });
15
17
  function useAutoComplete() {
@@ -18,7 +18,6 @@ exports.AutoComplete = exports.defaultProps = void 0;
18
18
  const jsx_runtime_1 = require("react/jsx-runtime");
19
19
  const react_1 = require("react");
20
20
  const classnames_1 = __importDefault(require("classnames"));
21
- const cmdk_1 = require("cmdk");
22
21
  const constants_1 = require("../../components-core/constants");
23
22
  const misc_1 = require("../../components-core/utils/misc");
24
23
  const AutoComplete_module_scss_1 = __importDefault(require("../../components/AutoComplete/AutoComplete.module.scss"));
@@ -28,7 +27,6 @@ const AutoCompleteContext_1 = require("./AutoCompleteContext");
28
27
  const OptionContext_1 = require("../Select/OptionContext");
29
28
  const ThemeContext_1 = require("../../components-core/theming/ThemeContext");
30
29
  const react_popover_1 = require("@radix-ui/react-popover");
31
- const ItemWithLabel_1 = require("../FormItem/ItemWithLabel");
32
30
  const HiddenOption_1 = require("../Select/HiddenOption");
33
31
  const parts_1 = require("../../components-core/parts");
34
32
  const PART_LIST_WRAPPER = "listWrapper";
@@ -65,6 +63,34 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
65
63
  const inputId = id || generatedId;
66
64
  const [searchTerm, setSearchTerm] = (0, react_1.useState)("");
67
65
  const [isFocused, setIsFocused] = (0, react_1.useState)(false);
66
+ const [selectedIndex, setSelectedIndex] = (0, react_1.useState)(-1);
67
+ // Filter options based on search term
68
+ const filteredOptions = (0, react_1.useMemo)(() => {
69
+ if (!searchTerm || searchTerm.trim() === "") {
70
+ return Array.from(options);
71
+ }
72
+ const searchLower = searchTerm.toLowerCase();
73
+ return Array.from(options).filter((option) => {
74
+ const extendedValue = option.value + " " + option.label + " " + (option.keywords || []).join(" ");
75
+ return extendedValue.toLowerCase().includes(searchLower);
76
+ });
77
+ }, [options, searchTerm]);
78
+ // Check if we should show creatable item
79
+ const shouldShowCreatable = (0, react_1.useMemo)(() => {
80
+ if (!creatable || !searchTerm || searchTerm.trim() === "")
81
+ return false;
82
+ // Check if the search term already exists as an option
83
+ const searchTermExists = Array.from(options).some((option) => option.value === searchTerm || option.label === searchTerm);
84
+ if (searchTermExists)
85
+ return false;
86
+ // Check if it's already selected
87
+ if (Array.isArray(value) && value.includes(searchTerm))
88
+ return false;
89
+ if (value === searchTerm)
90
+ return false;
91
+ // Only show creatable if there are no matching filtered options
92
+ return filteredOptions.length === 0;
93
+ }, [creatable, searchTerm, options, value, filteredOptions]);
68
94
  // Set initial state based on the initialValue prop
69
95
  (0, react_1.useEffect)(() => {
70
96
  if (initialValue !== undefined) {
@@ -96,15 +122,12 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
96
122
  }, [value, options]);
97
123
  const toggleOption = (0, react_1.useCallback)((selectedItem) => {
98
124
  var _a;
99
- if (multi) {
100
- setInputValue("");
101
- setSearchTerm("");
102
- }
103
- else {
104
- setOpen(true);
105
- }
106
125
  if (selectedItem === "")
107
126
  return;
127
+ // Check if the option is enabled
128
+ const option = Array.from(options).find((opt) => opt.value === selectedItem);
129
+ if (option && option.enabled === false)
130
+ return;
108
131
  const newSelectedValue = multi
109
132
  ? Array.isArray(value)
110
133
  ? value.includes(selectedItem)
@@ -116,8 +139,18 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
116
139
  : selectedItem;
117
140
  updateState({ value: newSelectedValue });
118
141
  onDidChange(newSelectedValue);
142
+ if (multi) {
143
+ setInputValue("");
144
+ setSearchTerm("");
145
+ // Keep dropdown open for multi-select
146
+ }
147
+ else {
148
+ // Close dropdown for single select
149
+ setOpen(false);
150
+ setSearchTerm("");
151
+ }
119
152
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
120
- }, [multi, value, updateState, onDidChange]);
153
+ }, [multi, value, updateState, onDidChange, options]);
121
154
  (0, react_1.useEffect)(() => {
122
155
  if (!Array.isArray(selectedValue)) {
123
156
  setInputValue((selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue.label) || "");
@@ -141,6 +174,131 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
141
174
  return optionsSet;
142
175
  });
143
176
  }, []);
177
+ // Combined list of all items (creatable + filtered options)
178
+ const allItems = (0, react_1.useMemo)(() => {
179
+ const items = [];
180
+ if (shouldShowCreatable) {
181
+ items.push({ type: "creatable", value: searchTerm, label: `Create "${searchTerm}"` });
182
+ }
183
+ filteredOptions.forEach((option) => {
184
+ items.push(Object.assign({ type: "option" }, option));
185
+ });
186
+ return items;
187
+ }, [shouldShowCreatable, searchTerm, filteredOptions]);
188
+ // Helper functions to find next/previous enabled option
189
+ const findNextEnabledIndex = (0, react_1.useCallback)((currentIndex) => {
190
+ for (let i = currentIndex + 1; i < allItems.length; i++) {
191
+ const item = allItems[i];
192
+ if (item.type === "creatable" || item.enabled !== false) {
193
+ return i;
194
+ }
195
+ }
196
+ // Wrap around to beginning
197
+ for (let i = 0; i <= currentIndex; i++) {
198
+ const item = allItems[i];
199
+ if (item.type === "creatable" || item.enabled !== false) {
200
+ return i;
201
+ }
202
+ }
203
+ return -1;
204
+ }, [allItems]);
205
+ const findPreviousEnabledIndex = (0, react_1.useCallback)((currentIndex) => {
206
+ for (let i = currentIndex - 1; i >= 0; i--) {
207
+ const item = allItems[i];
208
+ if (item.type === "creatable" || item.enabled !== false) {
209
+ return i;
210
+ }
211
+ }
212
+ // Wrap around to end
213
+ for (let i = allItems.length - 1; i >= currentIndex; i--) {
214
+ const item = allItems[i];
215
+ if (item.type === "creatable" || item.enabled !== false) {
216
+ return i;
217
+ }
218
+ }
219
+ return -1;
220
+ }, [allItems]);
221
+ // Reset selected index when options change, but select matching option when dropdown opens
222
+ (0, react_1.useEffect)(() => {
223
+ if (!open) {
224
+ setSelectedIndex(-1);
225
+ }
226
+ else if (!multi && open && inputValue) {
227
+ // For single-select, when dropdown opens and there's an input value, try to find and select the matching option
228
+ const matchingIndex = allItems.findIndex((item) => {
229
+ var _a, _b;
230
+ if (item.type === "creatable")
231
+ return false;
232
+ return ((_a = item.label) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === inputValue.toLowerCase() ||
233
+ ((_b = item.value) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === inputValue.toLowerCase();
234
+ });
235
+ if (matchingIndex !== -1) {
236
+ setSelectedIndex(matchingIndex);
237
+ }
238
+ else {
239
+ setSelectedIndex(-1);
240
+ }
241
+ }
242
+ else if (!multi && open && !inputValue) {
243
+ setSelectedIndex(-1);
244
+ }
245
+ // For multi-select, don't reset selectedIndex when dropdown is open - preserve keyboard navigation
246
+ }, [allItems, multi, open, inputValue]);
247
+ // Keyboard navigation
248
+ const handleKeyDown = (0, react_1.useCallback)((event) => {
249
+ if (!open)
250
+ return;
251
+ switch (event.key) {
252
+ case "ArrowDown":
253
+ event.preventDefault();
254
+ setSelectedIndex((prev) => {
255
+ const nextIndex = findNextEnabledIndex(prev);
256
+ return nextIndex !== -1 ? nextIndex : prev;
257
+ });
258
+ break;
259
+ case "ArrowUp":
260
+ event.preventDefault();
261
+ setSelectedIndex((prev) => {
262
+ const prevIndex = findPreviousEnabledIndex(prev);
263
+ return prevIndex !== -1 ? prevIndex : prev;
264
+ });
265
+ break;
266
+ case "Enter":
267
+ event.preventDefault();
268
+ if (selectedIndex >= 0 && selectedIndex < allItems.length) {
269
+ const selectedItem = allItems[selectedIndex];
270
+ if (selectedItem.type === "creatable") {
271
+ const newOption = { value: searchTerm, label: searchTerm, enabled: true };
272
+ onOptionAdd(newOption);
273
+ onItemCreated(searchTerm);
274
+ toggleOption(searchTerm);
275
+ }
276
+ else if (selectedItem.enabled !== false) {
277
+ // Only toggle if the option is enabled
278
+ toggleOption(selectedItem.value);
279
+ }
280
+ }
281
+ else if (allItems.length === 1) {
282
+ // If there's only one item (creatable or regular) and no selection, select it
283
+ const singleItem = allItems[0];
284
+ if (singleItem.type === "creatable") {
285
+ const newOption = { value: searchTerm, label: searchTerm, enabled: true };
286
+ onOptionAdd(newOption);
287
+ onItemCreated(searchTerm);
288
+ toggleOption(searchTerm);
289
+ }
290
+ else if (singleItem.enabled !== false) {
291
+ // Only toggle if the option is enabled
292
+ toggleOption(singleItem.value);
293
+ }
294
+ }
295
+ break;
296
+ case "Escape":
297
+ event.preventDefault();
298
+ setOpen(false);
299
+ break;
300
+ }
301
+ }, [open, selectedIndex, allItems, searchTerm, onOptionAdd, onItemCreated, toggleOption, setOpen, findNextEnabledIndex, findPreviousEnabledIndex]);
144
302
  // Render the "empty list" message
145
303
  const emptyListNode = (0, react_1.useMemo)(() => emptyListTemplate !== null && emptyListTemplate !== void 0 ? emptyListTemplate : ((0, jsx_runtime_1.jsxs)("div", { className: AutoComplete_module_scss_1.default.autoCompleteEmpty, children: [(0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "noresult" }), (0, jsx_runtime_1.jsx)("span", { children: "List is empty" })] })), [emptyListTemplate]);
146
304
  // Register component API for external interactions
@@ -168,106 +326,129 @@ exports.AutoComplete = (0, react_1.forwardRef)(function AutoComplete(_a, forward
168
326
  onChange: toggleOption,
169
327
  options,
170
328
  inputValue,
329
+ searchTerm,
171
330
  open,
172
331
  setOpen,
332
+ setSelectedIndex,
173
333
  optionRenderer,
174
334
  };
175
- }, [inputValue, multi, options, toggleOption, value, open, setOpen, optionRenderer]);
335
+ }, [inputValue, searchTerm, multi, options, toggleOption, value, open, setOpen, setSelectedIndex, optionRenderer]);
176
336
  return ((0, jsx_runtime_1.jsx)(AutoCompleteContext_1.AutoCompleteContext.Provider, { value: autoCompleteContextValue, children: (0, jsx_runtime_1.jsx)(OptionContext_1.OptionContext.Provider, { value: optionContextValue, children: (0, jsx_runtime_1.jsxs)(OptionTypeProvider_1.default, { Component: HiddenOption_1.HiddenOption, children: [(0, jsx_runtime_1.jsx)(react_popover_1.Popover, { open: open, onOpenChange: (isOpen) => {
177
337
  if (readOnly)
178
338
  return;
179
339
  setOpen(isOpen);
180
- }, modal: false, children: (0, jsx_runtime_1.jsxs)(cmdk_1.Command, { ref: dropdownRef, className: AutoComplete_module_scss_1.default.command, filter: (value, search, keywords) => {
181
- if (readOnly)
182
- return 1;
183
- if (!searchTerm || searchTerm.trim() === "")
184
- return 1;
185
- const extendedValue = value + " " + keywords.join(" ");
186
- if (extendedValue.toLowerCase().includes(search.toLowerCase()))
187
- return 1;
188
- return 0;
189
- }, children: [(0, jsx_runtime_1.jsx)(ItemWithLabel_1.ItemWithLabel, Object.assign({}, rest, { id: inputId, ref: forwardedRef, labelPosition: labelPosition, label: label, labelWidth: labelWidth, labelBreak: labelBreak, required: required, enabled: enabled, onFocus: onFocus, onBlur: onBlur, className: className, children: (0, jsx_runtime_1.jsx)(react_popover_1.PopoverTrigger, { asChild: true, children: (0, jsx_runtime_1.jsxs)("div", { "data-part-id": PART_LIST_WRAPPER, ref: setReferenceElement, style: Object.assign({ width: "100%" }, style), className: (0, classnames_1.default)(className, AutoComplete_module_scss_1.default.badgeListWrapper, AutoComplete_module_scss_1.default[validationStatus], {
190
- [AutoComplete_module_scss_1.default.disabled]: !enabled,
191
- [AutoComplete_module_scss_1.default.focused]: isFocused,
192
- }), children: [Array.isArray(selectedValue) && ((0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.badgeList, children: selectedValue.map((v, index) => ((0, jsx_runtime_1.jsxs)("span", { className: AutoComplete_module_scss_1.default.badge, children: [v === null || v === void 0 ? void 0 : v.label, !readOnly && ((0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "close", size: "sm", onClick: (event) => {
193
- event.stopPropagation();
194
- toggleOption(v.value);
195
- } }))] }, index))) })), (0, jsx_runtime_1.jsx)(cmdk_1.CommandInput, { onFocus: (ev) => {
196
- setIsFocused(true);
197
- onFocus(ev);
198
- }, onBlur: (ev) => {
199
- if (inputValue === "" && !multi) {
200
- clearValue();
201
- }
202
- else {
203
- if (!Array.isArray(selectedValue) && selectedValue) {
204
- setInputValue(selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue.label);
340
+ if (!isOpen) {
341
+ // Reset highlighted option when dropdown closes
342
+ setSelectedIndex(-1);
343
+ }
344
+ }, modal: false, children: (0, jsx_runtime_1.jsxs)("div", { ref: dropdownRef, className: AutoComplete_module_scss_1.default.command, children: [(0, jsx_runtime_1.jsx)(react_popover_1.PopoverTrigger, { asChild: true, ref: setReferenceElement, children: (0, jsx_runtime_1.jsxs)("div", Object.assign({}, rest, { ref: forwardedRef, "data-part-id": PART_LIST_WRAPPER, style: Object.assign({ width: "100%" }, style), className: (0, classnames_1.default)(className, AutoComplete_module_scss_1.default.badgeListWrapper, AutoComplete_module_scss_1.default[validationStatus], {
345
+ [AutoComplete_module_scss_1.default.disabled]: !enabled,
346
+ [AutoComplete_module_scss_1.default.focused]: isFocused,
347
+ }), "aria-expanded": open, children: [Array.isArray(selectedValue) && selectedValue.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.badgeList, children: selectedValue.map((v, index) => ((0, jsx_runtime_1.jsxs)("span", { className: AutoComplete_module_scss_1.default.badge, children: [v === null || v === void 0 ? void 0 : v.label, !readOnly && ((0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "close", size: "sm", onClick: (event) => {
348
+ event.stopPropagation();
349
+ toggleOption(v.value);
350
+ } }))] }, index))) })), (0, jsx_runtime_1.jsxs)("div", { className: AutoComplete_module_scss_1.default.inputWrapper, children: [(0, jsx_runtime_1.jsx)("input", { role: "combobox", id: inputId, onFocus: (ev) => {
351
+ setIsFocused(true);
352
+ onFocus(ev);
353
+ }, onBlur: (ev) => {
354
+ if (inputValue === "" && !multi) {
355
+ clearValue();
205
356
  }
206
357
  else {
207
- setInputValue("");
358
+ if (!Array.isArray(selectedValue) && selectedValue) {
359
+ setInputValue(selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue.label);
360
+ }
361
+ else {
362
+ setInputValue("");
363
+ }
364
+ }
365
+ onBlur(ev);
366
+ setIsFocused(false);
367
+ }, onKeyDown: (event) => {
368
+ if (readOnly)
369
+ return;
370
+ // Handle opening dropdown
371
+ if (event.key === "ArrowDown" && !open) {
372
+ setOpen(true);
373
+ return;
374
+ }
375
+ // Handle keyboard navigation when dropdown is open
376
+ if (open) {
377
+ handleKeyDown(event);
208
378
  }
209
- }
210
- onBlur(ev);
211
- setIsFocused(false);
212
- }, onKeyDown: (event) => {
213
- if (readOnly)
214
- return;
215
- if (event.key === "ArrowDown") {
379
+ else if (event.key === "Enter") {
380
+ setOpen(true);
381
+ }
382
+ }, "data-part-id": parts_1.PART_INPUT, readOnly: readOnly, autoFocus: autoFocus, "aria-autocomplete": "list", ref: inputRef, value: inputValue, disabled: !enabled, onChange: (event) => {
216
383
  setOpen(true);
217
- }
218
- if (event.key === "Enter") {
219
- setOpen((prev) => !prev);
220
- }
221
- }, id: inputId, "data-part-id": parts_1.PART_INPUT, readOnly: readOnly, autoFocus: autoFocus, "aria-expanded": open, ref: inputRef, value: inputValue, disabled: !enabled, onValueChange: (value) => {
222
- setOpen(true);
223
- setInputValue(value);
224
- setSearchTerm(value);
225
- }, placeholder: !readOnly ? placeholder : "", className: AutoComplete_module_scss_1.default.commandInput }), (0, jsx_runtime_1.jsxs)("div", { className: AutoComplete_module_scss_1.default.actions, children: [(value === null || value === void 0 ? void 0 : value.length) > 0 && enabled && !readOnly && ((0, jsx_runtime_1.jsx)("span", { className: AutoComplete_module_scss_1.default.action, onClick: (event) => {
226
- event.stopPropagation();
227
- clearValue();
228
- }, children: (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "close" }) })), (0, jsx_runtime_1.jsx)("span", { className: AutoComplete_module_scss_1.default.action, onClick: () => {
229
- if (readOnly)
230
- return;
231
- setOpen(!open);
232
- }, children: (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "chevrondown" }) })] })] }) }) })), open && ((0, jsx_runtime_1.jsx)(react_popover_1.Portal, { container: root, children: (0, jsx_runtime_1.jsx)(react_popover_1.PopoverContent, { asChild: true, style: { width, height: dropdownHeight }, className: AutoComplete_module_scss_1.default.popoverContent, align: "start", onOpenAutoFocus: (e) => e.preventDefault(), children: (0, jsx_runtime_1.jsxs)(cmdk_1.CommandList, { role: "listbox", className: AutoComplete_module_scss_1.default.commandList, style: { height: dropdownHeight }, children: [(0, jsx_runtime_1.jsx)(cmdk_1.CommandEmpty, { children: emptyListNode }), creatable && (0, jsx_runtime_1.jsx)(CreatableItem, { onNewItem: onItemCreated }), (0, jsx_runtime_1.jsx)(cmdk_1.CommandGroup, { children: Array.from(options).map(({ value, label, enabled, keywords }) => ((0, jsx_runtime_1.jsx)(AutoCompleteOption, { value: value, label: label, enabled: enabled, keywords: keywords, readOnly: readOnly }, value))) })] }) }) }))] }) }), children] }) }) }));
384
+ setInputValue(event.target.value);
385
+ setSearchTerm(event.target.value);
386
+ }, placeholder: !readOnly ? placeholder : "", className: AutoComplete_module_scss_1.default.commandInput }), (0, jsx_runtime_1.jsxs)("div", { className: AutoComplete_module_scss_1.default.actions, children: [(value === null || value === void 0 ? void 0 : value.length) > 0 && enabled && !readOnly && ((0, jsx_runtime_1.jsx)("span", { className: AutoComplete_module_scss_1.default.action, onClick: (event) => {
387
+ event.stopPropagation();
388
+ clearValue();
389
+ }, children: (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "close" }) })), (0, jsx_runtime_1.jsx)("span", { className: AutoComplete_module_scss_1.default.action, onClick: () => {
390
+ var _a;
391
+ if (readOnly)
392
+ return;
393
+ setOpen(!open);
394
+ // Focus the input after opening dropdown
395
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
396
+ }, children: (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "chevrondown" }) })] })] })] })) }), open && ((0, jsx_runtime_1.jsx)(react_popover_1.Portal, { container: root, children: (0, jsx_runtime_1.jsx)(react_popover_1.PopoverContent, { style: { width, height: dropdownHeight }, className: AutoComplete_module_scss_1.default.popoverContent, align: "start", onOpenAutoFocus: (e) => e.preventDefault(), children: (0, jsx_runtime_1.jsxs)("div", { role: "listbox", className: AutoComplete_module_scss_1.default.commandList, style: { height: dropdownHeight }, children: [filteredOptions.length === 0 && !shouldShowCreatable && ((0, jsx_runtime_1.jsx)("div", { children: emptyListNode })), shouldShowCreatable && ((0, jsx_runtime_1.jsx)(CreatableItem, { onNewItem: onItemCreated, isHighlighted: selectedIndex === 0 })), (0, jsx_runtime_1.jsx)("div", { children: filteredOptions.map(({ value, label, enabled, keywords }, index) => {
397
+ const itemIndex = shouldShowCreatable ? index + 1 : index;
398
+ return ((0, jsx_runtime_1.jsx)(AutoCompleteOption, { value: value, label: label, enabled: enabled, keywords: keywords, readOnly: readOnly, isHighlighted: selectedIndex === itemIndex, itemIndex: itemIndex }, value));
399
+ }) })] }) }) }))] }) }), children] }) }) }));
233
400
  });
234
- function CreatableItem({ onNewItem }) {
235
- const { value, options, inputValue, onChange, setOpen } = (0, AutoCompleteContext_1.useAutoComplete)();
401
+ function CreatableItem({ onNewItem, isHighlighted = false }) {
402
+ const { value, options, searchTerm, onChange, setOpen, setSelectedIndex } = (0, AutoCompleteContext_1.useAutoComplete)();
236
403
  const { onOptionAdd } = (0, OptionContext_1.useOption)();
237
- if (isOptionsExist(options, [{ value: inputValue, label: inputValue }]) ||
238
- (Array.isArray(value) && (value === null || value === void 0 ? void 0 : value.find((s) => s === inputValue))) ||
239
- inputValue === value) {
404
+ if (isOptionsExist(options, [{ value: searchTerm, label: searchTerm }]) ||
405
+ (Array.isArray(value) && (value === null || value === void 0 ? void 0 : value.find((s) => s === searchTerm))) ||
406
+ searchTerm === value) {
240
407
  return (0, jsx_runtime_1.jsx)("span", { style: { display: "none" } });
241
408
  }
242
- const Item = ((0, jsx_runtime_1.jsx)(cmdk_1.CommandItem, { value: inputValue, className: AutoComplete_module_scss_1.default.autoCompleteOption, onMouseDown: (e) => {
409
+ const handleClick = () => {
410
+ const newOption = { value: searchTerm, label: searchTerm, enabled: true };
411
+ onOptionAdd(newOption);
412
+ onNewItem === null || onNewItem === void 0 ? void 0 : onNewItem(searchTerm);
413
+ onChange(searchTerm);
414
+ setOpen(false);
415
+ };
416
+ const Item = ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(AutoComplete_module_scss_1.default.autoCompleteOption, {
417
+ [AutoComplete_module_scss_1.default.highlighted]: isHighlighted,
418
+ }), onMouseDown: (e) => {
243
419
  e.preventDefault();
244
420
  e.stopPropagation();
245
- }, onSelect: (value) => {
246
- const newOption = { value, label: value, enabled: true };
247
- onOptionAdd(newOption);
248
- onNewItem === null || onNewItem === void 0 ? void 0 : onNewItem(value);
249
- onChange(value);
250
- setOpen(false);
251
- }, children: `Create "${inputValue}"` }));
421
+ }, onMouseEnter: () => {
422
+ if (setSelectedIndex) {
423
+ setSelectedIndex(0); // CreatableItem is always at index 0
424
+ }
425
+ }, onClick: handleClick, role: "option", "aria-selected": isHighlighted, children: `Create "${searchTerm}"` }));
252
426
  // For normal creatable
253
- if (inputValue.length > 0) {
427
+ if (searchTerm.length > 0) {
254
428
  return Item;
255
429
  }
256
430
  return (0, jsx_runtime_1.jsx)("span", { style: { display: "none" } });
257
431
  }
258
432
  function AutoCompleteOption(option) {
259
- const { value, label, enabled = true, keywords, readOnly, children } = option;
433
+ const { value, label, enabled = true, keywords, readOnly, children, isHighlighted = false, itemIndex, } = option;
260
434
  const id = (0, react_1.useId)();
261
- const { value: selectedValue, onChange, multi, setOpen, optionRenderer } = (0, AutoCompleteContext_1.useAutoComplete)();
435
+ const { value: selectedValue, onChange, multi, setOpen, setSelectedIndex, optionRenderer } = (0, AutoCompleteContext_1.useAutoComplete)();
262
436
  const selected = multi ? selectedValue === null || selectedValue === void 0 ? void 0 : selectedValue.includes(value) : selectedValue === value;
263
- return ((0, jsx_runtime_1.jsx)(cmdk_1.CommandItem, { id: id, disabled: !enabled, value: `${value}`, className: (0, classnames_1.default)(AutoComplete_module_scss_1.default.autoCompleteOption, {
437
+ const handleClick = () => {
438
+ if (!readOnly && enabled) {
439
+ onChange(value);
440
+ setOpen(false);
441
+ }
442
+ };
443
+ return ((0, jsx_runtime_1.jsx)("div", { id: id, role: "option", "aria-disabled": !enabled, "aria-selected": isHighlighted, className: (0, classnames_1.default)(AutoComplete_module_scss_1.default.autoCompleteOption, {
264
444
  [AutoComplete_module_scss_1.default.disabledOption]: !enabled,
445
+ [AutoComplete_module_scss_1.default.highlighted]: isHighlighted,
265
446
  }), onMouseDown: (e) => {
266
447
  e.preventDefault();
267
448
  e.stopPropagation();
268
- }, onSelect: () => {
269
- if (!readOnly && enabled)
270
- onChange(value);
271
- setOpen(false);
272
- }, "data-state": enabled ? (selected ? "checked" : undefined) : "disabled", keywords: keywords, children: children ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.autoCompleteOptionContent, children: children }), selected && (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "checkmark" })] })) : optionRenderer ? (optionRenderer({ label, value, enabled }, selectedValue, false)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.autoCompleteOptionContent, children: label }), selected && (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "checkmark" })] })) }, id));
449
+ }, onMouseEnter: () => {
450
+ if (itemIndex !== undefined && setSelectedIndex && enabled) {
451
+ setSelectedIndex(itemIndex);
452
+ }
453
+ }, onClick: handleClick, children: children ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.autoCompleteOptionContent, children: children }), selected && (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "checkmark" })] })) : optionRenderer ? (optionRenderer({ label, value, enabled }, selectedValue, false)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: AutoComplete_module_scss_1.default.autoCompleteOptionContent, children: label }), selected && (0, jsx_runtime_1.jsx)(IconNative_1.default, { name: "checkmark" })] })) }));
273
454
  }