@swiftwc/ui 0.0.0-dev.57 → 0.0.0-dev.59

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.
@@ -0,0 +1,7 @@
1
+ export declare class ColorScheme extends HTMLElement {
2
+ static get observedAttributes(): string[];
3
+ constructor();
4
+ connectedCallback(): void;
5
+ disconnectedCallback(): void;
6
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
7
+ }
@@ -0,0 +1,54 @@
1
+ import { devFlags } from '../internal/utils';
2
+ export class ColorScheme extends HTMLElement {
3
+ static get observedAttributes() {
4
+ return ['dark'];
5
+ }
6
+ constructor() {
7
+ super();
8
+ }
9
+ connectedCallback() {
10
+ if (devFlags.debug)
11
+ console.debug(`${ColorScheme.name} ⚡️ connect`);
12
+ this.hidden = true;
13
+ this.inert = true;
14
+ }
15
+ disconnectedCallback() {
16
+ if (devFlags.debug)
17
+ console.debug(`${ColorScheme.name} ⚡️ disconnect`);
18
+ }
19
+ attributeChangedCallback(name, oldValue, newValue) {
20
+ if (devFlags.debug)
21
+ console.debug(`${ColorScheme.name} ⚡️ attr-change [${name}]`);
22
+ // self.CSS.registerProperty({ name: '--canvas', syntax: '<color>', inherits: false, initialValue: 'oklch(100% 0 0deg)' })
23
+ if (newValue)
24
+ self.CSS.registerProperty({ name: '--canvas', syntax: '<color>', inherits: false, initialValue: 'oklch(0% 0 0deg)' });
25
+ // @property --canvas {
26
+ // syntax: "<color>";
27
+ // initial-value: oklch(100% 0 0deg);
28
+ // inherits: false;
29
+ // }
30
+ // @media (prefers-color-scheme: dark) {
31
+ // @property --canvas {
32
+ // syntax: "<color>";
33
+ // initial-value: oklch(0% 0 0deg);
34
+ // inherits: false;
35
+ // }
36
+ // }
37
+ // @property --canvastext {
38
+ // syntax: "<color>";
39
+ // initial-value: oklch(0% 0 0deg);
40
+ // inherits: false;
41
+ // }
42
+ // @media (prefers-color-scheme: dark) {
43
+ // @property --canvastext {
44
+ // syntax: "<color>";
45
+ // initial-value: oklch(100% 0 0deg);
46
+ // inherits: false;
47
+ // }
48
+ // }
49
+ // if (CSS.supports('interpolate-size', 'allow-keywords')) {
50
+ // self.CSS.registerProperty({ name: '--background', syntax: '<color>', inherits: false, initialValue: 'oklch(100% 0 0deg)' })
51
+ if (null !== newValue)
52
+ self.CSS.registerProperty({ name: '--background', syntax: '<color>', inherits: false, initialValue: 'oklch(0% 0 0deg)' });
53
+ }
54
+ }
@@ -24,7 +24,7 @@ export class ImageView extends HTMLElement {
24
24
  //this.setAttribute('class', `ph ph-${newValue}`)
25
25
  let image = this.querySelector(':scope>:not([slot])');
26
26
  if (newValue) {
27
- const el = image ?? $(`<i style="line-height: 1.05"></i>`, '>1');
27
+ const el = image ?? $(`<i style="line-height: 1"></i>`, '>1');
28
28
  el.setAttribute('class', `ph ph-${newValue}`);
29
29
  image ??= this.appendChild(el);
30
30
  // image ??= this.appendChild($(`<i style="line-height: 1.05"></i>`, '>1'))
@@ -3,6 +3,7 @@ export * from './body-view';
3
3
  export * from './bordered-button';
4
4
  export * from './bordered-prominent-button';
5
5
  export * from './borderless-button';
6
+ export * from './color-scheme';
6
7
  export * from './confirmation-dialog';
7
8
  export * from './content-unavailable-view';
8
9
  export * from './date-picker';
@@ -14,6 +15,7 @@ export * from './glass-prominent-button';
14
15
  export * from './h-stack';
15
16
  export * from './image-view';
16
17
  export * from './label-view';
18
+ export * from './labeled-content';
17
19
  export * from './list-view';
18
20
  export * from './menu-view';
19
21
  export * from './navigation-large-title';
@@ -3,6 +3,7 @@ export * from './body-view';
3
3
  export * from './bordered-button';
4
4
  export * from './bordered-prominent-button';
5
5
  export * from './borderless-button';
6
+ export * from './color-scheme';
6
7
  export * from './confirmation-dialog';
7
8
  export * from './content-unavailable-view';
8
9
  export * from './date-picker';
@@ -14,6 +15,7 @@ export * from './glass-prominent-button';
14
15
  export * from './h-stack';
15
16
  export * from './image-view';
16
17
  export * from './label-view';
18
+ export * from './labeled-content';
17
19
  export * from './list-view';
18
20
  export * from './menu-view';
19
21
  export * from './navigation-large-title';
@@ -0,0 +1,9 @@
1
+ export declare class LabeledContent extends HTMLElement {
2
+ #private;
3
+ static get observedAttributes(): string[];
4
+ static get template(): DocumentFragment;
5
+ constructor();
6
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
7
+ disconnectedCallback(): void;
8
+ connectedCallback(): void;
9
+ }
@@ -0,0 +1,115 @@
1
+ import { $, devFlags, renderLabel } from '../internal/utils';
2
+ export class LabeledContent extends HTMLElement {
3
+ static get observedAttributes() {
4
+ return ['value', 'label', 'header', 'footer', 'labeled-content-style', 'format'];
5
+ }
6
+ static #template;
7
+ static get template() {
8
+ return (this.#template ??= $(String.raw `
9
+ <div part="root labeled-content-container">
10
+ <slot name="header"></slot>
11
+ <div part="root labeled-content-stack">
12
+ <div part="root labeled-content-label-stack">
13
+ <slot name="label"></slot>
14
+ </div>
15
+ <div part="root labeled-content-value-stack">
16
+ <slot></slot>
17
+ </div>
18
+ </div>
19
+ <slot name="footer"></slot>
20
+ </div>
21
+ `));
22
+ }
23
+ #shadowRoot;
24
+ constructor() {
25
+ super();
26
+ this.#shadowRoot = this.attachShadow({ mode: 'closed' });
27
+ this.#shadowRoot.appendChild(document.importNode(this.constructor.template, true));
28
+ }
29
+ attributeChangedCallback(name, oldValue, newValue) {
30
+ if (devFlags.debug)
31
+ console.debug(`${LabeledContent.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
32
+ if (oldValue === newValue)
33
+ return;
34
+ switch (name) {
35
+ case 'header':
36
+ let header = this.querySelector(':scope>[slot=header]');
37
+ if (newValue) {
38
+ const el = header ?? $(`<header slot="header"></header>`, '>1');
39
+ renderLabel(el, ':scope>label-view', `<label-view font="callout"><span></span></label-view>`, newValue);
40
+ header ??= this.appendChild(el);
41
+ }
42
+ else
43
+ header?.remove();
44
+ break;
45
+ case 'footer':
46
+ let footer = this.querySelector(':scope>[slot=footer]');
47
+ if (newValue) {
48
+ const el = footer ?? $(`<footer slot="footer"></footer>`, '>1');
49
+ renderLabel(el, ':scope>label-view', `<label-view font="callout"><span></span></label-view>`, newValue);
50
+ footer ??= this.appendChild(el);
51
+ }
52
+ else
53
+ footer?.remove();
54
+ break;
55
+ case 'value':
56
+ renderLabel(this, ':scope>label-view:not([slot])', `<label-view><span></span></label-view>`, this.#fmt(newValue, this.getAttribute('format')));
57
+ break;
58
+ case 'format':
59
+ renderLabel(this, ':scope>label-view:not([slot])', `<label-view><span></span></label-view>`, this.#fmt(this.getAttribute('value'), newValue));
60
+ break;
61
+ case 'label':
62
+ renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label"><span></span></label-view>`, newValue);
63
+ break;
64
+ }
65
+ }
66
+ disconnectedCallback() {
67
+ if (devFlags.debug)
68
+ console.debug(`${LabeledContent.name} ⚡️ disconnect`);
69
+ }
70
+ connectedCallback() {
71
+ if (devFlags.debug)
72
+ console.debug(`${LabeledContent.name} ⚡️ connect`);
73
+ }
74
+ // <type>:<locale>;<option>=<value>;<option>=<value>
75
+ #fmt = (value, format) => {
76
+ const parseFormat = (format) => {
77
+ if (!format)
78
+ return {
79
+ type: 'text',
80
+ locale: navigator.language,
81
+ options: {},
82
+ };
83
+ const [type, locale = navigator.language, opts = {}] = format.split(':', 3), loc = new Intl.Locale(locale), options = Object.fromEntries(new URLSearchParams(opts));
84
+ return {
85
+ type,
86
+ locale: loc.toString(),
87
+ options,
88
+ };
89
+ };
90
+ const { type, locale, options } = parseFormat(format);
91
+ switch (type) {
92
+ case 'currency':
93
+ return new Intl.NumberFormat(locale, { style: 'currency', ...options }).format(Number(value)); // currency:en-US;currency=USD "$1,234.50"
94
+ case 'percent':
95
+ return new Intl.NumberFormat(locale, { style: 'percent' }).format(Number(value)); // percent:en-US "42%"
96
+ case 'unit':
97
+ return new Intl.NumberFormat(locale, { style: 'unit', ...options }).format(Number(value)); // percent:en-US:unit=kilometer-per-hour "50 km/h"
98
+ case 'number':
99
+ return new Intl.NumberFormat(locale, options).format(Number(value)); // number:en-US:notation=compact "1.2M"
100
+ case 'date':
101
+ return new Intl.DateTimeFormat(locale, options).format(new Date(value ?? '2000')); // { dateStyle: 'full', timeStyle: 'short' } "Saturday, July 4, 2026 at 2:30 PM"
102
+ case 'relative-time': {
103
+ const [number = 0, unit = 'day'] = value?.split('~') ?? [];
104
+ return new Intl.RelativeTimeFormat(locale, options).format(Number(number), unit); // -1~day { numeric: 'auto' } "yesterday"
105
+ }
106
+ case 'list':
107
+ return new Intl.ListFormat(locale, options).format(value?.split('~') ?? []); // '' { style: 'long', type: 'conjunction' } "Foo, Bar, and Baz"
108
+ case 'region':
109
+ return value ? new Intl.DisplayNames(locale, { type: 'region' }).of(value) : ''; // "Greece"
110
+ // "Foo, Bar, and Baz"
111
+ default:
112
+ return value;
113
+ }
114
+ };
115
+ }
@@ -9,6 +9,8 @@ export type PickerStyle = (typeof pickerStyles)[number];
9
9
  *
10
10
  * @attr {DictEntry[]} dictionary - Renders all options using this array
11
11
  *
12
+ * @attr {vertical|horizontal|auto} label-value-placement
13
+ *
12
14
  * @slot list
13
15
  */
14
16
  export declare class PickerView extends FormAssociatedBase {
@@ -67,6 +67,10 @@ const extractTagFromOption = (node) => {
67
67
  if (devFlags.debug)
68
68
  console.debug(`PickerView: extractCurrentValueFromOption`);
69
69
  return node instanceof HTMLOptionElement ? (node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim()) || '' : (node.label ?? node.value ?? '');
70
+ }, extractIconFromOption = (node) => {
71
+ if (devFlags.debug)
72
+ console.debug(`PickerView: extractIconFromOption`);
73
+ return node instanceof HTMLOptionElement ? node.getAttribute('data-system-image') : (node.systemImage ?? null);
70
74
  }, extractLabelFromGroup = (node) => {
71
75
  if (devFlags.debug)
72
76
  console.debug(`PickerView: extractLabelFromGroup`);
@@ -107,6 +111,8 @@ const collectLeafValues = (node) => (node.children.length ? node.children.flatMa
107
111
  *
108
112
  * @attr {DictEntry[]} dictionary - Renders all options using this array
109
113
  *
114
+ * @attr {vertical|horizontal|auto} label-value-placement
115
+ *
110
116
  * @slot list
111
117
  */
112
118
  export class PickerView extends FormAssociatedBase {
@@ -345,10 +351,10 @@ export class PickerView extends FormAssociatedBase {
345
351
  // current value label only
346
352
  const currentValueLabel = this.querySelector(':scope>label-view:not([slot])') ?? this.appendChild($(`<label-view></label-view>`, '>1'));
347
353
  // reset state
348
- if (currentValueLabel) {
349
- renderLabelIcon(currentValueLabel, 'dots-three'); // overwritten
350
- renderLabelTitle(currentValueLabel, this.#currentValueLabel); // overwritten
351
- }
354
+ // if (currentValueLabel) {
355
+ // renderLabelIcon(currentValueLabel, 'dots-three') // overwritten
356
+ // renderLabelTitle(currentValueLabel, this.#currentValueLabel) // overwritten
357
+ // }
352
358
  // clear all siblings
353
359
  for (const el of this.querySelectorAll(':scope>:not([slot])'))
354
360
  if (currentValueLabel !== el)
@@ -384,10 +390,10 @@ export class PickerView extends FormAssociatedBase {
384
390
  if (menu !== el)
385
391
  el.remove();
386
392
  const currentValueLabel = menu.querySelector(':scope>label-view[slot=label]') ?? menu.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
387
- if (currentValueLabel) {
388
- renderLabelIcon(currentValueLabel, 'dots-three'); // overwritten
389
- renderLabelTitle(currentValueLabel, this.#currentValueLabel); // overwritten
390
- }
393
+ // if (currentValueLabel) {
394
+ // renderLabelIcon(currentValueLabel, 'dots-three') // overwritten
395
+ // renderLabelTitle(currentValueLabel, this.#currentValueLabel) // overwritten
396
+ // }
391
397
  if (this.hasAttribute('help'))
392
398
  currentValueLabel?.setAttribute('help', this.getAttribute('help') ?? '');
393
399
  _a.#reflectButtons(input.source, menu);
@@ -403,12 +409,14 @@ export class PickerView extends FormAssociatedBase {
403
409
  for (const el of this.querySelectorAll(':scope>:not([slot])'))
404
410
  if (inlineList !== el)
405
411
  el.remove();
412
+ // add label as a plain element
406
413
  const value = this.getAttribute(this.constructor.ATTR.LABEL);
407
414
  if (value) {
408
- const label = $(`<label-view><span></span></label-view>`, '>1');
409
- if (label)
410
- renderLabelTitle(label, value); //label.setAttribute('title', value)
411
- section.insertAdjacentElement('beforeend', label);
415
+ const hStack = $(`<h-stack distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view><label-view><span></span></label-view></h-stack>`, '>1');
416
+ if (hStack)
417
+ renderLabel(hStack, ':scope>label-view:nth-child(2)', `<label-view><span></span></label-view>`, value);
418
+ // if (label) renderLabelTitle(label, value) //label.setAttribute('title', value)
419
+ section.insertAdjacentElement('beforeend', hStack);
412
420
  }
413
421
  if ('dictionary' === input.mode)
414
422
  _a.#reflectButtons(input.source.filter((el) => 0 === el.children.length), section);
@@ -457,7 +465,7 @@ export class PickerView extends FormAssociatedBase {
457
465
  #lastIndexedRoot = [];
458
466
  get #currentValueLabel() {
459
467
  const cvl = this.#lastRenderedLabelMap[this.#selection];
460
- return (this.getAttribute('current-value-label') ?? '').replaceAll('{{selection}}', this.#selection).replaceAll('{{currentValueLabel}}', this.#selection) || cvl || this.#selection;
468
+ return (this.getAttribute('current-value-label') ?? '').replaceAll('{{selection}}', this.#selection).replaceAll('{{currentValueLabel}}', this.#selection) || cvl;
461
469
  }
462
470
  get #internals() {
463
471
  return getInternals(this);
@@ -761,8 +769,9 @@ export class PickerView extends FormAssociatedBase {
761
769
  // chevron = hStack?.querySelector<HTMLElement>(':scope>label-view')
762
770
  btn.setAttribute('value', extractTagFromOption(node));
763
771
  // if (selection !== btn.getAttribute('value')) chevron?.style.setProperty('visibility', 'hidden')
764
- const label = $(`<label-view><span></span></label-view>`, '>1');
772
+ const label = $(`<label-view></label-view>`, '>1');
765
773
  renderLabelTitle(label, extractCurrentValueFromOption(node)); // label.querySelector('span')!.textContent = extractCurrentValueFromOption(node) //label.setAttribute('title', extractCurrentValueFromOption(node))
774
+ renderLabelIcon(label, extractIconFromOption(node));
766
775
  hStack?.appendChild(label);
767
776
  return btn;
768
777
  }
@@ -770,7 +779,8 @@ export class PickerView extends FormAssociatedBase {
770
779
  if (devFlags.debug)
771
780
  console.debug(`${_a.name} #wrapOptgroupTag`);
772
781
  const labelT = `<label-view><span></span></label-view>`, summaryT = `<summary><h-stack distribution="leading" template="auto spacer"><label-view data-role="check" style="visibility: hidden"><image-view slot="icon" system-name="check"></image-view></label-view>${labelT}</h-stack></summary>`;
773
- const group = $(`<details is="disclosure-group">${summaryT}</details>`, '>1'), hStack = group.querySelector(':scope>summary>h-stack'); // ?? group.appendChild($(summaryT, '>1'))
782
+ const group = $(`<details is="disclosure-group" disclosure-style="marker-trailing">${summaryT}</details>`, '>1'), // NOTE: already applied, here it covers spawned sheets
783
+ hStack = group.querySelector(':scope>summary>h-stack'); // ?? group.appendChild($(summaryT, '>1'))
774
784
  // summaryLabel = summary.querySelector(':scope>label-view') ?? summary.appendChild($(labelT, '>1'))
775
785
  // if (node.hasAttribute('label')) summaryLabel.setAttribute('title', node.getAttribute('label') ?? '')
776
786
  // if (node.hasAttribute('data-system-image')) summaryLabel.setAttribute('system-image', node.getAttribute('data-system-image') ?? '')
@@ -839,99 +849,82 @@ export class PickerView extends FormAssociatedBase {
839
849
  #reflectLabel(value) {
840
850
  if (devFlags.debug)
841
851
  console.debug(`${_a.name} #reflectLabel`);
842
- renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label" foreground="secondary"><span></span></label-view>`, value);
852
+ renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label"><span></span></label-view>`, value);
843
853
  this.#renderSlotted([]);
844
854
  }
845
855
  #reflectSelectionOnButtons() {
846
856
  if (devFlags.debug)
847
857
  console.debug(`${_a.name} #reflectSelectionOnButtons`);
848
- const groupMap = indexGroups(this.#lastIndexedRoot);
849
- const groupContainsSelection = (source) => source instanceof Element
850
- ? [...source.querySelectorAll('option')].some((opt) => extractTagFromOption(opt) === this.#selection)
851
- : collectLeafValues(source).includes(this.#selection);
852
- const syncButtons = (root) => {
853
- // 1. plain value buttons — unchanged
854
- for (const el of root.querySelectorAll('button[value]:not([slot])'))
855
- el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
856
- // 2. details/optgroups unchanged
857
- for (const details of root.querySelectorAll('details[is="disclosure-group"]')) {
858
- const hasSelectedDescendant = [...details.querySelectorAll('button[value]')].some((btn) => btn.getAttribute('value') === this.#selection);
859
- details.querySelector(':scope>summary label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden');
860
- }
861
- // 3. nav-link buttons resolved by groupId, same map used for resync
862
- for (const btn of root.querySelectorAll('button[navigation-link]:not([value])')) {
863
- const source = btn.dataset.groupId ? groupMap.get(btn.dataset.groupId) : undefined;
864
- const hasSelectedDescendant = source ? groupContainsSelection(source) : false;
865
- btn.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden');
866
- }
867
- };
868
- syncButtons(this);
869
- if (this.#spawn)
870
- syncButtons(this.#spawn);
858
+ self.requestAnimationFrame(() => {
859
+ this.ariaCurrent = this.#selection;
860
+ const groupMap = indexGroups(this.#lastIndexedRoot);
861
+ const groupContainsSelection = (source) => source instanceof Element
862
+ ? [...source.querySelectorAll('option')].some((opt) => extractTagFromOption(opt) === this.#selection)
863
+ : collectLeafValues(source).includes(this.#selection);
864
+ const syncButtons = (root) => {
865
+ // 1. plain value buttons unchanged
866
+ for (const el of root.querySelectorAll('button[value]:not([slot])'))
867
+ // $.prop('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden', el.querySelector<HTMLElement>('label-view[data-role="check"]'))
868
+ el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
869
+ // 2. details/optgroups unchanged
870
+ for (const details of root.querySelectorAll('details[is="disclosure-group"]')) {
871
+ const hasSelectedDescendant = [...details.querySelectorAll('button[value]')].some((btn) => btn.getAttribute('value') === this.#selection);
872
+ details.querySelector(':scope>summary label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden');
873
+ }
874
+ // 3. nav-link buttons resolved by groupId, same map used for resync
875
+ for (const btn of root.querySelectorAll('button[navigation-link]:not([value])')) {
876
+ const source = btn.dataset.groupId ? groupMap.get(btn.dataset.groupId) : undefined;
877
+ const hasSelectedDescendant = source ? groupContainsSelection(source) : false;
878
+ btn.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden');
879
+ }
880
+ };
881
+ syncButtons(this);
882
+ if (this.#spawn)
883
+ syncButtons(this.#spawn);
884
+ });
871
885
  }
872
- // #reflectSelectionOnButtons() {
873
- // if (devFlags.debug) console.debug(`${PickerView.name} #reflectSelectionOnButtons`)
874
- // const syncButtons = (root: Element | HTMLElement) => {
875
- // // 1. Sync plain value buttons (existing behavior)
876
- // for (const el of root.querySelectorAll<HTMLButtonElement>('button[value]:not([slot])'))
877
- // el.querySelector<HTMLElement>('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden')
878
- // // 2. Sync `details` (optgroups) — open/mark if any descendant option matches selection
879
- // for (const details of root.querySelectorAll<HTMLElement>('details[is="disclosure-group"]')) {
880
- // // ORRR details.matches(':has(> button[value]:not([slot]) label-view[data-role="check"][style*=visible])'))
881
- // const hasSelectedDescendant = [...details.querySelectorAll<HTMLButtonElement>('button[value]')].some((btn) => btn.getAttribute('value') === this.#selection)
882
- // // Show/hide the check on the summary's label-view
883
- // details.querySelector<HTMLElement>(':scope>summary label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden')
884
- // }
885
- // // 3. Sync nav-link buttons (those without `value` that spawn sub-pages)
886
- // // These wrap <datalist> children — mark them if any descendant value matches
887
- // const navLinkBtns = root.querySelectorAll<HTMLButtonElement>('button[navigation-link]:not([value])')
888
- // navLinkBtns.forEach((btn, i) => {
889
- // const hasSelectedDescendant = this.#lastRenderedGroupMap[i]?.includes(this.#selection) ?? false
890
- // btn.querySelector<HTMLElement>('label-view[data-role="check"]')?.style.setProperty('visibility', hasSelectedDescendant ? 'visible' : 'hidden')
891
- // })
892
- // }
893
- // // Run on the host element (inline/menu styles)
894
- // syncButtons(this)
895
- // // Also sync the spawn if open (sheet/navigation-link styles)
896
- // if (this.#spawn) syncButtons(this.#spawn)
897
- // // // walk all rendered buttons (inline has buttons in list, menu has buttons in menu-view)
898
- // // for (const el of this.querySelectorAll<HTMLButtonElement>('button[value]:not([slot])'))
899
- // // el.querySelector<HTMLElement>('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden')
900
- // // // also sync the spawn (sheet/navigation) if open
901
- // // if (this.#spawn)
902
- // // for (const el of this.#spawn.querySelectorAll<HTMLButtonElement>('list-view button[value]:not([slot])'))
903
- // // el.querySelector<HTMLElement>('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden')
904
- // }
905
886
  /**
906
887
  * Overwrite cvlabel with the label prop of the current(find[value === #selection]) option/dictentry
907
888
  */
908
889
  #reflectSelectionOnCurrentValueLabel() {
909
890
  if (devFlags.debug)
910
891
  console.debug(`${_a.name} #reflectSelectionOnCurrentValueLabel`);
911
- switch (this.pickerStyle) {
912
- case 'sheet':
913
- case 'navigation-link': {
914
- const currentValueLabel = this.querySelector(':scope>label-view:not([slot])');
915
- if (!currentValueLabel)
892
+ self.requestAnimationFrame(() => {
893
+ switch (this.pickerStyle) {
894
+ case 'sheet':
895
+ case 'navigation-link': {
896
+ const currentValueLabel = this.querySelector(':scope>label-view:not([slot])');
897
+ if (!currentValueLabel)
898
+ break;
899
+ const cvl = this.#currentValueLabel;
900
+ if (!cvl)
901
+ currentValueLabel.setAttribute('foreground', 'secondary');
902
+ else
903
+ currentValueLabel.removeAttribute('foreground');
904
+ renderLabelTitle(currentValueLabel, cvl || this.getAttribute('placeholder') || this.#selection);
905
+ renderLabelIcon(currentValueLabel, 'dots-three');
916
906
  break;
917
- renderLabelTitle(currentValueLabel, this.#currentValueLabel);
918
- renderLabelIcon(currentValueLabel, 'dots-three');
919
- break;
920
- }
921
- case 'menu': {
922
- const currentValueLabel = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]');
923
- if (!currentValueLabel)
907
+ }
908
+ case 'menu': {
909
+ const currentValueLabel = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]');
910
+ if (!currentValueLabel)
911
+ break;
912
+ const cvl = this.#currentValueLabel;
913
+ if (!cvl)
914
+ currentValueLabel.setAttribute('foreground', 'secondary');
915
+ else
916
+ currentValueLabel.removeAttribute('foreground');
917
+ renderLabelTitle(currentValueLabel, cvl || this.getAttribute('placeholder') || this.#selection);
918
+ renderLabelIcon(currentValueLabel, 'dots-three');
924
919
  break;
925
- renderLabelTitle(currentValueLabel, this.#currentValueLabel);
926
- renderLabelIcon(currentValueLabel, 'dots-three');
927
- break;
928
- }
929
- case 'inline':
930
- default: {
931
- //
932
- break;
920
+ }
921
+ case 'inline':
922
+ default: {
923
+ //
924
+ break;
925
+ }
933
926
  }
934
- }
927
+ });
935
928
  }
936
929
  #reflectTriggerHelp() {
937
930
  if (devFlags.debug)