@pure-ds/core 0.7.26 → 0.7.28
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/.cursorrules +15 -0
- package/.github/copilot-instructions.md +15 -0
- package/custom-elements.json +160 -1
- package/dist/types/pds.d.ts +55 -1
- package/dist/types/public/assets/js/pds-ask.d.ts +1 -2
- package/dist/types/public/assets/js/pds-ask.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds-autocomplete.d.ts +36 -25
- package/dist/types/public/assets/js/pds-autocomplete.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds-enhancers.d.ts +4 -4
- package/dist/types/public/assets/js/pds-enhancers.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds-manager.d.ts +159 -444
- package/dist/types/public/assets/js/pds-manager.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds-toast.d.ts +6 -7
- package/dist/types/public/assets/js/pds-toast.d.ts.map +1 -1
- package/dist/types/public/assets/js/pds.d.ts +3 -4
- package/dist/types/public/assets/js/pds.d.ts.map +1 -1
- package/dist/types/public/assets/pds/components/pds-omnibox.d.ts +46 -0
- package/dist/types/public/assets/pds/components/pds-omnibox.d.ts.map +1 -1
- package/dist/types/src/js/common/ask.d.ts.map +1 -1
- package/dist/types/src/js/pds-core/pds-generator.d.ts.map +1 -1
- package/package.json +2 -2
- package/public/assets/js/app.js +1 -1
- package/public/assets/js/pds-ask.js +6 -6
- package/public/assets/js/pds-manager.js +91 -51
- package/public/assets/pds/components/pds-omnibox.js +70 -3
- package/public/assets/pds/components/pds-tags.js +160 -122
- package/public/assets/pds/core/pds-ask.js +6 -6
- package/public/assets/pds/core/pds-manager.js +91 -51
- package/public/assets/pds/custom-elements.json +175 -1
- package/public/assets/pds/pds-css-complete.json +1 -1
- package/public/assets/pds/vscode-custom-data.json +32 -0
- package/src/js/pds-core/pds-generator.js +75 -17
- package/src/js/pds.d.ts +55 -1
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} PdsOmniboxCategoryOptions
|
|
3
|
+
* @property {(options?: Object) => boolean} [trigger] - Determines whether the category is active for the current query
|
|
4
|
+
* @property {(options?: Object) => Promise<Array<Object>>|Array<Object>} [getItems] - Returns result items for this category
|
|
5
|
+
* @property {(item?: Object) => any} [action] - Called when a result item is selected
|
|
6
|
+
* @property {boolean} [useIconForInput] - Uses first result icon in the input field when enabled
|
|
7
|
+
* @property {number} [sortIndex] - Category ordering hint (higher values appear first)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} PdsOmniboxSettings
|
|
12
|
+
* @property {boolean} [hideCategory] - Hides category labels in suggestions
|
|
13
|
+
* @property {string} [itemGrid] - CSS grid-template-columns used for result rows
|
|
14
|
+
* @property {(item?: Object) => (string|null)} [iconHandler] - Custom renderer for item icons
|
|
15
|
+
* @property {boolean} [useIconForInput] - Global fallback for using an item icon in the input
|
|
16
|
+
* @property {Object<string, PdsOmniboxCategoryOptions>} categories - Category map keyed by category name
|
|
17
|
+
*/
|
|
18
|
+
|
|
1
19
|
/**
|
|
2
20
|
* Omnibox search input with PDS styling and form-associated behavior.
|
|
3
21
|
*
|
|
@@ -11,7 +29,7 @@
|
|
|
11
29
|
* @attr {boolean} required - Mark the input as required
|
|
12
30
|
* @attr {string} autocomplete - Native autocomplete attribute (default: off)
|
|
13
31
|
*
|
|
14
|
-
* @property {
|
|
32
|
+
* @property {PdsOmniboxSettings} settings - AutoComplete settings object (required by consumer)
|
|
15
33
|
*/
|
|
16
34
|
import { PDS } from "#pds";
|
|
17
35
|
|
|
@@ -43,6 +61,7 @@ export class PdsOmnibox extends HTMLElement {
|
|
|
43
61
|
#autoCompleteResizeHandler;
|
|
44
62
|
#autoCompleteScrollHandler;
|
|
45
63
|
#autoCompleteViewportHandler;
|
|
64
|
+
#autoCompleteFocusTimer;
|
|
46
65
|
#lengthProbe;
|
|
47
66
|
#suggestionsUpdatedHandler;
|
|
48
67
|
#suggestionsObserver;
|
|
@@ -71,6 +90,10 @@ export class PdsOmnibox extends HTMLElement {
|
|
|
71
90
|
}
|
|
72
91
|
|
|
73
92
|
disconnectedCallback() {
|
|
93
|
+
if (this.#autoCompleteFocusTimer) {
|
|
94
|
+
clearTimeout(this.#autoCompleteFocusTimer);
|
|
95
|
+
this.#autoCompleteFocusTimer = null;
|
|
96
|
+
}
|
|
74
97
|
this.#teardownAutoCompleteSizing();
|
|
75
98
|
this.#teardownSuggestionsObserver();
|
|
76
99
|
const autoComplete = this.#input?._autoComplete;
|
|
@@ -601,8 +624,19 @@ export class PdsOmnibox extends HTMLElement {
|
|
|
601
624
|
}
|
|
602
625
|
|
|
603
626
|
this.#wrapAutoCompleteResultsHandler(this.#input._autoComplete);
|
|
604
|
-
|
|
605
|
-
|
|
627
|
+
this.#wrapAutoCompleteController(this.#input._autoComplete);
|
|
628
|
+
if (this.#autoCompleteFocusTimer) {
|
|
629
|
+
clearTimeout(this.#autoCompleteFocusTimer);
|
|
630
|
+
}
|
|
631
|
+
const input = this.#input;
|
|
632
|
+
this.#autoCompleteFocusTimer = setTimeout(() => {
|
|
633
|
+
this.#autoCompleteFocusTimer = null;
|
|
634
|
+
if (!this.isConnected || !input || this.#input !== input) return;
|
|
635
|
+
const autoComplete = input._autoComplete;
|
|
636
|
+
if (!autoComplete || typeof autoComplete.focusHandler !== "function") {
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
autoComplete.focusHandler({ target: input });
|
|
606
640
|
this.#setupAutoCompleteSizing();
|
|
607
641
|
this.#updateSuggestionMaxHeight();
|
|
608
642
|
this.#setupSuggestionsObserver();
|
|
@@ -669,6 +703,39 @@ export class PdsOmnibox extends HTMLElement {
|
|
|
669
703
|
};
|
|
670
704
|
}
|
|
671
705
|
|
|
706
|
+
#wrapAutoCompleteController(autoComplete) {
|
|
707
|
+
if (!autoComplete || autoComplete.__pdsControllerWrapped) return;
|
|
708
|
+
const originalController = autoComplete.controller?.bind(autoComplete);
|
|
709
|
+
if (typeof originalController !== "function") return;
|
|
710
|
+
|
|
711
|
+
autoComplete.__pdsControllerWrapped = true;
|
|
712
|
+
autoComplete.controller = () => {
|
|
713
|
+
const controller = originalController();
|
|
714
|
+
if (!controller || typeof controller !== "object") return controller;
|
|
715
|
+
|
|
716
|
+
return {
|
|
717
|
+
...controller,
|
|
718
|
+
clear: (reason, ...args) => {
|
|
719
|
+
const reasonText = String(reason ?? "").toLowerCase();
|
|
720
|
+
if (reasonText === "blurred" && this.#isInputFocused()) {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (typeof controller.clear === "function") {
|
|
724
|
+
return controller.clear.call(controller, reason, ...args);
|
|
725
|
+
}
|
|
726
|
+
return undefined;
|
|
727
|
+
},
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
#isInputFocused() {
|
|
733
|
+
if (!this.#input) return false;
|
|
734
|
+
const activeInShadow = this.#root?.activeElement === this.#input;
|
|
735
|
+
const activeInDocument = document.activeElement === this.#input;
|
|
736
|
+
return activeInShadow || activeInDocument;
|
|
737
|
+
}
|
|
738
|
+
|
|
672
739
|
#setupAutoCompleteSizing() {
|
|
673
740
|
if (this.#autoCompleteResizeHandler) return;
|
|
674
741
|
this.#autoCompleteResizeHandler = () => this.#updateSuggestionMaxHeight();
|
|
@@ -1,22 +1,45 @@
|
|
|
1
1
|
import { PDS } from "#pds";
|
|
2
|
+
import "./pds-omnibox.js";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_OMNIBOX_OPTIONS = {
|
|
5
|
+
hideCategory: true,
|
|
6
|
+
itemGrid: "0 1fr 0",
|
|
7
|
+
iconHandler: () => "",
|
|
8
|
+
};
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
|
-
*
|
|
11
|
+
* Form-associated multi-select tags control built on top of `pds-omnibox`.
|
|
12
|
+
*
|
|
13
|
+
* Users select suggestions from the omnibox and each selection is rendered as a removable
|
|
14
|
+
* chip. The component keeps selection state synchronized across:
|
|
15
|
+
* - the `value` attribute (comma-separated ids)
|
|
16
|
+
* - the `value` property (`string[]`)
|
|
17
|
+
* - form value via ElementInternals
|
|
18
|
+
*
|
|
19
|
+
* The `options` object uses the same structure as `pds-omnibox.settings`.
|
|
20
|
+
* See `pds-omnibox` JSDoc for the full options schema.
|
|
21
|
+
* Before applying it, the component deep-merges internal defaults:
|
|
22
|
+
* - `hideCategory: true`
|
|
23
|
+
* - `itemGrid: "0 1fr 0"`
|
|
24
|
+
* - `iconHandler: () => ""`
|
|
25
|
+
*
|
|
26
|
+
* When `required` is set, at least one selected value is required for validity.
|
|
5
27
|
*
|
|
6
28
|
* @element pds-tags
|
|
7
29
|
* @formAssociated
|
|
8
30
|
*
|
|
9
|
-
* @attr {string} name - Form field name
|
|
10
|
-
* @attr {string} placeholder -
|
|
31
|
+
* @attr {string} name - Form field name; selected values are submitted under this name
|
|
32
|
+
* @attr {string} placeholder - Placeholder shown in omnibox input (default: "Search tags...")
|
|
11
33
|
* @attr {string} value - Comma-separated selected item ids
|
|
12
|
-
* @attr {string} options - JSON
|
|
13
|
-
* @attr {
|
|
14
|
-
* @attr {boolean}
|
|
15
|
-
*
|
|
34
|
+
* @attr {string} options - JSON object with the same shape as `pds-omnibox.settings`
|
|
35
|
+
* @attr {boolean} disabled - Disables omnibox interaction and chip remove actions
|
|
36
|
+
* @attr {boolean} required - Requires at least one selected value for validity
|
|
37
|
+
*
|
|
38
|
+
* @property {PdsOmniboxSettings|null} options - Omnibox options object (see `pds-omnibox.js` typedef)
|
|
39
|
+
* @property {string[]} value - Selected option ids (array form)
|
|
16
40
|
*
|
|
17
|
-
* @
|
|
18
|
-
* @
|
|
19
|
-
* @property {string[]} value - Selected option ids
|
|
41
|
+
* @fires {Event} input - Fired whenever selection changes
|
|
42
|
+
* @fires {Event} change - Fired whenever selection changes
|
|
20
43
|
*/
|
|
21
44
|
class PdsTags extends HTMLElement {
|
|
22
45
|
static formAssociated = true;
|
|
@@ -26,7 +49,6 @@ class PdsTags extends HTMLElement {
|
|
|
26
49
|
"placeholder",
|
|
27
50
|
"value",
|
|
28
51
|
"options",
|
|
29
|
-
"settings",
|
|
30
52
|
"disabled",
|
|
31
53
|
"required",
|
|
32
54
|
];
|
|
@@ -63,7 +85,6 @@ class PdsTags extends HTMLElement {
|
|
|
63
85
|
this.#upgradeProperty("value");
|
|
64
86
|
this.#upgradeProperty("name");
|
|
65
87
|
this.#upgradeProperty("placeholder");
|
|
66
|
-
this.#upgradeProperty("settings");
|
|
67
88
|
this.#upgradeProperty("disabled");
|
|
68
89
|
this.#upgradeProperty("required");
|
|
69
90
|
|
|
@@ -135,26 +156,21 @@ class PdsTags extends HTMLElement {
|
|
|
135
156
|
if (name === "value") {
|
|
136
157
|
if (this.#syncingValueAttribute) return;
|
|
137
158
|
this.#selectedIds = new Set(this.#parseValueList(newValue));
|
|
159
|
+
this.#reconcileSelection();
|
|
138
160
|
this.#renderChips();
|
|
161
|
+
void this.#hydrateSelectedItemsFromSource();
|
|
139
162
|
this.#syncFormValue();
|
|
140
163
|
return;
|
|
141
164
|
}
|
|
142
165
|
|
|
143
166
|
if (name === "options") {
|
|
144
|
-
this.#
|
|
145
|
-
this.#syncSelectedItemsFromItems();
|
|
167
|
+
this.#sourceSettings = this.#parseOptionsAttribute(newValue);
|
|
146
168
|
this.#reconcileSelection();
|
|
147
169
|
this.#renderChips();
|
|
148
|
-
|
|
149
|
-
this.#applyOmniboxSettings();
|
|
150
|
-
this.#syncFormValue();
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (name === "settings") {
|
|
155
|
-
this.#sourceSettings = this.#parseSettingsAttribute(newValue);
|
|
170
|
+
void this.#hydrateSelectedItemsFromSource();
|
|
156
171
|
this.#settingsInitialized = false;
|
|
157
172
|
this.#applyOmniboxSettings();
|
|
173
|
+
this.#syncFormValue();
|
|
158
174
|
return;
|
|
159
175
|
}
|
|
160
176
|
|
|
@@ -184,38 +200,23 @@ class PdsTags extends HTMLElement {
|
|
|
184
200
|
}
|
|
185
201
|
|
|
186
202
|
get options() {
|
|
187
|
-
return this.#
|
|
203
|
+
return this.#sourceSettings;
|
|
188
204
|
}
|
|
189
205
|
|
|
190
206
|
set options(value) {
|
|
191
207
|
this.#debug("set options", {
|
|
192
208
|
receivedType: value == null ? String(value) : Array.isArray(value) ? "array" : typeof value,
|
|
193
|
-
|
|
209
|
+
hasCategories: Boolean(value?.categories),
|
|
194
210
|
});
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
this.#syncSelectedItemsFromItems();
|
|
199
|
-
this.#debug("set options:normalized", { itemsCount: this.#items.length });
|
|
211
|
+
this.#sourceSettings = value && typeof value === "object" && !Array.isArray(value)
|
|
212
|
+
? value
|
|
213
|
+
: null;
|
|
200
214
|
this.#reconcileSelection();
|
|
201
215
|
this.#renderChips();
|
|
202
|
-
|
|
203
|
-
this.#applyOmniboxSettings();
|
|
204
|
-
this.#syncFormValue();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
get settings() {
|
|
208
|
-
return this.#sourceSettings;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
set settings(value) {
|
|
212
|
-
this.#debug("set settings", {
|
|
213
|
-
receivedType: value == null ? String(value) : typeof value,
|
|
214
|
-
hasCategories: Boolean(value?.categories),
|
|
215
|
-
});
|
|
216
|
-
this.#sourceSettings = value && typeof value === "object" ? value : null;
|
|
216
|
+
void this.#hydrateSelectedItemsFromSource();
|
|
217
217
|
this.#settingsInitialized = false;
|
|
218
218
|
this.#applyOmniboxSettings();
|
|
219
|
+
this.#syncFormValue();
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
get value() {
|
|
@@ -228,10 +229,10 @@ class PdsTags extends HTMLElement {
|
|
|
228
229
|
? value.map((item) => String(item))
|
|
229
230
|
: this.#parseValueList(String(value ?? ""));
|
|
230
231
|
this.#selectedIds = new Set(incoming);
|
|
231
|
-
this.#syncSelectedItemsFromItems();
|
|
232
232
|
this.#debug("set value:parsed", { selected: Array.from(this.#selectedIds) });
|
|
233
233
|
this.#reconcileSelection();
|
|
234
234
|
this.#renderChips();
|
|
235
|
+
void this.#hydrateSelectedItemsFromSource();
|
|
235
236
|
this.#applyOmniboxSettings();
|
|
236
237
|
this.#syncFormValue();
|
|
237
238
|
}
|
|
@@ -297,6 +298,7 @@ class PdsTags extends HTMLElement {
|
|
|
297
298
|
|
|
298
299
|
this.#syncAttributes();
|
|
299
300
|
this.#applyOmniboxSettings();
|
|
301
|
+
await this.#hydrateSelectedItemsFromSource();
|
|
300
302
|
this.#renderChips();
|
|
301
303
|
this.#syncFormValue();
|
|
302
304
|
this.#debug("ensureOmnibox:end", {
|
|
@@ -326,17 +328,10 @@ class PdsTags extends HTMLElement {
|
|
|
326
328
|
hasValueAttr: this.hasAttribute("value"),
|
|
327
329
|
});
|
|
328
330
|
if (this.hasAttribute("options")) {
|
|
329
|
-
this.#
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
this.#debug("hydrateFromAttributes:options", { itemsCount: this.#items.length });
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (this.hasAttribute("settings")) {
|
|
337
|
-
this.#sourceSettings = this.#parseSettingsAttribute(this.getAttribute("settings"));
|
|
338
|
-
this.#debug("hydrateFromAttributes:settings", {
|
|
339
|
-
hasSettings: Boolean(this.#sourceSettings),
|
|
331
|
+
this.#sourceSettings = this.#parseOptionsAttribute(this.getAttribute("options"));
|
|
332
|
+
this.#items = [];
|
|
333
|
+
this.#debug("hydrateFromAttributes:options", {
|
|
334
|
+
hasOptions: Boolean(this.#sourceSettings),
|
|
340
335
|
hasCategories: Boolean(this.#sourceSettings?.categories),
|
|
341
336
|
});
|
|
342
337
|
}
|
|
@@ -348,14 +343,71 @@ class PdsTags extends HTMLElement {
|
|
|
348
343
|
|
|
349
344
|
this.#reconcileSelection();
|
|
350
345
|
this.#syncAttributes();
|
|
351
|
-
this.#
|
|
346
|
+
if (!this.#selectedIds.size || !this.#sourceSettings?.categories) {
|
|
347
|
+
this.#renderChips();
|
|
348
|
+
}
|
|
352
349
|
this.#syncFormValue();
|
|
353
350
|
this.#debug("hydrateFromAttributes:end", {
|
|
354
|
-
|
|
351
|
+
knownItemsCount: this.#items.length,
|
|
355
352
|
selected: Array.from(this.#selectedIds),
|
|
356
353
|
});
|
|
357
354
|
}
|
|
358
355
|
|
|
356
|
+
async #hydrateSelectedItemsFromSource() {
|
|
357
|
+
if (!this.#selectedIds.size) return;
|
|
358
|
+
const sourceCategories = this.#sourceSettings?.categories;
|
|
359
|
+
if (!sourceCategories || typeof sourceCategories !== "object") return;
|
|
360
|
+
|
|
361
|
+
const knownIds = new Set(this.#items.map((item) => item.id));
|
|
362
|
+
const unresolved = new Set(
|
|
363
|
+
Array.from(this.#selectedIds).filter((id) => !this.#selectedItems.has(id) && !knownIds.has(id)),
|
|
364
|
+
);
|
|
365
|
+
if (!unresolved.size) return;
|
|
366
|
+
|
|
367
|
+
this.#debug("hydrateSelectedItemsFromSource:start", {
|
|
368
|
+
selected: Array.from(this.#selectedIds),
|
|
369
|
+
unresolved: Array.from(unresolved),
|
|
370
|
+
categoryCount: Object.keys(sourceCategories).length,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
for (const [categoryName, categoryConfig] of Object.entries(sourceCategories)) {
|
|
374
|
+
if (!unresolved.size) break;
|
|
375
|
+
const sourceGetItems = categoryConfig?.getItems;
|
|
376
|
+
if (typeof sourceGetItems !== "function") continue;
|
|
377
|
+
|
|
378
|
+
try {
|
|
379
|
+
const incoming = await sourceGetItems({ search: "" });
|
|
380
|
+
const normalized = this.#normalizeResultItems(incoming);
|
|
381
|
+
if (!normalized.length) continue;
|
|
382
|
+
|
|
383
|
+
this.#rememberKnownItems(normalized);
|
|
384
|
+
for (const item of normalized) {
|
|
385
|
+
if (!unresolved.has(item.id)) continue;
|
|
386
|
+
this.#selectedItems.set(item.id, item);
|
|
387
|
+
unresolved.delete(item.id);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
this.#debug("hydrateSelectedItemsFromSource:category", {
|
|
391
|
+
categoryName,
|
|
392
|
+
resolvedCount: normalized.length,
|
|
393
|
+
remaining: Array.from(unresolved),
|
|
394
|
+
});
|
|
395
|
+
} catch (error) {
|
|
396
|
+
this.#debug("hydrateSelectedItemsFromSource:error", {
|
|
397
|
+
categoryName,
|
|
398
|
+
error: String(error),
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
this.#reconcileSelection();
|
|
404
|
+
if (this.#selectedIds.size) this.#renderChips();
|
|
405
|
+
this.#debug("hydrateSelectedItemsFromSource:end", {
|
|
406
|
+
remaining: Array.from(unresolved),
|
|
407
|
+
selectedItemsCount: this.#selectedItems.size,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
359
411
|
#syncAttributes() {
|
|
360
412
|
this.#debug("syncAttributes:start", {
|
|
361
413
|
hasOmnibox: Boolean(this.#omnibox),
|
|
@@ -427,6 +479,7 @@ class PdsTags extends HTMLElement {
|
|
|
427
479
|
: [];
|
|
428
480
|
|
|
429
481
|
const normalized = this.#normalizeResultItems(incoming);
|
|
482
|
+
this.#rememberKnownItems(normalized);
|
|
430
483
|
for (const item of normalized) {
|
|
431
484
|
this.#selectedItems.set(item.id, item);
|
|
432
485
|
}
|
|
@@ -462,14 +515,12 @@ class PdsTags extends HTMLElement {
|
|
|
462
515
|
});
|
|
463
516
|
|
|
464
517
|
const { categories: _ignoredCategories, ...sourceSettingsWithoutCategories } = sourceSettings;
|
|
465
|
-
|
|
466
|
-
return {
|
|
518
|
+
const normalizedSettings = {
|
|
467
519
|
...sourceSettingsWithoutCategories,
|
|
468
520
|
categories,
|
|
469
|
-
hideCategory: true,
|
|
470
|
-
itemGrid: "0 1fr 0",
|
|
471
|
-
iconHandler: () => "",
|
|
472
521
|
};
|
|
522
|
+
|
|
523
|
+
return this.#deepMergeOptions(DEFAULT_OMNIBOX_OPTIONS, normalizedSettings);
|
|
473
524
|
}
|
|
474
525
|
|
|
475
526
|
#matchesQuery(item, query) {
|
|
@@ -601,30 +652,19 @@ class PdsTags extends HTMLElement {
|
|
|
601
652
|
|
|
602
653
|
#parseOptionsAttribute(value) {
|
|
603
654
|
this.#debug("parseOptionsAttribute:start", { hasValue: Boolean(value), rawType: typeof value });
|
|
604
|
-
if (!value) return [];
|
|
605
|
-
try {
|
|
606
|
-
const parsed = JSON.parse(value);
|
|
607
|
-
this.#debug("parseOptionsAttribute:parsed", { isArray: Array.isArray(parsed), length: Array.isArray(parsed) ? parsed.length : undefined });
|
|
608
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
609
|
-
} catch {
|
|
610
|
-
this.#debug("parseOptionsAttribute:error", { value });
|
|
611
|
-
return [];
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
#parseSettingsAttribute(value) {
|
|
616
|
-
this.#debug("parseSettingsAttribute:start", { hasValue: Boolean(value), rawType: typeof value });
|
|
617
655
|
if (!value) return null;
|
|
618
656
|
try {
|
|
619
657
|
const parsed = JSON.parse(value);
|
|
620
|
-
const valid = parsed && typeof parsed === "object"
|
|
621
|
-
|
|
658
|
+
const valid = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
659
|
+
? parsed
|
|
660
|
+
: null;
|
|
661
|
+
this.#debug("parseOptionsAttribute:parsed", {
|
|
622
662
|
valid: Boolean(valid),
|
|
623
663
|
hasCategories: Boolean(valid?.categories),
|
|
624
664
|
});
|
|
625
665
|
return valid;
|
|
626
666
|
} catch {
|
|
627
|
-
this.#debug("
|
|
667
|
+
this.#debug("parseOptionsAttribute:error", { value });
|
|
628
668
|
return null;
|
|
629
669
|
}
|
|
630
670
|
}
|
|
@@ -709,11 +749,12 @@ class PdsTags extends HTMLElement {
|
|
|
709
749
|
|
|
710
750
|
#reconcileSelection() {
|
|
711
751
|
this.#debug("reconcileSelection:start", { selectedBefore: Array.from(this.#selectedIds) });
|
|
712
|
-
if (!this.#selectedIds.size)
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
752
|
+
if (!this.#selectedIds.size) {
|
|
753
|
+
for (const id of Array.from(this.#selectedItems.keys())) {
|
|
754
|
+
this.#selectedItems.delete(id);
|
|
755
|
+
}
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
717
758
|
for (const id of Array.from(this.#selectedItems.keys())) {
|
|
718
759
|
if (!this.#selectedIds.has(id)) this.#selectedItems.delete(id);
|
|
719
760
|
}
|
|
@@ -721,52 +762,49 @@ class PdsTags extends HTMLElement {
|
|
|
721
762
|
}
|
|
722
763
|
|
|
723
764
|
#resolveSourceSettings() {
|
|
724
|
-
if (this.#sourceSettings
|
|
725
|
-
|
|
726
|
-
|
|
765
|
+
if (this.#sourceSettings && typeof this.#sourceSettings === "object") {
|
|
766
|
+
return this.#sourceSettings;
|
|
767
|
+
}
|
|
768
|
+
return null;
|
|
727
769
|
}
|
|
728
770
|
|
|
729
|
-
#
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (!
|
|
734
|
-
|
|
771
|
+
#rememberKnownItems(items = []) {
|
|
772
|
+
if (!Array.isArray(items) || items.length === 0) return;
|
|
773
|
+
const byId = new Map(this.#items.map((item) => [item.id, item]));
|
|
774
|
+
for (const item of items) {
|
|
775
|
+
if (!item?.id) continue;
|
|
776
|
+
byId.set(item.id, item);
|
|
735
777
|
}
|
|
778
|
+
this.#items = Array.from(byId.values());
|
|
779
|
+
}
|
|
736
780
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
icon: item.icon,
|
|
749
|
-
description: item.description,
|
|
750
|
-
}));
|
|
751
|
-
},
|
|
752
|
-
};
|
|
781
|
+
#deepMergeOptions(base = {}, override = {}) {
|
|
782
|
+
const output = this.#clonePlainObject(base);
|
|
783
|
+
for (const [key, value] of Object.entries(override || {})) {
|
|
784
|
+
if (
|
|
785
|
+
this.#isPlainObject(value)
|
|
786
|
+
&& this.#isPlainObject(output[key])
|
|
787
|
+
) {
|
|
788
|
+
output[key] = this.#deepMergeOptions(output[key], value);
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
output[key] = value;
|
|
753
792
|
}
|
|
793
|
+
return output;
|
|
794
|
+
}
|
|
754
795
|
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
796
|
+
#clonePlainObject(value) {
|
|
797
|
+
if (!this.#isPlainObject(value)) return value;
|
|
798
|
+
return Object.fromEntries(
|
|
799
|
+
Object.entries(value).map(([key, entry]) => [
|
|
800
|
+
key,
|
|
801
|
+
this.#isPlainObject(entry) ? this.#clonePlainObject(entry) : entry,
|
|
802
|
+
]),
|
|
803
|
+
);
|
|
761
804
|
}
|
|
762
805
|
|
|
763
|
-
#
|
|
764
|
-
|
|
765
|
-
for (const item of this.#items) {
|
|
766
|
-
if (this.#selectedIds.has(item.id)) {
|
|
767
|
-
this.#selectedItems.set(item.id, item);
|
|
768
|
-
}
|
|
769
|
-
}
|
|
806
|
+
#isPlainObject(value) {
|
|
807
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
770
808
|
}
|
|
771
809
|
|
|
772
810
|
#resetOmniboxSearchState(reason = "unknown") {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
function
|
|
1
|
+
function E(i){let e=Array.isArray(i?.strings)?i.strings:[],c=Array.isArray(i?.values)?i.values:[],p=new Set,h=[],g=/(\s)(\.[\w-]+)=\s*$/;for(let o=0;o<e.length;o+=1){let t=e[o]??"",d=t.match(g);if(d&&o<c.length){let r=d[2].slice(1),a=`pds-val-${o}`;t=t.replace(g,`$1data-pds-prop="${r}:${a}"`),p.add(o)}h.push(t),o<c.length&&!p.has(o)&&h.push(`<!--pds-val-${o}-->`)}let s=document.createElement("template");s.innerHTML=h.join("");let n=(o,t)=>{let d=o.parentNode;if(!d)return;if(t==null){d.removeChild(o);return}let b=r=>{if(r!=null){if(r instanceof Node){d.insertBefore(r,o);return}if(Array.isArray(r)){r.forEach(a=>b(a));return}d.insertBefore(document.createTextNode(String(r)),o)}};b(t),d.removeChild(o)},l=document.createTreeWalker(s.content,NodeFilter.SHOW_COMMENT),u=[];for(;l.nextNode();){let o=l.currentNode;o?.nodeValue?.startsWith("pds-val-")&&u.push(o)}return u.forEach(o=>{let t=Number(o.nodeValue.replace("pds-val-",""));n(o,c[t])}),s.content.querySelectorAll("*").forEach(o=>{let t=o.getAttribute("data-pds-prop");if(!t)return;let[d,b]=t.split(":"),r=Number(String(b).replace("pds-val-",""));d&&Number.isInteger(r)&&(o[d]=c[r]),o.removeAttribute("data-pds-prop")}),s.content}function S(i,e){if(e==null)return;if(typeof e=="object"&&Array.isArray(e.strings)&&Array.isArray(e.values)){i.appendChild(E(e));return}if(e instanceof Node){i.appendChild(e);return}if(Array.isArray(e)){e.forEach(p=>S(i,p));return}let c=typeof e=="string"?e:String(e);i.appendChild(document.createTextNode(c))}function q(i){if(!i)return!0;let e=!0,c=n=>{if(!n||typeof n!="object")return"<unknown>";let l=n.tagName?String(n.tagName).toLowerCase():"node",u=n.id?`#${n.id}`:"",y=typeof n.getAttribute=="function"?n.getAttribute("name"):null,o=y?`[name="${y}"]`:"";return`${l}${u}${o}`},p=(n,l)=>{if(!n||typeof n.querySelectorAll!="function")return;let u=Array.from(n.querySelectorAll(":invalid"));if(!u.length)return;let y=u.map(o=>{let t=typeof o.validationMessage=="string"?o.validationMessage:"";return`${c(o)}${t?` \u2014 ${t}`:""}`});console.warn(`ask.validateDialogFormTree: invalid controls in ${l}:`,y)},h=(n,l)=>{try{let u=typeof n.reportValidity=="function"?n.reportValidity():n.checkValidity?.()??!0;return u||p(n,l),u}catch(u){return console.error(`ask.validateDialogFormTree: validation threw in ${l}`,u),!1}};e=h(i,"host dialog form")&&e;let g=Array.from(i.querySelectorAll("form"));for(let n of g){if(n===i)continue;e=h(n,`nested light DOM form ${c(n)}`)&&e}let s=Array.from(i.querySelectorAll("*"));for(let n of s){let l=n?.shadowRoot;if(!l)continue;let u=Array.from(l.querySelectorAll("form"));for(let y of u)e=h(y,`shadow form under ${c(n)}`)&&e}return e}function L(){let i=navigator.userAgent,e=/Safari/i.test(i),c=/(Chrome|Chromium|CriOS|FxiOS|EdgiOS|OPiOS|Opera)/i.test(i);return e&&!c}function M(i){if(window.matchMedia?.("(prefers-reduced-motion: reduce)").matches)return;let e=window.matchMedia?.("(max-width: 639px)").matches,c=i.classList.contains("dialog-no-scale-animation")?"pds-dialog-fade-enter":e?"pds-dialog-enter-mobile":"pds-dialog-enter";i.style.animation="none",i.offsetWidth,i.style.animation=`${c} var(--transition-normal) ease`,i.addEventListener("animationend",()=>{i.style.animation=""},{once:!0})}function T(i={}){return i?.liquidGlassEffects===!0}async function V(i,e={}){let c={title:"Confirm",type:"confirm",buttons:{ok:{name:"OK",primary:!0},cancel:{name:"Cancel",cancel:!0}}};e={...c,...e};let p=e.buttons&&typeof e.buttons=="object"?e.buttons:c.buttons,h=s=>{if(s==null)return{actionCode:"dismiss",actionKind:"dismiss",button:null};let n=p?.[s]??null,l=s==="ok"?"ok":s==="dismiss"?"dismiss":n?.cancel||s==="cancel"?"cancel":"custom";return{actionCode:s,actionKind:l,button:n}},g=s=>{if(typeof s>"u"||s===null||s===!0)return{allow:!0};if(s===!1)return{allow:!1};if(typeof s=="object"){let n=Object.prototype.hasOwnProperty.call(s,"result")||Object.prototype.hasOwnProperty.call(s,"value");return{allow:s.allow!==!1,hasResult:n,result:Object.prototype.hasOwnProperty.call(s,"result")?s.result:s.value}}return{allow:!!s}};return new Promise(s=>{let n=!1,l=(r,a,{shouldClose:f=!0}={})=>{if(!n&&(n=!0,s(r),!(!f||!a?.open)))try{a.close()}catch(m){console.warn("ask: dialog.close() failed",m)}},u=async r=>{if(r.actionKind!=="ok"||typeof e.beforeClose!="function")return{allow:!0};try{let a=await e.beforeClose(r);return g(a)}catch(a){return console.error("ask.beforeClose: validation failed",a),{allow:!1}}},y=({actionKind:r,form:a})=>r==="ok"?e.useForm&&a?new FormData(a):!0:!1,o=async({actionCode:r,form:a,submitter:f,originalEvent:m,bypassValidation:w=!1,shouldClose:O=!0})=>{if(n)return;let{actionKind:v,button:F}=h(r),A=a||t.querySelector("form")||null;if(e.useForm&&v==="ok"&&A&&!w&&!q(A))return;let C=y({actionKind:v,form:A}),$=await u({actionCode:r,actionKind:v,dialog:t,form:A,formData:e.useForm&&v==="ok"&&A?C:null,submitter:f,originalEvent:m,options:e,button:F,defaultResult:C});if(!$.allow)return;let x=$.hasResult?$.result:C;l(x,t,{shouldClose:O})},t=document.createElement("dialog");L()&&t.classList.add("dialog-no-scale-animation"),T(e)&&t.classList.add("liquid-glass"),e.size&&t.classList.add(`dialog-${e.size}`),e.type&&t.classList.add(`dialog-${e.type}`),e.class&&(Array.isArray(e.class)?t.classList.add(...e.class):t.classList.add(e.class)),e.maxHeight&&t.style.setProperty("--dialog-max-height",e.maxHeight);let d=Object.entries(p).map(([r,a])=>{let f=a.primary?"btn-primary btn-sm":"btn-outline btn-sm",m=a.cancel?"button":"submit",w=a.formNoValidate?" formnovalidate":"";return`<button type="${m}" class="${f}" value="${r}"${w}>${a.name}</button>`});if(e.useForm){let r=document.createElement("div");S(r,i);let a=r.querySelector("form");if(a){t.innerHTML=`
|
|
2
2
|
<header>
|
|
3
3
|
<h2>${e.title}</h2>
|
|
4
4
|
</header>
|
|
5
|
-
`;let
|
|
5
|
+
`;let f=document.createElement("article");for(f.className="dialog-body";a.firstChild;)f.appendChild(a.firstChild);a.appendChild(f);let m=document.createElement("footer");m.innerHTML=d.join(""),a.appendChild(m),t.appendChild(a)}else t.innerHTML=`
|
|
6
6
|
<header>
|
|
7
7
|
<h2>${e.title}</h2>
|
|
8
8
|
</header>
|
|
9
9
|
<article id="msg-container"></article>
|
|
10
10
|
<footer>
|
|
11
|
-
${
|
|
11
|
+
${d.join("")}
|
|
12
12
|
</footer>
|
|
13
|
-
`,
|
|
13
|
+
`,t.querySelector("#msg-container").appendChild(r)}else{t.innerHTML=`
|
|
14
14
|
<form method="dialog">
|
|
15
15
|
<header>
|
|
16
16
|
<h2>${e.title}</h2>
|
|
@@ -19,7 +19,7 @@ function g(o){let e=Array.isArray(o?.strings)?o.strings:[],d=Array.isArray(o?.va
|
|
|
19
19
|
<article id="msg-container"></article>
|
|
20
20
|
|
|
21
21
|
<footer>
|
|
22
|
-
${
|
|
22
|
+
${d.join("")}
|
|
23
23
|
</footer>
|
|
24
24
|
</form>
|
|
25
|
-
`;let r=
|
|
25
|
+
`;let r=t.querySelector("#msg-container");S(r,i)}t.addEventListener("click",r=>{let a=r.target.closest('button[value="cancel"]');a&&o({actionCode:"cancel",form:t.querySelector("form"),submitter:a,originalEvent:r})});let b=()=>{let r=t.querySelector("form");if(r){if(r.dataset.askSubmitBound==="true")return;r.dataset.askSubmitBound="true",r.addEventListener("submit",a=>{a.preventDefault();let f=a.submitter?.value??(e.useForm?"ok":void 0),m=!!a.submitter?.hasAttribute("formnovalidate");o({actionCode:f,form:r,submitter:a.submitter,originalEvent:a,bypassValidation:m})})}else requestAnimationFrame(b)};t.addEventListener("cancel",r=>{r.preventDefault(),o({actionCode:"dismiss",form:t.querySelector("form"),originalEvent:r})}),t.addEventListener("close",()=>{n||l(!1,t,{shouldClose:!1}),setTimeout(()=>t.remove(),200)}),document.body.appendChild(t),requestAnimationFrame(b),typeof e.rendered=="function"&&e.rendered(t),t.showModal(),requestAnimationFrame(()=>M(t))})}export{V as ask};
|