@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.
@@ -1235,7 +1235,7 @@
1235
1235
  };
1236
1236
  issueWarning$4('dev-mode', `Lit is in dev mode. Not recommended for production!`);
1237
1237
  }
1238
- const wrap = global$2.ShadyDOM?.inUse &&
1238
+ const wrap$1 = global$2.ShadyDOM?.inUse &&
1239
1239
  global$2.ShadyDOM?.noPatch === true
1240
1240
  ? global$2.ShadyDOM.wrap
1241
1241
  : (node) => node;
@@ -1287,7 +1287,7 @@
1287
1287
  const nodeMarker = `<${markerMatch}>`;
1288
1288
  const d = document;
1289
1289
  // Creates a dynamic marker. We never have to search for these in the DOM.
1290
- const createMarker = () => d.createComment('');
1290
+ const createMarker$1 = () => d.createComment('');
1291
1291
  const isPrimitive = (value) => value === null || (typeof value != 'object' && typeof value != 'function');
1292
1292
  const isArray = Array.isArray;
1293
1293
  const isIterable = (value) => isArray(value) ||
@@ -1719,7 +1719,7 @@
1719
1719
  // normalized when cloning in IE (could simplify when
1720
1720
  // IE is no longer supported)
1721
1721
  for (let i = 0; i < lastIndex; i++) {
1722
- node.append(strings[i], createMarker());
1722
+ node.append(strings[i], createMarker$1());
1723
1723
  // Walk past the marker node we just added
1724
1724
  walker.nextNode();
1725
1725
  parts.push({ type: CHILD_PART, index: ++nodeIndex });
@@ -1727,7 +1727,7 @@
1727
1727
  // Note because this marker is added after the walker's current
1728
1728
  // node, it will be walked to in the outer loop (and ignored), so
1729
1729
  // we don't need to adjust nodeIndex here
1730
- node.append(strings[lastIndex], createMarker());
1730
+ node.append(strings[lastIndex], createMarker$1());
1731
1731
  }
1732
1732
  }
1733
1733
  }
@@ -1857,7 +1857,7 @@
1857
1857
  if (nodeIndex === templatePart.index) {
1858
1858
  let part;
1859
1859
  if (templatePart.type === CHILD_PART) {
1860
- part = new ChildPart(node, node.nextSibling, this, options);
1860
+ part = new ChildPart$1(node, node.nextSibling, this, options);
1861
1861
  }
1862
1862
  else if (templatePart.type === ATTRIBUTE_PART) {
1863
1863
  part = new templatePart.ctor(node, templatePart.name, templatePart.strings, this, options);
@@ -1907,7 +1907,7 @@
1907
1907
  }
1908
1908
  }
1909
1909
  }
