@swiftwc/ui 0.0.0-dev.38 → 0.0.0-dev.39

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.
Files changed (60) hide show
  1. package/generated/client/index.js +5 -3
  2. package/generated/components/alert-dialog.js +17 -9
  3. package/generated/components/body-view.js +5 -3
  4. package/generated/components/bordered-button.js +7 -4
  5. package/generated/components/bordered-prominent-button.js +7 -4
  6. package/generated/components/borderless-button.js +7 -4
  7. package/generated/components/confirmation-dialog.js +17 -9
  8. package/generated/components/content-unavailable-view.js +7 -4
  9. package/generated/components/date-picker.js +32 -19
  10. package/generated/components/disclosure-group.js +9 -5
  11. package/generated/components/fine-tooltip.js +7 -4
  12. package/generated/components/form-view.js +5 -3
  13. package/generated/components/glass-button.js +7 -4
  14. package/generated/components/glass-prominent-button.js +7 -4
  15. package/generated/components/label-view.js +7 -4
  16. package/generated/components/list-view.js +5 -3
  17. package/generated/components/menu-view.js +19 -10
  18. package/generated/components/navigation-large-title.js +9 -5
  19. package/generated/components/navigation-split-view.js +7 -4
  20. package/generated/components/navigation-stack.js +7 -4
  21. package/generated/components/navigation-title.js +7 -4
  22. package/generated/components/picker-view.d.ts +1 -0
  23. package/generated/components/picker-view.js +181 -77
  24. package/generated/components/plain-button.js +5 -3
  25. package/generated/components/progress-view.js +11 -6
  26. package/generated/components/screen-view.js +3 -2
  27. package/generated/components/scroll-view-proxy.js +5 -3
  28. package/generated/components/scroll-view.js +17 -9
  29. package/generated/components/search-view.js +7 -4
  30. package/generated/components/section-view.js +7 -4
  31. package/generated/components/sheet-view.js +11 -6
  32. package/generated/components/sidebar-toggle.js +9 -5
  33. package/generated/components/sidebar-view.js +3 -2
  34. package/generated/components/sticky-container.js +5 -3
  35. package/generated/components/tab-bar.js +7 -4
  36. package/generated/components/tab-item.js +15 -8
  37. package/generated/components/tab-view.js +14 -8
  38. package/generated/components/table-view.js +11 -6
  39. package/generated/components/text-field.js +25 -15
  40. package/generated/components/tool-bar-item-group.js +5 -3
  41. package/generated/components/tool-bar-item.js +7 -4
  42. package/generated/components/tool-bar.js +7 -4
  43. package/generated/components/v-keyboard.js +9 -5
  44. package/generated/components/v-stack.js +5 -3
  45. package/generated/css/index.css +6 -2
  46. package/generated/events/picker-view.d.ts +1 -0
  47. package/generated/internal/class/css-style-observer.js +3 -2
  48. package/generated/internal/class/form-associated-base.js +7 -5
  49. package/generated/internal/class/navigation-view.js +5 -3
  50. package/generated/internal/decorators/adaptive-slot.js +7 -4
  51. package/generated/internal/privateNamespace.js +22 -13
  52. package/generated/internal/utils/dev-flags.d.ts +3 -0
  53. package/generated/internal/utils/dev-flags.js +5 -0
  54. package/generated/internal/utils/index.d.ts +1 -1
  55. package/generated/internal/utils/index.js +1 -1
  56. package/generated/snapshot/index.js +5 -3
  57. package/package.json +1 -1
  58. package/scss/base/_layout.scss +6 -7
  59. package/generated/internal/utils/debug.d.ts +0 -1
  60. package/generated/internal/utils/debug.js +0 -3
@@ -5,7 +5,7 @@ import { FormAssociatedBase, getInternals } from '../internal/class/form-associa
5
5
  import { MutationObserverSet } from '../internal/class/mutation-observer-set';
6
6
  import { NavigationPath } from '../internal/class/navigation-path';
7
7
  import { queryInsertPosition, startViewTransition } from '../internal/privateNamespace';
8
- import { $, debug, kebabCase, onoff } from '../internal/utils';
8
+ import { $, devFlags, kebabCase, onoff } from '../internal/utils';
9
9
  const pickerStyles = ['menu', 'inline', 'navigation-link', 'sheet', 'automatic'];
