@aurodesignsystem-dev/auro-formkit 0.0.0-pr1395.0 → 0.0.0-pr1395.2

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 (44) hide show
  1. package/components/checkbox/demo/api.min.js +1 -1
  2. package/components/checkbox/demo/index.min.js +1 -1
  3. package/components/checkbox/dist/index.js +1 -1
  4. package/components/checkbox/dist/registered.js +1 -1
  5. package/components/combobox/demo/api.min.js +151 -40
  6. package/components/combobox/demo/index.min.js +151 -40
  7. package/components/combobox/dist/index.js +3 -3
  8. package/components/combobox/dist/registered.js +3 -3
  9. package/components/counter/demo/api.min.js +2 -2
  10. package/components/counter/demo/index.min.js +2 -2
  11. package/components/counter/dist/index.js +2 -2
  12. package/components/counter/dist/registered.js +2 -2
  13. package/components/datepicker/demo/api.min.js +74 -13
  14. package/components/datepicker/demo/index.min.js +74 -13
  15. package/components/datepicker/dist/datepickerKeyboardStrategy.d.ts +4 -0
  16. package/components/datepicker/dist/index.js +74 -13
  17. package/components/datepicker/dist/registered.js +74 -13
  18. package/components/dropdown/demo/api.min.js +1 -1
  19. package/components/dropdown/demo/index.min.js +1 -1
  20. package/components/dropdown/dist/index.js +1 -1
  21. package/components/dropdown/dist/registered.js +1 -1
  22. package/components/form/demo/api.min.js +240 -63
  23. package/components/form/demo/index.min.js +240 -63
  24. package/components/input/demo/api.min.js +1 -1
  25. package/components/input/demo/index.min.js +1 -1
  26. package/components/input/dist/index.js +1 -1
  27. package/components/input/dist/registered.js +1 -1
  28. package/components/menu/demo/api.md +1 -1
  29. package/components/menu/demo/api.min.js +148 -37
  30. package/components/menu/demo/index.min.js +148 -37
  31. package/components/menu/dist/auro-menu.context.d.ts +15 -3
  32. package/components/menu/dist/auro-menu.d.ts +1 -1
  33. package/components/menu/dist/index.js +148 -37
  34. package/components/menu/dist/registered.js +148 -37
  35. package/components/radio/demo/api.min.js +1 -1
  36. package/components/radio/demo/index.min.js +1 -1
  37. package/components/radio/dist/index.js +1 -1
  38. package/components/radio/dist/registered.js +1 -1
  39. package/components/select/demo/api.min.js +158 -42
  40. package/components/select/demo/index.min.js +158 -42
  41. package/components/select/dist/index.js +10 -5
  42. package/components/select/dist/registered.js +10 -5
  43. package/custom-elements.json +1514 -1429
  44. package/package.json +11 -4