1910
- class ChildPart {
1910
+ class ChildPart$1 {
1911
1911
  // See comment in Disconnectable interface for why this is a getter
1912
1912
  get _$isConnected() {
1913
1913
  // ChildParts that are not at the root should always be created with a
@@ -1954,7 +1954,7 @@
1954
1954
  * consists of all child nodes of `.parentNode`.
1955
1955
  */
1956
1956
  get parentNode() {
1957
- let parentNode = wrap(this._$startNode).parentNode;
1957
+ let parentNode = wrap$1(this._$startNode).parentNode;
1958
1958
  const parent = this._$parent;
1959
1959
  if (parent !== undefined &&
1960
1960
  parentNode?.nodeType === 11 /* Node.DOCUMENT_FRAGMENT */) {
@@ -2028,7 +2028,7 @@
2028
2028
  }
2029
2029
  }
2030
2030
  _insert(node) {
2031
- return wrap(wrap(this._$startNode).parentNode).insertBefore(node, this._$endNode);
2031
+ return wrap$1(wrap$1(this._$startNode).parentNode).insertBefore(node, this._$endNode);
2032
2032
  }
2033
2033
  _commitNode(value) {
2034
2034
  if (this._$committedValue !== value) {
@@ -2075,7 +2075,7 @@
2075
2075
  // Text node. We can now just replace the text content (.data) of the node.
2076
2076
  if (this._$committedValue !== nothing &&
2077
2077
  isPrimitive(this._$committedValue)) {
2078
- const node = wrap(this._$startNode).nextSibling;
2078
+ const node = wrap$1(this._$startNode).nextSibling;
2079
2079
  {
2080
2080
  if (this._textSanitizer === undefined) {
2081
2081
  this._textSanitizer = createSanitizer(node, 'data', 'property');
@@ -2201,7 +2201,7 @@
2201
2201
  // TODO (justinfagnani): test perf impact of always creating two parts
2202
2202
  // instead of sharing parts between nodes
2203
2203
  // https://github.com/lit/lit/issues/1266
2204
- itemParts.push((itemPart = new ChildPart(this._insert(createMarker()), this._insert(createMarker()), this, this.options)));
2204
+ itemParts.push((itemPart = new ChildPart$1(this._insert(createMarker$1()), this._insert(createMarker$1()), this, this.options)));
2205
2205
  }
2206
2206
  else {
2207
2207
  // Reuse an existing part
@@ -2212,7 +2212,7 @@
2212
2212
  }
2213
2213
  if (partIndex < itemParts.length) {
2214
2214
  // itemParts always have end nodes
2215
- this._$clear(itemPart && wrap(itemPart._$endNode).nextSibling, partIndex);
2215
+ this._$clear(itemPart && wrap$1(itemPart._$endNode).nextSibling, partIndex);
2216
2216
  // Truncate the parts array so _value reflects the current state
2217
2217
  itemParts.length = partIndex;
2218
2218
  }
@@ -2228,11 +2228,11 @@
2228
2228
  *
2229
2229
  * @internal
2230
2230
  */
2231
- _$clear(start = wrap(this._$startNode).nextSibling, from) {
2231
+ _$clear(start = wrap$1(this._$startNode).nextSibling, from) {
2232
2232
  this._$notifyConnectionChanged?.(false, true, from);
2233
2233
  while (start && start !== this._$endNode) {
2234
- const n = wrap(start).nextSibling;
2235
- wrap(start).remove();
2234
+ const n = wrap$1(start).nextSibling;
2235
+ wrap$1(start).remove();
2236
2236
  start = n;
2237
2237
  }
2238
2238
  }
@@ -2350,7 +2350,7 @@
2350
2350
  /** @internal */
2351
2351
  _commitValue(value) {
2352
2352
  if (value === nothing) {
2353
- wrap(this.element).removeAttribute(this.name);
2353
+ wrap$1(this.element).removeAttribute(this.name);
2354
2354
  }
2355
2355
  else {
2356
2356
  {
@@ -2367,7 +2367,7 @@
2367
2367
  value,
2368
2368
  options: this.options,
2369
2369
  });
2370
- wrap(this.element).setAttribute(this.name, (value ?? ''));
2370
+ wrap$1(this.element).setAttribute(this.name, (value ?? ''));
2371
2371
  }
2372
2372
  }
2373
2373
  }
@@ -2411,7 +2411,7 @@
2411
2411
  value: !!(value && value !== nothing),
2412
2412
  options: this.options,
2413
2413
  });
2414
- wrap(this.element).toggleAttribute(this.name, !!value && value !== nothing);
2414
+ wrap$1(this.element).toggleAttribute(this.name, !!value && value !== nothing);
2415
2415
  }
2416
2416
  }
2417
2417
  class EventPart extends AttributePart {
@@ -2502,10 +2502,46 @@
2502
2502
  resolveDirective(this, value);
2503
2503
  }
2504
2504
  }
2505
+ /**
2506
+ * END USERS SHOULD NOT RELY ON THIS OBJECT.
2507
+ *
2508
+ * Private exports for use by other Lit packages, not intended for use by
2509
+ * external users.
2510
+ *
2511
+ * We currently do not make a mangled rollup build of the lit-ssr code. In order
2512
+ * to keep a number of (otherwise private) top-level exports mangled in the
2513
+ * client side code, we export a _$LH object containing those members (or
2514
+ * helper methods for accessing private fields of those members), and then
2515
+ * re-export them for use in lit-ssr. This keeps lit-ssr agnostic to whether the
2516
+ * client-side code is being used in `dev` mode or `prod` mode.
2517
+ *
2518
+ * This has a unique name, to disambiguate it from private exports in
2519
+ * lit-element, which re-exports all of lit-html.
2520
+ *
2521
+ * @private
2522
+ */
2523
+ const _$LH = {
2524
+ // Used in lit-ssr
2525
+ _boundAttributeSuffix: boundAttributeSuffix,
2526
+ _marker: marker,
2527
+ _markerMatch: markerMatch,
2528
+ _HTML_RESULT: HTML_RESULT,
2529
+ _getTemplateHtml: getTemplateHtml,
2530
+ // Used in tests and private-ssr-support
2531
+ _TemplateInstance: TemplateInstance,
2532
+ _isIterable: isIterable,
2533
+ _resolveDirective: resolveDirective,
2534
+ _ChildPart: ChildPart$1,
2535
+ _AttributePart: AttributePart,
2536
+ _BooleanAttributePart: BooleanAttributePart,
2537
+ _EventPart: EventPart,
2538
+ _PropertyPart: PropertyPart,
2539
+ _ElementPart: ElementPart,
2540
+ };
2505
2541
  // Apply polyfills if available
2506
2542
  const polyfillSupport$2 = global$2.litHtmlPolyfillSupportDevMode
2507
2543
  ;
2508
- polyfillSupport$2?.(Template, ChildPart);
2544
+ polyfillSupport$2?.(Template, ChildPart$1);
2509
2545
  // IMPORTANT: do not change the property name or the assignment expression.
2510
2546
  // This line will be used in regexes to search for lit-html usage.
2511
2547
  (global$2.litHtmlVersions ??= []).push('3.2.0');
@@ -2564,7 +2600,7 @@
2564
2600
  const endNode = options?.renderBefore ?? null;
2565
2601
  // This property needs to remain unminified.
2566
2602
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2567
- partOwnerNode['_$litPart$'] = part = new ChildPart(container.insertBefore(createMarker(), endNode), endNode, undefined, options ?? {});
2603
+ partOwnerNode['_$litPart$'] = part = new ChildPart$1(container.insertBefore(createMarker$1(), endNode), endNode, undefined, options ?? {});
2568
2604
  }
