@swiftwc/ui 0.0.0-dev.36 → 0.0.0-dev.37
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 -28
- 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 +313 -192
- package/generated/components/plain-button.js +3 -3
- package/generated/components/progress-view.js +6 -6
- 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 -4
- 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 +18 -36
- 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[type=search]');
|
|
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,7 +349,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
168
349
|
: 'automatic';
|
|
169
350
|
}
|
|
170
351
|
#render() {
|
|
171
|
-
|
|
352
|
+
debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
|
|
172
353
|
if (!this.isConnected)
|
|
173
354
|
return;
|
|
174
355
|
// const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)
|
|
@@ -184,39 +365,11 @@ export class PickerView extends FormAssociatedBase {
|
|
|
184
365
|
this.#slots = new Map();
|
|
185
366
|
}, 'slots');
|
|
186
367
|
CleanupRegistry.unregister(this, 'validities');
|
|
187
|
-
CleanupRegistry.register(this, onoff(
|
|
368
|
+
CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
|
|
188
369
|
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
|
-
}
|
|
370
|
+
CleanupRegistry.register(this, onoff('slotchange', this.#handleListSlotchange, this.#slots?.get('list')).on(), 'datalist');
|
|
371
|
+
// switch (this.pickerStyle) {
|
|
372
|
+
// }
|
|
220
373
|
}
|
|
221
374
|
#sendValueToForm = (dispatchEvent = true) => {
|
|
222
375
|
// input.value has already been updated/synced !!
|
|
@@ -237,116 +390,113 @@ export class PickerView extends FormAssociatedBase {
|
|
|
237
390
|
if (dispatchEvent)
|
|
238
391
|
this.dispatchEvent(new CustomEvent('selection', { detail: { selection: this.#selection }, bubbles: true, composed: true }));
|
|
239
392
|
};
|
|
240
|
-
#
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
393
|
+
#handleTriggerClick = async ({ type, target }) => {
|
|
394
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
395
|
+
if (!(target instanceof HTMLElement && target))
|
|
396
|
+
return;
|
|
397
|
+
this.#spawn?.remove?.();
|
|
398
|
+
const level0 = this.#renderPage('sheet' === this.pickerStyle ? 'sheet-view' : 'body-view', this.hasAttribute('searchable'), this.getAttribute('label'));
|
|
399
|
+
if (!level0)
|
|
244
400
|
return;
|
|
245
|
-
const
|
|
401
|
+
const path = new NavigationPath(target)?.hydrate();
|
|
402
|
+
await startViewTransition(target, 'forwards', async () => {
|
|
403
|
+
this.#spawn = level0;
|
|
404
|
+
update(path, level0);
|
|
405
|
+
});
|
|
406
|
+
};
|
|
407
|
+
#handlePageClick = async (evt) => {
|
|
408
|
+
debug(`${_a.name} ⚡️ ${evt?.type}`);
|
|
409
|
+
evt.stopImmediatePropagation();
|
|
410
|
+
evt.preventDefault();
|
|
411
|
+
const { target } = evt;
|
|
412
|
+
if (!(target instanceof HTMLElement && target))
|
|
413
|
+
return;
|
|
414
|
+
const btn = target.closest('button');
|
|
246
415
|
if (!btn)
|
|
247
416
|
return;
|
|
248
|
-
|
|
249
|
-
this.#
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
417
|
+
const proceed = () => {
|
|
418
|
+
this.#spawn?.remove();
|
|
419
|
+
this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
|
|
420
|
+
this.#sendValueToForm();
|
|
421
|
+
};
|
|
422
|
+
const { body } = new NavigationPath(this.#spawn)?.hydrate();
|
|
423
|
+
if (!body)
|
|
424
|
+
return proceed();
|
|
425
|
+
await startViewTransition(body, 'backwards', async () => {
|
|
426
|
+
proceed();
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
#handleClick({ type, target }) {
|
|
430
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
431
|
+
if (!(target instanceof HTMLElement && target))
|
|
432
|
+
return;
|
|
433
|
+
const btn = target.closest('button');
|
|
434
|
+
if (!btn)
|
|
435
|
+
return;
|
|
436
|
+
this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
|
|
254
437
|
this.#sendValueToForm();
|
|
255
438
|
}
|
|
256
|
-
#
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (!slot)
|
|
439
|
+
#handleValiditiesSlotchange = ({ type, target: slot }) => {
|
|
440
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
441
|
+
if (!(slot instanceof HTMLSlotElement && slot))
|
|
260
442
|
return;
|
|
261
|
-
const assigned = slot.assignedElements(
|
|
262
|
-
|
|
263
|
-
// for (const el of this.#trackedElements)
|
|
264
|
-
// if (!assigned.includes(el)) {
|
|
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
|
-
// }
|
|
443
|
+
const assigned = slot.assignedElements();
|
|
444
|
+
this.#validityObservers.syncObservations(assigned, ['value', 'label']);
|
|
279
445
|
if (0 < assigned.length)
|
|
280
|
-
this.#
|
|
446
|
+
this.#renderValidityMsgs([]);
|
|
281
447
|
};
|
|
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
|
-
}
|
|
448
|
+
#handleListSlotchange = ({ type, target: slot }) => {
|
|
449
|
+
debug(`${_a.name} ⚡️ ${type}`);
|
|
450
|
+
if (!(slot instanceof HTMLSlotElement && slot))
|
|
451
|
+
return;
|
|
452
|
+
const assigned = slot.assignedElements();
|
|
453
|
+
this.#observers.syncObservations(assigned);
|
|
454
|
+
if (0 < assigned.length)
|
|
455
|
+
this.#renderList([]);
|
|
456
|
+
};
|
|
457
|
+
static #wrapOptionTag(node) {
|
|
458
|
+
const btn = $(`<button type="button" tabindex="0"></button>`, '>1');
|
|
459
|
+
btn.setAttribute('value', node.getAttribute('value') ?? node.textContent?.trim() ?? '');
|
|
460
|
+
const label = $(`<label-view></label-view>`, '>1');
|
|
461
|
+
label.setAttribute('title', node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim() ?? '');
|
|
462
|
+
btn.appendChild(label);
|
|
302
463
|
return btn;
|
|
303
464
|
}
|
|
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);
|
|
465
|
+
static #wrapOptgroupTag(node) {
|
|
466
|
+
const labelT = `<label-view></label-view>`, summaryT = `<summary>${labelT}</summary>`;
|
|
467
|
+
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'));
|
|
468
|
+
if (node.hasAttribute('label'))
|
|
469
|
+
summaryLabel.setAttribute('title', node.getAttribute('label') ?? '');
|
|
470
|
+
if (node.hasAttribute('data-system-image'))
|
|
471
|
+
summaryLabel.setAttribute('system-image', node.getAttribute('data-system-image') ?? '');
|
|
472
|
+
return group;
|
|
473
|
+
}
|
|
474
|
+
static #reflectButtons(nodes, container) {
|
|
475
|
+
for (const node of nodes)
|
|
476
|
+
switch (node.tagName) {
|
|
477
|
+
case 'DATALIST': {
|
|
478
|
+
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'));
|
|
479
|
+
if (node.hasAttribute('data-label'))
|
|
480
|
+
label.setAttribute('title', node.getAttribute('data-label') ?? '');
|
|
481
|
+
if (node.hasAttribute('data-system-image'))
|
|
482
|
+
label.setAttribute('system-image', node.getAttribute('data-system-image') ?? '');
|
|
483
|
+
_a.#reflectButtons([...node.children], group);
|
|
484
|
+
container.appendChild(group);
|
|
485
|
+
break;
|
|
328
486
|
}
|
|
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
|
-
};
|
|
487
|
+
case 'OPTGROUP': {
|
|
488
|
+
const group = _a.#wrapOptgroupTag(node);
|
|
489
|
+
_a.#reflectButtons([...node.children], group);
|
|
490
|
+
container.appendChild(group);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
case 'OPTION':
|
|
494
|
+
default: {
|
|
495
|
+
container.appendChild(_a.#wrapOptionTag(node));
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
350
500
|
#reflectPlaceholder(value) {
|
|
351
501
|
const input = this.#shadowRoot.querySelector('input');
|
|
352
502
|
if (input) {
|
|
@@ -357,17 +507,6 @@ export class PickerView extends FormAssociatedBase {
|
|
|
357
507
|
}
|
|
358
508
|
}
|
|
359
509
|
#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
510
|
let label = this.querySelector(':scope>[slot=label]');
|
|
372
511
|
if (value) {
|
|
373
512
|
label ??= this.appendChild($(`<span slot="label"></span>`, '>1'));
|
|
@@ -375,25 +514,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
375
514
|
}
|
|
376
515
|
else
|
|
377
516
|
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 ?? ''
|
|
517
|
+
this.#renderList([]);
|
|
397
518
|
}
|
|
398
519
|
// Optional: form participation properties
|
|
399
520
|
get name() {
|
|
@@ -428,7 +549,7 @@ export class PickerView extends FormAssociatedBase {
|
|
|
428
549
|
message = kebabCase(key);
|
|
429
550
|
break;
|
|
430
551
|
}
|
|
431
|
-
|
|
552
|
+
debug(`${_a.name} ⚡️ validity-change`);
|
|
432
553
|
return this.#internals.setValidity(flags, this.#customValidity || message, anchor);
|
|
433
554
|
};
|
|
434
555
|
setCustomValidity = (message) => {
|