@swiftwc/ui 0.0.0-dev.37 → 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 +34 -23
  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 +185 -83
  24. package/generated/components/plain-button.js +5 -3
  25. package/generated/components/progress-view.js +11 -8
  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 -5
  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 +13 -8
  39. package/generated/components/text-field.js +27 -17
  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);
@@ -63,18 +73,30 @@ export class PickerView extends FormAssociatedBase {
63
73
  <button type="button">Filters</button>
64
74
  </v-stack>
65
75
  </sticky-container>`);
66
- const searchInput = sv?.querySelector('input[type=search]');
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,14 +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})`);
353
- if (!this.isConnected)
354
- return;
402
+ if (devFlags.debug)
403
+ console.debug(`${_a.name} ⚡️ render (${this.pickerStyle})`);
355
404
  // const style = this.getAttribute((this.constructor as typeof PickerView).ATTR.PICKER_STYLE)
356
405
  if (this.#lastRenderedStyle === this.pickerStyle)
357
406
  return; // skip if already applied
@@ -368,6 +417,7 @@ export class PickerView extends FormAssociatedBase {
368
417
  CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
369
418
  CleanupRegistry.unregister(this, 'datalist'); //off1()
370
419
  CleanupRegistry.register(this, onoff('slotchange', this.#handleListSlotchange, this.#slots?.get('list')).on(), 'datalist');
420
+ // #renderSlotted should run automatically now by slotchange initial event
371
421
  // switch (this.pickerStyle) {
372
422
  // }
373
423
  }
@@ -391,32 +441,36 @@ export class PickerView extends FormAssociatedBase {
391
441
  this.dispatchEvent(new CustomEvent('selection', { detail: { selection: this.#selection }, bubbles: true, composed: true }));
392
442
  };
393
443
  #handleTriggerClick = async ({ type, target }) => {
394
- debug(`${_a.name} ⚡️ ${type}`);
444
+ if (devFlags.debug)
445
+ console.debug(`${_a.name} ⚡️ ${type}`);
395
446
  if (!(target instanceof HTMLElement && target))
396
447
  return;
397
448
  this.#spawn?.remove?.();
398
- 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'));
399
450
  if (!level0)
400
451
  return;
401
452
  const path = new NavigationPath(target)?.hydrate();
402
453
  await startViewTransition(target, 'forwards', async () => {
403
454
  this.#spawn = level0;
404
455
  update(path, level0);
456
+ this.#reflectSelectionOnButtons();
405
457
  });
406
458
  };
407
- #handlePageClick = async (evt) => {
408
- debug(`${_a.name} ⚡️ ${evt?.type}`);
459
+ #handlePageBtnClick = async (evt) => {
460
+ if (devFlags.debug)
461
+ console.debug(`${_a.name} ⚡️ ${evt?.type}`);
409
462
  evt.stopImmediatePropagation();
410
463
  evt.preventDefault();
411
- const { target } = evt;
412
- if (!(target instanceof HTMLElement && target))
413
- return;
414
- const btn = target.closest('button');
415
- if (!btn)
464
+ const { currentTarget: btn } = evt;
465
+ if (!(btn instanceof HTMLElement && btn))
416
466
  return;
467
+ // const btn = target.closest('button')
468
+ // if (!btn) return
417
469
  const proceed = () => {
418
470
  this.#spawn?.remove();
419
- this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
471
+ this.#selection = btn.getAttribute('value') ?? '';
472
+ this.#reflectSelectionOnButtons();
473
+ this.#reflectCurrentValueLabel();
420
474
  this.#sendValueToForm();
421
475
  };
422
476
  const { body } = new NavigationPath(this.#spawn)?.hydrate();
@@ -427,39 +481,46 @@ export class PickerView extends FormAssociatedBase {
427
481
  });
428
482
  };
429
483
  #handleClick({ type, target }) {
430
- debug(`${_a.name} ⚡️ ${type}`);
484
+ if (devFlags.debug)
485
+ console.debug(`${_a.name} ⚡️ ${type}`);
431
486
  if (!(target instanceof HTMLElement && target))
432
487
  return;
433
488
  const btn = target.closest('button');
434
489
  if (!btn)
435
490
  return;
436
- this.#selection = btn.getAttribute('value') ?? btn.textContent?.trim() ?? '';
491
+ this.#selection = btn.getAttribute('value') ?? '';
492
+ this.#reflectSelectionOnButtons();
493
+ this.#reflectCurrentValueLabel();
437
494
  this.#sendValueToForm();
438
495
  }
439
496
  #handleValiditiesSlotchange = ({ type, target: slot }) => {
440
- debug(`${_a.name} ⚡️ ${type}`);
497
+ if (devFlags.debug)
498
+ console.debug(`${_a.name} ⚡️ ${type}`);
441
499
  if (!(slot instanceof HTMLSlotElement && slot))
442
500
  return;
443
501
  const assigned = slot.assignedElements();
444
502
  this.#validityObservers.syncObservations(assigned, ['value', 'label']);
445
- if (0 < assigned.length)
446
- this.#renderValidityMsgs([]);
503
+ // if (0 < assigned.length)
504
+ this.#renderValidityMsgs([]);
447
505
  };
448
506
  #handleListSlotchange = ({ type, target: slot }) => {
449
- debug(`${_a.name} ⚡️ ${type}`);
507
+ if (devFlags.debug)
508
+ console.debug(`${_a.name} ⚡️ ${type}`);
450
509
  if (!(slot instanceof HTMLSlotElement && slot))
451
510
  return;
452
511
  const assigned = slot.assignedElements();
453
512
  this.#observers.syncObservations(assigned);
454
- if (0 < assigned.length)
455
- this.#renderList([]);
513
+ // if (0 < assigned.length)
514
+ this.#renderSlotted([]);
456
515
  };
457
516
  static #wrapOptionTag(node) {
458
- const btn = $(`<button type="button" tabindex="0"></button>`, '>1');
459
- 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')
460
521
  const label = $(`<label-view></label-view>`, '>1');
461
- label.setAttribute('title', node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim() ?? '');
462
- btn.appendChild(label);
522
+ label.setAttribute('title', extractCurrentValueFromOption(node));
523
+ hStack?.appendChild(label);
463
524
  return btn;
464
525
  }
465
526
  static #wrapOptgroupTag(node) {
@@ -507,14 +568,51 @@ export class PickerView extends FormAssociatedBase {
507
568
  }
508
569
  }
