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
@@ -86,6 +86,8 @@ exports.NumberBox = (0, react_1.forwardRef)(function NumberBox(_a, forwardedRef)
86
86
  // Ensure the provided minimum is not smaller than the 0 if zeroOrPositive is set to true
87
87
  min = Math.max(zeroOrPositive ? 0 : -numberbox_abstractions_1.NUMBERBOX_MAX_VALUE, min);
88
88
  // Step must be an integer since floating point arithmetic needs a deeper dive.
89
+ // probably some way to integrate with https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat
90
+ // since there are footguns, like 0.1 + 0.2 = 0.0000...04
89
91
  const _step = (_b = (0, numberbox_abstractions_1.toUsableNumber)(step, true)) !== null && _b !== void 0 ? _b : numberbox_abstractions_1.DEFAULT_STEP;
90
92
  const inputRef = (0, react_1.useRef)(null);
91
93
  const upButton = (0, react_1.useRef)(null);
@@ -414,10 +416,22 @@ exports.NumberBox = (0, react_1.forwardRef)(function NumberBox(_a, forwardedRef)
414
416
  if (!integersOnly && currentInputValue.endsWith(numberbox_abstractions_1.DECIMAL_SEPARATOR)) {
415
417
  // --- Add trailing zero if the value ends with decimal separator
416
418
  finalValue = currentInputValue + "0";
417
- // --- Update the input and state with the new value
418
- if (inputRef.current) {
419
- inputRef.current.value = finalValue;
419
+ }
420
+ // --- Convert to number and clamp to min/max bounds
421
+ const numericValue = (0, numberbox_abstractions_1.toUsableNumber)(finalValue, integersOnly);
422
+ if (!(0, numberbox_abstractions_1.isEmptyLike)(numericValue)) {
423
+ const clampedValue = (0, numberbox_abstractions_1.clamp)(numericValue, min, max);
424
+ if (clampedValue !== numericValue) {
425
+ const clampedString = clampedValue.toString();
426
+ finalValue = clampedString;
427
+ // --- Update the input field immediately
428
+ if (inputRef.current) {
429
+ inputRef.current.value = clampedString;
430
+ }
420
431
  }
432
+ }
433
+ // --- Update the state if the final value is different from current input
434
+ if (finalValue !== currentInputValue) {
421
435
  updateValue(finalValue, finalValue);
422
436
  }
423
437
  else {
@@ -425,7 +439,7 @@ exports.NumberBox = (0, react_1.forwardRef)(function NumberBox(_a, forwardedRef)
425
439
  setValueStrRep((0, numberbox_abstractions_1.mapToRepresentation)(value));
426
440
  }
427
441
  onBlur === null || onBlur === void 0 ? void 0 : onBlur();
428
- }, [value, onBlur, integersOnly, updateValue]);
442
+ }, [value, onBlur, integersOnly, updateValue, min, max]);
429
443
  const focus = (0, react_1.useCallback)(() => {
430
444
  var _a;
431
445
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
@@ -25,6 +25,8 @@ exports.OptionMd = (0, metadata_helpers_1.createMetadata)({
25
25
  valueType: "boolean",
26
26
  defaultValue: OptionNative_1.defaultProps.enabled,
27
27
  },
28
+ keywords: (0, metadata_helpers_1.d)("An array of keywords that can be used for searching and filtering the option. " +
29
+ "These keywords are not displayed but help users find the option through search."),
28
30
  },
29
31
  });
30
32
  exports.optionComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.OptionMd, ({ node, extractValue, className, renderChild, layoutContext }) => {
@@ -36,7 +38,7 @@ exports.optionComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP,
36
38
  }
37
39
  const hasTextNodeChild = ((_a = node.children) === null || _a === void 0 ? void 0 : _a.length) === 1 && (node.children[0].type === "TextNode" || node.children[0].type === "TextNodeCData");
38
40
  const textNodeChild = hasTextNodeChild ? renderChild(node.children) : undefined;
