@govtechsg/sgds-web-component 3.4.0-rc.2 → 3.4.0-rc.3

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.
@@ -1234,7 +1234,7 @@
1234
1234
  };
1235
1235
  issueWarning$4('dev-mode', `Lit is in dev mode. Not recommended for production!`);
1236
1236
  }
1237
- const wrap = global$2.ShadyDOM?.inUse &&
1237
+ const wrap$1 = global$2.ShadyDOM?.inUse &&
1238
1238
  global$2.ShadyDOM?.noPatch === true
1239
1239
  ? global$2.ShadyDOM.wrap
1240
1240
  : (node) => node;
@@ -1286,7 +1286,7 @@
1286
1286
  const nodeMarker = `<${markerMatch}>`;
1287
1287
  const d = document;
1288
1288
  // Creates a dynamic marker. We never have to search for these in the DOM.
1289
- const createMarker = () => d.createComment('');
1289
+ const createMarker$1 = () => d.createComment('');
1290
1290
  const isPrimitive = (value) => value === null || (typeof value != 'object' && typeof value != 'function');
1291
1291
  const isArray = Array.isArray;
1292
1292
  const isIterable = (value) => isArray(value) ||
@@ -1718,7 +1718,7 @@
1718
1718
  // normalized when cloning in IE (could simplify when
1719
1719
  // IE is no longer supported)
1720
1720
  for (let i = 0; i < lastIndex; i++) {
1721
- node.append(strings[i], createMarker());
1721
+ node.append(strings[i], createMarker$1());
1722
1722
  // Walk past the marker node we just added
1723
1723
  walker.nextNode();
1724
1724
  parts.push({ type: CHILD_PART, index: ++nodeIndex });
@@ -1726,7 +1726,7 @@
1726
1726
  // Note because this marker is added after the walker's current
1727
1727
  // node, it will be walked to in the outer loop (and ignored), so
1728
1728
  // we don't need to adjust nodeIndex here
1729
- node.append(strings[lastIndex], createMarker());
1729
+ node.append(strings[lastIndex], createMarker$1());
1730
1730
  }
1731
1731
  }
1732
1732
  }
@@ -1856,7 +1856,7 @@
1856
1856
  if (nodeIndex === templatePart.index) {
1857
1857
  let part;
1858
1858
  if (templatePart.type === CHILD_PART) {
1859
- part = new ChildPart(node, node.nextSibling, this, options);
1859
+ part = new ChildPart$1(node, node.nextSibling, this, options);
1860
1860
  }
1861
1861
  else if (templatePart.type === ATTRIBUTE_PART) {
1862
1862
  part = new templatePart.ctor(node, templatePart.name, templatePart.strings, this, options);
@@ -1906,7 +1906,7 @@
1906
1906
  }
1907
1907
  }
1908
1908
  }