2569
2605
  part._$setValue(value);
2570
2606
  debugLogEvent$1 &&
@@ -11469,7 +11505,8 @@
11469
11505
  * Copyright 2020 Google LLC
11470
11506
  * SPDX-License-Identifier: BSD-3-Clause
11471
11507
  */
11472
- window.ShadyDOM?.inUse &&
11508
+ const { _ChildPart: ChildPart } = _$LH;
11509
+ const wrap = window.ShadyDOM?.inUse &&
11473
11510
  window.ShadyDOM?.noPatch === true
11474
11511
  ? window.ShadyDOM.wrap
11475
11512
  : (node) => node;
@@ -11482,6 +11519,79 @@
11482
11519
  * parts do not.
11483
11520
  */
11484
11521
  const isSingleExpression = (part) => part.strings === undefined;
11522
+ const createMarker = () => document.createComment('');
11523
+ /**
11524
+ * Inserts a ChildPart into the given container ChildPart's DOM, either at the
11525
+ * end of the container ChildPart, or before the optional `refPart`.
11526
+ *
11527
+ * This does not add the part to the containerPart's committed value. That must
11528
+ * be done by callers.
11529
+ *
11530
+ * @param containerPart Part within which to add the new ChildPart
11531
+ * @param refPart Part before which to add the new ChildPart; when omitted the
11532
+ * part added to the end of the `containerPart`
11533
+ * @param part Part to insert, or undefined to create a new part
11534
+ */
11535
+ const insertPart = (containerPart, refPart, part) => {
11536
+ const container = wrap(containerPart._$startNode).parentNode;
11537
+ const refNode = refPart === undefined ? containerPart._$endNode : refPart._$startNode;
11538
+ if (part === undefined) {
11539
+ const startNode = wrap(container).insertBefore(createMarker(), refNode);
11540
+ const endNode = wrap(container).insertBefore(createMarker(), refNode);
11541
+ part = new ChildPart(startNode, endNode, containerPart, containerPart.options);
11542
+ }
11543
+ else {
11544
+ const endNode = wrap(part._$endNode).nextSibling;
11545
+ const oldParent = part._$parent;
11546
+ const parentChanged = oldParent !== containerPart;
11547
+ if (parentChanged) {
11548
+ part._$reparentDisconnectables?.(containerPart);
11549
+ // Note that although `_$reparentDisconnectables` updates the part's
11550
+ // `_$parent` reference after unlinking from its current parent, that
11551
+ // method only exists if Disconnectables are present, so we need to
11552
+ // unconditionally set it here
11553
+ part._$parent = containerPart;
11554
+ // Since the _$isConnected getter is somewhat costly, only
11555
+ // read it once we know the subtree has directives that need
11556
+ // to be notified
11557
+ let newConnectionState;
11558
+ if (part._$notifyConnectionChanged !== undefined &&
11559
+ (newConnectionState = containerPart._$isConnected) !==
11560
+ oldParent._$isConnected) {
11561
+ part._$notifyConnectionChanged(newConnectionState);
11562
+ }
11563
+ }
11564
+ if (endNode !== refNode || parentChanged) {
11565
+ let start = part._$startNode;
11566
+ while (start !== endNode) {
11567
+ const n = wrap(start).nextSibling;
11568
+ wrap(container).insertBefore(start, refNode);
11569
+ start = n;
11570
+ }
11571
+ }
11572
+ }
11573
+ return part;
11574
+ };
11575
+ /**
11576
+ * Sets the value of a Part.
11577
+ *
11578
+ * Note that this should only be used to set/update the value of user-created
11579
+ * parts (i.e. those created using `insertPart`); it should not be used
11580
+ * by directives to set the value of the directive's container part. Directives
11581
+ * should return a value from `update`/`render` to update their part state.
11582
+ *
11583
+ * For directives that require setting their part value asynchronously, they
11584
+ * should extend `AsyncDirective` and call `this.setValue()`.
11585
+ *
11586
+ * @param part Part to set
11587
+ * @param value Value to set
11588
+ * @param index For `AttributePart`s, the index to set
11589
+ * @param directiveParent Used internally; should not be set by user
11590
+ */
11591
+ const setChildPartValue = (part, value, directiveParent = part) => {
11592
+ part._$setValue(value, directiveParent);
11593
+ return part;
11594
+ };
11485
11595
  // A sentinel value that can never appear as a part value except when set by
11486
11596
  // live(). Used to force a dirty-check to fail and cause a re-render.
11487
11597
  const RESET_VALUE = {};
@@ -11497,6 +11607,36 @@
11497
11607
  * @param value
11498
11608
  */
11499
11609
  const setCommittedValue = (part, value = RESET_VALUE) => (part._$committedValue = value);