39
- return ((0, jsx_runtime_1.jsx)(OptionNative_1.OptionNative, { label: label || textNodeChild, value: value !== undefined && value !== "" ? value : label, enabled: extractValue.asOptionalBoolean(node.props.enabled), className: className, optionRenderer: ((_b = node.children) === null || _b === void 0 ? void 0 : _b.length) > 0
41
+ return ((0, jsx_runtime_1.jsx)(OptionNative_1.OptionNative, { label: label || textNodeChild, value: value !== undefined && value !== "" ? value : label, enabled: extractValue.asOptionalBoolean(node.props.enabled), keywords: extractValue.asOptionalStringArray(node.props.keywords), className: className, optionRenderer: ((_b = node.children) === null || _b === void 0 ? void 0 : _b.length) > 0
40
42
  ? !hasTextNodeChild ? (contextVars) => ((0, jsx_runtime_1.jsx)(container_helpers_1.MemoizedItem, { node: node.children, renderChild: renderChild, contextVars: contextVars, layoutContext: layoutContext })) : undefined
41
43
  : undefined, children: !hasTextNodeChild && renderChild(node.children) }));
42
44
  });
@@ -10,7 +10,7 @@ function HiddenOption(option) {
10
10
  const [node, setNode] = (0, react_1.useState)(null);
11
11
  const opt = (0, react_1.useMemo)(() => {
12
12
  var _a, _b;
13
- return Object.assign(Object.assign({}, option), { label: (_a = label !== null && label !== void 0 ? label : node === null || node === void 0 ? void 0 : node.textContent) !== null && _a !== void 0 ? _a : "", keywords: [(_b = label !== null && label !== void 0 ? label : node === null || node === void 0 ? void 0 : node.textContent) !== null && _b !== void 0 ? _b : ""] });
13
+ return Object.assign(Object.assign({}, option), { label: (_a = label !== null && label !== void 0 ? label : node === null || node === void 0 ? void 0 : node.textContent) !== null && _a !== void 0 ? _a : "", keywords: option.keywords || [(_b = label !== null && label !== void 0 ? label : node === null || node === void 0 ? void 0 : node.textContent) !== null && _b !== void 0 ? _b : ""] });
14
14
  }, [option, node, label]);
15
15
  (0, react_1.useEffect)(() => {
16
16
  onOptionAdd(opt);
@@ -55,10 +55,12 @@ exports.StandaloneSelectionStore = StandaloneSelectionStore;
55
55
  const SelectionStore = ({ updateState = lodash_es_1.noop, idKey = exports.defaultProps.idKey, children, selectedItems = exports.defaultProps.selectedItems, registerComponentApi = lodash_es_1.noop, }) => {
56
56
  const [items, setItems] = (0, react_1.useState)(selectedItems);
57
57
  const valueInitializedRef = (0, react_1.useRef)(false);
58
+ const currentItemsRef = (0, react_1.useRef)(selectedItems);
58
59
  const refreshSelection = (0, misc_1.useEvent)((allItems = constants_1.EMPTY_ARRAY) => {
59
60
  const safeAllItems = allItems || constants_1.EMPTY_ARRAY;
60
61
  const safeSelectedItems = selectedItems || constants_1.EMPTY_ARRAY;
61
62
  setItems(safeAllItems);
63
+ currentItemsRef.current = safeAllItems; // Update the ref synchronously
62
64
  let value = safeAllItems.filter((item) => !!safeSelectedItems.find((si) => si && item && si[idKey] === item[idKey]));
63
65
  if (!(0, lodash_es_1.isEqual)(safeSelectedItems, value) || !valueInitializedRef.current) {
64
66
  valueInitializedRef.current = true;
@@ -68,7 +70,7 @@ const SelectionStore = ({ updateState = lodash_es_1.noop, idKey = exports.defaul
68
70
  }
69
71
  });
70
72
  const setSelectedRowIds = (0, misc_1.useEvent)((rowIds) => {
71
- const safeItems = items || constants_1.EMPTY_ARRAY;
73
+ const safeItems = currentItemsRef.current || constants_1.EMPTY_ARRAY; // Use ref instead of state
72
74
  updateState({ value: safeItems.filter((item) => rowIds.includes(item[idKey])) });
73
75
  });
74
76
  const clearSelection = (0, misc_1.useEvent)(() => {
@@ -141,29 +141,62 @@ fixtures_1.test.describe("Basic Functionality", () => {
141
141
  yield slider.press("ArrowRight");
142
142
  yield (0, fixtures_1.expect)(page.getByTestId("slider-value")).toHaveText("0.1");
143
143
  }));
144
- fixtures_1.test.fixme("minStepsBetweenThumbs maintains thumb separation", component_test_helpers_1.SKIP_REASON.TO_BE_IMPLEMENTED("Need to test actual thumb separation logic"), (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
145
- yield initTestBed(`<Slider initialValue="{[2, 7]}" minStepsBetweenThumbs="5" />`);
146
- // Test that thumbs maintain minimum separation
147
- yield (0, fixtures_1.expect)(page.getByRole("slider")).toBeVisible();
144
+ (0, fixtures_1.test)("component handles multiple thumbs", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
145
+ yield initTestBed(`<Slider initialValue="{[2, 4]}" />`);
146
+ const thumbs = page.getByRole("slider");
147
+ yield (0, fixtures_1.expect)(thumbs).toHaveCount(2);
148
+ }));
149
+ (0, fixtures_1.test)("all thumbs are interactable via mouse", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createSliderDriver, page, }) {
150
+ yield initTestBed(`
151
+ <Fragment>
152
+ <Slider id="slider" initialValue="{[2, 4]}" minValue="0" maxValue="10" />
153
+ <Text testId="sliderValue0">{slider.value[0]}</Text>
154
+ <Text testId="sliderValue1">{slider.value[1]}</Text>
155
+ </Fragment>
156
+ `);
157
+ const driver = yield createSliderDriver("slider");
158
+ yield driver.dragThumbByMouse("start", 0);
159
+ yield driver.dragThumbByMouse("end", 1);
160
+ yield (0, fixtures_1.expect)(page.getByTestId("sliderValue0")).toHaveText("0");
161
+ yield (0, fixtures_1.expect)(page.getByTestId("sliderValue1")).toHaveText("10");
162
+ }));
163
+ (0, fixtures_1.test)("all thumbs are interactable via keyboard", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createSliderDriver, page }) {
164
+ yield initTestBed(`
165
+ <Fragment>
166
+ <Slider id="slider" initialValue="{[2, 4]}" minValue="0" maxValue="10" />
167
+ <Text testId="sliderValue0">{slider.value[0]}</Text>
168
+ <Text testId="sliderValue1">{slider.value[1]}</Text>
169
+ </Fragment>
170
+ `);
171
+ const driver = yield createSliderDriver("slider");
172
+ yield driver.stepThumbByKeyboard("ArrowLeft", 0);
173
+ yield driver.stepThumbByKeyboard("ArrowRight", 1);
174
+ yield (0, fixtures_1.expect)(page.getByTestId("sliderValue0")).toHaveText("1");
175
+ yield (0, fixtures_1.expect)(page.getByTestId("sliderValue1")).toHaveText("5");
176
+ }));
177
+ (0, fixtures_1.test)("minStepsBetweenThumbs maintains thumb separation", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, createSliderDriver, page }) {
178
+ yield initTestBed(`
179
+ <Fragment>
180
+ <Slider id="slider" initialValue="{[0, 5]}" minStepsBetweenThumbs="3" minValue="0" maxValue="10" />
181
+ <Text testId="sliderValue1">{slider.value[1]}</Text>
182
+ </Fragment>
183
+ `);
184
+ const driver = yield createSliderDriver("slider");
185
+ yield driver.stepThumbByKeyboard("ArrowLeft", 1, 3); // Try to move left by 3 steps
186
+ yield (0, fixtures_1.expect)(page.getByTestId("sliderValue1")).toHaveText("3");
148
187
  }));
149
188
  (0, fixtures_1.test)("enabled=false disables control", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
150
189
  yield initTestBed(`<Slider enabled="false" />`);
151
190
  yield (0, fixtures_1.expect)(page.getByRole("slider")).toBeDisabled();
152
191
  }));
