@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.
- package/components/ComboBox/index.umd.js +629 -43
- package/components/ComboBox/index.umd.js.map +1 -1
- package/components/ComboBox/sgds-combo-box.d.ts +11 -1
- package/components/ComboBox/sgds-combo-box.js +60 -23
- package/components/ComboBox/sgds-combo-box.js.map +1 -1
- package/components/index.umd.js +629 -43
- package/components/index.umd.js.map +1 -1
- package/index.umd.js +629 -43
- package/index.umd.js.map +1 -1
- package/package.json +1 -1
- package/react/components/ComboBox/sgds-combo-box.cjs.js +60 -23
- package/react/components/ComboBox/sgds-combo-box.cjs.js.map +1 -1
- package/react/components/ComboBox/sgds-combo-box.js +60 -23
- package/react/components/ComboBox/sgds-combo-box.js.map +1 -1
package/components/index.umd.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
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.
|
|
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
|