1909
- class ChildPart {
1909
+ class ChildPart$1 {
1910
1910
  // See comment in Disconnectable interface for why this is a getter
1911
1911
  get _$isConnected() {
1912
1912
  // ChildParts that are not at the root should always be created with a
@@ -1953,7 +1953,7 @@
1953
1953
  * consists of all child nodes of `.parentNode`.
1954
1954
  */
1955
1955
  get parentNode() {
1956
- let parentNode = wrap(this._$startNode).parentNode;
1956
+ let parentNode = wrap$1(this._$startNode).parentNode;
1957
1957
  const parent = this._$parent;
1958
1958
  if (parent !== undefined &&
1959
1959
  parentNode?.nodeType === 11 /* Node.DOCUMENT_FRAGMENT */) {
@@ -2027,7 +2027,7 @@
2027
2027
  }
2028
2028
  }
2029
2029
  _insert(node) {
2030
- return wrap(wrap(this._$startNode).parentNode).insertBefore(node, this._$endNode);
2030
+ return wrap$1(wrap$1(this._$startNode).parentNode).insertBefore(node, this._$endNode);
2031
2031
  }
2032
2032
  _commitNode(value) {
2033
2033
  if (this._$committedValue !== value) {
@@ -2074,7 +2074,7 @@
2074
2074
  // Text node. We can now just replace the text content (.data) of the node.
2075
2075
  if (this._$committedValue !== nothing &&
2076
2076
  isPrimitive(this._$committedValue)) {
2077
- const node = wrap(this._$startNode).nextSibling;
2077
+ const node = wrap$1(this._$startNode).nextSibling;
2078
2078
  {
2079
2079
  if (this._textSanitizer === undefined) {
2080
2080
  this._textSanitizer = createSanitizer(node, 'data', 'property');
@@ -2200,7 +2200,7 @@
2200
2200
  // TODO (justinfagnani): test perf impact of always creating two parts
2201
2201
  // instead of sharing parts between nodes
2202
2202
  // https://github.com/lit/lit/issues/1266
2203
- itemParts.push((itemPart = new ChildPart(this._insert(createMarker()), this._insert(createMarker()), this, this.options)));
2203
+ itemParts.push((itemPart = new ChildPart$1(this._insert(createMarker$1()), this._insert(createMarker$1()), this, this.options)));
2204
2204
  }
2205
2205
  else {
2206
2206
  // Reuse an existing part
@@ -2211,7 +2211,7 @@
2211
2211
  }
2212
2212
  if (partIndex < itemParts.length) {
2213
2213
  // itemParts always have end nodes
2214
- this._$clear(itemPart && wrap(itemPart._$endNode).nextSibling, partIndex);
2214
+ this._$clear(itemPart && wrap$1(itemPart._$endNode).nextSibling, partIndex);
2215
2215
  // Truncate the parts array so _value reflects the current state
2216
2216
  itemParts.length = partIndex;
2217
2217
  }
@@ -2227,11 +2227,11 @@
2227
2227
  *
2228
2228
  * @internal
2229
2229
  */
2230
- _$clear(start = wrap(this._$startNode).nextSibling, from) {
2230
+ _$clear(start = wrap$1(this._$startNode).nextSibling, from) {
2231
2231
  this._$notifyConnectionChanged?.(false, true, from);
2232
2232
  while (start && start !== this._$endNode) {
2233
- const n = wrap(start).nextSibling;
2234
- wrap(start).remove();
2233
+ const n = wrap$1(start).nextSibling;
2234
+ wrap$1(start).remove();
2235
2235
  start = n;
2236
2236
  }
2237
2237
  }
@@ -2349,7 +2349,7 @@
2349
2349
  /** @internal */
2350
2350
  _commitValue(value) {
2351
2351
  if (value === nothing) {
2352
- wrap(this.element).removeAttribute(this.name);
2352
+ wrap$1(this.element).removeAttribute(this.name);
2353
2353
  }
2354
2354
  else {
2355
2355
  {
@@ -2366,7 +2366,7 @@
2366
2366
  value,
2367
2367
  options: this.options,
2368
2368
  });
2369
- wrap(this.element).setAttribute(this.name, (value ?? ''));
2369
+ wrap$1(this.element).setAttribute(this.name, (value ?? ''));
2370
2370
  }
2371
2371
  }
2372
2372
  }
@@ -2410,7 +2410,7 @@
2410
2410
  value: !!(value && value !== nothing),
2411
2411
  options: this.options,
2412
2412
  });
2413
- wrap(this.element).toggleAttribute(this.name, !!value && value !== nothing);
2413
+ wrap$1(this.element).toggleAttribute(this.name, !!value && value !== nothing);
2414
2414
  }
2415
2415
  }
2416
2416
  class EventPart extends AttributePart {
@@ -2501,10 +2501,46 @@
2501
2501
  resolveDirective(this, value);
2502
2502
  }
2503
2503
  }
2504
+ /**
2505
+ * END USERS SHOULD NOT RELY ON THIS OBJECT.
2506
+ *
2507
+ * Private exports for use by other Lit packages, not intended for use by
2508
+ * external users.
2509
+ *
2510
+ * We currently do not make a mangled rollup build of the lit-ssr code. In order
2511
+ * to keep a number of (otherwise private) top-level exports mangled in the
2512
+ * client side code, we export a _$LH object containing those members (or
2513
+ * helper methods for accessing private fields of those members), and then
2514
+ * re-export them for use in lit-ssr. This keeps lit-ssr agnostic to whether the
2515
+ * client-side code is being used in `dev` mode or `prod` mode.
2516
+ *
2517
+ * This has a unique name, to disambiguate it from private exports in
2518
+ * lit-element, which re-exports all of lit-html.
2519
+ *
2520
+ * @private
2521
+ */
2522
+ const _$LH = {
2523
+ // Used in lit-ssr
2524
+ _boundAttributeSuffix: boundAttributeSuffix,
2525
+ _marker: marker,
2526
+ _markerMatch: markerMatch,
2527
+ _HTML_RESULT: HTML_RESULT,
2528
+ _getTemplateHtml: getTemplateHtml,
2529
+ // Used in tests and private-ssr-support
2530
+ _TemplateInstance: TemplateInstance,
2531
+ _isIterable: isIterable,
2532
+ _resolveDirective: resolveDirective,
2533
+ _ChildPart: ChildPart$1,
2534
+ _AttributePart: AttributePart,
2535
+ _BooleanAttributePart: BooleanAttributePart,
2536
+ _EventPart: EventPart,
2537
+ _PropertyPart: PropertyPart,
2538
+ _ElementPart: ElementPart,
2539
+ };
2504
2540
  // Apply polyfills if available
2505
2541
  const polyfillSupport$2 = global$2.litHtmlPolyfillSupportDevMode
2506
2542
  ;
2507
- polyfillSupport$2?.(Template, ChildPart);
2543
+ polyfillSupport$2?.(Template, ChildPart$1);
2508
2544
  // IMPORTANT: do not change the property name or the assignment expression.
2509
2545
  // This line will be used in regexes to search for lit-html usage.
2510
2546
  (global$2.litHtmlVersions ??= []).push('3.2.0');
@@ -2563,7 +2599,7 @@
2563
2599
  const endNode = options?.renderBefore ?? null;
2564
2600
  // This property needs to remain unminified.
2565
2601
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2566
- partOwnerNode['_$litPart$'] = part = new ChildPart(container.insertBefore(createMarker(), endNode), endNode, undefined, options ?? {});
2602
+ partOwnerNode['_$litPart$'] = part = new ChildPart$1(container.insertBefore(createMarker$1(), endNode), endNode, undefined, options ?? {});
2567
2603
  }
2568
2604
  part._$setValue(value);
2569
2605
  debugLogEvent$1 &&
@@ -4487,7 +4523,8 @@
4487
4523
  * Copyright 2020 Google LLC
4488
4524
  * SPDX-License-Identifier: BSD-3-Clause
4489
4525
  */
4490
- window.ShadyDOM?.inUse &&
4526
+ const { _ChildPart: ChildPart } = _$LH;
4527
+ const wrap = window.ShadyDOM?.inUse &&
4491
4528
  window.ShadyDOM?.noPatch === true