153
- (0, fixtures_1.test)("readOnly prevents interaction", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
192
+ (0, fixtures_1.test)("readOnly prevents interaction", (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page, createSliderDriver }) {
154
193
  yield initTestBed(`
155
194
  <Fragment>
156
195
  <Slider id="mySlider" readOnly="true" />
157
196
  <Text testId="slider-value" value="{mySlider.value}" />
158
197
  </Fragment>`);
159
- const slider = page.getByRole("slider");
160
- const sliderOffsetWidth = yield page.locator("[data-track]").evaluate((el) => {
161
- return el.getBoundingClientRect().width;
162
- });
163
- yield slider.hover();
164
- yield page.mouse.down({ button: "left" });
165
- yield page.mouse.move(sliderOffsetWidth, 0); // Attempt to drag to end
166
- yield page.mouse.up();
198
+ const driver = yield createSliderDriver("mySlider");
199
+ yield driver.dragThumbByMouse("end");
167
200
  yield (0, fixtures_1.expect)(page.getByTestId("slider-value")).toHaveText("0"); // Value should remain unchanged
168
201
  }));
169
202
  fixtures_1.test.fixme("autoFocus focuses slider on mount", component_test_helpers_1.SKIP_REASON.XMLUI_BUG("autoFocus does not seem to work with radix-ui, need to double-check"), (_a) => __awaiter(void 0, [_a], void 0, function* ({ initTestBed, page }) {
@@ -108,7 +108,7 @@ exports.Slider = (0, react_2.forwardRef)((_a, forwardedRef) => {
108
108
  id = id || _id;
109
109
  const inputRef = (0, react_1.useRef)(null);
110
110
  const tooltipRef = (0, react_1.useRef)(null);
111
- const thumbRef = (0, react_1.useRef)(null);
111
+ const thumbsRef = (0, react_1.useRef)([]);
112
112
  min = parseValue(min, exports.defaultProps.min);
113
113
  max = parseValue(max, exports.defaultProps.max);
114
114
  // Initialize localValue properly
@@ -181,18 +181,22 @@ exports.Slider = (0, react_2.forwardRef)((_a, forwardedRef) => {
181
181
  }, [updateValue, readOnly]);
182
182
  // Component APIs
183
183
  const handleOnFocus = (0, react_1.useCallback)((ev) => {
184
- var _a;
185
- (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
184
+ onShowTooltip();
186
185
  onFocus === null || onFocus === void 0 ? void 0 : onFocus(ev);
187
- }, [onFocus]);
186
+ }, [onFocus, onShowTooltip]);
188
187
  const handleOnBlur = (0, react_1.useCallback)((ev) => {
189
- var _a;
190
- (_a = thumbRef.current) === null || _a === void 0 ? void 0 : _a.focus();
191
188
  onBlur === null || onBlur === void 0 ? void 0 : onBlur(ev);
192
189
  }, [onBlur]);
193
190
  const focus = (0, react_1.useCallback)(() => {
194
191
  var _a;
195
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
192
+ // Focus the first available thumb
193
+ const firstThumb = thumbsRef.current.find(thumb => thumb !== null);
194
+ if (firstThumb) {
195
+ firstThumb.focus();
196
+ }
197
+ else {
198
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
199
+ }
196
200
  }, []);
197
201
  const setValue = (0, misc_1.useEvent)((newValue) => {
198
202
  if (readOnly || !enabled) {
@@ -210,6 +214,10 @@ exports.Slider = (0, react_2.forwardRef)((_a, forwardedRef) => {
210
214
  }, [focus, registerComponentApi, setValue]);
211
215
  // Ensure we always have at least one thumb
212
216
  const displayValue = localValue.length > 0 ? localValue : formatValue(undefined, min, min, max);
217
+ // Clean up thumbs ref array when number of thumbs changes
218
+ (0, react_1.useEffect)(() => {
219
+ thumbsRef.current = thumbsRef.current.slice(0, displayValue.length);
220
+ }, [displayValue.length]);
213
221
  return ((0, jsx_runtime_1.jsx)(ItemWithLabel_1.ItemWithLabel, Object.assign({}, rest, { labelPosition: labelPosition, label: label, labelWidth: labelWidth, labelBreak: labelBreak, required: required, enabled: enabled, onFocus: onFocus, onBlur: onBlur, style: style, className: className, ref: forwardedRef, id: id, isInputTemplateUsed: true, children: (0, jsx_runtime_1.jsx)("div", { className: Slider_module_scss_1.default.sliderContainer, "data-slider-container": true, children: (0, jsx_runtime_1.jsxs)(react_slider_1.Root, { minStepsBetweenThumbs: minStepsBetweenThumbs, ref: inputRef, tabIndex: tabIndex, "aria-readonly": readOnly, className: (0, classnames_1.default)(className, Slider_module_scss_1.default.sliderRoot, {
214
222
  [Slider_module_scss_1.default.disabled]: !enabled,
215
223
  [Slider_module_scss_1.default.readOnly]: readOnly,
@@ -221,8 +229,10 @@ exports.Slider = (0, react_2.forwardRef)((_a, forwardedRef) => {
221
229
  [Slider_module_scss_1.default.valid]: validationStatus === "valid",
222
230
  }), style: rangeStyle ? Object.assign({}, rangeStyle) : undefined, children: (0, jsx_runtime_1.jsx)(react_slider_1.Range, { "data-range": true, className: (0, classnames_1.default)(Slider_module_scss_1.default.sliderRange, {
223
231
  [Slider_module_scss_1.default.disabled]: !enabled,
224
- }) }) }), displayValue.map((_, index) => ((0, jsx_runtime_1.jsx)(TooltipNative_1.Tooltip, { ref: tooltipRef, text: valueFormat(displayValue[index]), delayDuration: 100, open: showValues && showTooltip, children: (0, jsx_runtime_1.jsx)(react_slider_1.Thumb, { ref: index === 0 ? thumbRef : null, className: (0, classnames_1.default)(Slider_module_scss_1.default.sliderThumb, {
232
+ }) }) }), displayValue.map((_, index) => ((0, jsx_runtime_1.jsx)(TooltipNative_1.Tooltip, { ref: tooltipRef, text: valueFormat(displayValue[index]), delayDuration: 100, open: showValues && showTooltip, children: (0, jsx_runtime_1.jsx)(react_slider_1.Thumb, { id: id, "aria-required": required, ref: (el) => {
233
+ thumbsRef.current[index] = el;
234
+ }, className: (0, classnames_1.default)(Slider_module_scss_1.default.sliderThumb, {
225
235
  [Slider_module_scss_1.default.disabled]: !enabled,
226
- }), style: thumbStyle ? Object.assign({}, thumbStyle) : undefined, id: id, "data-thumb-index": index, autoFocus: autoFocus, "aria-required": required }) }, index)))] }) }) })));
236
+ }), style: thumbStyle ? Object.assign({}, thumbStyle) : undefined, "data-thumb-index": index, autoFocus: autoFocus && index === 0 }) }, index)))] }) }) })));
227
237
  });
