@swiftwc/ui 0.0.0-dev.36 → 0.0.0-dev.38
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/generated/client/index.d.ts +2 -9
- package/generated/client/index.js +5 -156
- package/generated/components/alert-dialog.js +18 -21
- package/generated/components/body-view.js +3 -2
- package/generated/components/bordered-button.js +4 -4
- package/generated/components/bordered-prominent-button.js +4 -4
- package/generated/components/borderless-button.js +4 -4
- package/generated/components/confirmation-dialog.js +18 -21
- package/generated/components/content-unavailable-view.js +4 -4
- package/generated/components/date-picker.d.ts +0 -10
- package/generated/components/date-picker.js +43 -30
- package/generated/components/disclosure-group.js +5 -5
- package/generated/components/fine-tooltip.js +4 -4
- package/generated/components/form-view.js +3 -3
- package/generated/components/glass-button.js +4 -4
- package/generated/components/glass-prominent-button.js +4 -4
- package/generated/components/index.d.ts +1 -0
- package/generated/components/index.js +1 -0
- package/generated/components/label-view.js +4 -4
- package/generated/components/list-view.js +3 -3
- package/generated/components/menu-view.js +14 -16
- package/generated/components/navigation-large-title.js +5 -5
- package/generated/components/navigation-split-view.js +4 -3
- package/generated/components/navigation-stack.js +4 -3
- package/generated/components/navigation-title.js +4 -4
- package/generated/components/picker-view.d.ts +2 -2
- package/generated/components/picker-view.js +314 -195
- package/generated/components/plain-button.js +3 -3
- package/generated/components/progress-view.js +6 -8
- package/generated/components/screen-view.js +2 -1
- package/generated/components/scroll-view-proxy.js +3 -2
- package/generated/components/scroll-view.js +15 -18
- package/generated/components/search-view.d.ts +8 -0
- package/generated/components/search-view.js +27 -0
- package/generated/components/section-view.js +4 -5
- package/generated/components/sheet-view.js +6 -34
- package/generated/components/sidebar-toggle.js +9 -10
- package/generated/components/sidebar-view.js +6 -8
- package/generated/components/sticky-container.js +3 -3
- package/generated/components/tab-bar.js +8 -10
- package/generated/components/tab-item.js +8 -8
- package/generated/components/tab-view.js +10 -11
- package/generated/components/table-view.js +19 -37
- package/generated/components/text-field.js +32 -17
- package/generated/components/tool-bar-item-group.js +17 -19
- package/generated/components/tool-bar-item.js +22 -23
- package/generated/components/tool-bar.d.ts +2 -2
- package/generated/components/tool-bar.js +7 -7
- package/generated/components/v-keyboard.js +5 -5
- package/generated/components/v-stack.js +3 -2
- package/generated/css/index.css +283 -272
- package/generated/events/picker-view.d.ts +6 -0
- package/generated/i18n/locales/el.d.ts +1 -0
- package/generated/i18n/locales/el.js +1 -0
- package/generated/i18n/locales/en.d.ts +1 -0
- package/generated/i18n/locales/en.js +1 -0
- package/generated/internal/class/css-style-observer.js +2 -2
- package/generated/internal/class/form-associated-base.d.ts +0 -4
- package/generated/internal/class/form-associated-base.js +21 -41
- package/generated/internal/class/mutation-observer-set.d.ts +9 -0
- package/generated/internal/class/mutation-observer-set.js +42 -0
- package/generated/internal/class/mutation-observer-singleton.js +18 -0
- package/generated/internal/class/navigation-view.js +3 -3
- package/generated/internal/decorators/adaptive-slot.d.ts +1 -0
- package/generated/internal/decorators/adaptive-slot.js +47 -0
- package/generated/internal/decorators/custom-element.d.ts +1 -1
- package/generated/internal/decorators/custom-element.js +2 -1
- package/generated/internal/decorators/index.d.ts +4 -0
- package/generated/internal/decorators/index.js +4 -0
- package/generated/internal/privateNamespace.d.ts +10 -0
- package/generated/internal/privateNamespace.js +159 -1
- package/generated/internal/utils/debug.d.ts +1 -0
- package/generated/internal/utils/debug.js +3 -0
- package/generated/internal/utils/index.d.ts +3 -3
- package/generated/internal/utils/index.js +5 -3
- package/generated/namespace-browser/base.d.ts +9 -0
- package/generated/namespace-browser/base.js +23 -0
- package/generated/snapshot/index.d.ts +1 -1
- package/generated/snapshot/index.js +4 -4
- package/package.json +2 -1
- package/scss/_components.scss +2 -0
- package/scss/base/_root.scss +10 -8
- package/scss/colors/_index.scss +9 -12
- package/scss/components/_index.scss +2 -0
- package/scss/components/_menu-view.scss +11 -5
- package/scss/components/_picker-view.scss +11 -2
- package/scss/components/_scroll-view.scss +4 -0
- package/scss/components/_search-view.scss +18 -0
- package/scss/components/_sheet-view.scss +2 -1
- package/scss/components/_sticky-container.scss +1 -1
- package/scss/placeholders/_lists.scss +35 -35
- package/scss/utils/_index.scss +21 -7
- package/scss/utils/_stacks.scss +5 -43
- package/web-components.html-data/en.json +11 -0
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
var _a;
|
|
2
|
+
import { I18n } from '../i18n';
|
|
2
3
|
import { CleanupRegistry } from '../internal/class/cleanup-registry';
|
|
3
|
-
import { FormAssociatedBase, getInternals
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { FormAssociatedBase, getInternals } from '../internal/class/form-associated-base';
|
|
5
|
+
import { MutationObserverSet } from '../internal/class/mutation-observer-set';
|
|
6
|
+
import { NavigationPath } from '../internal/class/navigation-path';
|
|
7
|
+
import { queryInsertPosition, startViewTransition } from '../internal/privateNamespace';
|
|
8
|
+
import { $, debug, kebabCase, onoff } from '../internal/utils';
|
|
9
|
+
const pickerStyles = ['menu', 'inline', 'navigation-link', 'sheet', 'automatic'];
|
|
10
|
+
const update = (path, node, overwrite = true) => {
|
|
11
|
+
if (!(path instanceof NavigationPath))
|
|
12
|
+
throw new Error('invalid view');
|
|
13
|
+
const { component, page } = path;
|
|
14
|
+
if (!component)
|
|
15
|
+
return;
|
|
16
|
+
const position = queryInsertPosition(component); //'afterend'
|
|
17
|
+
// const lookFor = 'beforebegin' === position ? 'previousElementSibling' : 'nextElementSibling'
|
|
18
|
+
if (page)
|
|
19
|
+
page.insertAdjacentElement(position, node);
|
|
20
|
+
};
|
|
21
|
+
const replaceList = (current, source) => {
|
|
22
|
+
const { page: oldPage, toolBarConfig: oldToolbar } = new NavigationPath(current).hydrate(), { page: newPage, toolBarConfig: newToolbar } = new NavigationPath(source).hydrate();
|
|
23
|
+
const oldList = oldPage?.querySelector('list-view'), newList = newPage?.querySelector('list-view'), oldBackBtn = oldToolbar?.at(0)?.querySelector('button'), newBackBtn = newToolbar?.at(0)?.querySelector('button');
|
|
24
|
+
// pre replace
|
|
25
|
+
const oldOpenedDetails = [...(oldList?.querySelectorAll('details[open]>summary') ?? [])].map((item) => item.textContent.trim());
|
|
26
|
+
// replace
|
|
27
|
+
if (oldList && newList)
|
|
28
|
+
oldList.replaceWith(newList);
|
|
29
|
+
if (oldBackBtn && newBackBtn)
|
|
30
|
+
oldBackBtn.replaceWith(newBackBtn);
|
|
31
|
+
// post replace
|
|
32
|
+
for (const label of oldOpenedDetails)
|
|
33
|
+
for (const summary of newList?.querySelectorAll('details>summary') ?? [])
|
|
34
|
+
if (summary.textContent.trim() === label) {
|
|
35
|
+
summary.parentElement?.setAttribute('open', 'open');
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
8
39
|
export class PickerView extends FormAssociatedBase {
|
|
9
40
|
static get ATTR() {
|
|
10
41
|
return {
|
|
@@ -12,37 +43,182 @@ export class PickerView extends FormAssociatedBase {
|
|
|
12
43
|
LABEL: 'label',
|
|
13
44
|
PICKER_STYLE: 'picker-style',
|
|
14
45
|
SELECTION: 'selection',
|
|
46
|
+
SEARCHABLE: 'searchable',
|
|
15
47
|
};
|
|
16
48
|
}
|
|
17
49
|
static get observedAttributes() {
|
|
18
50
|
return Object.values(this.ATTR);
|
|
19
51
|
}
|
|
20
52
|
static #templates = new Map();
|
|
53
|
+
#spawn;
|
|
54
|
+
#renderPage = (tag, searchable = false, title, node) => {
|
|
55
|
+
const body = $(`<${'sheet-view' === tag ? 'dialog is="sheet-view"' : 'body-view'}><scroll-view><v-stack placement="leading fill"><list-view preferred-expanded-style="inset"></list-view></v-stack></scroll-view><tool-bar><tool-bar-item slot="top-bar-leading"><button type="button" tabindex="0"><label-view system-image="caret-left"></label-view></button></tool-bar-item></tool-bar></${'sheet-view' === tag ? 'dialog' : 'body-view'}>`, '>1'), sv = body.querySelector('scroll-view'), list = body.querySelector('list-view'), backBtn = body.querySelector('button');
|
|
56
|
+
if (title)
|
|
57
|
+
sv?.setAttribute('navigation-inline-title', title);
|
|
58
|
+
if (searchable) {
|
|
59
|
+
list?.style.setProperty('--list--sticky-block-size', '70px');
|
|
60
|
+
list?.insertAdjacentHTML('afterend', `<sticky-container edge="navbar" style="order: -1" padding>
|
|
61
|
+
<v-stack spacing="0" alignment="fill">
|
|
62
|
+
<input is="search-view" placeholder="Search">
|
|
63
|
+
<button type="button">Filters</button>
|
|
64
|
+
</v-stack>
|
|
65
|
+
</sticky-container>`);
|
|
66
|
+
const searchInput = sv?.querySelector('input'); // FIXME: compoennt is=search-view??
|
|
67
|
+
searchInput?.addEventListener('focus', (evt) => {
|
|
68
|
+
if (this.#spawn)
|
|
69
|
+
this.dispatchEvent(new CustomEvent('picker:searchfocus', { detail: { element: this.#spawn }, bubbles: true, composed: true }));
|
|
70
|
+
});
|
|
71
|
+
searchInput?.addEventListener('blur', (evt) => {
|
|
72
|
+
if (this.#spawn)
|
|
73
|
+
this.dispatchEvent(new CustomEvent('picker:searchblur', { detail: { element: this.#spawn }, bubbles: true, composed: true }));
|
|
74
|
+
});
|
|
75
|
+
searchInput?.addEventListener('input', (evt) => {
|
|
76
|
+
if (this.#spawn)
|
|
77
|
+
this.dispatchEvent(new CustomEvent('picker:searchinput', { detail: { element: this.#spawn }, bubbles: true, composed: true }));
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
backBtn?.setAttribute('help', I18n.t('ButtonRole').Back);
|
|
81
|
+
backBtn?.addEventListener('click', async (evt) => {
|
|
82
|
+
evt.stopImmediatePropagation();
|
|
83
|
+
evt.preventDefault();
|
|
84
|
+
const { target } = evt;
|
|
85
|
+
if (!(target instanceof HTMLElement && target))
|
|
86
|
+
return;
|
|
87
|
+
const { component } = new NavigationPath(target)?.hydrate();
|
|
88
|
+
await startViewTransition(target, 'backwards', async () => {
|
|
89
|
+
component?.remove();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
const elements = node ? Array.from(node.children) : (this.#slots?.get('list')?.assignedElements() ?? []);
|
|
93
|
+
for (const el of elements) {
|
|
94
|
+
if (!(el instanceof HTMLElement))
|
|
95
|
+
continue;
|
|
96
|
+
switch (el.tagName) {
|
|
97
|
+
case 'OPTGROUP': {
|
|
98
|
+
const details = _a.#wrapOptgroupTag(el);
|
|
99
|
+
_a.#reflectButtons([...el.children], details);
|
|
100
|
+
for (const btn of details.querySelectorAll(':scope>button'))
|
|
101
|
+
btn.addEventListener('click', this.#handlePageClick);
|
|
102
|
+
list?.appendChild(details);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'OPTION': {
|
|
106
|
+
const btn = _a.#wrapOptionTag(el);
|
|
107
|
+
btn.addEventListener('click', this.#handlePageClick);
|
|
108
|
+
list?.appendChild(btn);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
default: {
|
|
112
|
+
const btn = $(`<button type="button" tabindex="0"><label-view></label-view></button>`, '>1'), label = btn.querySelector(':scope>label-view');
|
|
113
|
+
if (el.dataset.label)
|
|
114
|
+
label?.setAttribute('title', el.dataset.label);
|
|
115
|
+
btn.addEventListener('click', async (evt) => {
|
|
116
|
+
evt.stopImmediatePropagation();
|
|
117
|
+
evt.preventDefault();
|
|
118
|
+
const { target } = evt;
|
|
119
|
+
if (!(target instanceof HTMLElement && target))
|
|
120
|
+
return;
|
|
121
|
+
const newPage = this.#renderPage('body-view', this.hasAttribute('searchable'), el.dataset.label, el);
|
|
122
|
+
if (!newPage)
|
|
123
|
+
return;
|
|
124
|
+
const path = new NavigationPath(target)?.hydrate();
|
|
125
|
+
await startViewTransition(target, 'forwards', async () => {
|
|
126
|
+
update(path, newPage);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
list?.appendChild(btn);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return body;
|
|
135
|
+
};
|
|
136
|
+
#renderList = (entries) => {
|
|
137
|
+
debug(`${_a.name} ⚡️ mutation`);
|
|
138
|
+
switch (this.pickerStyle) {
|
|
139
|
+
case 'sheet':
|
|
140
|
+
case 'navigation-link': {
|
|
141
|
+
// label
|
|
142
|
+
const label = this.querySelector(':scope>label-view') ?? this.appendChild($(`<label-view system-image="dots-three"></label-view>`, '>1'));
|
|
143
|
+
label.setAttribute('title', 'rtyty');
|
|
144
|
+
CleanupRegistry.unregister(this, 'trigger');
|
|
145
|
+
CleanupRegistry.register(this, onoff('click', this.#handleTriggerClick, label).on(), 'trigger');
|
|
146
|
+
this.appendChild(label);
|
|
147
|
+
// rebuild snapshot(tree)
|
|
148
|
+
if (!this.#spawn)
|
|
149
|
+
break;
|
|
150
|
+
// rerender level 0
|
|
151
|
+
replaceList(this.#spawn, this.#renderPage('body-view', this.hasAttribute('searchable'), this.getAttribute('label')));
|
|
152
|
+
for (const el of this.#spawn.querySelectorAll('body-view')) {
|
|
153
|
+
const depth = $.ancestors('body-view,[is=sheet-view]', el).indexOf(this.#spawn);
|
|
154
|
+
if (0 >= depth)
|
|
155
|
+
continue;
|
|
156
|
+
const datalist = this.querySelector(`:scope>${Array.from({ length: depth }, () => 'datalist').join('>')}`);
|
|
157
|
+
if (!datalist) {
|
|
158
|
+
el.remove();
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
replaceList(el, this.#renderPage('body-view', this.hasAttribute('searchable'), datalist.dataset.label, datalist));
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
case 'menu': {
|
|
166
|
+
const menu = this.querySelector(':scope>menu-view:not([slot])') ?? this.appendChild($(`<menu-view tabindex="0"></menu-view>`, '>1')), label = menu.querySelector(':scope>label-view[slot=label]') ?? menu.appendChild($(`<label-view slot="label" system-image="dots-three"></label-view>`, '>1'));
|
|
167
|
+
label.setAttribute('title', 'rtyty');
|
|
168
|
+
for (const el of menu.querySelectorAll(':scope>:not([slot])'))
|
|
169
|
+
el.remove();
|
|
170
|
+
_a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements() ?? [])], menu);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'inline':
|
|
174
|
+
default: {
|
|
175
|
+
const inlineList = this.querySelector(':scope>list-view:not([slot])') ?? this.appendChild($(`<list-view><section-view></section-view></list-view>`, '>1')), section = inlineList.querySelector(':scope>section-view') ?? inlineList.appendChild($(`<section-view></section-view>`, '>1'));
|
|
176
|
+
for (const el of section.querySelectorAll(':scope>:not([slot])'))
|
|
177
|
+
el.remove(); // section.innerHTML = ''
|
|
178
|
+
const label = this.getAttribute(this.constructor.ATTR.LABEL);
|
|
179
|
+
if (label) {
|
|
180
|
+
const el = $(`<label-view></label-view>`, '>1');
|
|
181
|
+
el.setAttribute('title', label);
|
|
182
|
+
section.insertAdjacentElement('beforeend', el);
|
|
183
|
+
}
|
|
184
|
+
_a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements({ flatten: true }) ?? [])].filter((el) => el.matches('option')), section);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
#renderValidityMsgs = (entries) => {
|
|
190
|
+
debug(`${_a.name} ⚡️ mutation`);
|
|
191
|
+
this.setValidity(this.validity, this.validationMessage);
|
|
192
|
+
};
|
|
21
193
|
#lastRenderedStyle; //string | null
|
|
22
194
|
#shadowRoot;
|
|
23
195
|
#slots = new Map();
|
|
196
|
+
#validityObservers = new MutationObserverSet(this.#renderValidityMsgs);
|
|
197
|
+
#observers = new MutationObserverSet(this.#renderList);
|
|
24
198
|
#customValidity = '';
|
|
25
|
-
// #validitiesSlot?: HTMLSlotElement
|
|
26
|
-
// #datalistSlot?: HTMLSlotElement
|
|
27
|
-
// #tagSlot?: HTMLSlotElement
|
|
28
|
-
#trackedElements = new Set();
|
|
29
199
|
#selection = '';
|
|
30
200
|
get #internals() {
|
|
31
201
|
return getInternals(this);
|
|
32
202
|
}
|
|
33
203
|
get template() {
|
|
34
|
-
// const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE) ?? ''
|
|
35
204
|
if (!_a.#templates.has(this.pickerStyle))
|
|
36
205
|
switch (this.pickerStyle) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
206
|
+
case 'sheet':
|
|
207
|
+
case 'navigation-link':
|
|
208
|
+
_a.#templates.set(this.pickerStyle, $(String.raw `
|
|
209
|
+
<label part="root picker-stack">
|
|
210
|
+
<div part="root picker-label-stack">
|
|
211
|
+
<slot name="label"></slot>
|
|
212
|
+
</div>
|
|
213
|
+
<div part="root picker-input-stack">
|
|
214
|
+
<slot></slot>
|
|
215
|
+
</div>
|
|
216
|
+
<slot name="list" hidden></slot>
|
|
217
|
+
<slot name="validity-options" hidden></slot>
|
|
218
|
+
</label>
|
|
219
|
+
`));
|
|
220
|
+
break;
|
|
44
221
|
case 'menu':
|
|
45
|
-
// template.innerHTML = `<slot name="options"></slot><slot></slot>`
|
|
46
222
|
_a.#templates.set(this.pickerStyle, $(String.raw `
|
|
47
223
|
<label part="root picker-stack">
|
|
48
224
|
<div part="root picker-label-stack">
|
|
@@ -51,8 +227,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
51
227
|
<div part="root picker-input-stack">
|
|
52
228
|
<slot></slot>
|
|
53
229
|
</div>
|
|
54
|
-
<slot name="
|
|
55
|
-
<slot name="tag" hidden></slot>
|
|
230
|
+
<slot name="list" hidden></slot>
|
|
56
231
|
<slot name="validity-options" hidden></slot>
|
|
57
232
|
</label>
|
|
58
233
|
`));
|
|
@@ -97,7 +272,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
97
272
|
// <option value="0" label="0%"></option>
|
|
98
273
|
// </datalist>
|
|
99
274
|
// </div>
|
|
100
|
-
// <slot name="
|
|
275
|
+
// <slot name="list" hidden></slot>
|
|
101
276
|
// </label>`,
|
|
102
277
|
// })
|
|
103
278
|
// )
|
|
@@ -112,8 +287,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
112
287
|
<div part="root picker-input-stack">
|
|
113
288
|
<slot></slot>
|
|
114
289
|
</div>
|
|
115
|
-
<slot name="
|
|
116
|
-
<slot name="tag" hidden></slot>
|
|
290
|
+
<slot name="list" hidden></slot>
|
|
117
291
|
<slot name="validity-options" hidden></slot>
|
|
118
292
|
</label>`));
|
|
119
293
|
break;
|
|
@@ -123,10 +297,12 @@ export class PickerView extends FormAssociatedBase {
|
|
|
123
297
|
constructor() {
|
|
124
298
|
super();
|
|
125
299
|
this.#shadowRoot = this.attachShadow({ mode: 'closed' });
|
|
126
|
-
// this.#render() // Snapshot.waitReady.then(this.#render.bind(this))
|
|
127
300
|
}
|
|
128
301
|
connectedCallback() {
|
|
129
|
-
|
|
302
|
+
debug(`${_a.name} ⚡️ connect`);
|
|
303
|
+
CleanupRegistry.register(this, onoff('localechange', () => {
|
|
304
|
+
this.#renderList([]);
|
|
305
|
+
}, I18n.on).on());
|
|
130
306
|
CleanupRegistry.register(this, onoff('click', this.#handleClick, this).on());
|
|
131
307
|
if (!this.hasAttribute(this.constructor.ATTR.PICKER_STYLE))
|
|
132
308
|
this.#render(); // will be picked up by attr-change!
|
|
@@ -137,12 +313,13 @@ export class PickerView extends FormAssociatedBase {
|
|
|
137
313
|
this.#sendValueToForm(false);
|
|
138
314
|
}
|
|
139
315
|
disconnectedCallback() {
|
|
140
|
-
|
|
316
|
+
debug(`${_a.name} ⚡️ disconnect`);
|
|
141
317
|
CleanupRegistry.unregister(this);
|
|
142
|
-
|
|
318
|
+
this.#validityObservers.unobserveAll();
|
|
319
|
+
this.#observers.unobserveAll();
|
|
143
320
|
}
|
|
144
321
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
145
|
-
|
|
322
|
+
debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
|
|
146
323
|
switch (name) {
|
|
147
324
|
case this.constructor.ATTR.PLACEHOLDER:
|
|
148
325
|
this.#reflectPlaceholder(newValue);
|
|
@@ -155,7 +332,11 @@ export class PickerView extends FormAssociatedBase {
|
|
|
155
332
|
if (oldValue === newValue)
|
|
156
333
|
break;
|
|
157
334
|
this.#render();
|
|
158
|
-
|
|
335
|
+
break;
|
|
336
|
+
case this.constructor.ATTR.SEARCHABLE:
|
|
337
|
+
if (oldValue === newValue)
|
|
338
|
+
break;
|
|
339
|
+
this.#renderList([]);
|
|
159
340
|
break;
|
|
160
341
|
case this.constructor.ATTR.SELECTION:
|
|
161
342
|
// nothing happens
|
|
@@ -168,9 +349,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
168
349
|
: 'automatic';
|
|
169
350
|
}
|
|
170
351
|
#render() {
|
|
171
|
-
|
|
172
|
-
if (!this.isConnected)
|
|
173
|
-
return;
|
|
352
|
+
debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
|
|
174
353
|
// const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)
|
|
175
354
|
if (this.#lastRenderedStyle === this.pickerStyle)
|
|
176
355
|
return; // skip if already applied
|
|
@@ -184,39 +363,11 @@ export class PickerView extends FormAssociatedBase {
|
|
|
184
363
|
this.#slots = new Map();
|
|
185
364
|
}, 'slots');
|
|
186
365
|
CleanupRegistry.unregister(this, 'validities');
|
|
187
|
-
CleanupRegistry.register(this, onoff(
|
|
366
|
+
CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
|
|
188
367
|
CleanupRegistry.unregister(this, 'datalist'); //off1()
|
|
189
|
-
CleanupRegistry.register(this, onoff('slotchange', this.#
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// if (0 < (this.#slots?.get('options')?.assignedElements({ flatten: true }) ?? []).length) this.#handleTagMutation()
|
|
193
|
-
// if (0 < (this.#slots?.get('tag')?.assignedElements({ flatten: true }) ?? []).length) this.#handleTagMutation()
|
|
194
|
-
//this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)) {
|
|
195
|
-
switch (this.pickerStyle
|
|
196
|
-
// case 'menu':
|
|
197
|
-
// // if (0 < (this.#slots?.get('options')?.assignedElements({ flatten: true }) ?? []).length) this.#handleMenuDatalistMutation()
|
|
198
|
-
// break
|
|
199
|
-
// case 'gg':
|
|
200
|
-
// // reattach input listener
|
|
201
|
-
// const input = this.#shadowRoot.querySelector('input')
|
|
202
|
-
// if (input) {
|
|
203
|
-
// input.addEventListener('input', () => {
|
|
204
|
-
// this.#internals!.setFormValue(input.value)
|
|
205
|
-
// })
|
|
206
|
-
// // restore placeholder if set
|
|
207
|
-
// const placeholder = this.getAttribute((this.constructor as typeof PickerView).ATTR.PLACEHOLDER)
|
|
208
|
-
// if (placeholder) input.setAttribute('placeholder', placeholder)
|
|
209
|
-
// }
|
|
210
|
-
// // restore label if set
|
|
211
|
-
// const label = this.getAttribute((this.constructor as typeof PickerView).ATTR.LABEL)
|
|
212
|
-
// if (label) this.#reflectLabel(label)
|
|
213
|
-
// break
|
|
214
|
-
// case 'inline':
|
|
215
|
-
// default:
|
|
216
|
-
// // if (0 < (this.#slots?.get('options')?.assignedElements({ flatten: true }) ?? []).length) this.#handleInlineDatalistMutation()
|
|
217
|
-
// break
|
|
218
|
-
) {
|
|
219
|
-
}
|
|
368
|
+
CleanupRegistry.register(this, onoff('slotchange', this.#handleListSlotchange, this.#slots?.get('list')).on(), 'datalist');
|
|
369
|
+
// switch (this.pickerStyle) {
|
|
370
|
+
// }
|
|
220
371
|
}
|
|
221
372
|
#sendValueToForm = (dispatchEvent = true) => {
|
|
222
373
|
// input.value has already been updated/synced !!
|
|
@@ -237,116 +388,113 @@ export class PickerView extends FormAssociatedBase {
|
|
|
237
388
|
if (dispatchEvent)
|
|
238
389
|
this.dispatchEvent(new CustomEvent('selection', { detail: { selection: this.#selection }, bubbles: true, composed: true }));
|
|
239
390
|
};
|
|
240
|
-
#
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (!target)
|
|
391
|
+
#handleTriggerClick = async ({ type, target }) => {
|
|
392
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
393
|
+
if (!(target instanceof HTMLElement && target))
|
|
244
394
|
return;
|
|
245
|
-
|
|
395
|
+
this.#spawn?.remove?.();
|
|
396
|
+
const level0 = this.#renderPage('sheet' === this.pickerStyle ? 'sheet-view' : 'body-view', this.hasAttribute('searchable'), this.getAttribute('label'));
|
|
397
|
+
if (!level0)
|
|
398
|
+
return;
|
|
399
|
+
const path = new NavigationPath(target)?.hydrate();
|
|
400
|
+
await startViewTransition(target, 'forwards', async () => {
|
|
401
|
+
this.#spawn = level0;
|
|
402
|
+
update(path, level0);
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
#handlePageClick = async (evt) => {
|
|
406
|
+
debug(`${_a.name} ⚡️ ${evt?.type}`);
|
|
407
|
+
evt.stopImmediatePropagation();
|
|
408
|
+
evt.preventDefault();
|
|
409
|
+
const { target } = evt;
|
|
410
|
+
if (!(target instanceof HTMLElement && target))
|
|
411
|
+
return;
|
|
412
|
+
const btn = target.closest('button');
|
|
246
413
|
if (!btn)
|
|
247
414
|
return;
|
|
248
|
-
|
|
249
|
-
this.#
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
415
|
+
const proceed = () => {
|
|
416
|
+
this.#spawn?.remove();
|
|
417
|
+
this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
|
|
418
|
+
this.#sendValueToForm();
|
|
419
|
+
};
|
|
420
|
+
const { body } = new NavigationPath(this.#spawn)?.hydrate();
|
|
421
|
+
if (!body)
|
|
422
|
+
return proceed();
|
|
423
|
+
await startViewTransition(body, 'backwards', async () => {
|
|
424
|
+
proceed();
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
#handleClick({ type, target }) {
|
|
428
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
429
|
+
if (!(target instanceof HTMLElement && target))
|
|
430
|
+
return;
|
|
431
|
+
const btn = target.closest('button');
|
|
432
|
+
if (!btn)
|
|
433
|
+
return;
|
|
434
|
+
this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
|
|
254
435
|
this.#sendValueToForm();
|
|
255
436
|
}
|
|
256
|
-
#
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (!slot)
|
|
437
|
+
#handleValiditiesSlotchange = ({ type, target: slot }) => {
|
|
438
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
439
|
+
if (!(slot instanceof HTMLSlotElement && slot))
|
|
260
440
|
return;
|
|
261
|
-
const assigned = slot.assignedElements(
|
|
262
|
-
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
// observers.unobserve(el)
|
|
266
|
-
// this.#trackedElements.delete(el)
|
|
267
|
-
// }
|
|
268
|
-
// for (const el of assigned) {
|
|
269
|
-
// if (!this.#trackedElements.has(el))
|
|
270
|
-
// observers.observe(el, this.#handleTagMutation, {
|
|
271
|
-
// attributes: true,
|
|
272
|
-
// characterData: true,
|
|
273
|
-
// subtree: true,
|
|
274
|
-
// childList: true,
|
|
275
|
-
// // attributeFilter: ['value', 'label'],
|
|
276
|
-
// })
|
|
277
|
-
// this.#trackedElements.add(el)
|
|
278
|
-
// }
|
|
279
|
-
if (0 < assigned.length)
|
|
280
|
-
this.#handleTagMutation();
|
|
441
|
+
const assigned = slot.assignedElements();
|
|
442
|
+
this.#validityObservers.syncObservations(assigned, ['value', 'label']);
|
|
443
|
+
// if (0 < assigned.length)
|
|
444
|
+
this.#renderValidityMsgs([]);
|
|
281
445
|
};
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (node.hasAttribute('label'))
|
|
298
|
-
label.setAttribute('title', node.getAttribute('label') ?? '');
|
|
299
|
-
btn.appendChild(label);
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
446
|
+
#handleListSlotchange = ({ type, target: slot }) => {
|
|
447
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
448
|
+
if (!(slot instanceof HTMLSlotElement && slot))
|
|
449
|
+
return;
|
|
450
|
+
const assigned = slot.assignedElements();
|
|
451
|
+
this.#observers.syncObservations(assigned);
|
|
452
|
+
// if (0 < assigned.length)
|
|
453
|
+
this.#renderList([]);
|
|
454
|
+
};
|
|
455
|
+
static #wrapOptionTag(node) {
|
|
456
|
+
const btn = $(`<button type="button" tabindex="0"></button>`, '>1');
|
|
457
|
+
btn.setAttribute('value', node.getAttribute('value') ?? node.textContent?.trim() ?? '');
|
|
458
|
+
const label = $(`<label-view></label-view>`, '>1');
|
|
459
|
+
label.setAttribute('title', node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim() ?? '');
|
|
460
|
+
btn.appendChild(label);
|
|
302
461
|
return btn;
|
|
303
462
|
}
|
|
304
|
-
#
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const el = $(`<label-view></label-view>`, '>1');
|
|
326
|
-
el.setAttribute('title', label);
|
|
327
|
-
section.insertAdjacentElement('beforeend', el);
|
|
463
|
+
static #wrapOptgroupTag(node) {
|
|
464
|
+
const labelT = `<label-view></label-view>`, summaryT = `<summary>${labelT}</summary>`;
|
|
465
|
+
const group = $(`<details is="disclosure-group">${summaryT}</details>`, '>1'), summary = group.querySelector(':scope>summary') ?? group.appendChild($(summaryT, '>1')), summaryLabel = summary.querySelector(':scope>label-view') ?? summary.appendChild($(labelT, '>1'));
|
|
466
|
+
if (node.hasAttribute('label'))
|
|
467
|
+
summaryLabel.setAttribute('title', node.getAttribute('label') ?? '');
|
|
468
|
+
if (node.hasAttribute('data-system-image'))
|
|
469
|
+
summaryLabel.setAttribute('system-image', node.getAttribute('data-system-image') ?? '');
|
|
470
|
+
return group;
|
|
471
|
+
}
|
|
472
|
+
static #reflectButtons(nodes, container) {
|
|
473
|
+
for (const node of nodes)
|
|
474
|
+
switch (node.tagName) {
|
|
475
|
+
case 'DATALIST': {
|
|
476
|
+
const group = $(`<menu-view tabindex="0"></menu-view>`, '>1'), label = group.querySelector(':scope>label-view[slot=label]') ?? group.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
|
|
477
|
+
if (node.hasAttribute('data-label'))
|
|
478
|
+
label.setAttribute('title', node.getAttribute('data-label') ?? '');
|
|
479
|
+
if (node.hasAttribute('data-system-image'))
|
|
480
|
+
label.setAttribute('system-image', node.getAttribute('data-system-image') ?? '');
|
|
481
|
+
_a.#reflectButtons([...node.children], group);
|
|
482
|
+
container.appendChild(group);
|
|
483
|
+
break;
|
|
328
484
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
// btn.type = 'button'
|
|
343
|
-
// btn.tabIndex = 0
|
|
344
|
-
// btn.appendChild(PickerView.wrapTag(el, sourceSlot?.name))
|
|
345
|
-
// this.insertAdjacentElement('beforeend', btn)
|
|
346
|
-
// }
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
};
|
|
485
|
+
case 'OPTGROUP': {
|
|
486
|
+
const group = _a.#wrapOptgroupTag(node);
|
|
487
|
+
_a.#reflectButtons([...node.children], group);
|
|
488
|
+
container.appendChild(group);
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
case 'OPTION':
|
|
492
|
+
default: {
|
|
493
|
+
container.appendChild(_a.#wrapOptionTag(node));
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
350
498
|
#reflectPlaceholder(value) {
|
|
351
499
|
const input = this.#shadowRoot.querySelector('input');
|
|
352
500
|
if (input) {
|
|
@@ -357,17 +505,6 @@ export class PickerView extends FormAssociatedBase {
|
|
|
357
505
|
}
|
|
358
506
|
}
|
|
359
507
|
#reflectLabel(value) {
|
|
360
|
-
// switch (this.getAttribute('picker-style')) {
|
|
361
|
-
// case 'menu':
|
|
362
|
-
// this.#slot!.assignedElements({ flatten: true })[0].setAttribute('label', value ?? '')
|
|
363
|
-
// // if (!mv) {
|
|
364
|
-
// // mv = document.createElement('label-view')
|
|
365
|
-
// // mv.slot = 'label'
|
|
366
|
-
// // this.append(mv)
|
|
367
|
-
// // }
|
|
368
|
-
// // mv.textContent = newValue //el2.replaceChildren(escapeHTMLPolicy.createHTML(newValue))
|
|
369
|
-
// break
|
|
370
|
-
// default:
|
|
371
508
|
let label = this.querySelector(':scope>[slot=label]');
|
|
372
509
|
if (value) {
|
|
373
510
|
label ??= this.appendChild($(`<span slot="label"></span>`, '>1'));
|
|
@@ -375,25 +512,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
375
512
|
}
|
|
376
513
|
else
|
|
377
514
|
label?.remove();
|
|
378
|
-
this.#
|
|
379
|
-
// const el =
|
|
380
|
-
// (this.#labelSlot?.assignedElements({ flatten: true })[0] as HTMLElement) ??
|
|
381
|
-
// (() => {
|
|
382
|
-
// const el = document.createElement('span')
|
|
383
|
-
// el.slot = 'label'
|
|
384
|
-
// return this.appendChild(el)
|
|
385
|
-
// })()
|
|
386
|
-
// el.textContent = value ?? '' // el.replaceChildren(escapeHTMLPolicy.createHTML(newValue))
|
|
387
|
-
// break
|
|
388
|
-
// }
|
|
389
|
-
// const assigned = this.#labelSlot?.assignedElements({ flatten: true }) as HTMLElement[] | undefined
|
|
390
|
-
// let el = assigned?.[0]
|
|
391
|
-
// if (!el) {
|
|
392
|
-
// el = document.createElement('span')
|
|
393
|
-
// el.slot = 'label'
|
|
394
|
-
// this.append(el)
|
|
395
|
-
// }
|
|
396
|
-
// el.textContent = value ?? ''
|
|
515
|
+
this.#renderList([]);
|
|
397
516
|
}
|
|
398
517
|
// Optional: form participation properties
|
|
399
518
|
get name() {
|
|
@@ -428,7 +547,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
428
547
|
message = kebabCase(key);
|
|
429
548
|
break;
|
|
430
549
|
}
|
|
431
|
-
|
|
550
|
+
debug(`${_a.name} ⚡️ validity-change`);
|
|
432
551
|
return this.#internals.setValidity(flags, this.#customValidity || message, anchor);
|
|
433
552
|
};
|
|
434
553
|
setCustomValidity = (message) => {
|