@swiftwc/ui 0.0.0-dev.47 → 0.0.0-dev.49

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 (34) hide show
  1. package/generated/components/content-unavailable-view.d.ts +2 -2
  2. package/generated/components/content-unavailable-view.js +3 -3
  3. package/generated/components/date-picker.js +2 -9
  4. package/generated/components/image-view.d.ts +9 -0
  5. package/generated/components/image-view.js +46 -0
  6. package/generated/components/index.d.ts +1 -0
  7. package/generated/components/index.js +1 -0
  8. package/generated/components/label-view.js +6 -16
  9. package/generated/components/menu-view.js +2 -8
  10. package/generated/components/navigation-title.js +4 -12
  11. package/generated/components/picker-view.d.ts +2 -0
  12. package/generated/components/picker-view.js +58 -22
  13. package/generated/components/progress-view.js +2 -8
  14. package/generated/components/scroll-view.js +4 -12
  15. package/generated/components/section-view.js +7 -5
  16. package/generated/components/text-field.js +2 -9
  17. package/generated/css/index.css +76 -21
  18. package/generated/internal/utils/dev-flags.js +1 -1
  19. package/generated/internal/utils/index.d.ts +3 -0
  20. package/generated/internal/utils/index.js +3 -0
  21. package/generated/internal/utils/render-label-icon.d.ts +2 -0
  22. package/generated/internal/utils/render-label-icon.js +19 -0
  23. package/generated/internal/utils/render-label-title.d.ts +2 -0
  24. package/generated/internal/utils/render-label-title.js +16 -0
  25. package/generated/internal/utils/render-label.d.ts +1 -0
  26. package/generated/internal/utils/render-label.js +19 -0
  27. package/package.json +1 -1
  28. package/scss/components/_image-view.scss +9 -0
  29. package/scss/components/_index.scss +1 -0
  30. package/scss/components/_label-view.scss +8 -8
  31. package/scss/components/_tool-bar.scss +2 -2
  32. package/scss/placeholders/_lists.scss +1 -1
  33. package/scss/utils/_index.scss +29 -10
  34. package/web-components.html-data/en.json +8 -1
@@ -5,9 +5,9 @@
5
5
  *
6
6
  * @example <content-unavailable-view search="foo"></content-unavailable-view>
7
7
  *
8
- * @example <content-unavailable-view padding><label-view title="No Mail"><i class="ph ph-tray" slot="image" foreground="secondary"></i></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
8
+ * @example <content-unavailable-view padding><label-view title="No Mail"><i class="ph ph-tray" slot="icon" foreground="secondary"></i></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
9
9
  *
10
- * @example <content-unavailable-view padding><label-view title="No Mail"><svg slot="image" foreground="secondary" ...>...</svg></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
10
+ * @example <content-unavailable-view padding><label-view title="No Mail"><svg slot="icon" foreground="secondary" ...>...</svg></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
11
11
  *
12
12
  * @slot description
13
13
  * @slot actions
@@ -8,9 +8,9 @@ import { $, devFlags, onoff } from '../internal/utils';
8
8
  *
9
9
  * @example <content-unavailable-view search="foo"></content-unavailable-view>
10
10
  *
11
- * @example <content-unavailable-view padding><label-view title="No Mail"><i class="ph ph-tray" slot="image" foreground="secondary"></i></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
11
+ * @example <content-unavailable-view padding><label-view title="No Mail"><i class="ph ph-tray" slot="icon" foreground="secondary"></i></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
12
12
  *
13
- * @example <content-unavailable-view padding><label-view title="No Mail"><svg slot="image" foreground="secondary" ...>...</svg></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
13
+ * @example <content-unavailable-view padding><label-view title="No Mail"><svg slot="icon" foreground="secondary" ...>...</svg></label-view><label-view title="New mails you receive will appear here." foreground="secondary" slot="description"></label-view><button is="borderless-button" type="button" tabindex="0" slot="actions"><label-view title="Switch Account"></label-view></button></content-unavailable-view>
14
14
  *
15
15
  * @slot description
16
16
  * @slot actions
@@ -69,7 +69,7 @@ export class ContentUnavailableView extends HTMLElement {
69
69
  console.debug(`${ContentUnavailableView.name} ⚡️ connect`);
70
70
  }
71
71
  #renderSearch = (search) => {