228
238
  exports.Slider.displayName = "Slider";
@@ -37,6 +37,12 @@ exports.TableMd = (0, metadata_helpers_1.createMetadata)({
37
37
  `property is useful when data is loaded conditionally or receiving it takes some time.`),
38
38
  headerHeight: (0, metadata_helpers_1.d)(`This optional property is used to specify the height of the table header.`),
39
39
  rowsSelectable: (0, metadata_helpers_1.d)(`Indicates whether the rows are selectable (\`true\`) or not (\`false\`).`),
40
+ initiallySelected: (0, metadata_helpers_1.d)(`An array of IDs that should be initially selected when the table is rendered. ` +
41
+ `This property only has an effect when the rowsSelectable property is set to \`true\`.`),
42
+ syncWithAppState: (0, metadata_helpers_1.d)(`An AppState instance to synchronize the table's selection state with. The table will ` +
43
+ `read from and write to the 'selectedIds' property of the AppState object. When provided, ` +
44
+ `this takes precedence over the initiallySelected property for initial selection. ` +
45
+ `You can use the AppState's didUpdate event to receive notifications when the selection changes.`),
40
46
  pageSize: (0, metadata_helpers_1.d)(`This property defines the number of rows to display per page when pagination is enabled.`),
41
47
  pageSizeOptions: {
42
48
  description: "This property holds an array of page sizes (numbers) the user can select for " +
@@ -240,7 +246,7 @@ const TableWithColumns = (0, react_1.memo)((0, react_1.forwardRef)(({ extractVal
240
246
  const tableContent = ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(TableContext_1.TableContext.Provider, { value: tableContextValue, children: renderChild(node.children) }, tableKey), (0, jsx_runtime_1.jsx)(TableContext_1.TableContext.Provider, { value: columnRefresherContextValue, children: renderChild(node.children) }), (0, jsx_runtime_1.jsx)(TableNative_1.Table, { className: className, ref: ref, data: data, columns: columns, pageSizeOptions: extractValue(node.props.pageSizeOptions), pageSize: extractValue.asOptionalNumber(node.props.pageSize), rowsSelectable: extractValue.asOptionalBoolean(node.props.rowsSelectable), registerComponentApi: registerComponentApi, noDataRenderer: node.props.noDataTemplate &&
241
247
  (() => {
242
248
  return renderChild(node.props.noDataTemplate);
243
- }), hideNoDataView: node.props.noDataTemplate === null || node.props.noDataTemplate === "", loading: extractValue.asOptionalBoolean(node.props.loading), isPaginated: extractValue.asOptionalBoolean((_a = node.props) === null || _a === void 0 ? void 0 : _a.isPaginated), headerHeight: extractValue.asSize(node.props.headerHeight), rowDisabledPredicate: lookupSyncCallback(node.props.rowDisabledPredicate), sortBy: extractValue((_b = node.props) === null || _b === void 0 ? void 0 : _b.sortBy), sortingDirection: extractValue((_c = node.props) === null || _c === void 0 ? void 0 : _c.sortDirection), iconSortAsc: extractValue.asOptionalString((_d = node.props) === null || _d === void 0 ? void 0 : _d.iconSortAsc), iconSortDesc: extractValue.asOptionalString((_e = node.props) === null || _e === void 0 ? void 0 : _e.iconSortDesc), iconNoSort: extractValue.asOptionalString((_f = node.props) === null || _f === void 0 ? void 0 : _f.iconNoSort), sortingDidChange: lookupEventHandler("sortingDidChange"), onSelectionDidChange: lookupEventHandler("selectionDidChange"), willSort: lookupEventHandler("willSort"), uid: node.uid, autoFocus: extractValue.asOptionalBoolean(node.props.autoFocus), hideHeader: extractValue.asOptionalBoolean(node.props.hideHeader), enableMultiRowSelection: extractValue.asOptionalBoolean(node.props.enableMultiRowSelection), alwaysShowSelectionHeader: extractValue.asOptionalBoolean(node.props.alwaysShowSelectionHeader), noBottomBorder: extractValue.asOptionalBoolean(node.props.noBottomBorder), paginationControlsLocation: extractValue.asOptionalString(node.props.paginationControlsLocation), cellVerticalAlign: extractValue.asOptionalString(node.props.cellVerticalAlign), buttonRowPosition: extractValue.asOptionalString(node.props.buttonRowPosition), pageSizeSelectorPosition: extractValue.asOptionalString(node.props.pageSizeSelectorPosition), pageInfoPosition: extractValue.asOptionalString(node.props.pageInfoPosition), showCurrentPage: extractValue.asOptionalBoolean(node.props.showCurrentPage), showPageInfo: extractValue.asOptionalBoolean(node.props.showPageInfo), showPageSizeSelector: extractValue.asOptionalBoolean(node.props.showPageSizeSelector) })] }));
249
+ }), hideNoDataView: node.props.noDataTemplate === null || node.props.noDataTemplate === "", loading: extractValue.asOptionalBoolean(node.props.loading), isPaginated: extractValue.asOptionalBoolean((_a = node.props) === null || _a === void 0 ? void 0 : _a.isPaginated), headerHeight: extractValue.asSize(node.props.headerHeight), rowDisabledPredicate: lookupSyncCallback(node.props.rowDisabledPredicate), sortBy: extractValue((_b = node.props) === null || _b === void 0 ? void 0 : _b.sortBy), sortingDirection: extractValue((_c = node.props) === null || _c === void 0 ? void 0 : _c.sortDirection), iconSortAsc: extractValue.asOptionalString((_d = node.props) === null || _d === void 0 ? void 0 : _d.iconSortAsc), iconSortDesc: extractValue.asOptionalString((_e = node.props) === null || _e === void 0 ? void 0 : _e.iconSortDesc), iconNoSort: extractValue.asOptionalString((_f = node.props) === null || _f === void 0 ? void 0 : _f.iconNoSort), sortingDidChange: lookupEventHandler("sortingDidChange"), onSelectionDidChange: lookupEventHandler("selectionDidChange"), willSort: lookupEventHandler("willSort"), uid: node.uid, autoFocus: extractValue.asOptionalBoolean(node.props.autoFocus), hideHeader: extractValue.asOptionalBoolean(node.props.hideHeader), enableMultiRowSelection: extractValue.asOptionalBoolean(node.props.enableMultiRowSelection), alwaysShowSelectionHeader: extractValue.asOptionalBoolean(node.props.alwaysShowSelectionHeader), noBottomBorder: extractValue.asOptionalBoolean(node.props.noBottomBorder), paginationControlsLocation: extractValue.asOptionalString(node.props.paginationControlsLocation), cellVerticalAlign: extractValue.asOptionalString(node.props.cellVerticalAlign), buttonRowPosition: extractValue.asOptionalString(node.props.buttonRowPosition), pageSizeSelectorPosition: extractValue.asOptionalString(node.props.pageSizeSelectorPosition), pageInfoPosition: extractValue.asOptionalString(node.props.pageInfoPosition), showCurrentPage: extractValue.asOptionalBoolean(node.props.showCurrentPage), showPageInfo: extractValue.asOptionalBoolean(node.props.showPageInfo), showPageSizeSelector: extractValue.asOptionalBoolean(node.props.showPageSizeSelector), initiallySelected: extractValue(node.props.initiallySelected), syncWithAppState: extractValue(node.props.syncWithAppState) })] }));
244
250
  if (selectionContext === null) {
245
251
  return (0, jsx_runtime_1.jsx)(SelectionStoreNative_1.StandaloneSelectionStore, { children: tableContent });
246
252
  }