11610
+ /**
11611
+ * Returns the committed value of a ChildPart.
11612
+ *
11613
+ * The committed value is used for change detection and efficient updates of
11614
+ * the part. It can differ from the value set by the template or directive in
11615
+ * cases where the template value is transformed before being committed.
11616
+ *
11617
+ * - `TemplateResult`s are committed as a `TemplateInstance`
11618
+ * - Iterables are committed as `Array<ChildPart>`
11619
+ * - All other types are committed as the template value or value returned or
11620
+ * set by a directive.
11621
+ *
11622
+ * @param part
11623
+ */
11624
+ const getCommittedValue = (part) => part._$committedValue;
11625
+ /**
11626
+ * Removes a ChildPart from the DOM, including any of its content.
11627
+ *
11628
+ * @param part The Part to remove
11629
+ */
11630
+ const removePart = (part) => {
11631
+ part._$notifyConnectionChanged?.(false, true);
11632
+ let start = part._$startNode;
11633
+ const end = wrap(part._$endNode).nextSibling;
11634
+ while (start !== end) {
11635
+ const n = wrap(start).nextSibling;
11636
+ wrap(start).remove();
11637
+ start = n;
11638
+ }
11639
+ };
11500
11640
 
11501
11641
  /**
11502
11642
  * @license
@@ -14837,6 +14977,416 @@
14837
14977
 
14838
14978
  var css_248z$K = 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)}`;
14839
14979
 
14980
+ /**
14981
+ * @license
14982
+ * Copyright 2017 Google LLC
14983
+ * SPDX-License-Identifier: BSD-3-Clause
14984
+ */
14985
+ // Helper for generating a map of array item to its index over a subset
14986
+ // of an array (used to lazily generate `newKeyToIndexMap` and
14987
+ // `oldKeyToIndexMap`)
14988
+ const generateMap = (list, start, end) => {
14989
+ const map = new Map();
14990
+ for (let i = start; i <= end; i++) {
14991
+ map.set(list[i], i);
14992
+ }
14993
+ return map;
14994
+ };
14995
+ class RepeatDirective extends Directive {
14996
+ constructor(partInfo) {
14997
+ super(partInfo);
14998
+ if (partInfo.type !== PartType.CHILD) {
14999
+ throw new Error('repeat() can only be used in text expressions');
15000
+ }
15001
+ }
15002
+ _getValuesAndKeys(items, keyFnOrTemplate, template) {
15003
+ let keyFn;
15004
+ if (template === undefined) {
15005
+ template = keyFnOrTemplate;
15006
+ }
15007
+ else if (keyFnOrTemplate !== undefined) {
15008
+ keyFn = keyFnOrTemplate;
15009
+ }
15010
+ const keys = [];
15011
+ const values = [];
15012
+ let index = 0;
15013
+ for (const item of items) {
15014
+ keys[index] = keyFn ? keyFn(item, index) : index;
15015
+ values[index] = template(item, index);
15016
+ index++;
15017
+ }
15018
+ return {
15019
+ values,
15020
+ keys,
15021
+ };
15022
+ }
15023
+ render(items, keyFnOrTemplate, template) {
15024
+ return this._getValuesAndKeys(items, keyFnOrTemplate, template).values;
15025
+ }
15026
+ update(containerPart, [items, keyFnOrTemplate, template]) {
15027
+ // Old part & key lists are retrieved from the last update (which may
15028
+ // be primed by hydration)
15029
+ const oldParts = getCommittedValue(containerPart);
15030
+ const { values: newValues, keys: newKeys } = this._getValuesAndKeys(items, keyFnOrTemplate, template);
15031
+ // We check that oldParts, the committed value, is an Array as an
15032
+ // indicator that the previous value came from a repeat() call. If
15033
+ // oldParts is not an Array then this is the first render and we return
15034
+ // an array for lit-html's array handling to render, and remember the
15035
+ // keys.
15036
+ if (!Array.isArray(oldParts)) {
15037
+ this._itemKeys = newKeys;
15038
+ return newValues;
15039
+ }
15040
+ // In SSR hydration it's possible for oldParts to be an array but for us
15041
+ // to not have item keys because the update() hasn't run yet. We set the
15042
+ // keys to an empty array. This will cause all oldKey/newKey comparisons
15043
+ // to fail and execution to fall to the last nested brach below which
15044
+ // reuses the oldPart.
15045
+ const oldKeys = (this._itemKeys ??= []);
15046
+ // New part list will be built up as we go (either reused from
15047
+ // old parts or created for new keys in this update). This is
15048
+ // saved in the above cache at the end of the update.
15049
+ const newParts = [];
15050
+ // Maps from key to index for current and previous update; these
15051
+ // are generated lazily only when needed as a performance
15052
+ // optimization, since they are only required for multiple
15053
+ // non-contiguous changes in the list, which are less common.
15054
+ let newKeyToIndexMap;
15055
+ let oldKeyToIndexMap;
15056
+ // Head and tail pointers to old parts and new values
15057
+ let oldHead = 0;
15058
+ let oldTail = oldParts.length - 1;
15059
+ let newHead = 0;
15060
+ let newTail = newValues.length - 1;
15061
+ // Overview of O(n) reconciliation algorithm (general approach
15062
+ // based on ideas found in ivi, vue, snabbdom, etc.):
15063
+ //
15064
+ // * We start with the list of old parts and new values (and
15065
+ // arrays of their respective keys), head/tail pointers into
15066
+ // each, and we build up the new list of parts by updating
15067
+ // (and when needed, moving) old parts or creating new ones.
15068
+ // The initial scenario might look like this (for brevity of
15069
+ // the diagrams, the numbers in the array reflect keys
15070
+ // associated with the old parts or new values, although keys
15071
+ // and parts/values are actually stored in parallel arrays
15072
+ // indexed using the same head/tail pointers):
15073
+ //
15074
+ // oldHead v v oldTail
15075
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
15076
+ // newParts: [ , , , , , , ]
15077
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] <- reflects the user's new
15078
+ // item order
15079
+ // newHead ^ ^ newTail
15080
+ //
15081
+ // * Iterate old & new lists from both sides, updating,
15082
+ // swapping, or removing parts at the head/tail locations
15083
+ // until neither head nor tail can move.
15084
+ //
15085
+ // * Example below: keys at head pointers match, so update old
15086
+ // part 0 in-place (no need to move it) and record part 0 in
15087
+ // the `newParts` list. The last thing we do is advance the
15088
+ // `oldHead` and `newHead` pointers (will be reflected in the
15089
+ // next diagram).
15090
+ //
15091
+ // oldHead v v oldTail
15092
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
15093
+ // newParts: [0, , , , , , ] <- heads matched: update 0
15094
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldHead
15095
+ // & newHead
15096
+ // newHead ^ ^ newTail
15097
+ //
15098
+ // * Example below: head pointers don't match, but tail
15099
+ // pointers do, so update part 6 in place (no need to move
15100
+ // it), and record part 6 in the `newParts` list. Last,
15101
+ // advance the `oldTail` and `oldHead` pointers.
15102
+ //
15103
+ // oldHead v v oldTail
15104
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
15105
+ // newParts: [0, , , , , , 6] <- tails matched: update 6
15106
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldTail
15107
+ // & newTail
15108
+ // newHead ^ ^ newTail
15109
+ //
15110
+ // * If neither head nor tail match; next check if one of the
15111
+ // old head/tail items was removed. We first need to generate
15112
+ // the reverse map of new keys to index (`newKeyToIndexMap`),
15113
+ // which is done once lazily as a performance optimization,
15114
+ // since we only hit this case if multiple non-contiguous
15115
+ // changes were made. Note that for contiguous removal
15116
+ // anywhere in the list, the head and tails would advance
15117
+ // from either end and pass each other before we get to this
15118
+ // case and removals would be handled in the final while loop
15119
+ // without needing to generate the map.
15120
+ //
15121
+ // * Example below: The key at `oldTail` was removed (no longer
15122
+ // in the `newKeyToIndexMap`), so remove that part from the
15123
+ // DOM and advance just the `oldTail` pointer.
15124
+ //
15125
+ // oldHead v v oldTail
15126
+ // oldKeys: [0, 1, 2, 3, 4, 5, 6]
15127
+ // newParts: [0, , , , , , 6] <- 5 not in new map: remove
15128
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] 5 and advance oldTail
15129
+ // newHead ^ ^ newTail
15130
+ //
15131
+ // * Once head and tail cannot move, any mismatches are due to
15132
+ // either new or moved items; if a new key is in the previous
15133
+ // "old key to old index" map, move the old part to the new
15134
+ // location, otherwise create and insert a new part. Note
15135
+ // that when moving an old part we null its position in the
15136
+ // oldParts array if it lies between the head and tail so we
15137
+ // know to skip it when the pointers get there.
15138
+ //
15139
+ // * Example below: neither head nor tail match, and neither
15140
+ // were removed; so find the `newHead` key in the
15141
+ // `oldKeyToIndexMap`, and move that old part's DOM into the
15142
+ // next head position (before `oldParts[oldHead]`). Last,
15143
+ // null the part in the `oldPart` array since it was
15144
+ // somewhere in the remaining oldParts still to be scanned
15145
+ // (between the head and tail pointers) so that we know to
15146
+ // skip that old part on future iterations.
15147
+ //
15148
+ // oldHead v v oldTail
15149
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
15150
+ // newParts: [0, 2, , , , , 6] <- stuck: update & move 2
15151
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] into place and advance
15152
+ // newHead
15153
+ // newHead ^ ^ newTail
15154
+ //
15155
+ // * Note that for moves/insertions like the one above, a part
15156
+ // inserted at the head pointer is inserted before the
15157
+ // current `oldParts[oldHead]`, and a part inserted at the
15158
+ // tail pointer is inserted before `newParts[newTail+1]`. The
15159
+ // seeming asymmetry lies in the fact that new parts are
15160
+ // moved into place outside in, so to the right of the head
15161
+ // pointer are old parts, and to the right of the tail
15162
+ // pointer are new parts.
15163
+ //
15164
+ // * We always restart back from the top of the algorithm,
15165
+ // allowing matching and simple updates in place to
15166
+ // continue...
15167
+ //
15168
+ // * Example below: the head pointers once again match, so
15169
+ // simply update part 1 and record it in the `newParts`
15170
+ // array. Last, advance both head pointers.
15171
+ //
15172
+ // oldHead v v oldTail
15173
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
15174
+ // newParts: [0, 2, 1, , , , 6] <- heads matched: update 1
15175
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance both oldHead
15176
+ // & newHead
15177
+ // newHead ^ ^ newTail
15178
+ //
15179
+ // * As mentioned above, items that were moved as a result of
15180
+ // being stuck (the final else clause in the code below) are
15181
+ // marked with null, so we always advance old pointers over
15182
+ // these so we're comparing the next actual old value on
15183
+ // either end.
15184
+ //
15185
+ // * Example below: `oldHead` is null (already placed in
15186
+ // newParts), so advance `oldHead`.
15187
+ //
15188
+ // oldHead v v oldTail
15189
+ // oldKeys: [0, 1, -, 3, 4, 5, 6] <- old head already used:
15190
+ // newParts: [0, 2, 1, , , , 6] advance oldHead
15191
+ // newKeys: [0, 2, 1, 4, 3, 7, 6]
15192
+ // newHead ^ ^ newTail
15193
+ //
15194
+ // * Note it's not critical to mark old parts as null when they
15195
+ // are moved from head to tail or tail to head, since they
15196
+ // will be outside the pointer range and never visited again.
15197
+ //
15198
+ // * Example below: Here the old tail key matches the new head
15199
+ // key, so the part at the `oldTail` position and move its
15200
+ // DOM to the new head position (before `oldParts[oldHead]`).
15201
+ // Last, advance `oldTail` and `newHead` pointers.
15202
+ //
15203
+ // oldHead v v oldTail
15204
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
15205
+ // newParts: [0, 2, 1, 4, , , 6] <- old tail matches new
15206
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] head: update & move 4,
15207
+ // advance oldTail & newHead
15208
+ // newHead ^ ^ newTail
15209
+ //
15210
+ // * Example below: Old and new head keys match, so update the
15211
+ // old head part in place, and advance the `oldHead` and
15212
+ // `newHead` pointers.
15213
+ //
15214
+ // oldHead v oldTail
15215
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
15216
+ // newParts: [0, 2, 1, 4, 3, ,6] <- heads match: update 3
15217
+ // newKeys: [0, 2, 1, 4, 3, 7, 6] and advance oldHead &
15218
+ // newHead
15219
+ // newHead ^ ^ newTail
15220
+ //
15221
+ // * Once the new or old pointers move past each other then all
15222
+ // we have left is additions (if old list exhausted) or
15223
+ // removals (if new list exhausted). Those are handled in the
15224
+ // final while loops at the end.
15225
+ //
15226
+ // * Example below: `oldHead` exceeded `oldTail`, so we're done
15227
+ // with the main loop. Create the remaining part and insert
15228
+ // it at the new head position, and the update is complete.
15229
+ //
15230
+ // (oldHead > oldTail)
15231
+ // oldKeys: [0, 1, -, 3, 4, 5, 6]
15232
+ // newParts: [0, 2, 1, 4, 3, 7 ,6] <- create and insert 7
15233
+ // newKeys: [0, 2, 1, 4, 3, 7, 6]
15234
+ // newHead ^ newTail
15235
+ //
15236
+ // * Note that the order of the if/else clauses is not
15237
+ // important to the algorithm, as long as the null checks
15238
+ // come first (to ensure we're always working on valid old
15239
+ // parts) and that the final else clause comes last (since
15240
+ // that's where the expensive moves occur). The order of
15241
+ // remaining clauses is just a simple guess at which cases
15242
+ // will be most common.
15243
+ //
15244
+ // * Note, we could calculate the longest
15245
+ // increasing subsequence (LIS) of old items in new position,
15246
+ // and only move those not in the LIS set. However that costs
15247
+ // O(nlogn) time and adds a bit more code, and only helps
15248
+ // make rare types of mutations require fewer moves. The
15249
+ // above handles removes, adds, reversal, swaps, and single
15250
+ // moves of contiguous items in linear time, in the minimum
15251
+ // number of moves. As the number of multiple moves where LIS
15252
+ // might help approaches a random shuffle, the LIS
15253
+ // optimization becomes less helpful, so it seems not worth
15254
+ // the code at this point. Could reconsider if a compelling
15255
+ // case arises.
15256
+ while (oldHead <= oldTail && newHead <= newTail) {
15257
+ if (oldParts[oldHead] === null) {
15258
+ // `null` means old part at head has already been used
15259
+ // below; skip
15260
+ oldHead++;
15261
+ }
15262
+ else if (oldParts[oldTail] === null) {
15263
+ // `null` means old part at tail has already been used
15264
+ // below; skip
15265
+ oldTail--;
15266
+ }
15267
+ else if (oldKeys[oldHead] === newKeys[newHead]) {
15268
+ // Old head matches new head; update in place
15269
+ newParts[newHead] = setChildPartValue(oldParts[oldHead], newValues[newHead]);
15270
+ oldHead++;
15271
+ newHead++;
15272
+ }
15273
+ else if (oldKeys[oldTail] === newKeys[newTail]) {
15274
+ // Old tail matches new tail; update in place
15275
+ newParts[newTail] = setChildPartValue(oldParts[oldTail], newValues[newTail]);
15276
+ oldTail--;
15277
+ newTail--;
15278
+ }
15279
+ else if (oldKeys[oldHead] === newKeys[newTail]) {
15280
+ // Old head matches new tail; update and move to new tail
15281
+ newParts[newTail] = setChildPartValue(oldParts[oldHead], newValues[newTail]);
15282
+ insertPart(containerPart, newParts[newTail + 1], oldParts[oldHead]);
15283
+ oldHead++;
15284
+ newTail--;
15285
+ }
15286
+ else if (oldKeys[oldTail] === newKeys[newHead]) {
15287
+ // Old tail matches new head; update and move to new head
15288
+ newParts[newHead] = setChildPartValue(oldParts[oldTail], newValues[newHead]);
15289
+ insertPart(containerPart, oldParts[oldHead], oldParts[oldTail]);
15290
+ oldTail--;
15291
+ newHead++;
15292
+ }
15293
+ else {
15294
+ if (newKeyToIndexMap === undefined) {
15295
+ // Lazily generate key-to-index maps, used for removals &
15296
+ // moves below
15297
+ newKeyToIndexMap = generateMap(newKeys, newHead, newTail);
15298
+ oldKeyToIndexMap = generateMap(oldKeys, oldHead, oldTail);
15299
+ }
15300
+ if (!newKeyToIndexMap.has(oldKeys[oldHead])) {
15301
+ // Old head is no longer in new list; remove
15302
+ removePart(oldParts[oldHead]);
15303
+ oldHead++;
15304
+ }
15305
+ else if (!newKeyToIndexMap.has(oldKeys[oldTail])) {
15306
+ // Old tail is no longer in new list; remove
15307
+ removePart(oldParts[oldTail]);
15308
+ oldTail--;
15309
+ }
15310
+ else {
15311
+ // Any mismatches at this point are due to additions or
15312
+ // moves; see if we have an old part we can reuse and move
15313
+ // into place
15314
+ const oldIndex = oldKeyToIndexMap.get(newKeys[newHead]);
15315
+ const oldPart = oldIndex !== undefined ? oldParts[oldIndex] : null;
15316
+ if (oldPart === null) {
15317
+ // No old part for this value; create a new one and
15318
+ // insert it
15319
+ const newPart = insertPart(containerPart, oldParts[oldHead]);
15320
+ setChildPartValue(newPart, newValues[newHead]);
15321
+ newParts[newHead] = newPart;
15322
+ }
15323
+ else {
15324
+ // Reuse old part
15325
+ newParts[newHead] = setChildPartValue(oldPart, newValues[newHead]);
15326
+ insertPart(containerPart, oldParts[oldHead], oldPart);
15327
+ // This marks the old part as having been used, so that
15328
+ // it will be skipped in the first two checks above
15329
+ oldParts[oldIndex] = null;
15330
+ }
15331
+ newHead++;
15332
+ }
15333
+ }
15334
+ }
15335
+ // Add parts for any remaining new values
15336
+ while (newHead <= newTail) {
15337
+ // For all remaining additions, we insert before last new
15338
+ // tail, since old pointers are no longer valid
15339
+ const newPart = insertPart(containerPart, newParts[newTail + 1]);
15340
+ setChildPartValue(newPart, newValues[newHead]);
15341
+ newParts[newHead++] = newPart;
15342
+ }
15343
+ // Remove any remaining unused old parts
15344
+ while (oldHead <= oldTail) {
15345
+ const oldPart = oldParts[oldHead++];
15346
+ if (oldPart !== null) {
15347
+ removePart(oldPart);
15348
+ }
15349
+ }
15350
+ // Save order of new parts for next round
15351
+ this._itemKeys = newKeys;
15352
+ // Directly set part value, bypassing it's dirty-checking
15353
+ setCommittedValue(containerPart, newParts);
15354
+ return noChange;
15355
+ }
15356
+ }
15357
+ /**
15358
+ * A directive that repeats a series of values (usually `TemplateResults`)
15359
+ * generated from an iterable, and updates those items efficiently when the
15360
+ * iterable changes based on user-provided `keys` associated with each item.
15361
+ *
15362
+ * Note that if a `keyFn` is provided, strict key-to-DOM mapping is maintained,
15363
+ * meaning previous DOM for a given key is moved into the new position if
15364
+ * needed, and DOM will never be reused with values for different keys (new DOM
15365
+ * will always be created for new keys). This is generally the most efficient
15366
+ * way to use `repeat` since it performs minimum unnecessary work for insertions
15367
+ * and removals.
15368
+ *
15369
+ * The `keyFn` takes two parameters, the item and its index, and returns a unique key value.
15370
+ *
15371
+ * ```js
15372
+ * html`
15373
+ * <ol>
15374
+ * ${repeat(this.items, (item) => item.id, (item, index) => {
15375
+ * return html`<li>${index}: ${item.name}</li>`;
15376
+ * })}
15377
+ * </ol>
15378
+ * `
15379
+ * ```
15380
+ *
15381
+ * **Important**: If providing a `keyFn`, keys *must* be unique for all items in a
15382
+ * given call to `repeat`. The behavior when two or more items have the same key
15383
+ * is undefined.
15384
+ *
15385
+ * If no `keyFn` is provided, this directive will perform similar to mapping
15386
+ * items to values, and DOM will be reused against potentially different items.
15387
+ */
15388
+ const repeat = directive(RepeatDirective);
15389
+
14840
15390
  /**
14841
15391
  * @summary ComboBox component is used for users to make one or more selections from a list through user input, keyboard or mouse actions
14842
15392
  *
@@ -14905,6 +15455,29 @@
14905
15455
  if (!this._isTouched && this.value === "")
14906
15456
  return;
14907
15457
  this.invalid = !this._mixinReportValidity();
15458
+ // When value is updated by user and it doesn't map to selectedItems, we should re-map selectedItems
15459
+ const selectedItemVal = this.selectedItems.map(val => val.value).join(";");
15460
+ if (selectedItemVal !== this.value) {
15461
+ this._updateValueAndDisplayValue();
15462
+ }
15463
+ }
15464
+ _handleMenuListChange() {
15465
+ this._updateValueAndDisplayValue();
15466
+ this._renderedMenu = this.menuList;
15467
+ }
15468
+ _updateValueAndDisplayValue() {
15469
+ var _a, _b;
15470
+ const valueArray = this.value.split(";");
15471
+ const initialSelectedItem = this.menuList.filter(({ value }) => valueArray.includes(value));
15472
+ this.selectedItems = [...initialSelectedItem];
15473
+ // When the new filtered items don't match value we update it
15474
+ const updatedValue = initialSelectedItem.map(item => item.value).join(";");
15475
+ if (updatedValue !== this.value) {
15476
+ this.value = updatedValue;
15477
+ }
15478
+ if (!this.multiSelect) {
15479
+ this.displayValue = (_b = (_a = initialSelectedItem[0]) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : "";
15480
+ }
14908
15481
  }
14909
15482
  // Called each time the user types in the <sgds-input>, we set .value and show the menu
14910
15483
  async _handleInputChange(e) {
@@ -15027,29 +15600,39 @@
15027
15600
  }
15028
15601
  _renderMenu() {
15029
15602
  const emptyMenu = html$1 ` <div class="empty-menu">No options</div> `;
15030
- const menu = this._renderedMenu.map(item => {
15031
- let isActive = false;
15032
- if (this.multiSelect) {
15033
- const selectedItemValueArray = this.selectedItems.map(i => i.value);
15034
- isActive = selectedItemValueArray.includes(item.value);
15035
- }
15036
- else {
15037
- isActive = item.value === this.value;
15038
- }
15039
- return html$1 `
15040
- <sgds-combo-box-item
15041
- ?active=${isActive}
15042
- ?checkbox=${this.multiSelect}
15043
- value=${item.value}
15044
- @sgds-select=${this._handleItemSelected}
15045
- @sgds-unselect=${this._handleItemUnselect}
15046
- >
15047
- ${item.label}
15048
- </sgds-combo-box-item>
15049
- `;
15050
- });
15051
- return this._renderedMenu.length === 0 ? emptyMenu : menu;
15603
+ const menu = this._renderedMenu.length === 0
15604
+ ? emptyMenu
15605
+ : repeat(this._renderedMenu, item => item.value, item => {
15606
+ let isActive = false;
15607
+ if (this.multiSelect) {
15608
+ const selectedItemValueArray = this.selectedItems.map(i => i.value);
15609
+ isActive = selectedItemValueArray.includes(item.value);
15610
+ }
15611
+ else {
15612
+ isActive = item.value === this.value;
15613
+ }
15614
+ return html$1 `
15615
+ <sgds-combo-box-item
15616
+ ?active=${isActive}
15617
+ ?checkbox=${this.multiSelect}
15618
+ value=${item.value}
15619
+ @sgds-select=${this._handleItemSelected}
15620
+ @sgds-unselect=${this._handleItemUnselect}
15621
+ >
15622
+ ${item.label}
15623
+ </sgds-combo-box-item>
15624
+ `;
15625
+ });
15626
+ return menu;
15052
15627
  }
15628
+ /**
15629
+ * Used `repeat` helper from Lit to render instead of .map:
15630
+ * The reassigning of value is affecting the truncation on badge as it is not triggering the slot change event.
15631
+ *
15632
+ * To compare this to lit-html's default handling for lists, consider reversing a large list of names:
15633
+ * For a list created using Array.map, lit-html maintains the DOM nodes for the list items, but reassigns the values
15634
+ * 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.
15635
+ */
15053
15636
  _renderInput() {
15054
15637
  const wantFeedbackStyle = this.hasFeedback;
15055
15638
  return html$1 `
@@ -15065,7 +15648,7 @@
15065
15648
  <div class="combobox-input-container">
15066
15649
  ${this.multiSelect
15067
15650
  ? html$1 `
15068
- ${this.selectedItems.map(item => html$1 `<sgds-badge
15651
+ ${repeat(this.selectedItems, item => item.value, item => html$1 `<sgds-badge
15069
15652
  outlined
15070
15653
  variant="neutral"
15071
15654
  show
@@ -15148,6 +15731,9 @@
15148
15731
  __decorate([
15149
15732
  watch("value", { waitUntilFirstUpdate: true })
15150
15733
  ], SgdsComboBox.prototype, "_handleValueChange", null);
15734
+ __decorate([
15735
+ watch("menuList", { waitUntilFirstUpdate: true })
15736
+ ], SgdsComboBox.prototype, "_handleMenuListChange", null);
15151
15737
 
15152
15738
  /**
15153
15739
  * @name toDate