10
10
  const update = (path, node, overwrite = true) => {
11
11
  if (!(path instanceof NavigationPath))
@@ -18,23 +18,32 @@ const update = (path, node, overwrite = true) => {
18
18
  if (page)
19
19
  page.insertAdjacentElement(position, node);
20
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
21
+ const reflectSpawnedElement = (current, source) => {
22
+ const { page: oldSv, toolBarConfig: oldToolbar } = new NavigationPath(current).hydrate(), { page: newSv, toolBarConfig: newToolbar } = new NavigationPath(source).hydrate();
23
+ const oldVStack = oldSv?.querySelector(':scope>v-stack'), newVStack = newSv?.querySelector(':scope>v-stack'), oldBackBtn = oldToolbar?.at(0)?.querySelector('button'), newBackBtn = newToolbar?.at(0)?.querySelector('button');
24
+ // pre replace list (always exist)
25
+ const oldOpenedDetails = [...(oldVStack?.querySelectorAll('list-view details[open]>summary') ?? [])].map((item) => item.textContent.trim());
26
+ // replace list (always exist)
27
+ if (oldVStack && newVStack)
28
+ oldVStack.replaceWith(newVStack);
29
+ // post replace list (always exist)
32
30
  for (const label of oldOpenedDetails)
33
- for (const summary of newList?.querySelectorAll('details>summary') ?? [])
31
+ for (const summary of newVStack?.querySelectorAll('list-view details>summary') ?? [])
34
32
  if (summary.textContent.trim() === label) {
35
33
  summary.parentElement?.setAttribute('open', 'open');
36
34
  break;
37
35
  }
36
+ // replace backbtns (always exist)
37
+ if (oldBackBtn && newBackBtn)
38
+ oldBackBtn.replaceWith(newBackBtn);
39
+ // overwrite navtitle (always exist)
40
+ oldSv?.setAttribute('navigation-inline-title', newSv?.getAttribute('navigation-inline-title') ?? '');
41
+ };
42
+ const extractTagFromOption = (node) => {
43
+ return ((node.getAttribute('value') ?? node.textContent?.trim()) || node.getAttribute('label')) ?? '';
44
+ };
45
+ const extractCurrentValueFromOption = (node) => {
46
+ return (node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim()) || '';
38
47
  };
39
48
  export class PickerView extends FormAssociatedBase {
40
49
  static get ATTR() {
@@ -44,6 +53,7 @@ export class PickerView extends FormAssociatedBase {
44
53
  PICKER_STYLE: 'picker-style',
45
54
  SELECTION: 'selection',
46
55
  SEARCHABLE: 'searchable',
56
+ CURRENT_VALUE_LABEL: 'current-value-label',
47
57
  };
48
58
  }
49
59
  static get observedAttributes() {
@@ -51,7 +61,7 @@ export class PickerView extends FormAssociatedBase {
51
61
  }
52
62
  static #templates = new Map();
53
63
  #spawn;
54
- #renderPage = (tag, searchable = false, title, node) => {
64
+ #spawnPage = (tag, searchable = false, title, node) => {
55
65
  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
66
  if (title)
57
67
  sv?.setAttribute('navigation-inline-title', title);
@@ -66,15 +76,27 @@ export class PickerView extends FormAssociatedBase {
66
76
  const searchInput = sv?.querySelector('input'); // FIXME: compoennt is=search-view??
67
77
  searchInput?.addEventListener('focus', (evt) => {
68
78
  if (this.#spawn)
69
- this.dispatchEvent(new CustomEvent('picker:searchfocus', { detail: { element: this.#spawn }, bubbles: true, composed: true }));
79
+ this.dispatchEvent(new CustomEvent('picker:searchfocus', {
80
+ detail: { element: this.#spawn, search: evt.target instanceof HTMLInputElement && evt.target ? evt.target.value : '' },
81
+ bubbles: true,
82
+ composed: true,
83
+ }));
70
84
  });
71
85
  searchInput?.addEventListener('blur', (evt) => {
72
86
  if (this.#spawn)
73
- this.dispatchEvent(new CustomEvent('picker:searchblur', { detail: { element: this.#spawn }, bubbles: true, composed: true }));
87
+ this.dispatchEvent(new CustomEvent('picker:searchblur', {
88
+ detail: { element: this.#spawn, search: evt.target instanceof HTMLInputElement && evt.target ? evt.target.value : '' },
89
+ bubbles: true,
90
+ composed: true,
91
+ }));
74
92
  });
75
93
  searchInput?.addEventListener('input', (evt) => {
76
94
  if (this.#spawn)
77
- this.dispatchEvent(new CustomEvent('picker:searchinput', { detail: { element: this.#spawn }, bubbles: true, composed: true }));
95
+ this.dispatchEvent(new CustomEvent('picker:searchinput', {
96
+ detail: { element: this.#spawn, search: evt.target instanceof HTMLInputElement && evt.target ? evt.target.value : '' },
97
+ bubbles: true,
98
+ composed: true,
99
+ }));
78
100
  });
79
101
  }
80
102
  backBtn?.setAttribute('help', I18n.t('ButtonRole').Back);
@@ -98,13 +120,13 @@ export class PickerView extends FormAssociatedBase {
98
120
  const details = _a.#wrapOptgroupTag(el);
99
121
  _a.#reflectButtons([...el.children], details);
100
122
  for (const btn of details.querySelectorAll(':scope>button'))
101
- btn.addEventListener('click', this.#handlePageClick);
123
+ btn.addEventListener('click', this.#handlePageBtnClick);
102
124
  list?.appendChild(details);
103
125
  break;
104
126
  }
105
127
  case 'OPTION': {
106
128
  const btn = _a.#wrapOptionTag(el);
107
- btn.addEventListener('click', this.#handlePageClick);
129
+ btn.addEventListener('click', this.#handlePageBtnClick);
108
130
  list?.appendChild(btn);
109
131
  break;
110
132
  }
@@ -118,12 +140,13 @@ export class PickerView extends FormAssociatedBase {
118
140
  const { target } = evt;
119
141
  if (!(target instanceof HTMLElement && target))
120
142
  return;
121
- const newPage = this.#renderPage('body-view', this.hasAttribute('searchable'), el.dataset.label, el);
143
+ const newPage = this.#spawnPage('body-view', this.hasAttribute('searchable'), el.dataset.label, el);
122
144
  if (!newPage)
123
145
  return;
124
146
  const path = new NavigationPath(target)?.hydrate();
125
147
  await startViewTransition(target, 'forwards', async () => {
126
148
  update(path, newPage);
149
+ this.#reflectSelectionOnButtons();
127
150
  });
128
151
  });
129
152
  list?.appendChild(btn);
@@ -133,22 +156,28 @@ export class PickerView extends FormAssociatedBase {
133
156
  }
134
157
  return body;
135
158
  };
136
- #renderList = (entries) => {
137
- debug(`${_a.name} ⚡️ mutation`);
159
+ #renderSlotted = (entries) => {
160
+ if (devFlags.debug)
161
+ console.debug(`${_a.name} ⚡️ mutation`);
138
162
  switch (this.pickerStyle) {
139
163
  case 'sheet':
140
164
  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');
165
+ // current value label only
166
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])') ?? this.appendChild($(`<label-view></label-view>`, '>1'));
167
+ // reset state
168
+ currentValueLabel.setAttribute('system-image', 'dots-three'); // overwritten
169
+ currentValueLabel.setAttribute('title', this.#selection); // overwritten
170
+ // clear all siblings
171
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
172
+ if (currentValueLabel !== el)
173
+ el.remove();
144
174
  CleanupRegistry.unregister(this, 'trigger');
145
- CleanupRegistry.register(this, onoff('click', this.#handleTriggerClick, label).on(), 'trigger');
146
- this.appendChild(label);
175
+ CleanupRegistry.register(this, onoff('click', this.#handleTriggerClick, currentValueLabel).on(), 'trigger');
147
176
  // rebuild snapshot(tree)
148
177
  if (!this.#spawn)
149
178
  break;
150
179
  // rerender level 0
151
- replaceList(this.#spawn, this.#renderPage('body-view', this.hasAttribute('searchable'), this.getAttribute('label')));
180
+ reflectSpawnedElement(this.#spawn, this.#spawnPage('body-view', this.hasAttribute('searchable'), this.getAttribute('label')));
152
181
  for (const el of this.#spawn.querySelectorAll('body-view')) {
153
182
  const depth = $.ancestors('body-view,[is=sheet-view]', el).indexOf(this.#spawn);
154
183
  if (0 >= depth)
@@ -158,43 +187,58 @@ export class PickerView extends FormAssociatedBase {
158
187
  el.remove();
159
188
  break;
160
189
  }
161
- replaceList(el, this.#renderPage('body-view', this.hasAttribute('searchable'), datalist.dataset.label, datalist));
190
+ reflectSpawnedElement(el, this.#spawnPage('body-view', this.hasAttribute('searchable'), datalist.dataset.label, datalist));
162
191
  }
163
192
  break;
164
193
  }
165
194
  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();
195
+ const menu = this.querySelector(':scope>menu-view:not([slot])') ?? this.appendChild($(`<menu-view></menu-view>`, '>1'));
196
+ // reset state
197
+ menu.innerHTML = '';
198
+ menu.tabIndex = 0;
199
+ // clear all siblings
200
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
201
+ if (menu !== el)
202
+ el.remove();
203
+ const currentValueLabel = menu.querySelector(':scope>label-view[slot=label]') ?? menu.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
204
+ currentValueLabel.setAttribute('system-image', 'dots-three'); // overwritten
205
+ currentValueLabel.setAttribute('title', this.#selection); // overwritten
170
206
  _a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements() ?? [])], menu);
171
207
  break;
172
208
  }
173
209
  case 'inline':
174
210
  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);
211
+ const sectionTpl = `<section-view></section-view>`;
212
+ const inlineList = this.querySelector(':scope>list-view:not([slot])') ?? this.appendChild($(`<list-view>${sectionTpl}</list-view>`, '>1')), section = inlineList.querySelector(':scope>section-view') ?? inlineList.appendChild($(sectionTpl, '>1'));
213
+ // reset state
214
+ section.innerHTML = '';
215
+ // clear all siblings
216
+ for (const el of this.querySelectorAll(':scope>:not([slot])'))
217
+ if (inlineList !== el)
218
+ el.remove();
219
+ const value = this.getAttribute(this.constructor.ATTR.LABEL);
220
+ if (value) {
221
+ const label = $(`<label-view></label-view>`, '>1');
222
+ label.setAttribute('title', value);
223
+ section.insertAdjacentElement('beforeend', label);
183
224
  }
184
225
  _a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements({ flatten: true }) ?? [])].filter((el) => el.matches('option')), section);
185
226
  break;
186
227
  }
187
228
  }
229
+ this.#reflectSelectionOnButtons();
230
+ this.#reflectCurrentValueLabel();
188
231
  };
189
232
  #renderValidityMsgs = (entries) => {
190
- debug(`${_a.name} ⚡️ mutation`);
233
+ if (devFlags.debug)
234
+ console.debug(`${_a.name} ⚡️ mutation`);
191
235
  this.setValidity(this.validity, this.validationMessage);
192
236
  };
193
237
  #lastRenderedStyle; //string | null
194
238
  #shadowRoot;
195
239
  #slots = new Map();
196
240
  #validityObservers = new MutationObserverSet(this.#renderValidityMsgs);
197
- #observers = new MutationObserverSet(this.#renderList);
241
+ #observers = new MutationObserverSet(this.#renderSlotted);
198
242
  #customValidity = '';
199
243
  #selection = '';
200
244
  get #internals() {
@@ -299,9 +343,10 @@ export class PickerView extends FormAssociatedBase {
299
343
  this.#shadowRoot = this.attachShadow({ mode: 'closed' });
300
344
  }
301
345
  connectedCallback() {
302
- debug(`${_a.name} ⚡️ connect`);
346
+ if (devFlags.debug)
347
+ console.debug(`${_a.name} ⚡️ connect`);
303
348
  CleanupRegistry.register(this, onoff('localechange', () => {
304
- this.#renderList([]);
349
+ this.#renderSlotted([]);
305
350
  }, I18n.on).on());
306
351
  CleanupRegistry.register(this, onoff('click', this.#handleClick, this).on());
307
352
  if (!this.hasAttribute(this.constructor.ATTR.PICKER_STYLE))
@@ -313,20 +358,22 @@ export class PickerView extends FormAssociatedBase {
313
358
  this.#sendValueToForm(false);
314
359
  }
315
360
  disconnectedCallback() {
316
- debug(`${_a.name} ⚡️ disconnect`);
361
+ if (devFlags.debug)
362
+ console.debug(`${_a.name} ⚡️ disconnect`);
317
363
  CleanupRegistry.unregister(this);
318
364
  this.#validityObservers.unobserveAll();
319
365
  this.#observers.unobserveAll();
320
366
  }
321
367
  attributeChangedCallback(name, oldValue, newValue) {
322
- debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
368
+ if (devFlags.debug)
369
+ console.debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
323
370
  switch (name) {
324
371
  case this.constructor.ATTR.PLACEHOLDER:
325
372
  this.#reflectPlaceholder(newValue);
326
373
  break;
327
374
  case this.constructor.ATTR.LABEL:
328
375
  this.#reflectLabel(newValue);
329
- this.#sendValueToForm();
376
+ // this.#sendValueToForm()
330
377
  break;
331
378
  case this.constructor.ATTR.PICKER_STYLE:
332
379
  if (oldValue === newValue)
@@ -336,7 +383,11 @@ export class PickerView extends FormAssociatedBase {
336
383
  case this.constructor.ATTR.SEARCHABLE:
337
384
  if (oldValue === newValue)
338
385
  break;
339
- this.#renderList([]);
386
+ this.#renderSlotted([]);
387
+ break;
388
+ case this.constructor.ATTR.CURRENT_VALUE_LABEL:
389
+ // if (oldValue === newValue) break
390
+ this.#reflectCurrentValueLabel();
340
391
  break;
341
392
  case this.constructor.ATTR.SELECTION:
342
393
  // nothing happens
@@ -344,12 +395,12 @@ export class PickerView extends FormAssociatedBase {
344
395
  }
345
396
  }
346
397
  get pickerStyle() {
347
- return pickerStyles.includes(this.getAttribute(this.constructor.ATTR.PICKER_STYLE) ?? '')
348
- ? this.getAttribute(this.constructor.ATTR.PICKER_STYLE)
349
- : 'automatic';
398
+ const attr = this.constructor.ATTR.PICKER_STYLE;
399
+ return pickerStyles.includes(this.getAttribute(attr) ?? '') ? this.getAttribute(attr) : 'automatic';
350
400
  }
351
401
  #render() {
352
- debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
402
+ if (devFlags.debug)
403
+ console.debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
353
404
  // const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)
354
405
  if (this.#lastRenderedStyle === this.pickerStyle)
355
406
  return; // skip if already applied
@@ -366,6 +417,7 @@ export class PickerView extends FormAssociatedBase {
366
417
  CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
367
418
  CleanupRegistry.unregister(this, 'datalist'); //off1()
368
419
  CleanupRegistry.register(this, onoff('slotchange', this.#handleListSlotchange, this.#slots?.get('list')).on(), 'datalist');
420
+ // #renderSlotted should run automatically now by slotchange initial event
369
421
  // switch (this.pickerStyle) {
370
422
  // }
371
423
  }
@@ -389,32 +441,36 @@ export class PickerView extends FormAssociatedBase {
389
441
  this.dispatchEvent(new CustomEvent('selection', { detail: { selection: this.#selection }, bubbles: true, composed: true }));
390
442
  };
391
443
  #handleTriggerClick = async ({ type, target }) => {
392
- debug(`${_a.name} ⚡️ ${type}`);
444
+ if (devFlags.debug)
445
+ console.debug(`${_a.name} ⚡️ ${type}`);
393
446
  if (!(target instanceof HTMLElement && target))
394
447
  return;
395
448
  this.#spawn?.remove?.();
396
- const level0 = this.#renderPage('sheet' === this.pickerStyle ? 'sheet-view' : 'body-view', this.hasAttribute('searchable'), this.getAttribute('label'));
449
+ const level0 = this.#spawnPage('sheet' === this.pickerStyle ? 'sheet-view' : 'body-view', this.hasAttribute('searchable'), this.getAttribute('label'));
397
450
  if (!level0)
398
451
  return;
399
452
  const path = new NavigationPath(target)?.hydrate();
400
453
  await startViewTransition(target, 'forwards', async () => {
401
454
  this.#spawn = level0;
402
455
  update(path, level0);
456
+ this.#reflectSelectionOnButtons();
403
457
  });
404
458
  };
405
- #handlePageClick = async (evt) => {
406
- debug(`${_a.name} ⚡️ ${evt?.type}`);
459
+ #handlePageBtnClick = async (evt) => {
460
+ if (devFlags.debug)
461
+ console.debug(`${_a.name} ⚡️ ${evt?.type}`);
407
462
  evt.stopImmediatePropagation();
408
463
  evt.preventDefault();
409
- const { target } = evt;
410
- if (!(target instanceof HTMLElement && target))
411
- return;
412
- const btn = target.closest('button');
413
- if (!btn)
464
+ const { currentTarget: btn } = evt;
465
+ if (!(btn instanceof HTMLElement && btn))
414
466
  return;
467
+ // const btn = target.closest('button')
468
+ // if (!btn) return
415
469
  const proceed = () => {
416
470
  this.#spawn?.remove();
417
- this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
471
+ this.#selection = btn.getAttribute('value') ?? '';
472
+ this.#reflectSelectionOnButtons();
473
+ this.#reflectCurrentValueLabel();
418
474
  this.#sendValueToForm();
419
475
  };
420
476
  const { body } = new NavigationPath(this.#spawn)?.hydrate();
@@ -425,17 +481,21 @@ export class PickerView extends FormAssociatedBase {
425
481
  });
426
482
  };
427
483
  #handleClick({ type, target }) {
428
- debug(`${_a.name} ⚡️ ${type}`);
484
+ if (devFlags.debug)
485
+ console.debug(`${_a.name} ⚡️ ${type}`);
429
486
  if (!(target instanceof HTMLElement && target))
430
487
  return;
431
488
  const btn = target.closest('button');
432
489
  if (!btn)
433
490
  return;
434
- this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
491
+ this.#selection = btn.getAttribute('value') ?? '';
492
+ this.#reflectSelectionOnButtons();
493
+ this.#reflectCurrentValueLabel();
435
494
  this.#sendValueToForm();
436
495
  }
437
496
  #handleValiditiesSlotchange = ({ type, target: slot }) => {
438
- debug(`${_a.name} ⚡️ ${type}`);
497
+ if (devFlags.debug)
498
+ console.debug(`${_a.name} ⚡️ ${type}`);
439
499
  if (!(slot instanceof HTMLSlotElement && slot))
440
500
  return;
441
501
  const assigned = slot.assignedElements();
@@ -444,20 +504,23 @@ export class PickerView extends FormAssociatedBase {
444
504
  this.#renderValidityMsgs([]);
445
505
  };
446
506
  #handleListSlotchange = ({ type, target: slot }) => {
447
- debug(`${_a.name} ⚡️ ${type}`);
507
+ if (devFlags.debug)
508
+ console.debug(`${_a.name} ⚡️ ${type}`);
448
509
  if (!(slot instanceof HTMLSlotElement && slot))
449
510
  return;
450
511
  const assigned = slot.assignedElements();
451
512
  this.#observers.syncObservations(assigned);
452
513
  // if (0 < assigned.length)
453
- this.#renderList([]);
514
+ this.#renderSlotted([]);
454
515
  };
455
516
  static #wrapOptionTag(node) {
456
- const btn = $(`<button type="button" tabindex="0"></button>`, '>1');
457
- btn.setAttribute('value', node.getAttribute('value') ?? node.textContent?.trim() ?? '');
517
+ const btn = $(`<button type="button" tabindex="0"><h-stack template="auto spacer"><label-view system-image="check"></label-view></h-stack></button>`, '>1'), hStack = btn.querySelector(':scope>h-stack');
518
+ // chevron = hStack?.querySelector<HTMLElement>(':scope>label-view')
519
+ btn.setAttribute('value', extractTagFromOption(node));
520
+ // if (selection !== btn.getAttribute('value')) chevron?.style.setProperty('visibility', 'hidden')
458
521
  const label = $(`<label-view></label-view>`, '>1');
459
- label.setAttribute('title', node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim() ?? '');
460
- btn.appendChild(label);
522
+ label.setAttribute('title', extractCurrentValueFromOption(node));
523
+ hStack?.appendChild(label);
461
524
  return btn;
462
525
  }
463
526
  static #wrapOptgroupTag(node) {
@@ -505,14 +568,51 @@ export class PickerView extends FormAssociatedBase {
505
568
  }
506
569
  }
507
570
  #reflectLabel(value) {
508
- let label = this.querySelector(':scope>[slot=label]');
571
+ let label = this.querySelector(':scope>label-view[slot=label]');
509
572
  if (value) {
510
- label ??= this.appendChild($(`<span slot="label"></span>`, '>1'));
511
- label.textContent = value;
573
+ label ??= this.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
574
+ label.setAttribute('foreground', 'secondary');
575
+ label.setAttribute('title', value);
512
576
  }
513
577
  else
514
578
  label?.remove();
515
- this.#renderList([]);
579
+ this.#renderSlotted([]);
580
+ }
581
+ #reflectSelectionOnButtons() {
582
+ // walk all rendered buttons (inline has buttons in list, menu has buttons in menu-view)
583
+ for (const el of this.querySelectorAll('button[value]:not([slot])'))
584
+ el.querySelector('label-view[system-image="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
585
+ // also sync the spawn (sheet/navigation) if open
586
+ if (this.#spawn)
587
+ for (const el of this.#spawn.querySelectorAll('list-view button[value]:not([slot])'))
588
+ el.querySelector('label-view[system-image="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
589
+ }
590
+ #reflectCurrentValueLabel() {
591
+ const selectedTag = this.#slots
592
+ ?.get('list')
593
+ ?.assignedElements({ flatten: true })
594
+ .flatMap((el) => [...(el.matches('option') ? [el] : []), ...el.querySelectorAll('option')])
595
+ .find((el) => this.#selection === extractTagFromOption(el)), cvl = selectedTag ? extractCurrentValueFromOption(selectedTag) : '', final = (this.getAttribute('current-value-label') ?? '').replaceAll('{{selection}}', this.#selection).replaceAll('{{currentValueLabel}}', this.#selection) || cvl || this.#selection;
596
+ switch (this.pickerStyle) {
597
+ case 'sheet':
598
+ case 'navigation-link': {
599
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])');
600
+ currentValueLabel?.setAttribute('system-image', 'dots-three');
601
+ currentValueLabel?.setAttribute('title', final);
602
+ break;
603
+ }
604
+ case 'menu': {
605
+ const currentValueLabel = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]');
606
+ currentValueLabel?.setAttribute('system-image', 'dots-three');
607
+ currentValueLabel?.setAttribute('title', final);
608
+ break;
609
+ }
610
+ case 'inline':
611
+ default: {
612
+ //
613
+ break;
614
+ }
615
+ }
516
616
  }
517
617
  // Optional: form participation properties
518
618
  get name() {
@@ -547,7 +647,8 @@ export class PickerView extends FormAssociatedBase {
547
647
  message = kebabCase(key);
548
648
  break;
549
649
  }
550
- debug(`${_a.name} ⚡️ validity-change`);
650
+ if (devFlags.debug)
651
+ console.debug(`${_a.name} ⚡️ validity-change`);
551
652
  return this.#internals.setValidity(flags, this.#customValidity || message, anchor);
552
653
  };
553
654
  setCustomValidity = (message) => {
@@ -566,6 +667,9 @@ export class PickerView extends FormAssociatedBase {
566
667
  };
567
668
  formResetCallback = () => {
568
669
  this.#selection = '';
670
+ this.#reflectSelectionOnButtons();
671
+ this.#reflectCurrentValueLabel();
672
+ this.#sendValueToForm();
569
673
  };
570
674
  }
571
675
  _a = PickerView;
@@ -1,16 +1,18 @@
1
1
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
2
- import { debug, onoff } from '../internal/utils';
2
+ import { devFlags, onoff } from '../internal/utils';
3
3
  import { ButtonBase } from '../namespace-browser/base';
4
4
  export class PlainButton extends ButtonBase {
5
5
  constructor() {
6
6
  super();
7
7
  }
8
8
  static polyfillDisconnectedCallback(el) {
9
- debug(`${PlainButton.name} ⚡️ disconnect`);
9
+ if (devFlags.debug)
10
+ console.debug(`${PlainButton.name} ⚡️ disconnect`);
10
11
  CleanupRegistry.unregister(el);
11
12
  }
12
13
  static polyfillConnectedCallback(el) {
13
- debug(`${PlainButton.name} ⚡️ connect`);
14
+ if (devFlags.debug)
15
+ console.debug(`${PlainButton.name} ⚡️ connect`);
14
16
  el.tabIndex = 0;
15
17
  CleanupRegistry.register(el, onoff('click', PlainButton.#handleClick, el).on());
16
18
  }
@@ -1,7 +1,7 @@
1
1
  var _a;
2
2
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
3
3
  import { CSSStyleObserver } from '../internal/class/css-style-observer';
4
- import { $, debug } from '../internal/utils';
4
+ import { $, devFlags } from '../internal/utils';
5
5
  import { Snapshot } from '../snapshot';
6
6
  const progressViewStyles = ['circular', 'linear'];
7
7
  /**
@@ -55,11 +55,13 @@ export class ProgressView extends HTMLElement {
55
55
  this.#shadowRoot = this.attachShadow({ mode: 'closed' });
56
56
  }
57
57
  disconnectedCallback() {
58
- debug(`${_a.name} ⚡️ disconnect`);
58
+ if (devFlags.debug)
59
+ console.debug(`${_a.name} ⚡️ disconnect`);
59
60
  CleanupRegistry.unregister(this);
60
61
  }
61
62
  connectedCallback() {
62
- debug(`${_a.name} ⚡️ connect`);
63
+ if (devFlags.debug)
64
+ console.debug(`${_a.name} ⚡️ connect`);
63
65
  this.#cssStyleObserver = new CSSStyleObserver({
64
66
  properties: ['--progress-view-style-index'],
65
67
  });
@@ -67,7 +69,8 @@ export class ProgressView extends HTMLElement {
67
69
  Snapshot.waitReady.then(() => self.requestAnimationFrame(this.#handleStyleChange));
68
70
  }
69
71
  attributeChangedCallback(name, oldValue, newValue) {
70
- debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
72
+ if (devFlags.debug)
73
+ console.debug(`${_a.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
71
74
  switch (name) {
72
75
  case 'label': {
73
76
  const label = this.querySelector(':scope>label-view:not([slot])') ?? this.appendChild($(`<label-view foreground="secondary"></label-view>`, '>1'));
@@ -91,7 +94,8 @@ export class ProgressView extends HTMLElement {
91
94
  }
92
95
  }
93
96
  #handleStyleChange = () => {
94
- debug(`${_a.name} ⚡️ style`);
97
+ if (devFlags.debug)
98
+ console.debug(`${_a.name} ⚡️ style`);
95
99
  const raw = self.getComputedStyle(this).getPropertyValue('--progress-view-style-index').trim();
96
100
  const newValue = progressViewStyles[Number(raw)] ?? progressViewStyles[0];
97
101
  if (newValue === this.#progressViewStyle)
@@ -100,7 +104,8 @@ export class ProgressView extends HTMLElement {
100
104
  this.#render();
101
105
  };
102
106
  #render() {
103
- debug(`${_a.name} ⚡️ render (${this.#progressViewStyle})`);
107
+ if (devFlags.debug)
108
+ console.debug(`${_a.name} ⚡️ render (${this.#progressViewStyle})`);
104
109
  if (this.#lastRenderedStyle === this.#progressViewStyle)
105
110
  return; // skip if already applied
106
111
  this.#lastRenderedStyle = this.#progressViewStyle;
@@ -1,10 +1,11 @@
1
- import { debug } from '../internal/utils';
1
+ import { devFlags } from '../internal/utils';
2
2
  import { DialogBase } from '../namespace-browser/base';
3
3
  export class ScreenView extends DialogBase {
4
4
  constructor() {
5
5
  super();
6
6
  }
7
7
  disconnectedCallback() {
8
- debug(`${ScreenView.name} ⚡️ disconnect`);
8
+ if (devFlags.debug)
9
+ console.debug(`${ScreenView.name} ⚡️ disconnect`);
9
10
  }
10
11
  }
@@ -1,4 +1,4 @@
1
- import { debug } from '../internal/utils';
1
+ import { devFlags } from '../internal/utils';
2
2
  /**
3
3
  * @summary A wrapper to {hv}-stacks
4
4
  */
@@ -7,9 +7,11 @@ export class ScrollViewProxy extends HTMLElement {
7
7
  super();
8
8
  }
9
9
  disconnectedCallback() {
10
- debug(`${ScrollViewProxy.name} ⚡️ disconnect`);
10
+ if (devFlags.debug)
11
+ console.debug(`${ScrollViewProxy.name} ⚡️ disconnect`);
11
12
  }
12
13
  connectedCallback() {
13
- debug(`${ScrollViewProxy.name} ⚡️ connect`);
14
+ if (devFlags.debug)
15
+ console.debug(`${ScrollViewProxy.name} ⚡️ connect`);
14
16
  }
15
17
  }