4492
4529
  ? window.ShadyDOM.wrap
4493
4530
  : (node) => node;
@@ -4500,6 +4537,79 @@
4500
4537
  * parts do not.
4501
4538
  */
4502
4539
  const isSingleExpression = (part) => part.strings === undefined;
4540
+ const createMarker = () => document.createComment('');
4541
+ /**
4542
+ * Inserts a ChildPart into the given container ChildPart's DOM, either at the
4543
+ * end of the container ChildPart, or before the optional `refPart`.
4544
+ *
4545
+ * This does not add the part to the containerPart's committed value. That must
4546
+ * be done by callers.
4547
+ *
4548
+ * @param containerPart Part within which to add the new ChildPart
4549
+ * @param refPart Part before which to add the new ChildPart; when omitted the
4550
+ * part added to the end of the `containerPart`
4551
+ * @param part Part to insert, or undefined to create a new part
4552
+ */
4553
+ const insertPart = (containerPart, refPart, part) => {
4554
+ const container = wrap(containerPart._$startNode).parentNode;
4555
+ const refNode = refPart === undefined ? containerPart._$endNode : refPart._$startNode;
4556
+ if (part === undefined) {
4557
+ const startNode = wrap(container).insertBefore(createMarker(), refNode);
4558
+ const endNode = wrap(container).insertBefore(createMarker(), refNode);
4559
+ part = new ChildPart(startNode, endNode, containerPart, containerPart.options);
4560
+ }
4561
+ else {
4562
+ const endNode = wrap(part._$endNode).nextSibling;
4563
+ const oldParent = part._$parent;
4564
+ const parentChanged = oldParent !== containerPart;
4565
+ if (parentChanged) {
4566
+ part._$reparentDisconnectables?.(containerPart);
4567
+ // Note that although `_$reparentDisconnectables` updates the part's
4568
+ // `_$parent` reference after unlinking from its current parent, that
4569
+ // method only exists if Disconnectables are present, so we need to
4570
+ // unconditionally set it here
4571
+ part._$parent = containerPart;
4572
+ // Since the _$isConnected getter is somewhat costly, only
4573
+ // read it once we know the subtree has directives that need
4574
+ // to be notified
4575
+ let newConnectionState;
4576
+ if (part._$notifyConnectionChanged !== undefined &&
4577
+ (newConnectionState = containerPart._$isConnected) !==
4578
+ oldParent._$isConnected) {
4579
+ part._$notifyConnectionChanged(newConnectionState);
4580
+ }
4581
+ }
4582
+ if (endNode !== refNode || parentChanged) {
4583
+ let start = part._$startNode;
4584
+ while (start !== endNode) {
4585
+ const n = wrap(start).nextSibling;
4586
+ wrap(container).insertBefore(start, refNode);
4587
+ start = n;
4588
+ }
4589
+ }
4590
+ }
4591
+ return part;
4592
+ };
4593
+ /**
4594
+ * Sets the value of a Part.
4595
+ *
4596
+ * Note that this should only be used to set/update the value of user-created
4597
+ * parts (i.e. those created using `insertPart`); it should not be used
4598
+ * by directives to set the value of the directive's container part. Directives
4599
+ * should return a value from `update`/`render` to update their part state.
4600
+ *
4601
+ * For directives that require setting their part value asynchronously, they
4602
+ * should extend `AsyncDirective` and call `this.setValue()`.
4603
+ *
4604
+ * @param part Part to set
4605
+ * @param value Value to set
4606
+ * @param index For `AttributePart`s, the index to set
4607
+ * @param directiveParent Used internally; should not be set by user
4608
+ */
4609
+ const setChildPartValue = (part, value, directiveParent = part) => {
4610
+ part._$setValue(value, directiveParent);
4611
+ return part;
4612
+ };
4503
4613
  // A sentinel value that can never appear as a part value except when set by
4504
4614
  // live(). Used to force a dirty-check to fail and cause a re-render.
4505
4615
  const RESET_VALUE = {};
@@ -4515,6 +4625,36 @@
4515
4625
  * @param value
4516
4626
  */
4517
4627
  const setCommittedValue = (part, value = RESET_VALUE) => (part._$committedValue = value);
4628
+ /**
4629
+ * Returns the committed value of a ChildPart.
4630
+ *
4631
+ * The committed value is used for change detection and efficient updates of
4632
+ * the part. It can differ from the value set by the template or directive in
4633
+ * cases where the template value is transformed before being committed.
4634
+ *
4635
+ * - `TemplateResult`s are committed as a `TemplateInstance`
4636
+ * - Iterables are committed as `Array<ChildPart>`
4637
+ * - All other types are committed as the template value or value returned or
4638
+ * set by a directive.
4639
+ *
4640
+ * @param part
4641
+ */
4642
+ const getCommittedValue = (part) => part._$committedValue;
4643
+ /**
4644
+ * Removes a ChildPart from the DOM, including any of its content.
4645
+ *
4646
+ * @param part The Part to remove
4647
+ */
4648
+ const removePart = (part) => {
4649
+ part._$notifyConnectionChanged?.(false, true);
4650
+ let start = part._$startNode;
4651
+ const end = wrap(part._$endNode).nextSibling;
4652
+ while (start !== end) {
4653
+ const n = wrap(start).nextSibling;
4654
+ wrap(start).remove();
4655
+ start = n;
4656
+ }
4657
+ };
4518
4658
 