@@ -74,7 +74,7 @@ const getCommonPinningStyles = (column) => {
74
74
  // eslint-disable-next-line react/display-name
75
75
  exports.Table = (0, react_1.forwardRef)((_a, forwardedRef) => {
76
76
  var _b, _c, _d, _e, _f;
77
- var { data = exports.defaultProps.data, columns = exports.defaultProps.columns, isPaginated = exports.defaultProps.isPaginated, loading = exports.defaultProps.loading, headerHeight, rowsSelectable = exports.defaultProps.rowsSelectable, enableMultiRowSelection = exports.defaultProps.enableMultiRowSelection, pageSizeOptions = exports.defaultProps.pageSizeOptions, pageSize = (pageSizeOptions === null || pageSizeOptions === void 0 ? void 0 : pageSizeOptions[0]) || DEFAULT_PAGE_SIZES[0], currentPageIndex = 0, rowDisabledPredicate = defaultIsRowDisabled, sortBy, sortingDirection = exports.defaultProps.sortingDirection, iconSortAsc, iconSortDesc, iconNoSort, sortingDidChange, willSort, style, className, noDataRenderer, autoFocus = exports.defaultProps.autoFocus, hideHeader = exports.defaultProps.hideHeader, hideNoDataView = exports.defaultProps.hideNoDataView, alwaysShowSelectionHeader = exports.defaultProps.alwaysShowSelectionHeader, registerComponentApi, onSelectionDidChange, noBottomBorder = exports.defaultProps.noBottomBorder, paginationControlsLocation = exports.defaultProps.paginationControlsLocation, cellVerticalAlign = exports.defaultProps.cellVerticalAlign, buttonRowPosition = exports.defaultProps.buttonRowPosition, pageSizeSelectorPosition = exports.defaultProps.pageSizeSelectorPosition, pageInfoPosition = exports.defaultProps.pageInfoPosition, showCurrentPage = exports.defaultProps.showCurrentPage, showPageInfo = exports.defaultProps.showPageInfo, showPageSizeSelector = exports.defaultProps.showPageSizeSelector } = _a, rest = __rest(_a, ["data", "columns", "isPaginated", "loading", "headerHeight", "rowsSelectable", "enableMultiRowSelection", "pageSizeOptions", "pageSize", "currentPageIndex", "rowDisabledPredicate", "sortBy", "sortingDirection", "iconSortAsc", "iconSortDesc", "iconNoSort", "sortingDidChange", "willSort", "style", "className", "noDataRenderer", "autoFocus", "hideHeader", "hideNoDataView", "alwaysShowSelectionHeader", "registerComponentApi", "onSelectionDidChange", "noBottomBorder", "paginationControlsLocation", "cellVerticalAlign", "buttonRowPosition", "pageSizeSelectorPosition", "pageInfoPosition", "showCurrentPage", "showPageInfo", "showPageSizeSelector"])
77
+ var { data = exports.defaultProps.data, columns = exports.defaultProps.columns, isPaginated = exports.defaultProps.isPaginated, loading = exports.defaultProps.loading, headerHeight, rowsSelectable = exports.defaultProps.rowsSelectable, enableMultiRowSelection = exports.defaultProps.enableMultiRowSelection, initiallySelected = exports.defaultProps.initiallySelected, syncWithAppState, pageSizeOptions = exports.defaultProps.pageSizeOptions, pageSize = (pageSizeOptions === null || pageSizeOptions === void 0 ? void 0 : pageSizeOptions[0]) || DEFAULT_PAGE_SIZES[0], currentPageIndex = 0, rowDisabledPredicate = defaultIsRowDisabled, sortBy, sortingDirection = exports.defaultProps.sortingDirection, iconSortAsc, iconSortDesc, iconNoSort, sortingDidChange, willSort, style, className, noDataRenderer, autoFocus = exports.defaultProps.autoFocus, hideHeader = exports.defaultProps.hideHeader, hideNoDataView = exports.defaultProps.hideNoDataView, alwaysShowSelectionHeader = exports.defaultProps.alwaysShowSelectionHeader, registerComponentApi, onSelectionDidChange, noBottomBorder = exports.defaultProps.noBottomBorder, paginationControlsLocation = exports.defaultProps.paginationControlsLocation, cellVerticalAlign = exports.defaultProps.cellVerticalAlign, buttonRowPosition = exports.defaultProps.buttonRowPosition, pageSizeSelectorPosition = exports.defaultProps.pageSizeSelectorPosition, pageInfoPosition = exports.defaultProps.pageInfoPosition, showCurrentPage = exports.defaultProps.showCurrentPage, showPageInfo = exports.defaultProps.showPageInfo, showPageSizeSelector = exports.defaultProps.showPageSizeSelector } = _a, rest = __rest(_a, ["data", "columns", "isPaginated", "loading", "headerHeight", "rowsSelectable", "enableMultiRowSelection", "initiallySelected", "syncWithAppState", "pageSizeOptions", "pageSize", "currentPageIndex", "rowDisabledPredicate", "sortBy", "sortingDirection", "iconSortAsc", "iconSortDesc", "iconNoSort", "sortingDidChange", "willSort", "style", "className", "noDataRenderer", "autoFocus", "hideHeader", "hideNoDataView", "alwaysShowSelectionHeader", "registerComponentApi", "onSelectionDidChange", "noBottomBorder", "paginationControlsLocation", "cellVerticalAlign", "buttonRowPosition", "pageSizeSelectorPosition", "pageInfoPosition", "showCurrentPage", "showPageInfo", "showPageSizeSelector"])
78
78
  // cols
79
79
  ;
80
80
  const { getThemeVar } = (0, ThemeContext_1.useTheme)();
@@ -107,6 +107,8 @@ exports.Table = (0, react_1.forwardRef)((_a, forwardedRef) => {
107
107
  enableMultiRowSelection,
108
108
  rowDisabledPredicate,
109
109
  onSelectionDidChange,
110
+ initiallySelected,
111
+ syncWithAppState,
110
112
  });
111
113
  // --- Create data with order information whenever the items in the table change
112
114
  const dataWithOrder = (0, react_1.useMemo)(() => {
@@ -492,6 +494,7 @@ exports.defaultProps = {
492
494
  loading: false,
493
495
  rowsSelectable: false,
494
496
  enableMultiRowSelection: true,
497
+ initiallySelected: constants_1.EMPTY_ARRAY,
495
498
  pageSizeOptions: [5, 10, 15],
496
499
  sortingDirection: "ascending",
497
500
  autoFocus: false,
@@ -7,7 +7,76 @@ const misc_1 = require("../../components-core/utils/misc");
7
7
  const constants_1 = require("../../components-core/constants");
8
8
  const hooks_1 = require("../../components-core/utils/hooks");
9
9
  const SelectionStoreNative_1 = require("../SelectionStore/SelectionStoreNative");
10
- function useRowSelection({ items = constants_1.EMPTY_ARRAY, visibleItems = items, rowsSelectable, enableMultiRowSelection, rowDisabledPredicate, onSelectionDidChange, }) {
10
+ /**
11
+ * Hook for managing table row selection with optional bidirectional AppState synchronization.
12
+ *
13
+ * ## AppState Synchronization Mechanism
14
+ *
15
+ * When `syncWithAppState` is provided, this hook implements a robust bidirectional synchronization
16
+ * between the table's selection state and an AppState instance. The synchronization prevents
17
+ * infinite loops using a state machine approach.
18
+ *
19
+ * ### State Machine Design
20
+ *
21
+ * The sync operates through three states:
22
+ * - `idle`: Normal state, ready to respond to changes from either side
23
+ * - `updating_to_appstate`: Currently propagating table selection → AppState (blocks AppState → table)
24
+ * - `updating_from_appstate`: Currently propagating AppState → table selection (blocks table → AppState)
25
+ *
26
+ * ### Synchronization Flow
27
+ *
28
+ * **AppState → Table (External Updates)**:
29
+ * 1. AppState.value.selectedIds changes externally (e.g., from another component)
30
+ * 2. Effect detects change and validates it's not from our own update (using source tracking)
31
+ * 3. Sets state to `updating_from_appstate` to block reverse sync
32
+ * 4. Updates table selection via setSelectedRowIds()
33
+ * 5. Uses requestAnimationFrame to reset state to `idle` after update completes
34
+ *
35
+ * **Table → AppState (User Interaction)**:
36
+ * 1. User interacts with table (clicks, keyboard navigation)
37
+ * 2. selectedItems changes through normal table selection logic
38
+ * 3. Effect detects change and validates it's different from AppState
39
+ * 4. Sets state to `updating_to_appstate` to block reverse sync
40
+ * 5. Calls syncWithAppState.update({ selectedIds: [...] })
41
+ * 6. Uses requestAnimationFrame to reset state to `idle` after update completes
42
+ *
43
+ * ### Loop Prevention Strategy
44
+ *
45
+ * Multiple mechanisms prevent infinite loops:
46
+ * - **State Machine**: Directional blocking prevents simultaneous updates
47
+ * - **Source Tracking**: lastUpdateSourceRef tracks whether the last change came from 'table' or 'appstate'
48
+ * - **Value Tracking**: lastAppStateSelectionRef and lastTableSelectionRef track last known values
49
+ * - **Change Detection**: Only triggers updates when values actually differ using JSON.stringify comparison
50
+ * - **Frame-Based Reset**: Uses requestAnimationFrame instead of setTimeout for deterministic timing
51
+ *
52
+ * ### Usage with AppState
53
+ *
54
+ * ```typescript
55
+ * // In your component
56
+ * const appState = useAppState('myBucket');
57
+ *
58
+ * // Pass to Table
59
+ * <Table
60
+ * items={data}
61
+ * syncWithAppState={appState}
62
+ * // ... other props
63
+ * />
64
+ *
65
+ * // AppState will contain: { selectedIds: ['id1', 'id2', ...] }
66
+ * // Changes from either side are automatically synchronized
67
+ * ```
68
+ *
69
+ * ### Precedence Rules
70
+ *
71
+ * - When both `initiallySelected` and `syncWithAppState` are provided, `syncWithAppState` takes precedence
72
+ * - Multi-row selection limits are respected (single selection truncates to first ID)
73
+ * - Only valid item IDs (present in current `items` array) are synchronized
74
+ *
75
+ * @param options Configuration object for row selection behavior
76
+ * @returns Row selection operations and state management interface
77
+ */
78
+ function useRowSelection({ items = constants_1.EMPTY_ARRAY, visibleItems = items, rowsSelectable, enableMultiRowSelection, rowDisabledPredicate, onSelectionDidChange, initiallySelected = constants_1.EMPTY_ARRAY, syncWithAppState, }) {
79
+ var _a;
11
80
  // --- The focused index in the row source (if there is any)
12
81
  const [focusedIndex, setFocusedIndex] = (0, react_1.useState)(-1);
13
82
  // --- The current selection interval
@@ -18,10 +87,155 @@ function useRowSelection({ items = constants_1.EMPTY_ARRAY, visibleItems = items
18
87
  const walkableList = (0, react_1.useMemo)(() => {
19
88
  return visibleItems.map((item) => item[idKey]);
20
89
  }, [idKey, visibleItems]);
90
+ // --- Track if initial selection has been applied
91
+ const [initialSelectionApplied, setInitialSelectionApplied] = (0, react_1.useState)(false);
21
92
  // --- If the items change, refresh the selectable items (if the rows are selectable)
22
93
  (0, react_1.useEffect)(() => {
23
94
  refreshSelection(rowsSelectable ? items : constants_1.EMPTY_ARRAY);
24
95
  }, [refreshSelection, items, rowsSelectable]);
96
+ // --- Handle AppState synchronization
97
+ // This implements bidirectional sync between Table selection and AppState.
98
+ // The approach uses React's useEffect pattern which is appropriate for React-to-React communication.
99
+ // The new AppState didUpdate event is more useful for non-React integrations.
100
+ const appStateSelection = (_a = syncWithAppState === null || syncWithAppState === void 0 ? void 0 : syncWithAppState.value) === null || _a === void 0 ? void 0 : _a.selectedIds;
101
+ const prevAppStateSelection = (0, hooks_1.usePrevious)(appStateSelection);
102
+ // --- State machine for sync direction control
103
+ const [syncState, setSyncState] = (0, react_1.useState)('idle');
104
+ // --- Use refs to track the last known selections to prevent update loops
105
+ const lastAppStateSelectionRef = (0, react_1.useRef)();
106
+ const lastTableSelectionRef = (0, react_1.useRef)();
107
+ // --- Track the source of the last update to prevent echoing
108
+ const lastUpdateSourceRef = (0, react_1.useRef)(null);
109
+ // --- Sync from AppState to table selection (when AppState changes externally)
110
+ (0, react_1.useEffect)(() => {
111
+ // Skip if not selectable, no sync, no selection, or we're currently updating to AppState
112
+ if (!rowsSelectable ||
113
+ !syncWithAppState ||
114
+ !appStateSelection ||
115
+ syncState === 'updating_to_appstate') {
116
+ return;
117
+ }
118
+ // Only update if AppState selection actually changed and this wasn't caused by our own table update
119
+ const appStateChanged = appStateSelection !== prevAppStateSelection;
120
+ const isDifferentFromLastKnown = JSON.stringify([...(appStateSelection || [])].sort()) !==
121
+ JSON.stringify([...(lastAppStateSelectionRef.current || [])].sort());
122
+ const wasNotOurUpdate = lastUpdateSourceRef.current !== 'table';
123
+ if (appStateChanged && isDifferentFromLastKnown && wasNotOurUpdate && items.length > 0) {
124
+ // Set state machine to indicate we're updating from AppState
125
+ setSyncState('updating_from_appstate');
126
+ const validIds = appStateSelection.filter((id) => items.some((item) => item[idKey] === id));
127
+ const idsToSelect = enableMultiRowSelection ? validIds : validIds.slice(0, 1);
128
+ // Track what we're setting to prevent loop
129
+ lastAppStateSelectionRef.current = [...appStateSelection];
130
+ lastTableSelectionRef.current = [...idsToSelect];
131
+ lastUpdateSourceRef.current = 'appstate';
132
+ setSelectedRowIds(idsToSelect);
133
+ setInitialSelectionApplied(true);
134
+ }
135
+ }, [
136
+ appStateSelection,
137
+ prevAppStateSelection,
138
+ items,
139
+ rowsSelectable,
140
+ syncWithAppState,
141
+ idKey,
142
+ enableMultiRowSelection,
143
+ setSelectedRowIds,
144
+ syncState,
145
+ ]);
146
+ // --- Sync from table selection to AppState (when user interacts with table)
147
+ (0, react_1.useEffect)(() => {
148
+ var _a;
149
+ // Skip if not selectable, no sync, or currently updating from AppState
150
+ if (!rowsSelectable ||
151
+ !syncWithAppState ||
152
+ syncState === 'updating_from_appstate') {
153
+ return;
154
+ }
155
+ const currentSelectionIds = selectedItems.map((item) => item[idKey]);
156
+ const appStateSelectionIds = appStateSelection || [];
157
+ // Only update if table selection is different from AppState, different from our last update, and wasn't caused by AppState
158
+ const tableChanged = JSON.stringify([...currentSelectionIds].sort()) !==
159
+ JSON.stringify([...(lastTableSelectionRef.current || [])].sort());
160
+ const isDifferentFromAppState = JSON.stringify([...currentSelectionIds].sort()) !==
161
+ JSON.stringify([...appStateSelectionIds].sort());
162
+ const wasNotAppStateUpdate = lastUpdateSourceRef.current !== 'appstate';
163
+ if (tableChanged && isDifferentFromAppState && wasNotAppStateUpdate) {
164
+ // Set state machine to indicate we're updating to AppState
165
+ setSyncState('updating_to_appstate');
166
+ // Track what we're updating to prevent loop
167
+ lastTableSelectionRef.current = [...currentSelectionIds];
168
+ lastAppStateSelectionRef.current = [...currentSelectionIds];
169
+ lastUpdateSourceRef.current = 'table';
170
+ (_a = syncWithAppState.update) === null || _a === void 0 ? void 0 : _a.call(syncWithAppState, { selectedIds: currentSelectionIds });
171
+ }
172
+ }, [
173
+ selectedItems,
174
+ syncWithAppState,
175
+ appStateSelection,
176
+ idKey,
177
+ rowsSelectable,
178
+ syncState,
179
+ ]);
180
+ // --- Reset sync state machine to idle when updates are complete
181
+ (0, react_1.useEffect)(() => {
182
+ if (syncState !== 'idle') {
183
+ // Reset to idle state in the next tick to allow the current update to complete
184
+ const resetTimer = requestAnimationFrame(() => {
185
+ setSyncState('idle');
186
+ });
187
+ return () => cancelAnimationFrame(resetTimer);
188
+ }
189
+ }, [syncState, appStateSelection, selectedItems]);
190
+ // --- Clear update source when sync state becomes idle
191
+ (0, react_1.useEffect)(() => {
192
+ if (syncState === 'idle') {
193
+ // Use a separate frame to clear the source after the sync state is reset
194
+ const clearTimer = requestAnimationFrame(() => {
195
+ lastUpdateSourceRef.current = null;
196
+ });
197
+ return () => cancelAnimationFrame(clearTimer);
198
+ }
199
+ }, [syncState]);
200
+ // --- Set initial selection when component mounts and items are available
201
+ // Use a separate effect that runs after the refresh to ensure timing is correct
202
+ (0, react_1.useEffect)(() => {
203
+ // If we have AppState sync, don't use initiallySelected
204
+ if (syncWithAppState) {
205
+ return;
206
+ }
207
+ if (!rowsSelectable ||
208
+ !initiallySelected ||
209
+ initiallySelected.length === 0 ||
210
+ initialSelectionApplied) {
211
+ return;
212
+ }
213
+ // Only set initial selection when items are available and we haven't applied it yet
214
+ if (items.length > 0) {
215
+ // Use requestAnimationFrame to ensure this runs after the refreshSelection effect
216
+ const frameId = requestAnimationFrame(() => {
217
+ // Filter initiallySelected to only include IDs that exist in current items
218
+ const validIds = initiallySelected.filter((id) => items.some((item) => item[idKey] === id));
219
+ if (validIds.length > 0) {
220
+ // If multi-row selection is disabled, only select the first valid ID
221
+ const idsToSelect = enableMultiRowSelection ? validIds : [validIds[0]];
222
+ setSelectedRowIds(idsToSelect);
223
+ setInitialSelectionApplied(true);
224
+ }
225
+ });
226
+ return () => cancelAnimationFrame(frameId);
227
+ }
228
+ }, [
229
+ items,
230
+ initiallySelected,
231
+ rowsSelectable,
232
+ idKey,
233
+ enableMultiRowSelection,
234
+ setSelectedRowIds,
235
+ initialSelectionApplied,
236
+ selectedItems,
237
+ syncWithAppState,
238
+ ]);
25
239
  // --- If the multi-row selection switches to disabled, keep only the first selected item
26
240
  const prevEnableMultiRowSelection = (0, hooks_1.usePrevious)(enableMultiRowSelection);
27
241
  (0, react_1.useEffect)(() => {
@@ -33,10 +33,6 @@ exports.TextBoxMd = (0, metadata_helpers_1.createMetadata)({
33
33
  props: {
34
34
  placeholder: (0, metadata_helpers_1.dPlaceholder)(),
35
35
  initialValue: Object.assign(Object.assign({}, (0, metadata_helpers_1.dInitialValue)()), { defaultValue: TextBoxNative_1.defaultProps.initialValue }),
36
- label: (0, metadata_helpers_1.dLabel)(),
37
- labelPosition: (0, metadata_helpers_1.dLabelPosition)("top"),
38
- labelWidth: (0, metadata_helpers_1.dLabelWidth)(COMP),
39
- labelBreak: (0, metadata_helpers_1.dLabelBreak)(COMP),
40
36
  maxLength: (0, metadata_helpers_1.dMaxLength)(),
41
37
  autoFocus: (0, metadata_helpers_1.dAutoFocus)(),
42
38
  required: (0, metadata_helpers_1.dRequired)(),
@@ -123,7 +119,7 @@ exports.TextBoxMd = (0, metadata_helpers_1.createMetadata)({
123
119
  function renderTextBox(className, state, updateState, extractValue, node, lookupEventHandler, registerComponentApi, type = "text") {
124
120
  // TODO: How can we use the gap from the className?
125
121
  //delete layoutCss.gap;
126
- return ((0, jsx_runtime_1.jsx)(TextBoxNative_1.TextBox, { type: type, className: className, value: state.value, updateState: updateState, initialValue: extractValue(node.props.initialValue), maxLength: extractValue(node.props.maxLength), 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"), registerComponentApi: registerComponentApi, startText: extractValue.asOptionalString(node.props.startText), startIcon: extractValue.asOptionalString(node.props.startIcon), endText: extractValue.asOptionalString(node.props.endText), endIcon: extractValue.asOptionalString(node.props.endIcon), gap: extractValue.asOptionalString(node.props.gap), autoFocus: extractValue.asOptionalBoolean(node.props.autoFocus), readOnly: extractValue.asOptionalBoolean(node.props.readOnly), label: extractValue.asOptionalString(node.props.label), labelPosition: extractValue(node.props.labelPosition), labelWidth: extractValue.asOptionalString(node.props.labelWidth), labelBreak: extractValue.asOptionalBoolean(node.props.labelBreak), required: extractValue.asOptionalBoolean(node.props.required), showPasswordToggle: extractValue.asOptionalBoolean(node.props.showPasswordToggle, false), passwordVisibleIcon: extractValue.asOptionalString(node.props.passwordVisibleIcon), passwordHiddenIcon: extractValue.asOptionalString(node.props.passwordHiddenIcon) }));
122
+ return ((0, jsx_runtime_1.jsx)(TextBoxNative_1.TextBox, { type: type, className: className, value: state.value, updateState: updateState, initialValue: extractValue(node.props.initialValue), maxLength: extractValue(node.props.maxLength), 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"), registerComponentApi: registerComponentApi, startText: extractValue.asOptionalString(node.props.startText), startIcon: extractValue.asOptionalString(node.props.startIcon), endText: extractValue.asOptionalString(node.props.endText), endIcon: extractValue.asOptionalString(node.props.endIcon), gap: extractValue.asOptionalString(node.props.gap), autoFocus: extractValue.asOptionalBoolean(node.props.autoFocus), readOnly: extractValue.asOptionalBoolean(node.props.readOnly), required: extractValue.asOptionalBoolean(node.props.required), showPasswordToggle: extractValue.asOptionalBoolean(node.props.showPasswordToggle, false), passwordVisibleIcon: extractValue.asOptionalString(node.props.passwordVisibleIcon), passwordHiddenIcon: extractValue.asOptionalString(node.props.passwordHiddenIcon) }));
127
123
  }
128
124
  exports.textBoxComponentRenderer = (0, renderers_1.createComponentRenderer)(COMP, exports.TextBoxMd, ({ node, state, updateState, lookupEventHandler, extractValue, className, registerComponentApi, }) => {
129
125
  return renderTextBox(className, state, updateState, extractValue, node, lookupEventHandler, registerComponentApi);