@@ -59,6 +59,9 @@ export class MenuService {
59
59
  _subscribers: any[];
60
60
  internalUpdateInProgress: boolean;
61
61
  selectedOptions: any[];
62
+ _pendingValue: string | number | (string | number)[];
63
+ _pendingRetryScheduled: boolean;
64
+ _pendingRetryCount: number;
62
65
  /**
63
66
  * PROPERTY SYNCING
64
67
  */
@@ -149,6 +152,15 @@ export class MenuService {
149
152
  * @param {string|number|Array<string|number>} value - The value(s) to select.
150
153
  */
151
154
  selectByValue(value: string | number | Array<string | number>): void;
155
+ /**
156
+ * Queues a pending value and schedules a bounded retry.
157
+ * @param {string|number|Array<string|number>} value - The value to retry.
158
+ */
159
+ queuePendingValue(value: string | number | Array<string | number>): void;
160
+ /**
161
+ * Clears pending retry state.
162
+ */
163
+ clearPendingValue(): void;
152
164
  /**
153
165
  * Resets the selected options to an empty array.
154
166
  */
@@ -169,7 +181,7 @@ export class MenuService {
169
181
  /**
170
182
  * Stages an update to notify subscribers of state and value changes.
171
183
  */
172
- stageUpdate(): void;
184
+ stageUpdate(meta?: {}): void;
173
185
  /**
174
186
  * Notifies subscribers of a menu service event.
175
187
  * All notifications are sent to all subscribers.
@@ -179,11 +191,11 @@ export class MenuService {
179
191
  /**
180
192
  * Notifies subscribers of a state change (selected options has changed).
181
193
  */
182
- notifyStateChange(): void;
194
+ notifyStateChange(meta?: {}): void;
183
195
  /**
184
196
  * Notifies subscribers of a value change (current value has changed).
185
197
  */
186
- notifyValueChange(): void;
198
+ notifyValueChange(meta?: {}): void;
187
199
  /**
188
200
  * Dispatches a custom event from the host element.
189
201
  * @param {string} eventName
@@ -171,7 +171,7 @@ export class AuroMenu extends AuroElement {
171
171
  /**
172
172
  * @readonly
173
173
  * @returns {Array<HTMLElement>} - Returns the array of available menu options.
174
- * @deprecated use `options` property instead.
174
+ * @deprecated Use `options` property instead.
175
175
  */
176
176
  readonly get items(): Array<HTMLElement>;
177
177
  /**
@@ -532,10 +532,20 @@ class AuroMenuOption extends AuroElement {
532
532
  subscribe: true
533
533
  });
534
534
 
535
- // Establish the key property as early as possible
535
+ // Establish the key property as early as possible.
536
+ // When a framework (e.g. Svelte) inserts the element into the DOM before
537
+ // setting its `value` property, both `getAttribute('value')` and
538
+ // `getAttribute('key')` return null here. Setting `this.key = null`
539
+ // would block the fallback in `updated()` that assigns key from the
540
+ // value property (the guard checked `=== undefined`). Only assign key
541
+ // if at least one source attribute is actually present so that the
542
+ // `updated()` fallback can run when the value property arrives later.
536
543
  const valueAttr = this.getAttribute('value');
537
544
  const keyAttr = this.getAttribute('key');
538
- this.key = keyAttr !== null ? keyAttr : valueAttr;
545
+ const resolvedKey = keyAttr !== null ? keyAttr : valueAttr;
546
+ if (resolvedKey !== null) {
547
+ this.key = resolvedKey;
548
+ }
539
549
  }
540
550
 
541
551
  firstUpdated() {
@@ -585,8 +595,14 @@ class AuroMenuOption extends AuroElement {
585
595
  this.updateTextHighlight();
586
596
  }
587
597
 
588
- // Set the key to be the passed value if no key is provided
589
- if (changedProperties.has('value') && this.key === undefined) {
598
+ // Set the key to be the passed value if no key is provided.
599
+ // Loose equality (== null) is intentional: it catches both null AND
600
+ // undefined. When a framework (e.g. Svelte, React) inserts the element
601
+ // before setting its value property, connectedCallback skips key
602
+ // assignment because both attributes are null at that point. The Lit
603
+ // property default for `key` is undefined (not null), so strict
604
+ // === null would miss the case and the fallback would never run.
605
+ if (changedProperties.has('value') && this.key == null) { // eslint-disable-line eqeqeq, no-eq-null
590
606
  this.key = this.value;
591
607
  }
592
608
  }
@@ -955,6 +971,9 @@ class MenuService {
955
971
  this._subscribers = [];
956
972
  this.internalUpdateInProgress = false;
957
973
  this.selectedOptions = [];
974
+ this._pendingValue = null;
975
+ this._pendingRetryScheduled = false;
976
+ this._pendingRetryCount = 0;
958
977
  }
959
978
 
960
979
  /**
@@ -994,6 +1013,9 @@ class MenuService {
994
1013
  hostDisconnected() {
995
1014
  this._subscribers = [];
996
1015
  this._menuOptions = [];
1016
+ this._pendingValue = null;
1017
+ this._pendingRetryScheduled = false;
1018
+ this._pendingRetryCount = 0;
997
1019
  }
998
1020
 
999
1021
  /**
@@ -1196,17 +1218,22 @@ class MenuService {
1196
1218
  * @param {string|number|Array<string|number>} value - The value(s) to select.
1197
1219
  */
1198
1220
  selectByValue(value) {
1199
- // Early exit for invalid/empty values or internal updates
1200
- if (this.internalUpdateInProgress ||
1201
- this.host.internalUpdateInProgress ||
1202
- value === undefined ||
1221
+ const isEmptyValue = value === undefined ||
1203
1222
  value === null ||
1204
1223
  (Array.isArray(value) && value.length === 0) ||
1205
- (typeof value === 'string' && value.trim() === '')) {
1224
+ (typeof value === 'string' && value.trim() === '');
1225
+
1226
+ // Early exit for invalid/empty values
1227
+ if (isEmptyValue) {
1206
1228
  return;
1207
1229
  }
1208
1230
 
1209
- this.reset();
1231
+ // If an internal update cycle is still in progress, defer value application
1232
+ // rather than dropping it.
1233
+ if (this.internalUpdateInProgress || this.host.internalUpdateInProgress) {
1234
+ this.queuePendingValue(value);
1235
+ return;
1236
+ }
1210
1237
 
1211
1238
  // Normalize values to array of strings
1212
1239
  const normalizedValues = this._getNormalizedValues(value);
@@ -1218,33 +1245,100 @@ class MenuService {
1218
1245
  validatedValues = [normalizedValues[0]];
1219
1246
  }
1220
1247
 
1248
+ if (this._menuOptions.length === 0) {
1249
+ this.queuePendingValue(value);
1250
+ return;
1251
+ }
1252
+
1221
1253
  // Find matching options by comparing available options to validated values
1222
1254
  const trackedKeys = new Set();
1223
1255
  const optionsToSelect = this._menuOptions.filter(option => {
1224
1256
  const passesFilter = validatedValues.includes(option.key);
1225
1257
  const alreadyTracked = trackedKeys.has(option.key);
1258
+ const isActive = option.isActive;
1226
1259
 
1227
1260
  trackedKeys.add(option.key);
1228
1261
 
1229
1262
  // Include the option in the options to be selected if it passes the filter check and
1230
1263
  // either hasn't been tracked yet or selectAllMatchingOptions is true
1231
- return passesFilter && (!alreadyTracked || (alreadyTracked && this.selectAllMatchingOptions));
1264
+ return isActive && passesFilter && (!alreadyTracked || (alreadyTracked && this.selectAllMatchingOptions));
1232
1265
  });
1233
1266
 
1234
- // Handle selection result
1235
- if (optionsToSelect.length && !this.optionsArraysMatch(optionsToSelect, this.selectedOptions)) {
1236
- this.selectOptions(optionsToSelect);
1237
- } else {
1238
- this.stageUpdate();
1267
+ // Handle no matches: clear existing selection, but do not dispatch an intermediate
1268
+ // undefined value that can overwrite the host value in parent components.
1269
+ if (!optionsToSelect.length) {
1270
+ const hasUnresolvedKeys = this._menuOptions.some((option) => option.isActive && option.key == null);
1271
+
1272
+ if (hasUnresolvedKeys) {
1273
+ this.queuePendingValue(value);
1274
+ return;
1275
+ }
1276
+
1277
+ this.clearPendingValue();
1278
+
1279
+ if (this.selectedOptions.length > 0) {
1280
+ this.selectedOptions = [];
1281
+ }
1282
+
1283
+ // Always notify so the host resets any stale invalid value, even when
1284
+ // selectedOptions was already empty (e.g. double-clicking set-invalid).
1285
+ this.stageUpdate({ reason: 'no-match' });
1286
+
1287
+ // Dispatch failure event if no matches found
1288
+ if (validatedValues.length) {
1289
+ this.dispatchChangeEvent('auroMenu-selectValueFailure', {
1290
+ message: 'No matching options found for the provided value(s).',
1291
+ values: validatedValues
1292
+ });
1293
+ }
1294
+
1295
+ return;
1239
1296
  }
1240
1297
 
1241
- // Dispatch failure event if no matches found
1242
- if (!optionsToSelect.length && validatedValues.length) {
1243
- this.dispatchChangeEvent('auroMenu-selectValueFailure', {
1244
- message: 'No matching options found for the provided value(s).',
1245
- values: validatedValues
1246
- });
1298
+ this.clearPendingValue();
1299
+
1300
+ if (this.optionsArraysMatch(optionsToSelect, this.selectedOptions)) {
1301
+ return;
1302
+ }
1303
+
1304
+ // Apply programmatic selection as a single transaction and emit one final state.
1305
+ this.selectedOptions = optionsToSelect;
1306
+ this.stageUpdate();
1307
+ }
1308
+
1309
+ /**
1310
+ * Queues a pending value and schedules a bounded retry.
1311
+ * @param {string|number|Array<string|number>} value - The value to retry.
1312
+ */
1313
+ queuePendingValue(value) {
1314
+ this._pendingValue = value;
1315
+
1316
+ if (this._pendingRetryScheduled || this._pendingRetryCount >= 5) {
1317
+ return;
1247
1318
  }
1319
+
1320
+ this._pendingRetryScheduled = true;
1321
+ this._pendingRetryCount += 1;
1322
+
1323
+ setTimeout(() => {
1324
+ this._pendingRetryScheduled = false;
1325
+
1326
+ if (this._pendingValue == null) {
1327
+ return;
1328
+ }
1329
+
1330
+ const pendingValue = this._pendingValue;
1331
+ this.selectByValue(pendingValue);
1332
+ }, 0);
1333
+ }
1334
+
1335
+ /**
1336
+ * Clears pending retry state.
1337
+ */
1338
+ clearPendingValue() {
1339
+ this._pendingValue = null;
1340
+ this._pendingRetryScheduled = false;
1341
+ this._pendingRetryCount = 0;
1248
1342
  }
1249
1343
 
1250
1344
  /**
@@ -1283,9 +1377,9 @@ class MenuService {
1283
1377
  /**
1284
1378
  * Stages an update to notify subscribers of state and value changes.
1285
1379
  */
1286
- stageUpdate() {
1287
- this.notifyStateChange();
1288
- this.notifyValueChange();
1380
+ stageUpdate(meta = {}) {
1381
+ this.notifyStateChange(meta);
1382
+ this.notifyValueChange(meta);
1289
1383
  }
1290
1384
 
1291
1385
  /**
@@ -1300,14 +1394,18 @@ class MenuService {
1300
1394
  /**
1301
1395
  * Notifies subscribers of a state change (selected options has changed).
1302
1396
  */
1303
- notifyStateChange() {
1304
- this.notify({ type: 'stateChange', selectedOptions: this.selectedOptions });
1397
+ notifyStateChange(meta = {}) {
1398
+ this.notify({
1399
+ type: 'stateChange',
1400
+ selectedOptions: this.selectedOptions,
1401
+ ...meta
1402
+ });
1305
1403
  }
1306
1404
 
1307
1405
  /**
1308
1406
  * Notifies subscribers of a value change (current value has changed).
1309
1407
  */
1310
- notifyValueChange() {
1408
+ notifyValueChange(meta = {}) {
1311
1409
 
1312
1410
  // Prepare details for the event
1313
1411
  const details = {
@@ -1323,10 +1421,9 @@ class MenuService {
1323
1421
 
1324
1422
  this.notify({
1325
1423
  type: 'valueChange',
1424
+ ...meta,
1326
1425
  ...details
1327
1426
  });
1328
-
1329
- this.dispatchChangeEvent('auroMenu-selectedOption', details);
1330
1427
  }
1331
1428
 
1332
1429
  /**
@@ -1354,6 +1451,10 @@ class MenuService {
1354
1451
  addMenuOption(option) {
1355
1452
  this._menuOptions.push(option);
1356
1453
  this.notify({ type: 'optionsChange', options: this._menuOptions });
1454
+
1455
+ if (this._pendingValue != null) {
1456
+ this.queuePendingValue(this._pendingValue);
1457
+ }
1357
1458
  }
1358
1459
 
1359
1460
  /**
@@ -1363,6 +1464,10 @@ class MenuService {
1363
1464
  removeMenuOption(option) {
1364
1465
  this._menuOptions = this._menuOptions.filter(opt => opt !== option);
1365
1466
  this.notify({ type: 'optionsChange', options: this._menuOptions });
1467
+
1468
+ if (this._menuOptions.length === 0) {
1469
+ this.clearPendingValue();
1470
+ }
1366
1471
  }
1367
1472
 
1368
1473
  /**
@@ -1636,7 +1741,7 @@ class AuroMenu extends AuroElement {
1636
1741
  },
1637
1742
 
1638
1743
  /**
1639
- * Available menu options
1744
+ * Available menu options.
1640
1745
  * @readonly
1641
1746
  */
1642
1747
  options: {
@@ -1703,7 +1808,7 @@ class AuroMenu extends AuroElement {
1703
1808
  /**
1704
1809
  * @readonly
1705
1810
  * @returns {Array<HTMLElement>} - Returns the array of available menu options.
1706
- * @deprecated use `options` property instead.
1811
+ * @deprecated Use `options` property instead.
1707
1812
  */
1708
1813
  get items() {
1709
1814
  return this.options;
@@ -1811,7 +1916,7 @@ class AuroMenu extends AuroElement {
1811
1916
  const newValue = event.stringValue;
1812
1917
 
1813
1918
  // Check if the option or value has actually changed
1814
- if (newValue === undefined || (this.optionSelected !== newOption || this.stringValue !== newValue)) {
1919
+ if (this.optionSelected !== newOption || this.stringValue !== newValue) {
1815
1920
  this.optionSelected = newOption;
1816
1921
  this.setInternalValue(newValue);
1817
1922
  }
@@ -1885,8 +1990,13 @@ class AuroMenu extends AuroElement {
1885
1990
  updated(changedProperties) {
1886
1991
  super.updated(changedProperties);
1887
1992
 
1888
- // Update menu service properties on host update
1889
- if (changedProperties.has('value')) {
1993
+ // Apply value selection synchronously so that static-HTML fixtures
1994
+ // resolve within a single update cycle. The refactored selectByValue
1995
+ // no longer calls reset() first, so the destructive intermediate-event
1996
+ // cascade that originally required deferral is eliminated. If option
1997
+ // keys are not yet resolved (framework mount-order race), selectByValue
1998
+ // queues a bounded retry automatically via queuePendingValue.
1999
+ if (changedProperties.has('value') && !this.internalUpdateInProgress) {
1890
2000
  this.menuService.selectByValue(this.value);
1891
2001
  }
1892
2002
 
@@ -2066,12 +2176,13 @@ class AuroMenu extends AuroElement {
2066
2176
  * @param {any} source - The source that triggers this event.
2067
2177
  * @private
2068
2178
  */
2069
- notifySelectionChange({value, stringValue, keys, options} = {}) {
2179
+ notifySelectionChange({value, stringValue, keys, options, reason} = {}) {
2070
2180
  dispatchMenuEvent(this, 'auroMenu-selectedOption', {
2071
2181
  value,
2072
2182
  stringValue,
2073
2183
  keys,
2074
- options
2184
+ options,
2185
+ reason
2075
2186
  });
2076
2187
  }
2077
2188