4519
4659
  /**
4520
4660
  * @license
@@ -13145,6 +13285,416 @@
13145
13285
 
13146
13286
  var css_248z = css`:host{display:block;position:relative}.combobox{display:flex;flex-direction:column;gap:var(--sgds-form-gap-md)}.dropdown{display:flex;height:100%}.sgds.combobox{align-items:stretch;display:flex;flex-wrap:wrap;justify-content:flex-end;position:relative;width:-webkit-fill-available;width:-moz-available}.dropdown-menu{box-sizing:border-box;max-height:10rem;overflow-x:hidden;overflow-y:auto}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}.form-control-group.disabled{cursor:not-allowed;opacity:var(--sgds-opacity-50)}.form-control-group{align-items:center;background-color:var(--sgds-form-surface-default);border:var(--sgds-form-border-width-default) solid var(--sgds-border-color-default);border-radius:var(--sgds-form-border-radius-md);display:flex;gap:var(--sgds-form-gap-md);justify-content:space-between;min-height:var(--sgds-dimension-48);min-width:var(--sgds-dimension-256);padding:var(--sgds-form-padding-y) var(--sgds-form-padding-x);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;width:-webkit-fill-available;width:-moz-available}.form-control{appearance:none;background-clip:padding-box;background:none;border:none;color:var(--sgds-form-color-default);display:inline;flex-grow:1;font-size:var(--sgds-font-size-2);line-height:var(--sgds-line-height-body);outline:none;padding:0}.combobox-input-container{display:flex;flex-wrap:wrap;gap:var(--sgds-gap-xs);width:calc(100% - var(--sgds-icon-size-md, 1.25rem))}.empty-menu{padding:var(--sgds-padding-sm) var(--sgds-padding-lg,20px)}.form-control-group.readonly{border-color:var(--sgds-border-color-muted)}.form-control-group:not(.disabled):not(.is-invalid):hover{border:var(--sgds-form-border-width-thick) solid var(--sgds-border-color-emphasis)}.form-control-group:not(.disabled):not(.is-invalid):focus,.form-control-group:not(.disabled):not(.is-invalid):focus-within{border:var(--sgds-form-border-width-thick) solid var(--sgds-border-color-emphasis);box-shadow:var(--sgds-form-box-shadow-focus);outline:0}.form-control-group.is-invalid{border:var(--sgds-form-border-width-thick) solid var(--sgds-form-danger-border-color-default)}.form-control-group.disabled{background-color:var(--sgds-form-surface-muted)}`;
13147
13287
 
13288
+ /**
13289
+ * @license
13290
+ * Copyright 2017 Google LLC
13291
+ * SPDX-License-Identifier: BSD-3-Clause
13292
+ */
13293
+ // Helper for generating a map of array item to its index over a subset
13294
+ // of an array (used to lazily generate `newKeyToIndexMap` and
13295
+ // `oldKeyToIndexMap`)
13296
+ const generateMap = (list, start, end) => {
13297
+ const map = new Map();
13298
+ for (let i = start; i <= end; i++) {
13299
+ map.set(list[i], i);
13300
+ }
13301
+ return map;
13302
+ };
13303
+ class RepeatDirective extends Directive {
13304
+ constructor(partInfo) {
13305
+ super(partInfo);
13306
+ if (partInfo.type !== PartType.CHILD) {
13307
+ throw new Error('repeat() can only be used in text expressions');
13308
+ }
13309
+ }
13310
+ _getValuesAndKeys(items, keyFnOrTemplate, template) {
13311
+ let keyFn;
13312
+ if (template === undefined) {
13313
+ template = keyFnOrTemplate;
13314
+ }
13315
+ else if (keyFnOrTemplate !== undefined) {
13316
+ keyFn = keyFnOrTemplate;
13317
+ }
13318
+ const keys = [];
13319
+ const values = [];
13320
+ let index = 0;
13321
+ for (const item of items) {
13322
+ keys[index] = keyFn ? keyFn(item, index) : index;
13323
+ values[index] = template(item, index);
13324
+ index++;
13325
+ }
13326
+ return {
13327
+ values,
13328
+ keys,
13329
+ };
13330
+ }
13331
+ render(items, keyFnOrTemplate, template) {
13332
+ return this._getValuesAndKeys(items, keyFnOrTemplate, template).values;
13333
+ }
13334
+ update(containerPart, [items, keyFnOrTemplate, template]) {
13335
+ // Old part & key lists are retrieved from the last update (which may
13336
+ // be primed by hydration)
13337
+ const oldParts = getCommittedValue(containerPart);
13338
+ const { values: newValues, keys: newKeys } = this._getValuesAndKeys(items, keyFnOrTemplate, template);
13339
+ // We check that oldParts, the committed value, is an Array as an
13340
+ // indicator that the previous value came from a repeat() call. If
13341
+ // oldParts is not an Array then this is the first render and we return
13342
+ // an array for lit-html's array handling to render, and remember the
13343
+ // keys.
13344
+ if (!Array.isArray(oldParts)) {
13345
+ this._itemKeys = newKeys;
13346
+ return newValues;
13347
+ }
13348
+ // In SSR hydration it's possible for oldParts to be an array but for us
13349
+ // to not have item keys because the update() hasn't run yet. We set the
13350
+ // keys to an empty array. This will cause all oldKey/newKey comparisons
13351
+ // to fail and execution to fall to the last nested brach below which
13352
+ // reuses the oldPart.
13353
+ const oldKeys = (this._itemKeys ??= []);
13354
+ // New part list will be built up as we go (either reused from
13355
+ // old parts or created for new keys in this update). This is
13356
+ // saved in the above cache at the end of the update.
13357
+ const newParts = [];
13358
+ // Maps from key to index for current and previous update; these
13359
+ // are generated lazily only when needed as a performance
13360
+ // optimization, since they are only required for multiple
13361
+ // non-contiguous changes in the list, which are less common.
13362
+ let newKeyToIndexMap;
13363
+ let oldKeyToIndexMap;
13364
+ // Head and tail pointers to old parts and new values
13365
+ let oldHead = 0;
13366
+ let oldTail = oldParts.length - 1;
13367
+ let newHead = 0;
13368
+ let newTail = newValues.length - 1;
13369
+ // Overview of O(n) reconciliation algorithm (general approach
13370
+ // based on ideas found in ivi, vue, snabbdom, etc.):
13371
+ //
13372
+ // * We start with the list of old parts and new values (and
13373
+ // arrays of their respective keys), head/tail pointers into
13374
+ // each, and we build up the new list of parts by updating
13375
+ // (and when needed, moving) old parts or creating new ones.
13376
+ // The initial scenario might look like this (for brevity of
13377
+ // the diagrams, the numbers in the array reflect keys
13378
+ // associated with the old parts or new values, although keys
13379
+ // and parts/values are actually stored in parallel arrays
13380
+ // indexed using the same head/tail pointers):
13381
+ //
13382
+ // oldHead v v oldTail
13383
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
13384
+ // newParts: [ , , , , , , ]
13385
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] <- reflects the user's new
13386
+ // item order
13387
+ // newHead ^ ^ newTail
13388
+ //
13389
+ // * Iterate old & new lists from both sides, updating,
13390
+ // swapping, or removing parts at the head/tail locations
13391
+ // until neither head nor tail can move.
13392
+ //
13393
+ // * Example below: keys at head pointers match, so update old
13394
+ // part 0 in-place (no need to move it) and record part 0 in
13395
+ // the `newParts` list. The last thing we do is advance the
13396
+ // `oldHead` and `newHead` pointers (will be reflected in the
13397
+ // next diagram).
13398
+ //
13399
+ // oldHead v v oldTail
13400
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
13401
+ // newParts: [0, , , , , , ] <- heads matched: update 0
13402
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldHead
13403
+ // & newHead
13404
+ // newHead ^ ^ newTail
13405
+ //
13406
+ // * Example below: head pointers don't match, but tail
13407
+ // pointers do, so update part 6 in place (no need to move
13408
+ // it), and record part 6 in the `newParts` list. Last,
13409
+ // advance the `oldTail` and `oldHead` pointers.
13410
+ //
13411
+ // oldHead v v oldTail
13412
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
13413
+ // newParts: [0, , , , , , 6] <- tails matched: update 6
13414
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldTail
13415
+ // & newTail
13416
+ // newHead ^ ^ newTail
13417
+ //
13418
+ // * If neither head nor tail match; next check if one of the
13419
+ // old head/tail items was removed. We first need to generate
13420
+ // the reverse map of new keys to index (`newKeyToIndexMap`),
13421
+ // which is done once lazily as a performance optimization,
13422
+ // since we only hit this case if multiple non-contiguous
13423
+ // changes were made. Note that for contiguous removal
13424
+ // anywhere in the list, the head and tails would advance
13425
+ // from either end and pass each other before we get to this
13426
+ // case and removals would be handled in the final while loop
13427
+ // without needing to generate the map.
13428
+ //
13429
+ // * Example below: The key at `oldTail` was removed (no longer
13430
+ // in the `newKeyToIndexMap`), so remove that part from the
13431
+ // DOM and advance just the `oldTail` pointer.
13432
+ //
13433
+ // oldHead v v oldTail
13434
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
13435
+ // newParts: [0, , , , , , 6] <- 5 not in new map: remove
13436
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] 5 and advance oldTail
13437
+ // newHead ^ ^ newTail
13438
+ //
13439
+ // * Once head and tail cannot move, any mismatches are due to
13440
+ // either new or moved items; if a new key is in the previous
13441
+ // "old key to old index" map, move the old part to the new
13442
+ // location, otherwise create and insert a new part. Note
13443
+ // that when moving an old part we null its position in the
13444
+ // oldParts array if it lies between the head and tail so we
13445
+ // know to skip it when the pointers get there.
13446
+ //
13447
+ // * Example below: neither head nor tail match, and neither
13448
+ // were removed; so find the `newHead` key in the
13449
+ // `oldKeyToIndexMap`, and move that old part's DOM into the
13450
+ // next head position (before `oldParts[oldHead]`). Last,
13451
+ // null the part in the `oldPart` array since it was
13452
+ // somewhere in the remaining oldParts still to be scanned
13453
+ // (between the head and tail pointers) so that we know to
13454
+ // skip that old part on future iterations.
13455
+ //
13456
+ // oldHead v v oldTail
13457
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
13458
+ // newParts: [0, 2, , , , , 6] <- stuck: update & move 2
13459
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] into place and advance
13460
+ // newHead
13461
+ // newHead ^ ^ newTail
13462
+ //
13463
+ // * Note that for moves/insertions like the one above, a part
13464
+ // inserted at the head pointer is inserted before the
13465
+ // current `oldParts[oldHead]`, and a part inserted at the
13466
+ // tail pointer is inserted before `newParts[newTail+1]`. The
13467
+ // seeming asymmetry lies in the fact that new parts are
13468
+ // moved into place outside in, so to the right of the head
13469
+ // pointer are old parts, and to the right of the tail
13470
+ // pointer are new parts.
13471
+ //
13472
+ // * We always restart back from the top of the algorithm,
13473
+ // allowing matching and simple updates in place to
13474
+ // continue...
13475
+ //
13476
+ // * Example below: the head pointers once again match, so
13477
+ // simply update part 1 and record it in the `newParts`
13478
+ // array. Last, advance both head pointers.
13479
+ //
13480
+ // oldHead v v oldTail
13481
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
13482
+ // newParts: [0, 2, 1, , , , 6] <- heads matched: update 1
13483
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldHead
13484
+ // & newHead
13485
+ // newHead ^ ^ newTail
13486
+ //
13487
+ // * As mentioned above, items that were moved as a result of
13488
+ // being stuck (the final else clause in the code below) are
13489
+ // marked with null, so we always advance old pointers over
13490
+ // these so we're comparing the next actual old value on
13491
+ // either end.
13492
+ //
13493
+ // * Example below: `oldHead` is null (already placed in
13494
+ // newParts), so advance `oldHead`.
13495
+ //
13496
+ // oldHead v v oldTail
13497
+ // oldKeys: [0, 1, -, 3, 4, 5, 6] <- old head already used:
13498
+ // newParts: [0, 2, 1, , , , 6] advance oldHead
13499
+ // newKeys: [0, 2, 1, 4, 3, 7, 6]
13500
+ // newHead ^ ^ newTail
13501
+ //
13502
+ // * Note it's not critical to mark old parts as null when they
13503
+ // are moved from head to tail or tail to head, since they
13504
+ // will be outside the pointer range and never visited again.
13505
+ //
13506
+ // * Example below: Here the old tail key matches the new head
13507
+ // key, so the part at the `oldTail` position and move its
13508
+ // DOM to the new head position (before `oldParts[oldHead]`).
13509
+ // Last, advance `oldTail` and `newHead` pointers.
13510
+ //
13511
+ // oldHead v v oldTail
13512
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
13513
+ // newParts: [0, 2, 1, 4, , , 6] <- old tail matches new
13514
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] head: update & move 4,
13515
+ // advance oldTail & newHead
13516
+ // newHead ^ ^ newTail
13517
+ //
13518
+ // * Example below: Old and new head keys match, so update the
13519
+ // old head part in place, and advance the `oldHead` and
13520
+ // `newHead` pointers.
13521
+ //
13522
+ // oldHead v oldTail
13523
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
13524
+ // newParts: [0, 2, 1, 4, 3, ,6] <- heads match: update 3
13525
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance oldHead &
13526
+ // newHead
13527
+ // newHead ^ ^ newTail
13528
+ //
13529
+ // * Once the new or old pointers move past each other then all
13530
+ // we have left is additions (if old list exhausted) or
13531
+ // removals (if new list exhausted). Those are handled in the
13532
+ // final while loops at the end.
13533
+ //
13534
+ // * Example below: `oldHead` exceeded `oldTail`, so we're done
13535
+ // with the main loop. Create the remaining part and insert
13536
+ // it at the new head position, and the update is complete.
13537
+ //
13538
+ // (oldHead > oldTail)
13539
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
13540
+ // newParts: [0, 2, 1, 4, 3, 7 ,6] <- create and insert 7
13541
+ // newKeys: [0, 2, 1, 4, 3, 7, 6]
13542
+ // newHead ^ newTail
13543
+ //
13544
+ // * Note that the order of the if/else clauses is not
13545
+ // important to the algorithm, as long as the null checks
13546
+ // come first (to ensure we're always working on valid old
13547
+ // parts) and that the final else clause comes last (since
13548
+ // that's where the expensive moves occur). The order of
13549
+ // remaining clauses is just a simple guess at which cases
13550
+ // will be most common.
13551
+ //
13552
+ // * Note, we could calculate the longest
13553
+ // increasing subsequence (LIS) of old items in new position,
13554
+ // and only move those not in the LIS set. However that costs
13555
+ // O(nlogn) time and adds a bit more code, and only helps
13556
+ // make rare types of mutations require fewer moves. The
13557
+ // above handles removes, adds, reversal, swaps, and single
13558
+ // moves of contiguous items in linear time, in the minimum
13559
+ // number of moves. As the number of multiple moves where LIS
13560
+ // might help approaches a random shuffle, the LIS
13561
+ // optimization becomes less helpful, so it seems not worth
13562
+ // the code at this point. Could reconsider if a compelling
13563
+ // case arises.
13564
+ while (oldHead <= oldTail && newHead <= newTail) {
13565
+ if (oldParts[oldHead] === null) {
13566
+ // `null` means old part at head has already been used
13567
+ // below; skip
13568
+ oldHead++;
13569
+ }
13570
+ else if (oldParts[oldTail] === null) {
13571
+ // `null` means old part at tail has already been used
13572
+ // below; skip
13573
+ oldTail--;
13574
+ }
13575
+ else if (oldKeys[oldHead] === newKeys[newHead]) {
13576
+ // Old head matches new head; update in place
13577
+ newParts[newHead] = setChildPartValue(oldParts[oldHead], newValues[newHead]);
13578
+ oldHead++;
13579
+ newHead++;
13580
+ }
13581
+ else if (oldKeys[oldTail] === newKeys[newTail]) {
13582
+ // Old tail matches new tail; update in place
13583
+ newParts[newTail] = setChildPartValue(oldParts[oldTail], newValues[newTail]);
13584
+ oldTail--;
13585
+ newTail--;
13586
+ }
13587
+ else if (oldKeys[oldHead] === newKeys[newTail]) {
13588
+ // Old head matches new tail; update and move to new tail
13589
+ newParts[newTail] = setChildPartValue(oldParts[oldHead], newValues[newTail]);
13590
+ insertPart(containerPart, newParts[newTail + 1], oldParts[oldHead]);
13591
+ oldHead++;
13592
+ newTail--;
13593
+ }
13594
+ else if (oldKeys[oldTail] === newKeys[newHead]) {
13595
+ // Old tail matches new head; update and move to new head
13596
+ newParts[newHead] = setChildPartValue(oldParts[oldTail], newValues[newHead]);
13597
+ insertPart(containerPart, oldParts[oldHead], oldParts[oldTail]);
13598
+ oldTail--;
13599
+ newHead++;
13600
+ }
13601
+ else {
13602
+ if (newKeyToIndexMap === undefined) {
13603
+ // Lazily generate key-to-index maps, used for removals &
13604
+ // moves below
13605
+ newKeyToIndexMap = generateMap(newKeys, newHead, newTail);
13606
+ oldKeyToIndexMap = generateMap(oldKeys, oldHead, oldTail);
13607
+ }
13608
+ if (!newKeyToIndexMap.has(oldKeys[oldHead])) {
13609
+ // Old head is no longer in new list; remove
13610
+ removePart(oldParts[oldHead]);
13611
+ oldHead++;
13612
+ }
13613
+ else if (!newKeyToIndexMap.has(oldKeys[oldTail])) {
13614
+ // Old tail is no longer in new list; remove
13615
+ removePart(oldParts[oldTail]);
13616
+ oldTail--;
13617
+ }
13618
+ else {
13619
+ // Any mismatches at this point are due to additions or
13620
+ // moves; see if we have an old part we can reuse and move
13621
+ // into place
13622
+ const oldIndex = oldKeyToIndexMap.get(newKeys[newHead]);
13623
+ const oldPart = oldIndex !== undefined ? oldParts[oldIndex] : null;
13624
+ if (oldPart === null) {
13625
+ // No old part for this value; create a new one and
13626
+ // insert it
13627
+ const newPart = insertPart(containerPart, oldParts[oldHead]);
13628
+ setChildPartValue(newPart, newValues[newHead]);
13629
+ newParts[newHead] = newPart;
13630
+ }
13631
+ else {
13632
+ // Reuse old part
13633
+ newParts[newHead] = setChildPartValue(oldPart, newValues[newHead]);
13634
+ insertPart(containerPart, oldParts[oldHead], oldPart);
13635
+ // This marks the old part as having been used, so that
13636
+ // it will be skipped in the first two checks above
13637
+ oldParts[oldIndex] = null;
13638
+ }
13639
+ newHead++;
13640
+ }
13641
+ }
13642
+ }
13643
+ // Add parts for any remaining new values
13644
+ while (newHead <= newTail) {
13645
+ // For all remaining additions, we insert before last new
13646
+ // tail, since old pointers are no longer valid
13647
+ const newPart = insertPart(containerPart, newParts[newTail + 1]);
13648
+ setChildPartValue(newPart, newValues[newHead]);
13649
+ newParts[newHead++] = newPart;
13650
+ }
13651
+ // Remove any remaining unused old parts
13652
+ while (oldHead <= oldTail) {
13653
+ const oldPart = oldParts[oldHead++];
13654
+ if (oldPart !== null) {
13655
+ removePart(oldPart);
13656
+ }
13657
+ }
13658
+ // Save order of new parts for next round
13659
+ this._itemKeys = newKeys;
13660
+ // Directly set part value, bypassing it's dirty-checking
13661
+ setCommittedValue(containerPart, newParts);
13662
+ return noChange;
13663
+ }
13664
+ }
13665
+ /**
13666
+ * A directive that repeats a series of values (usually `TemplateResults`)
13667
+ * generated from an iterable, and updates those items efficiently when the
13668
+ * iterable changes based on user-provided `keys` associated with each item.
13669
+ *
13670
+ * Note that if a `keyFn` is provided, strict key-to-DOM mapping is maintained,
13671
+ * meaning previous DOM for a given key is moved into the new position if
13672
+ * needed, and DOM will never be reused with values for different keys (new DOM
13673
+ * will always be created for new keys). This is generally the most efficient
13674
+ * way to use `repeat` since it performs minimum unnecessary work for insertions
13675
+ * and removals.
13676
+ *
13677
+ * The `keyFn` takes two parameters, the item and its index, and returns a unique key value.
13678
+ *
13679
+ * ```js
13680
+ * html`
13681
+ * <ol>
13682
+ * ${repeat(this.items, (item) => item.id, (item, index) => {
13683
+ * return html`<li>${index}: ${item.name}</li>`;
13684
+ * })}
13685
+ * </ol>
13686
+ * `
13687
+ * ```
13688
+ *
13689
+ * **Important**: If providing a `keyFn`, keys *must* be unique for all items in a
13690
+ * given call to `repeat`. The behavior when two or more items have the same key
13691
+ * is undefined.
13692
+ *
13693
+ * If no `keyFn` is provided, this directive will perform similar to mapping
13694
+ * items to values, and DOM will be reused against potentially different items.
13695
+ */
13696
+ const repeat = directive(RepeatDirective);
13697
+
13148
13698
  /**
13149
13699
  * @summary ComboBox component is used for users to make one or more selections from a list through user input, keyboard or mouse actions
13150
13700
  *
@@ -13213,6 +13763,29 @@
13213
13763
  if (!this._isTouched && this.value === "")
13214
13764
  return;
13215
13765
  this.invalid = !this._mixinReportValidity();
13766
+ // When value is updated by user and it doesn't map to selectedItems, we should re-map selectedItems
13767
+ const selectedItemVal = this.selectedItems.map(val => val.value).join(";");
13768
+ if (selectedItemVal !== this.value) {
13769
+ this._updateValueAndDisplayValue();
13770
+ }
13771
+ }
13772
+ _handleMenuListChange() {
13773
+ this._updateValueAndDisplayValue();
13774
+ this._renderedMenu = this.menuList;
13775
+ }
13776
+ _updateValueAndDisplayValue() {
13777
+ var _a, _b;
13778
+ const valueArray = this.value.split(";");
13779
+ const initialSelectedItem = this.menuList.filter(({ value }) => valueArray.includes(value));
13780
+ this.selectedItems = [...initialSelectedItem];
13781
+ // When the new filtered items don't match value we update it
13782
+ const updatedValue = initialSelectedItem.map(item => item.value).join(";");
13783
+ if (updatedValue !== this.value) {
13784
+ this.value = updatedValue;
13785
+ }
13786
+ if (!this.multiSelect) {
13787
+ this.displayValue = (_b = (_a = initialSelectedItem[0]) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : "";
13788
+ }
13216
13789
  }
13217
13790
  // Called each time the user types in the <sgds-input>, we set .value and show the menu
13218
13791
  async _handleInputChange(e) {
@@ -13335,29 +13908,39 @@
13335
13908
  }
13336
13909
  _renderMenu() {
13337
13910
  const emptyMenu = html ` <div class="empty-menu">No options</div> `;
13338
- const menu = this._renderedMenu.map(item => {
13339
- let isActive = false;
13340
- if (this.multiSelect) {
13341
- const selectedItemValueArray = this.selectedItems.map(i => i.value);
13342
- isActive = selectedItemValueArray.includes(item.value);
13343
- }
13344
- else {
13345
- isActive = item.value === this.value;
13346
- }
13347
- return html `
13348
- <sgds-combo-box-item
13349
- ?active=${isActive}
13350
- ?checkbox=${this.multiSelect}
13351
- value=${item.value}
13352
- @sgds-select=${this._handleItemSelected}
13353
- @sgds-unselect=${this._handleItemUnselect}
13354
- >
13355
- ${item.label}
13356
- </sgds-combo-box-item>
13357
- `;
13358
- });
13359
- return this._renderedMenu.length === 0 ? emptyMenu : menu;
13911
+ const menu = this._renderedMenu.length === 0
13912
+ ? emptyMenu
13913
+ : repeat(this._renderedMenu, item => item.value, item => {
13914
+ let isActive = false;
13915
+ if (this.multiSelect) {
13916
+ const selectedItemValueArray = this.selectedItems.map(i => i.value);
13917
+ isActive = selectedItemValueArray.includes(item.value);
13918
+ }
13919
+ else {
13920
+ isActive = item.value === this.value;
13921
+ }
13922
+ return html `
13923
+ <sgds-combo-box-item
13924
+ ?active=${isActive}
13925
+ ?checkbox=${this.multiSelect}
13926
+ value=${item.value}
13927
+ @sgds-select=${this._handleItemSelected}
13928
+ @sgds-unselect=${this._handleItemUnselect}
13929
+ >
13930
+ ${item.label}
13931
+ </sgds-combo-box-item>
13932
+ `;
13933
+ });
13934
+ return menu;
13360
13935
  }
13936
+ /**
13937
+ * Used `repeat` helper from Lit to render instead of .map:
13938
+ * The reassigning of value is affecting the truncation on badge as it is not triggering the slot change event.
13939
+ *
13940
+ * To compare this to lit-html's default handling for lists, consider reversing a large list of names:
13941
+ * For a list created using Array.map, lit-html maintains the DOM nodes for the list items, but reassigns the values
13942
+ * For a list created using repeat, the repeat directive reorders the existing DOM nodes, so the nodes representing the first list item move to the last position.
13943
+ */
13361
13944
  _renderInput() {
13362
13945
  const wantFeedbackStyle = this.hasFeedback;
13363
13946
  return html `
@@ -13373,7 +13956,7 @@
13373
13956
  <div class="combobox-input-container">
13374
13957
  ${this.multiSelect
13375
13958
  ? html `
13376
- ${this.selectedItems.map(item => html `<sgds-badge
13959
+ ${repeat(this.selectedItems, item => item.value, item => html `<sgds-badge
13377
13960
  outlined
13378
13961
  variant="neutral"
13379
13962
  show
@@ -13456,6 +14039,9 @@
13456
14039
  __decorate([
13457
14040
  watch("value", { waitUntilFirstUpdate: true })
13458
14041
  ], SgdsComboBox.prototype, "_handleValueChange", null);
14042
+ __decorate([
14043
+ watch("menuList", { waitUntilFirstUpdate: true })
14044
+ ], SgdsComboBox.prototype, "_handleMenuListChange", null);
13459
14045
 
13460
14046
  register("sgds-combo-box", SgdsComboBox);
13461
14047