509
570
  #reflectLabel(value) {
510
- let label = this.querySelector(':scope>[slot=label]');
571
+ let label = this.querySelector(':scope>label-view[slot=label]');
511
572
  if (value) {
512
- label ??= this.appendChild($(`<span slot="label"></span>`, '>1'));
513
- label.textContent = value;
573
+ label ??= this.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
574
+ label.setAttribute('foreground', 'secondary');
575
+ label.setAttribute('title', value);
514
576
  }
515
577
  else
516
578
  label?.remove();
517
- 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
+ }
518
616
  }
519
617
  // Optional: form participation properties
520
618
  get name() {
@@ -549,7 +647,8 @@ export class PickerView extends FormAssociatedBase {
549
647
  message = kebabCase(key);
550
648
  break;
551
649
  }
552
- debug(`${_a.name} ⚡️ validity-change`);
650
+ if (devFlags.debug)
651
+ console.debug(`${_a.name} ⚡️ validity-change`);
553
652
  return this.#internals.setValidity(flags, this.#customValidity || message, anchor);
554
653
  };
555
654
  setCustomValidity = (message) => {
@@ -568,6 +667,9 @@ export class PickerView extends FormAssociatedBase {
568
667
  };
569
668
  formResetCallback = () => {
570
669
  this.#selection = '';
670
+ this.#reflectSelectionOnButtons();
671
+ this.#reflectCurrentValueLabel();
672
+ this.#sendValueToForm();
571
673
  };
572
674
  }
573
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,9 +104,8 @@ export class ProgressView extends HTMLElement {
100
104
  this.#render();
101
105
  };
102
106
  #render() {
103
- debug(`${_a.name} ⚡️ render (${this.#progressViewStyle})`);
104
- if (!this.isConnected)
105
- return;
107
+ if (devFlags.debug)
108
+ console.debug(`${_a.name} ⚡️ render (${this.#progressViewStyle})`);
106
109
  if (this.#lastRenderedStyle === this.#progressViewStyle)
107
110
  return; // skip if already applied
108
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
  }