72
- const titleLabel = this.querySelector(':scope>:not([slot])') ?? this.appendChild($(`<label-view><i class="ph ph-magnifying-glass" slot="image" foreground="secondary"></i></label-view>`, '>1')), descText = this.querySelector(':scope>[slot=description]') ?? this.appendChild($(`<label-view foreground="secondary" slot="description"></label-view>`, '>1'));
72
+ const titleLabel = this.querySelector(':scope>:not([slot])') ?? this.appendChild($(`<label-view><i class="ph ph-magnifying-glass" slot="icon" foreground="secondary"></i></label-view>`, '>1')), descText = this.querySelector(':scope>[slot=description]') ?? this.appendChild($(`<label-view foreground="secondary" slot="description"></label-view>`, '>1'));
73
73
  if (search) {
74
74
  const interpolate = (text, vars) => text.replaceAll(/\{(\w+)\}/g, (_, key) => vars[key] ?? '');
75
75
  titleLabel.setAttribute('title', interpolate(I18n.t('SearchUnavailableContent').Label, { search }));
@@ -3,7 +3,7 @@ import { I18n } from '../i18n';
3
3
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
4
4
  import { FormAssociatedBase, getInternals } from '../internal/class/form-associated-base';
5
5
  import { MutationObserverSet } from '../internal/class/mutation-observer-set';
6
- import { $, clamp, devFlags, kebabCase, onoff, set } from '../internal/utils';
6
+ import { $, clamp, devFlags, kebabCase, onoff, renderLabel, set } from '../internal/utils';
7
7
  const datePickerStyles = ['graphical', 'field', 'automatic'];
8
8
  export class DatePicker extends FormAssociatedBase {
9
9
  static get observedAttributes() {
@@ -89,14 +89,7 @@ export class DatePicker extends FormAssociatedBase {
89
89
  input.setAttribute('placeholder', map[input.name] ?? '');
90
90
  break;
91
91
  case 'label':
92
- let label = this.querySelector(':scope>label-view[slot=label]');
93
- if (newValue) {
94
- label ??= this.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
95
- label.setAttribute('foreground', 'secondary');
96
- label.setAttribute('title', newValue);
97
- }
98
- else
99
- label?.remove();
92
+ renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label" foreground="secondary"><span></span></label-view>`, newValue);
100
93
  // this.#sendValueToForm()
101
94
  break;
102
95
  case 'disabled':
@@ -0,0 +1,9 @@
1
+ export declare class ImageView 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,46 @@
1
+ import { $, devFlags } from '../internal/utils';
2
+ export class ImageView extends HTMLElement {
3
+ static get observedAttributes() {
4
+ return ['system-name'];
5
+ }
6
+ static #template;
7
+ static get template() {
8
+ return (this.#template ??= $(String.raw `<slot></slot>`));
9
+ }
10
+ #shadowRoot;
11
+ constructor() {
12
+ super();
13
+ this.#shadowRoot = this.attachShadow({ mode: 'closed' });
14
+ this.#shadowRoot.appendChild(document.importNode(this.constructor.template, true));
15
+ }
16
+ attributeChangedCallback(name, oldValue, newValue) {
17
+ if (devFlags.debug)
18
+ console.debug(`${ImageView.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
19
+ switch (name) {
20
+ case 'system-name':
21
+ if (oldValue === newValue)
22
+ break;
23
+ // this.innerHTML = `<i style="line-height: 1.05" class="ph ph-${newValue}"></i>`
24
+ //this.setAttribute('class', `ph ph-${newValue}`)
25
+ let image = this.querySelector(':scope>:not([slot])');
26
+ if (newValue) {
27
+ const el = image ?? $(`<i style="line-height: 1.05"></i>`, '>1');
28
+ el.setAttribute('class', `ph ph-${newValue}`);
29
+ image ??= this.appendChild(el);
30
+ // image ??= this.appendChild($(`<i style="line-height: 1.05"></i>`, '>1'))
31
+ // image.setAttribute('class', `ph ph-${newValue}`)
32
+ }
33
+ else
34
+ image?.remove();
35
+ break;
36
+ }
37
+ }
38
+ disconnectedCallback() {
39
+ if (devFlags.debug)
40
+ console.debug(`${ImageView.name} ⚡️ disconnect`);
41
+ }
42
+ connectedCallback() {
43
+ if (devFlags.debug)
44
+ console.debug(`${ImageView.name} ⚡️ connect`);
45
+ }
46
+ }
@@ -11,6 +11,7 @@ export * from './fine-tooltip';
11
11
  export * from './form-view';
12
12
  export * from './glass-button';
13
13
  export * from './glass-prominent-button';
14
+ export * from './image-view';
14
15
  export * from './label-view';
15
16
  export * from './list-view';
16
17
  export * from './menu-view';
@@ -11,6 +11,7 @@ export * from './fine-tooltip';
11
11
  export * from './form-view';
12
12
  export * from './glass-button';
13
13
  export * from './glass-prominent-button';
14
+ export * from './image-view';
14
15
  export * from './label-view';
15
16
  export * from './list-view';
16
17
  export * from './menu-view';
@@ -1,4 +1,4 @@
1
- import { $, devFlags } from '../internal/utils';
1
+ import { $, devFlags, renderLabelIcon, renderLabelTitle } from '../internal/utils';
2
2
  export class LabelView extends HTMLElement {
3
3
  static get observedAttributes() {
4
4
  return ['system-image', 'title', 'line-limit', 'truncation-mode'];
@@ -6,7 +6,7 @@ export class LabelView extends HTMLElement {
6
6
  static #template;
7
7
  static get template() {
8
8
  return (this.#template ??= $(String.raw `
9
- <slot name="image"></slot>
9
+ <slot name="icon"></slot>
10
10
  <slot></slot>
11
11
  `
12
12
  // String.raw`
@@ -27,24 +27,14 @@ export class LabelView extends HTMLElement {
27
27
  attributeChangedCallback(name, oldValue, newValue) {
28
28
  if (devFlags.debug)
29
29
  console.debug(`${LabelView.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
30
+ if (oldValue === newValue)
31
+ return;
30
32
  switch (name) {
31
33
  case 'system-image':
32
- let image = this.querySelector(':scope>[slot=image]');
33
- if (newValue) {
34
- image ??= this.appendChild($(`<i slot="image" style="line-height: 1.05"></i>`, '>1'));
35
- image.setAttribute('class', `ph ph-${newValue}`);
36
- }
37
- else
38
- image?.remove();
34
+ renderLabelIcon(this, newValue);
39
35
  break;
40
36
  case 'title':
41
- let title = this.querySelector(':scope>:not([slot])');
42
- if (newValue) {
43
- title ??= this.appendChild($(`<span></span>`, '>1'));
44
- title.textContent = newValue;
45
- }
46
- else
47
- title?.remove();
37
+ renderLabelTitle(this, newValue);
48
38
  break;
49
39
  }
50
40
  }
@@ -1,6 +1,6 @@
1
1
  var _a;
2
2
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
3
- import { $, devFlags, onoff, touchGlass } from '../internal/utils';
3
+ import { $, devFlags, onoff, renderLabel, touchGlass } from '../internal/utils';
4
4
  /**
5
5
  * @summary A control for presenting a menu of actions.
6
6
  */
@@ -58,13 +58,7 @@ export class MenuView extends HTMLElement {
58
58
  }
59
59
  break;
60
60
  case 'label':
61
- let label = this.querySelector(':scope>[slot=label]');
62
- if (newValue) {
63
- label ??= this.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
64
- label.setAttribute('title', newValue);
65
- }
66
- else
67
- label?.remove();
61
+ renderLabel(this, ':scope>[slot=label]', `<label-view slot="label"><span></span></label-view>`, newValue);
68
62
  break;
69
63
  }
70
64
  }
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  var NavigationTitle_1;
8
8
  import { microtaskOnConnected } from '../internal/decorators';
9
- import { $, devFlags } from '../internal/utils';
9
+ import { $, devFlags, renderLabel } from '../internal/utils';
10
10
  let NavigationTitle = NavigationTitle_1 = class NavigationTitle extends HTMLElement {
11
11
  static get observedAttributes() {
12
12
  return ['value', 'subtitle'];
@@ -28,18 +28,10 @@ let NavigationTitle = NavigationTitle_1 = class NavigationTitle extends HTMLElem
28
28
  console.debug(`${NavigationTitle_1.name} ⚡️ connect`);
29
29
  }
30
30
  #render = (title, subtitle) => {
31
- const titleTemplate = `<label-view line-limit="1" truncation-mode="tail" font="headline"></label-view>`, subtitleTemplate = `<label-view line-limit="1" truncation-mode="tail" foreground="secondary" font="callout"></label-view>`, vStactTemplate = `<v-stack spacing="0" alignment="fill">${titleTemplate}${subtitleTemplate}</v-stack>`;
31
+ const titleTemplate = `<label-view line-limit="1" truncation-mode="tail" font="headline"><span></span></label-view>`, subtitleTemplate = `<label-view line-limit="1" truncation-mode="tail" foreground="secondary" font="callout"><span></span></label-view>`, vStactTemplate = `<v-stack spacing="0" alignment="fill">${titleTemplate}${subtitleTemplate}</v-stack>`;
32
32
  const el = this.querySelector(':scope>:not([slot])') ?? this.appendChild($(`<navigation-large-title>${vStactTemplate}</navigation-large-title>`, '>1')), vStack = el.querySelector(':scope>v-stack') ?? el.appendChild($(vStactTemplate, '>1'));
33
- const titleLabel = vStack.querySelector(':scope>label-view:nth-child(1)') ?? vStack.appendChild($(titleTemplate, '>1'));
34
- if (title)
35
- titleLabel.setAttribute('title', title);
36
- else
37
- titleLabel?.removeAttribute('title');
38
- const subtitleLabel = vStack.querySelector(':scope>label-view:nth-child(2)') ?? vStack.appendChild($(subtitleTemplate, '>1'));
39
- if (subtitle)
40
- subtitleLabel.setAttribute('title', subtitle);
41
- else
42
- subtitleLabel?.removeAttribute('title');
33
+ renderLabel(vStack, ':scope>label-view:nth-child(1)', titleTemplate, title);
34
+ renderLabel(vStack, ':scope>label-view:nth-child(2)', subtitleTemplate, subtitle);
43
35
  };
44
36
  };
45
37
  NavigationTitle = NavigationTitle_1 = __decorate([
@@ -12,6 +12,8 @@ export declare class PickerView extends FormAssociatedBase {
12
12
  CURRENT_VALUE_LABEL: string;
13
13
  };
14
14
  static get observedAttributes(): string[];
15
+ get selection(): string;
16
+ set selection(v: string);
15
17
  get template(): DocumentFragment;
16
18
  constructor();
17
19
  attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
@@ -5,9 +5,11 @@ 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 { $, devFlags, kebabCase, onoff } from '../internal/utils';
8
+ import { $, devFlags, kebabCase, onoff, renderLabel, renderLabelIcon, renderLabelTitle } from '../internal/utils';
9
9
  const pickerStyles = ['menu', 'inline', 'navigation-link', 'sheet', 'automatic'];
10
10
  const update = (path, node, overwrite = true) => {
11
+ if (devFlags.debug)
12
+ console.debug(`PickerView: update`);
11
13
  if (!(path instanceof NavigationPath))
12
14
  throw new Error('invalid view');
13
15
  const { component, page } = path;
@@ -19,6 +21,8 @@ const update = (path, node, overwrite = true) => {
19
21
  page.insertAdjacentElement(position, node);
20
22
  };
21
23
  const reflectSpawnedElement = (current, source) => {
24
+ if (devFlags.debug)
25
+ console.debug(`PickerView: reflectSpawnedElement`);
22
26
  const { page: oldSv, toolBarConfig: oldToolbar } = new NavigationPath(current).hydrate(), { page: newSv, toolBarConfig: newToolbar } = new NavigationPath(source).hydrate();
23
27
  const oldList = oldSv?.querySelector('list-view'), newList = newSv?.querySelector('list-view'), oldBackBtn = oldToolbar?.at(0)?.querySelector('button'), newBackBtn = newToolbar?.at(0)?.querySelector('button');
24
28
  // pre replace list (always exists)
@@ -56,9 +60,13 @@ const reflectSpawnedElement = (current, source) => {
56
60
  oldSearch?.remove();
57
61
  };
58
62
  const extractTagFromOption = (node) => {
63
+ if (devFlags.debug)
64
+ console.debug(`PickerView: extractTagFromOption`);
59
65
  return ((node.getAttribute('value') ?? node.textContent?.trim()) || node.getAttribute('label')) ?? '';
60
66
  };
61
67
  const extractCurrentValueFromOption = (node) => {
68
+ if (devFlags.debug)
69
+ console.debug(`PickerView: extractCurrentValueFromOption`);
62
70
  return (node.getAttribute('label') ?? node.getAttribute('value') ?? node.textContent?.trim()) || '';
63
71
  };
64
72
  export class PickerView extends FormAssociatedBase {
@@ -78,6 +86,8 @@ export class PickerView extends FormAssociatedBase {
78
86
  static #templates = new Map();
79
87
  #spawn;
80
88
  #spawnPage = (tag, searchable = false, title, node) => {
89
+ if (devFlags.debug)
90
+ console.debug(`${_a.name} #spawnPage`);
81
91
  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');
82
92
  if (title)
83
93
  sv?.setAttribute('navigation-inline-title', title);
@@ -181,8 +191,10 @@ export class PickerView extends FormAssociatedBase {
181
191
  // current value label only
182
192
  const currentValueLabel = this.querySelector(':scope>label-view:not([slot])') ?? this.appendChild($(`<label-view></label-view>`, '>1'));
183
193
  // reset state
184
- currentValueLabel.setAttribute('system-image', 'dots-three'); // overwritten
185
- currentValueLabel.setAttribute('title', this.#currentValueLabel); // overwritten
194
+ if (currentValueLabel) {
195
+ renderLabelIcon(currentValueLabel, 'dots-three'); // overwritten
196
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel); // overwritten
197
+ }
186
198
  // clear all siblings
187
199
  for (const el of this.querySelectorAll(':scope>:not([slot])'))
188
200
  if (currentValueLabel !== el)
@@ -217,8 +229,10 @@ export class PickerView extends FormAssociatedBase {
217
229
  if (menu !== el)
218
230
  el.remove();
219
231
  const currentValueLabel = menu.querySelector(':scope>label-view[slot=label]') ?? menu.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
220
- currentValueLabel.setAttribute('system-image', 'dots-three'); // overwritten
221
- currentValueLabel.setAttribute('title', this.#currentValueLabel); // overwritten
232
+ if (currentValueLabel) {
233
+ renderLabelIcon(currentValueLabel, 'dots-three'); // overwritten
234
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel); // overwritten
235
+ }
222
236
  _a.#reflectButtons([...(this.#slots?.get('list')?.assignedElements() ?? [])], menu);
223
237
  break;
224
238
  }
@@ -257,6 +271,17 @@ export class PickerView extends FormAssociatedBase {
257
271
  #observers = new MutationObserverSet(this.#renderSlotted);
258
272
  #customValidity = '';
259
273
  #selection = '';
274
+ get selection() {
275
+ return this.#selection;
276
+ }
277
+ set selection(v) {
278
+ if (Object.is(this.#selection, v))
279
+ return;
280
+ this.#selection = v;
281
+ this.#reflectSelectionOnButtons();
282
+ this.#reflectCurrentValueLabel();
283
+ this.#sendValueToForm();
284
+ }
260
285
  get #internals() {
261
286
  return getInternals(this);
262
287
  }
@@ -530,16 +555,20 @@ export class PickerView extends FormAssociatedBase {
530
555
  this.#renderSlotted([]);
531
556
  };
532
557
  static #wrapOptionTag(node) {
533
- const btn = $(`<button type="button" tabindex="0"><h-stack distribution="leading" template="auto spacer"><label-view system-image="check"></label-view></h-stack></button>`, '>1'), hStack = btn.querySelector(':scope>h-stack');
558
+ if (devFlags.debug)
559
+ console.debug(`${_a.name} #wrapOptionTag`);
560
+ const btn = $(`<button type="button" tabindex="0"><h-stack distribution="leading" template="auto spacer"><label-view data-role="check"><image-view slot="icon" system-name="check"></image-view></label-view></h-stack></button>`, '>1'), hStack = btn.querySelector(':scope>h-stack');
534
561
  // chevron = hStack?.querySelector<HTMLElement>(':scope>label-view')
535
562
  btn.setAttribute('value', extractTagFromOption(node));
536
563
  // if (selection !== btn.getAttribute('value')) chevron?.style.setProperty('visibility', 'hidden')
537
- const label = $(`<label-view></label-view>`, '>1');
538
- label.setAttribute('title', extractCurrentValueFromOption(node));
564
+ const label = $(`<label-view><span></span></label-view>`, '>1');
565
+ label.querySelector('span').textContent = extractCurrentValueFromOption(node); //label.setAttribute('title', extractCurrentValueFromOption(node))
539
566
  hStack?.appendChild(label);
540
567
  return btn;
541
568
  }
542
569
  static #wrapOptgroupTag(node) {
570
+ if (devFlags.debug)
571
+ console.debug(`${_a.name} #wrapOptgroupTag`);
543
572
  const labelT = `<label-view></label-view>`, summaryT = `<summary>${labelT}</summary>`;
544
573
  const group = $(`<details is="disclosure-group">${summaryT}</details>`, '>1'), summary = group.querySelector(':scope>summary') ?? group.appendChild($(summaryT, '>1')), summaryLabel = summary.querySelector(':scope>label-view') ?? summary.appendChild($(labelT, '>1'));
545
574
  if (node.hasAttribute('label'))
@@ -549,6 +578,8 @@ export class PickerView extends FormAssociatedBase {
549
578
  return group;
550
579
  }
551
580
  static #reflectButtons(nodes, container) {
581
+ if (devFlags.debug)
582
+ console.debug(`${_a.name} #reflectButtons`);
552
583
  for (const node of nodes)
553
584
  switch (node.tagName) {
554
585
  case 'DATALIST': {
@@ -575,6 +606,8 @@ export class PickerView extends FormAssociatedBase {
575
606
  }
576
607
  }
577
608
  #reflectPlaceholder(value) {
609
+ if (devFlags.debug)
610
+ console.debug(`#reflectPlaceholder`);
578
611
  const input = this.#shadowRoot.querySelector('input');
579
612
  if (input) {
580
613
  if (value)
@@ -584,24 +617,21 @@ export class PickerView extends FormAssociatedBase {
584
617
  }
585
618
  }
586
619
  #reflectLabel(value) {
587
- let label = this.querySelector(':scope>label-view[slot=label]');
588
- if (value) {
589
- label ??= this.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
590
- label.setAttribute('foreground', 'secondary');
591
- label.setAttribute('title', value);
592
- }
593
- else
594
- label?.remove();
620
+ if (devFlags.debug)
621
+ console.debug(`${_a.name} #reflectLabel`);
622
+ renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label" foreground="secondary"><span></span></label-view>`, value);
595
623
  this.#renderSlotted([]);
596
624
  }
597
625
  #reflectSelectionOnButtons() {
626
+ if (devFlags.debug)
627
+ console.debug(`${_a.name} #reflectSelectionOnButtons`);
598
628
  // walk all rendered buttons (inline has buttons in list, menu has buttons in menu-view)
599
629
  for (const el of this.querySelectorAll('button[value]:not([slot])'))
600
- el.querySelector('label-view[system-image="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
630
+ el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
601
631
  // also sync the spawn (sheet/navigation) if open
602
632
  if (this.#spawn)
603
633
  for (const el of this.#spawn.querySelectorAll('list-view button[value]:not([slot])'))
604
- el.querySelector('label-view[system-image="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
634
+ el.querySelector('label-view[data-role="check"]')?.style.setProperty('visibility', el.getAttribute('value') === this.#selection ? 'visible' : 'hidden');
605
635
  }
606
636
  get #currentTag() {
607
637
  return this.#slots
@@ -615,18 +645,24 @@ export class PickerView extends FormAssociatedBase {
615
645
  return (this.getAttribute('current-value-label') ?? '').replaceAll('{{selection}}', this.#selection).replaceAll('{{currentValueLabel}}', this.#selection) || cvl || this.#selection;
616
646
  }
617
647
  #reflectCurrentValueLabel() {
648
+ if (devFlags.debug)
649
+ console.debug(`${_a.name} #reflectCurrentValueLabel`);
618
650
  switch (this.pickerStyle) {
619
651
  case 'sheet':
620
652
  case 'navigation-link': {
621
653
  const currentValueLabel = this.querySelector(':scope>label-view:not([slot])');
622
- currentValueLabel?.setAttribute('system-image', 'dots-three');
623
- currentValueLabel?.setAttribute('title', this.#currentValueLabel);
654
+ if (!currentValueLabel)
655
+ break;
656
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel);
657
+ renderLabelIcon(currentValueLabel, 'dots-three');
624
658
  break;
625
659
  }
626
660
  case 'menu': {
627
661
  const currentValueLabel = this.querySelector(':scope>menu-view:not([slot])>label-view[slot=label]');
628
- currentValueLabel?.setAttribute('system-image', 'dots-three');
629
- currentValueLabel?.setAttribute('title', this.#currentValueLabel);
662
+ if (!currentValueLabel)
663
+ break;
664
+ renderLabelTitle(currentValueLabel, this.#currentValueLabel);
665
+ renderLabelIcon(currentValueLabel, 'dots-three');
630
666
  break;
631
667
  }
632
668
  case 'inline':
@@ -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 { $, devFlags } from '../internal/utils';
4
+ import { $, devFlags, renderLabel } from '../internal/utils';
5
5
  import { Snapshot } from '../snapshot';
6
6
  const progressViewStyles = ['circular', 'linear'];
7
7
  /**
@@ -68,13 +68,7 @@ export class ProgressView extends HTMLElement {
68
68
  break;
69
69
  }
70
70
  case 'current-value-label': {
71
- let label = this.querySelector(':scope>[slot=current-value]');
72
- if (newValue) {
73
- label ??= this.appendChild($(`<label-view slot="current-value" font="callout" foreground="secondary"></label-view>`, '>1'));
74
- label.setAttribute('title', newValue);
75
- }
76
- else
77
- label?.remove();
71
+ renderLabel(this, ':scope>[slot=current-value]', `<label-view slot="current-value" font="callout" foreground="secondary"><span></span></label-view>`, newValue);
78
72
  break;
79
73
  }
80
74
  }
@@ -2,7 +2,7 @@ var _a;
2
2
  import { lifecycleObserver } from '../buses';
3
3
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
4
4
  import { ResizeObserverSingleton } from '../internal/class/resize-observer-singleton';
5
- import { $, devFlags, frame, onoff, slowHideShow } from '../internal/utils';
5
+ import { $, devFlags, frame, onoff, renderLabel, slowHideShow } from '../internal/utils';
6
6
  const observers = new ResizeObserverSingleton();
7
7
  export class ScrollView extends HTMLElement {
8
8
  static get observedAttributes() {
@@ -186,18 +186,10 @@ export class ScrollView extends HTMLElement {
186
186
  });
187
187
  }
188
188
  #renderNavTitle = (title, subtitle) => {
189
- const titleTemplate = `<label-view line-limit="1" truncation-mode="tail" font="headline"></label-view>`, subtitleTemplate = `<label-view line-limit="1" truncation-mode="tail" foreground="secondary" font="callout"></label-view>`;
189
+ const titleTemplate = `<label-view line-limit="1" truncation-mode="tail" font="headline"><span></span></label-view>`, subtitleTemplate = `<label-view line-limit="1" truncation-mode="tail" foreground="secondary" font="callout"><span></span></label-view>`;
190
190
  const vStack = this.querySelector(':scope>[slot=top-bar-principal]') ?? this.appendChild($(`<v-stack spacing="0" alignment="fill" slot="top-bar-principal">${titleTemplate}${subtitleTemplate}</v-stack>`, '>1'));
191
- const titleLabel = vStack.querySelector(':scope>label-view:nth-child(1)') ?? vStack.appendChild($(titleTemplate, '>1'));
192
- if (title)
193
- titleLabel.setAttribute('title', title);
194
- else
195
- titleLabel?.removeAttribute('title');
196
- let subtitleLabel = vStack.querySelector(':scope>label-view:nth-child(2)') ?? vStack.appendChild($(subtitleTemplate, '>1'));
197
- if (subtitle)
198
- subtitleLabel.setAttribute('title', subtitle);
199
- else
200
- subtitleLabel?.removeAttribute('title');
191
+ renderLabel(vStack, ':scope>label-view:nth-child(1)', titleTemplate, title);
192
+ renderLabel(vStack, ':scope>label-view:nth-child(2)', subtitleTemplate, subtitle);
201
193
  };
202
194
  }
203
195
  _a = ScrollView;
@@ -1,4 +1,4 @@
1
- import { $, devFlags } from '../internal/utils';
1
+ import { $, devFlags, renderLabel } from '../internal/utils';
2
2
  export class SectionView extends HTMLElement {
3
3
  static get observedAttributes() {
4
4
  return ['header', 'footer'];
@@ -39,8 +39,9 @@ export class SectionView extends HTMLElement {
39
39
  case 'header':
40
40
  let header = this.querySelector(':scope>[slot=header]');
41
41
  if (newValue) {
42
- header ??= this.appendChild($(`<header slot="header"><label-view line-limit="1" truncation-mode="tail" font="callout"></label-view></header>`, '>1'));
43
- header.querySelector('label-view')?.setAttribute('title', newValue);
42
+ const el = header ?? $(`<header slot="header"></header>`, '>1');
43
+ renderLabel(el, ':scope>label-view', `<label-view line-limit="1" truncation-mode="tail" font="callout"><span></span></label-view>`, newValue);
44
+ header ??= this.appendChild(el);
44
45
  }
45
46
  else
46
47
  header?.remove();
@@ -48,8 +49,9 @@ export class SectionView extends HTMLElement {
48
49
  case 'footer':
49
50
  let footer = this.querySelector(':scope>[slot=footer]');
50
51
  if (newValue) {
51
- footer ??= this.appendChild($(`<footer slot="footer"><label-view line-limit="1" truncation-mode="tail" font="callout"></label-view></footer>`, '>1'));
52
- footer.querySelector('label-view')?.setAttribute('title', newValue);
52
+ const el = footer ?? $(`<footer slot="footer"></footer>`, '>1');
53
+ renderLabel(el, ':scope>label-view', `<label-view line-limit="1" truncation-mode="tail" font="callout"><span></span></label-view>`, newValue);
54
+ footer ??= this.appendChild(el);
53
55
  }
54
56
  else
55
57
  footer?.remove();
@@ -3,7 +3,7 @@ import { I18n } from '../i18n';
3
3
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
4
4
  import { FormAssociatedBase, getInternals } from '../internal/class/form-associated-base';
5
5
  import { MutationObserverSet } from '../internal/class/mutation-observer-set';
6
- import { $, compareBigDecimals, devFlags, kebabCase, onoff, set } from '../internal/utils';
6
+ import { $, compareBigDecimals, devFlags, kebabCase, onoff, renderLabel, set } from '../internal/utils';
7
7
  const keyboardTypes = ['decimal-pad', 'number-pad', 'default'];
8
8
  /**
9
9
  * @slot label
@@ -137,14 +137,7 @@ export class TextField extends FormAssociatedBase {
137
137
  this.#sendValueToForm();
138
138
  break;
139
139
  case 'label':
140
- let label = this.querySelector(':scope>label-view[slot=label]');
141
- if (newValue) {
142
- label ??= this.appendChild($(`<label-view slot="label"></label-view>`, '>1'));
143
- label.setAttribute('foreground', 'secondary');
144
- label.setAttribute('title', newValue);
145
- }
146
- else
147
- label?.remove();
140
+ renderLabel(this, ':scope>label-view[slot=label]', `<label-view slot="label" foreground="secondary"><span></span></label-view>`, newValue);
148
141
  // this.#sendValueToForm()
149
142
  break;
150
143
  case 'disabled':
@@ -792,13 +792,13 @@
792
792
  background-color: var(--quaternary);
793
793
  transform: translateY(-50%) translateX(120%);
794
794
  }
795
- :where(table-view:not(table-view table-view, list-view table-view, [is=form-view] table-view) > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image], [is=form-view]:not([is=form-view] [is=form-view], list-view [is=form-view], table-view [is=form-view], [is=tab-bar] > [is=form-view]) > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image], list-view:not(list-view list-view, [is=form-view] list-view, table-view list-view) > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image],
796
- table-view:not(table-view table-view, list-view table-view, [is=form-view] table-view) section-view > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image],
797
- [is=form-view]:not([is=form-view] [is=form-view], list-view [is=form-view], table-view [is=form-view], [is=tab-bar] > [is=form-view]) section-view > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image],
798
- list-view:not(list-view list-view, [is=form-view] list-view, table-view list-view) section-view > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image],
799
- table-view:not(table-view table-view, list-view table-view, [is=form-view] table-view) [is=disclosure-group] > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image],
800
- [is=form-view]:not([is=form-view] [is=form-view], list-view [is=form-view], table-view [is=form-view], [is=tab-bar] > [is=form-view]) [is=disclosure-group] > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image],
801
- list-view:not(list-view list-view, [is=form-view] list-view, table-view list-view) [is=disclosure-group] > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=image]) {
795
+ :where(table-view:not(table-view table-view, list-view table-view, [is=form-view] table-view) > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon], [is=form-view]:not([is=form-view] [is=form-view], list-view [is=form-view], table-view [is=form-view], [is=tab-bar] > [is=form-view]) > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon], list-view:not(list-view list-view, [is=form-view] list-view, table-view list-view) > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon],
796
+ table-view:not(table-view table-view, list-view table-view, [is=form-view] table-view) section-view > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon],
797
+ [is=form-view]:not([is=form-view] [is=form-view], list-view [is=form-view], table-view [is=form-view], [is=tab-bar] > [is=form-view]) section-view > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon],
798
+ list-view:not(list-view list-view, [is=form-view] list-view, table-view list-view) section-view > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon],
799
+ table-view:not(table-view table-view, list-view table-view, [is=form-view] table-view) [is=disclosure-group] > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon],
800
+ [is=form-view]:not([is=form-view] [is=form-view], list-view [is=form-view], table-view [is=form-view], [is=tab-bar] > [is=form-view]) [is=disclosure-group] > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon],
801
+ list-view:not(list-view list-view, [is=form-view] list-view, table-view list-view) [is=disclosure-group] > :not([slot], section-view, [is=disclosure-group], picker-view, menu-view, menu-view *, whatever-name):is(label-view[list-item-tint])[list-item-tint] > [slot=icon]) {
802
802
  color: var(--accentColor);
803
803
  }
804
804
  }
@@ -1062,6 +1062,7 @@
1062
1062
  scroll-view:not([hidden]),
1063
1063
  body-view:not([hidden]),
1064
1064
  tab-view:not([hidden]),
1065
+ image-view:not([hidden]),
1065
1066
  [is=search-view]:not([hidden]),
1066
1067
  [is=sidebar-view]:not([hidden]),
1067
1068
  [is=tab-bar]:not([hidden]),
@@ -2578,15 +2579,15 @@
2578
2579
  --safe-area-inset-bottom: 0px;
2579
2580
  }
2580
2581
  }
2581
- :where(tool-bar > [slot^=top-bar-] > button:has(> label-view > [slot=image]),
2582
- tool-bar > [slot=cancellation-action] > button:has(> label-view > [slot=image]),
2583
- tool-bar > [slot=primary-action] > button:has(> label-view > [slot=image]),
2584
- tool-bar > [slot=confirmation-action] > button:has(> label-view > [slot=image]),
2585
- tool-bar > [slot=destructive-action] > button:has(> label-view > [slot=image])) {
2582
+ :where(tool-bar > [slot^=top-bar-] > button:has(> label-view > [slot=icon]),
2583
+ tool-bar > [slot=cancellation-action] > button:has(> label-view > [slot=icon]),
2584
+ tool-bar > [slot=primary-action] > button:has(> label-view > [slot=icon]),
2585
+ tool-bar > [slot=confirmation-action] > button:has(> label-view > [slot=icon]),
2586
+ tool-bar > [slot=destructive-action] > button:has(> label-view > [slot=icon])) {
2586
2587
  --label-style: icon-only;
2587
2588
  }
2588
2589
  @media (pointer: coarse) {
2589
- :where(tool-bar > [slot^=bottom-bar-] > button:has(> label-view > [slot=image])) {
2590
+ :where(tool-bar > [slot^=bottom-bar-] > button:has(> label-view > [slot=icon])) {
2590
2591
  --label-style: icon-only;
2591
2592
  }
2592
2593
  }
@@ -2617,7 +2618,7 @@
2617
2618
  --label-imagestack-fontsize: calc(var(--label-image-size) * 1rem);
2618
2619
  white-space: var(--label--host-white-space, );
2619
2620
  }
2620
- :where(label-view:has(> [slot=image])) {
2621
+ :where(label-view:has(> [slot=icon])) {
2621
2622
  --label--has-icon: yes;
2622
2623
  }
2623
2624
  :where(label-view:has(> :not([slot]))) {
@@ -2636,7 +2637,7 @@
2636
2637
  font-weight: var(--label--titlestack-font-weight, );
2637
2638
  display: var(--label--titlestack-display, none);
2638
2639
  }
2639
- label-view > [slot=image] {
2640
+ label-view > [slot=icon] {
2640
2641
  aspect-ratio: 1/1;
2641
2642
  inline-size: var(--label-imagestack-fontsize);
2642
2643
  block-size: var(--label-imagestack-fontsize);
@@ -2724,7 +2725,7 @@
2724
2725
  --label--titlestack-display: grid;
2725
2726
  }
2726
2727
  @container not style(--label-style) {
2727
- :where(label-view:not([label-style]):has(> [slot=image]):has(> :not([slot]))) {
2728
+ :where(label-view:not([label-style]):has(> [slot=icon]):has(> :not([slot]))) {
2728
2729
  --label-style: title-and-icon;
2729
2730
  --label--host-grid-template-columns: auto minmax(0, 1fr);
2730
2731
  --label--host-grid-template-rows: minmax(0, 1fr);
@@ -2733,13 +2734,13 @@
2733
2734
  --label--iconstack-display: grid;
2734
2735
  --label--titlestack-display: grid;
2735
2736
  }
2736
- :where(label-view:not([label-style]):has(> [slot=image]):not(:has(> :not([slot])))) {
2737
+ :where(label-view:not([label-style]):has(> [slot=icon]):not(:has(> :not([slot])))) {
2737
2738
  --label-style: icon-only;
2738
2739
  --label--host-align-items: center;
2739
2740
  --label--host-justify-items: normal;
2740
2741
  --label--iconstack-display: grid;
2741
2742
  }
2742
- :where(label-view:not([label-style]):has(> :not([slot])):not(:has(> [slot=image]))) {
2743
+ :where(label-view:not([label-style]):has(> :not([slot])):not(:has(> [slot=icon]))) {
2743
2744
  --label-style: title-only;
2744
2745
  --label--host-align-items: center;
2745
2746
  --label--host-justify-items: normal;
@@ -2797,7 +2798,7 @@
2797
2798
  }
2798
2799
  }
2799
2800
  @container (not style(--label-style)) and style(--label-flow: vertical) {
2800
- :where(label-view:not([label-style]):has(> [slot=image]):has(> :not([slot]))) {
2801
+ :where(label-view:not([label-style]):has(> [slot=icon]):has(> :not([slot]))) {
2801
2802
  --label-style: title-and-icon;
2802
2803
  --label--host-grid-template-columns: minmax(0, 1fr);
2803
2804
  --label--host-grid-template-rows: auto minmax(0, 1fr);
@@ -2806,13 +2807,13 @@
2806
2807
  --label--iconstack-display: grid;
2807
2808
  --label--titlestack-display: grid;
2808
2809
  }
2809
- :where(label-view:not([label-style]):has(> [slot=image]):not(:has(> :not([slot])))) {
2810
+ :where(label-view:not([label-style]):has(> [slot=icon]):not(:has(> :not([slot])))) {
2810
2811
  --label-style: icon-only;
2811
2812
  --label--host-align-items: normal;
2812
2813
  --label--host-justify-items: center;
2813
2814
  --label--iconstack-display: grid;
2814
2815
  }
2815
- :where(label-view:not([label-style]):has(> :not([slot])):not(:has(> [slot=image]))) {
2816
+ :where(label-view:not([label-style]):has(> :not([slot])):not(:has(> [slot=icon]))) {
2816
2817
  --label-style: title-only;
2817
2818
  --label--host-align-items: normal;
2818
2819
  --label--host-justify-items: center;
@@ -5253,9 +5254,63 @@
5253
5254
  :where([foreground=blue]) {
5254
5255
  color: var(--blue);
5255
5256
  }
5257
+ :where([foreground=red]) {
5258
+ color: var(--red);
5259
+ }
5260
+ :where([foreground=green]) {
5261
+ color: var(--green);
5262
+ }
5263
+ :where([foreground=orange]) {
5264
+ color: var(--orange);
5265
+ }
5256
5266
  :where([foreground="blue.secondary"]) {
5257
5267
  color: var(--blue2);
5258
5268
  }
5269
+ :where([foreground="red.secondary"]) {
5270
+ color: var(--red2);
5271
+ }
5272
+ :where([foreground="green.secondary"]) {
5273
+ color: var(--green2);
5274
+ }
5275
+ :where([foreground="orange.secondary"]) {
5276
+ color: var(--orange2);
5277
+ }
5278
+ :where([foreground="blue.tertiary"]) {
5279
+ color: var(--blue3);
5280
+ }
5281
+ :where([foreground="red.tertiary"]) {
5282
+ color: var(--red3);
5283
+ }
5284
+ :where([foreground="green.tertiary"]) {
5285
+ color: var(--green3);
5286
+ }
5287
+ :where([foreground="orange.tertiary"]) {
5288
+ color: var(--orange3);
5289
+ }
5290
+ :where([foreground="blue.quaternary"]) {
5291
+ color: var(--blue4);
5292
+ }
5293
+ :where([foreground="red.quaternary"]) {
5294
+ color: var(--red4);
5295
+ }
5296
+ :where([foreground="green.quaternary"]) {
5297
+ color: var(--green4);
5298
+ }
5299
+ :where([foreground="orange.quaternary"]) {
5300
+ color: var(--orange4);
5301
+ }
5302
+ :where([foreground="blue.quinary"]) {
5303
+ color: var(--blue5);
5304
+ }
5305
+ :where([foreground="red.quinary"]) {
5306
+ color: var(--red5);
5307
+ }
5308
+ :where([foreground="green.quinary"]) {
5309
+ color: var(--green5);
5310
+ }
5311
+ :where([foreground="orange.quinary"]) {
5312
+ color: var(--orange5);
5313
+ }
5259
5314
  :where([tint=gray],
5260
5315
  [list-item-tint=gray]) {
5261
5316
  --accentColorEffective: var(--gray);
@@ -1,5 +1,5 @@
1
1
  export default class {
2
2
  static get debug() {
3
- return false;
3
+ return true;
4
4
  }
5
5
  }
@@ -12,6 +12,9 @@ export { default as kebabCase } from './kebab-case';
12
12
  export { default as listActive } from './list-active';
13
13
  export { default as microtask } from './microtask';
14
14
  export { default as onoff } from './onoff';
15
+ export { default as renderLabel } from './render-label';
16
+ export { default as renderLabelIcon } from './render-label-icon';
17
+ export { default as renderLabelTitle } from './render-label-title';
15
18
  export { default as set } from './set';
16
19
  export { default as sleep } from './sleep';
17
20
  export { default as slowHideShow } from './slow-hide-show';
@@ -14,6 +14,9 @@ export { default as kebabCase } from './kebab-case';
14
14
  export { default as listActive } from './list-active';
15
15
  export { default as microtask } from './microtask';
16
16
  export { default as onoff } from './onoff';
17
+ export { default as renderLabel } from './render-label';
18
+ export { default as renderLabelIcon } from './render-label-icon';
19
+ export { default as renderLabelTitle } from './render-label-title';
17
20
  export { default as set } from './set';
18
21
  export { default as sleep } from './sleep';
19
22
  export { default as slowHideShow } from './slow-hide-show';
@@ -0,0 +1,2 @@
1
+ import type { LabelView } from '../../components';
2
+ export default function (label: LabelView, systemImage: string | null): void;
@@ -0,0 +1,19 @@
1
+ import $ from './cash';
2
+ export default function (label, systemImage) {
3
+ const img = label.querySelector(':scope>[slot=icon]');
4
+ // image ??= this.appendChild($(`<i slot="icon" style="line-height: 1.05"></i>`, '>1'))
5
+ // image.setAttribute('class', `ph ph-${newValue}`)
6
+ if (systemImage) {
7
+ if (!img) {
8
+ const newImg = $(`<i slot="icon" style="line-height: 1.05" class="ph ph-${systemImage}"></i>`, '>1');
9
+ // const newImg = $(`<image-view slot="icon"></image-view>`, '>1')
10
+ // newImg.setAttribute('system-name', systemImage)
11
+ // DOM manipulation last
12
+ label.appendChild(newImg);
13
+ }
14
+ else if (systemImage !== img.getAttribute('system-name'))
15
+ img.setAttribute('system-name', systemImage);
16
+ }
17
+ else
18
+ img?.remove();
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { LabelView } from '../../components';
2
+ export default function (label: LabelView, title: string | null): void;
@@ -0,0 +1,16 @@
1
+ import $ from './cash';
2
+ export default function (label, title) {
3
+ const span = label.querySelector(':scope>:not([slot])');
4
+ if (title) {
5
+ if (!span) {
6
+ const newSpan = $(`<span></span>`, '>1');
7
+ newSpan.textContent = title;
8
+ // DOM manipulation last
9
+ label.appendChild(newSpan);
10
+ }
11
+ else if (title !== span.textContent)
12
+ span.textContent = title;
13
+ }
14
+ else
15
+ span?.remove();
16
+ }
@@ -0,0 +1 @@
1
+ export default function (container: Element, selector: string, template: string, title?: string | null, systemImage?: string | null): void;
@@ -0,0 +1,19 @@
1
+ import $ from './cash';
2
+ import renderLabelIcon from './render-label-icon';
3
+ import renderLabelTitle from './render-label-title';
4
+ export default function (container, selector, template, title, systemImage) {
5
+ const existing = container.querySelector(selector);
6
+ if (existing) {
7
+ for (const { name, value } of $(template, '>1').attributes)
8
+ if (!existing.hasAttribute(name))
9
+ existing.setAttribute(name, value);
10
+ renderLabelIcon(existing, systemImage ?? null);
11
+ renderLabelTitle(existing, title ?? null);
12
+ }
13
+ else {
14
+ const newLabel = $(template, '>1');
15
+ renderLabelIcon(newLabel, systemImage ?? null);
16
+ renderLabelTitle(newLabel, title ?? null);
17
+ container.appendChild(newLabel);
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftwc/ui",
3
- "version": "0.0.0-dev.47",
3
+ "version": "0.0.0-dev.49",
4
4
  "description": "Elegant SwiftUI-inspired web components for standalone web apps and web extensions.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,9 @@
1
+ @use '../vars';
2
+ @use '../mixins';
3
+
4
+ @layer #{vars.$components-layer} {
5
+ // body-view {
6
+ // :where(&) {
7
+ // }
8
+ // }
9
+ }
@@ -10,6 +10,7 @@
10
10
  scroll-view,
11
11
  body-view,
12
12
  tab-view,
13
+ image-view,
13
14
  [is='search-view'],
14
15
  [is='sidebar-view'],
15
16
  [is='tab-bar'],
@@ -174,7 +174,7 @@ $label-line-ifs: (
174
174
  white-space: var(--label--host-white-space,);
175
175
  }
176
176
 
177
- &:has(> [slot='image']) {
177
+ &:has(> [slot='icon']) {
178
178
  :where(&) {
179
179
  --label--has-icon: yes;
180
180
  // background-color: green;
@@ -213,7 +213,7 @@ $label-line-ifs: (
213
213
  }
214
214
  }
215
215
 
216
- > [slot='image'] {
216
+ > [slot='icon'] {
217
217
  aspect-ratio: 1 / 1;
218
218
 
219
219
  inline-size: var(--label-imagestack-fontsize);
@@ -286,13 +286,13 @@ $label-line-ifs: (
286
286
  }
287
287
  @container not style(--label-style) {
288
288
  &:not([label-style]) {
289
- :where(&:has(> [slot='image']):has(> :not([slot]))) {
289
+ :where(&:has(> [slot='icon']):has(> :not([slot]))) {
290
290
  @include set-label-style(h-title-and-icon);
291
291
  }
292
- :where(&:has(> [slot='image']):not(:has(> :not([slot])))) {
292
+ :where(&:has(> [slot='icon']):not(:has(> :not([slot])))) {
293
293
  @include set-label-style(h-icon-only);
294
294
  }
295
- :where(&:has(> :not([slot])):not(:has(> [slot='image']))) {
295
+ :where(&:has(> :not([slot])):not(:has(> [slot='icon']))) {
296
296
  @include set-label-style(h-title-only);
297
297
  }
298
298
  }
@@ -314,13 +314,13 @@ $label-line-ifs: (
314
314
  }
315
315
  @container (not style(--label-style)) and style(--label-flow: vertical) {
316
316
  &:not([label-style]) {
317
- :where(&:has(> [slot='image']):has(> :not([slot]))) {
317
+ :where(&:has(> [slot='icon']):has(> :not([slot]))) {
318
318
  @include set-label-style(v-title-and-icon);
319
319
  }
320
- :where(&:has(> [slot='image']):not(:has(> :not([slot])))) {
320
+ :where(&:has(> [slot='icon']):not(:has(> :not([slot])))) {
321
321
  @include set-label-style(v-icon-only);
322
322
  }
323
- :where(&:has(> :not([slot])):not(:has(> [slot='image']))) {
323
+ :where(&:has(> :not([slot])):not(:has(> [slot='icon']))) {
324
324
  @include set-label-style(v-title-only);
325
325
  }
326
326
  }
@@ -117,7 +117,7 @@
117
117
  > [slot='confirmation-action'],
118
118
  > [slot='destructive-action'] {
119
119
  // > label-view,
120
- > button:has(> label-view > [slot='image']) {
120
+ > button:has(> label-view > [slot='icon']) {
121
121
  :where(&) {
122
122
  --label-style: icon-only;
123
123
  }
@@ -126,7 +126,7 @@
126
126
  // NOTE: By default bottom bar items with icons on iphone, prefer only icon to be shown, (except if they define/force w/ labelStyle)
127
127
  @media (pointer: coarse) {
128
128
  > [slot^='bottom-bar-'] {
129
- > button:has(> label-view > [slot='image']) {
129
+ > button:has(> label-view > [slot='icon']) {
130
130
  :where(&) {
131
131
  --label-style: icon-only;
132
132
  }
@@ -505,7 +505,7 @@ $list-item-selector: ':not([slot], section-view, [is="disclosure-group"], picker
505
505
  // }
506
506
  // }
507
507
  &:is(label-view[list-item-tint]) {
508
- :where(&[list-item-tint] > [slot='image']) {
508
+ :where(&[list-item-tint] > [slot='icon']) {
509
509
  color: var(--accentColor);
510
510
  }
511
511
  }
@@ -25,16 +25,35 @@
25
25
  }
26
26
  }
27
27
 
28
- :where([foreground='secondary']) {
29
- color: var(--secondary);
30
- }
31
-
32
- :where([foreground='blue']) {
33
- color: var(--blue);
34
- }
35
-
36
- :where([foreground='blue.secondary']) {
37
- color: var(--blue2);
28
+ @each $k,
29
+ $v
30
+ in (
31
+ secondary: --secondary,
32
+ blue: --blue,
33
+ red: --red,
34
+ green: --green,
35
+ orange: --orange,
36
+ 'blue.secondary': --blue2,
37
+ 'red.secondary': --red2,
38
+ 'green.secondary': --green2,
39
+ 'orange.secondary': --orange2,
40
+ 'blue.tertiary': --blue3,
41
+ 'red.tertiary': --red3,
42
+ 'green.tertiary': --green3,
43
+ 'orange.tertiary': --orange3,
44
+ 'blue.quaternary': --blue4,
45
+ 'red.quaternary': --red4,
46
+ 'green.quaternary': --green4,
47
+ 'orange.quaternary': --orange4,
48
+ 'blue.quinary': --blue5,
49
+ 'red.quinary': --red5,
50
+ 'green.quinary': --green5,
51
+ 'orange.quinary': --orange5
52
+ )
53
+ {
54
+ :where([foreground='#{"" + $k}']) {
55
+ color: var(#{$v});
56
+ }
38
57
  }
39
58
 
40
59
  @each $tint in gray, blue, red, green, orange {
@@ -166,6 +166,14 @@
166
166
  }
167
167
  ]
168
168
  },
169
+ {
170
+ "name": "image-view",
171
+ "attributes": [
172
+ {
173
+ "name": "system-name"
174
+ }
175
+ ]
176
+ },
169
177
  {
170
178
  "name": "label-view",
171
179
  "attributes": [
@@ -230,7 +238,6 @@
230
238
  },
231
239
  {
232
240
  "name": "navigation-title",
233
- "description": "// const _sync = Symbol('sync')",
234
241
  "attributes": [
235
242
  {
236
243
  "name": "value"