@swiftwc/ui 0.0.0-dev.17 → 0.0.0-dev.19

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 (46) hide show
  1. package/generated/client/index.js +3 -11
  2. package/generated/components/content-unavailable-view.d.ts +24 -0
  3. package/generated/components/content-unavailable-view.js +78 -0
  4. package/generated/components/date-picker.js +2 -4
  5. package/generated/components/index.d.ts +2 -3
  6. package/generated/components/index.js +2 -3
  7. package/generated/components/navigation-split-view.d.ts +3 -0
  8. package/generated/components/navigation-split-view.js +3 -0
  9. package/generated/components/navigation-stack.d.ts +3 -0
  10. package/generated/components/navigation-stack.js +3 -0
  11. package/generated/components/scroll-view-proxy.d.ts +8 -0
  12. package/generated/components/scroll-view-proxy.js +14 -0
  13. package/generated/components/sidebar-toggle.js +33 -60
  14. package/generated/components/tab-bar.d.ts +3 -0
  15. package/generated/components/tab-bar.js +3 -0
  16. package/generated/components/text-field.js +1 -1
  17. package/generated/css/index.css +186 -127
  18. package/generated/i18n/index.d.ts +20 -10
  19. package/generated/i18n/index.js +82 -54
  20. package/generated/i18n/locales/el.d.ts +8 -0
  21. package/generated/i18n/locales/el.js +8 -0
  22. package/generated/i18n/locales/en.d.ts +8 -0
  23. package/generated/i18n/locales/en.js +8 -0
  24. package/generated/i18n/locales/index.d.ts +4 -0
  25. package/generated/i18n/locales/index.js +4 -0
  26. package/generated/internal/utils/list-active.js +11 -11
  27. package/package.json +8 -4
  28. package/scss/_components.scss +2 -0
  29. package/scss/components/_bordered-button.scss +8 -0
  30. package/scss/components/_bordered-prominent-button.scss +8 -0
  31. package/scss/components/_borderless-button.scss +8 -0
  32. package/scss/components/_components.scss +76 -0
  33. package/scss/components/_content-unavailable-view.scss +45 -0
  34. package/scss/components/_glass-button.scss +8 -0
  35. package/scss/components/_glass-prominent-button.scss +8 -0
  36. package/scss/components/_h-flex-stack.scss +23 -0
  37. package/scss/components/_h-stack.scss +2 -2
  38. package/scss/components/_index.scss +4 -0
  39. package/scss/components/_label-view.scss +29 -22
  40. package/scss/components/_plain-button.scss +7 -0
  41. package/scss/components/_scroll-view-proxy.scss +18 -18
  42. package/scss/components/_v-stack.scss +19 -2
  43. package/scss/final/_touch-glass.scss +17 -0
  44. package/scss/placeholders/_lists.scss +10 -38
  45. package/scss/utils/_stacks.scss +72 -67
  46. package/web-components.html-data/en.json +19 -3
@@ -167,9 +167,7 @@ export const startViewTransition = async (target, type = 'forwards', updateCallb
167
167
  // prepare new
168
168
  const toolbarExclusion = 0 < modalViews.length
169
169
  ? (value, index, array) => value.parentElement?.matches('tool-bar:not(dialog tool-bar,body-view ~ tool-bar)')
170
- : (value, index, array) => value.parentElement?.matches('tool-bar:not(body-view ~ tool-bar)'), bodyExclusion = 0 < modalViews.length
171
- ? (item) => item.body?.matches('scroll-view:not(dialog scroll-view)')
172
- : (value, index, array) => value;
170
+ : (value, index, array) => value.parentElement?.matches('tool-bar:not(body-view ~ tool-bar)'), bodyExclusion = 0 < modalViews.length ? (item) => item.body?.matches('scroll-view:not(dialog scroll-view)') : (value, index, array) => value;
173
171
  for (const bti of newToolbars?.filter?.(toolbarExclusion) ?? [])
174
172
  bti.classList.add('fwnn'); // for (const ti of newToolbars ?? []) ti.classList.add('fwnn') //
175
173
  for (const bt of tos?.filter?.(bodyExclusion) ?? []) {
@@ -185,10 +183,7 @@ export const startViewTransition = async (target, type = 'forwards', updateCallb
185
183
  }
186
184
  else {
187
185
  console.debug(`⚡️ view-transition-start (${type})`);
188
- await Promise.allSettled([
189
- ...(from.body?.getAnimations().map(({ finished }) => finished) ?? []),
190
- ...(to?.body?.getAnimations().map(({ finished }) => finished) ?? []),
191
- ]);
186
+ await Promise.allSettled([...(from.body?.getAnimations().map(({ finished }) => finished) ?? []), ...(to?.body?.getAnimations().map(({ finished }) => finished) ?? [])]);
192
187
  console.debug(`⚡️ view-transition-end (${type})`);
193
188
  }
194
189
  if (0 < (to?.component?.querySelectorAll(`.${Snapshot.config?.['vt-fwd-class-name']},.bwd`) ?? []).length)
@@ -249,10 +244,7 @@ export const startViewTransition = async (target, type = 'forwards', updateCallb
249
244
  el.close(); // close old inbetween modals
250
245
  // capture trans
251
246
  console.debug(`⚡️ view-transition-start (${type})`);
252
- await Promise.allSettled([
253
- ...(from.body?.getAnimations().map(({ finished }) => finished) ?? []),
254
- ...(to.body?.getAnimations().map(({ finished }) => finished) ?? []),
255
- ]);
247
+ await Promise.allSettled([...(from.body?.getAnimations().map(({ finished }) => finished) ?? []), ...(to.body?.getAnimations().map(({ finished }) => finished) ?? [])]);
256
248
  console.debug(`⚡️ view-transition-end (${type})`);
257
249
  if (to.body?.closest(`.bwd,.${Snapshot.config?.['vt-fwd-class-name']}`))
258
250
  return;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @summary A message with a title and extra information that you show when part of your app can’t be used.
3
+ *
4
+ * @example <content-unavailable-view search></content-unavailable-view>
5
+ *
6
+ * @example <content-unavailable-view search="foo"></content-unavailable-view>
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>
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>
11
+ *
12
+ * @slot description
13
+ * @slot actions
14
+ *
15
+ */
16
+ export declare class ContentUnavailableView extends HTMLElement {
17
+ #private;
18
+ static get observedAttributes(): string[];
19
+ static get template(): DocumentFragment;
20
+ constructor();
21
+ disconnectedCallback(): void;
22
+ connectedCallback(): void;
23
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
24
+ }
@@ -0,0 +1,78 @@
1
+ import { CleanupRegistry } from '#internal/class/cleanup-registry.js';
2
+ import { $, onoff } from '#internal/utils/index.js';
3
+ import { I18n } from '../i18n';
4
+ /**
5
+ * @summary A message with a title and extra information that you show when part of your app can’t be used.
6
+ *
7
+ * @example <content-unavailable-view search></content-unavailable-view>
8
+ *
9
+ * @example <content-unavailable-view search="foo"></content-unavailable-view>
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>
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>
14
+ *
15
+ * @slot description
16
+ * @slot actions
17
+ *
18
+ */
19
+ export class ContentUnavailableView extends HTMLElement {
20
+ static get observedAttributes() {
21
+ return ['search'];
22
+ }
23
+ static #template;
24
+ static get template() {
25
+ return (this.#template ??= $(String.raw `
26
+ <div part="root content-unavailable-title-stack">
27
+ <slot></slot>
28
+ </div>
29
+ <div part="root content-unavailable-description-stack">
30
+ <slot name="description"></slot>
31
+ </div>
32
+ <div part="root content-unavailable-actions-stack">
33
+ <slot name="actions"></slot>
34
+ </div>`, ''));
35
+ }
36
+ #shadowRoot;
37
+ #slots;
38
+ constructor() {
39
+ super();
40
+ this.#shadowRoot = this.attachShadow({ mode: 'closed' });
41
+ this.#shadowRoot.appendChild(document.importNode(this.constructor.template, true));
42
+ this.#slots = {
43
+ '': this.#shadowRoot.querySelector('slot:not([name])') ?? undefined,
44
+ description: this.#shadowRoot.querySelector('slot[name="description"]') ?? undefined,
45
+ actions: this.#shadowRoot.querySelector('slot[name="actions"]') ?? undefined,
46
+ };
47
+ }
48
+ disconnectedCallback() {
49
+ console.debug(`${ContentUnavailableView.name} ⚡️ disconnect`);
50
+ }
51
+ connectedCallback() {
52
+ console.debug(`${ContentUnavailableView.name} ⚡️ connect`);
53
+ }
54
+ attributeChangedCallback(name, oldValue, newValue) {
55
+ console.debug(`${ContentUnavailableView.name} ⚡️ attr-change [${name}] ("${oldValue}" → "${newValue}")`);
56
+ switch (name) {
57
+ case 'search':
58
+ if (oldValue === newValue)
59
+ break;
60
+ this.#renderSearch(newValue);
61
+ CleanupRegistry.unregister(this, 'i18n');
62
+ CleanupRegistry.register(this, onoff('localechange', () => {
63
+ this.#renderSearch(this.getAttribute('search'));
64
+ }, I18n.on).on(), 'i18n');
65
+ break;
66
+ }
67
+ }
68
+ #renderSearch = (search) => {
69
+ const titleLabel = this.querySelector(':scope>:not([slot])') ?? this.appendChild($(`<label-view><i class="ph ph-magnifying-glass" slot="image" foreground="secondary"></i></label-view>`)), descText = this.querySelector(':scope>[slot=description]') ?? this.appendChild($(`<label-view foreground="secondary" slot="description"></label-view>`));
70
+ if (search) {
71
+ const interpolate = (text, vars) => text.replaceAll(/\{(\w+)\}/g, (_, key) => vars[key] ?? '');
72
+ titleLabel.setAttribute('title', interpolate(I18n.t('SearchUnavailableContent').Label, { search }));
73
+ }
74
+ else
75
+ titleLabel?.setAttribute('title', I18n.t('SearchUnavailableContent').NoLabel);
76
+ descText.setAttribute('title', I18n.t('SearchUnavailableContent').Description);
77
+ };
78
+ }
@@ -61,7 +61,7 @@ export class DatePicker extends FormAssociatedBase {
61
61
  super.connectedCallback();
62
62
  CleanupRegistry.register(this, onoff('click', this.#handleClick, this).on());
63
63
  this.#render();
64
- CleanupRegistry.register(this, onoff('change', () => {
64
+ CleanupRegistry.register(this, onoff('localechange', () => {
65
65
  this.#render();
66
66
  }, I18n.on).on());
67
67
  // finally
@@ -113,9 +113,7 @@ export class DatePicker extends FormAssociatedBase {
113
113
  }
114
114
  }
115
115
  get datePickerStyle() {
116
- return datePickerStyles.includes(this.getAttribute('date-picker-style') ?? '')
117
- ? this.getAttribute('date-picker-style')
118
- : 'automatic';
116
+ return datePickerStyles.includes(this.getAttribute('date-picker-style') ?? '') ? this.getAttribute('date-picker-style') : 'automatic';
119
117
  }
120
118
  #render() {
121
119
  console.debug(`${_a.name} ⚡️ render (${this.datePickerStyle})`);
@@ -1,11 +1,9 @@
1
- /**
2
- * @file Automatically generated by barrelsby.
3
- */
4
1
  export * from './body-view';
5
2
  export * from './bordered-button';
6
3
  export * from './bordered-prominent-button';
7
4
  export * from './borderless-button';
8
5
  export * from './confirmation-dialog';
6
+ export * from './content-unavailable-view';
9
7
  export * from './date-picker';
10
8
  export * from './disclosure-group';
11
9
  export * from './form-view';
@@ -21,6 +19,7 @@ export * from './navigation-title';
21
19
  export * from './picker-view';
22
20
  export * from './plain-button';
23
21
  export * from './screen-view';
22
+ export * from './scroll-view-proxy';
24
23
  export * from './scroll-view';
25
24
  export * from './section-view';
26
25
  export * from './sheet-view';
@@ -1,11 +1,9 @@
1
- /**
2
- * @file Automatically generated by barrelsby.
3
- */
4
1
  export * from './body-view';
5
2
  export * from './bordered-button';
6
3
  export * from './bordered-prominent-button';
7
4
  export * from './borderless-button';
8
5
  export * from './confirmation-dialog';
6
+ export * from './content-unavailable-view';
9
7
  export * from './date-picker';
10
8
  export * from './disclosure-group';
11
9
  export * from './form-view';
@@ -21,6 +19,7 @@ export * from './navigation-title';
21
19
  export * from './picker-view';
22
20
  export * from './plain-button';
23
21
  export * from './screen-view';
22
+ export * from './scroll-view-proxy';
24
23
  export * from './scroll-view';
25
24
  export * from './section-view';
26
25
  export * from './sheet-view';
@@ -1,4 +1,7 @@
1
1
  import { NavigationView } from '../internal/class/navigation-view';
2
+ /**
3
+ * @summary A view with two or three side-by-side sections, where what you choose in the left section changes what appears in the next section.
4
+ */
2
5
  export declare class NavigationSplitView extends NavigationView {
3
6
  constructor();
4
7
  disconnectedCallback(): void;
@@ -1,4 +1,7 @@
1
1
  import { NavigationView } from '../internal/class/navigation-view';
2
+ /**
3
+ * @summary A view with two or three side-by-side sections, where what you choose in the left section changes what appears in the next section.
4
+ */
2
5
  export class NavigationSplitView extends NavigationView {
3
6
  constructor() {
4
7
  super();
@@ -1,4 +1,7 @@
1
1
  import { NavigationView } from '../internal/class/navigation-view';
2
+ /**
3
+ * @summary A view that shows a main screen and lets you open other screens on top of it.
4
+ */
2
5
  export declare class NavigationStack extends NavigationView {
3
6
  constructor();
4
7
  disconnectedCallback(): void;
@@ -1,4 +1,7 @@
1
1
  import { NavigationView } from '../internal/class/navigation-view';
2
+ /**
3
+ * @summary A view that shows a main screen and lets you open other screens on top of it.
4
+ */
2
5
  export class NavigationStack extends NavigationView {
3
6
  constructor() {
4
7
  super();
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @summary A wrapper to {hv}-stacks
3
+ */
4
+ export declare class ScrollViewProxy extends HTMLElement {
5
+ constructor();
6
+ disconnectedCallback(): void;
7
+ connectedCallback(): void;
8
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @summary A wrapper to {hv}-stacks
3
+ */
4
+ export class ScrollViewProxy extends HTMLElement {
5
+ constructor() {
6
+ super();
7
+ }
8
+ disconnectedCallback() {
9
+ console.debug(`${ScrollViewProxy.name} ⚡️ disconnect`);
10
+ }
11
+ connectedCallback() {
12
+ console.debug(`${ScrollViewProxy.name} ⚡️ connect`);
13
+ }
14
+ }
@@ -1,19 +1,11 @@
1
1
  var _a;
2
2
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
3
3
  import { ResizeObserverSingleton } from '../internal/class/resize-observer-singleton';
4
- import { $, debounce, onoff } from '../internal/utils';
4
+ import { debounce, onoff } from '../internal/utils';
5
5
  const observers = new ResizeObserverSingleton();
6
6
  export class SidebarToggle extends HTMLElement {
7
- // #ro
8
- // #io
9
7
  constructor() {
10
8
  super();
11
- // this.#ro = new ResizeObserver(
12
- // debounce(this.#handleMeasure.bind(this), 100, true)
13
- // )
14
- // this.#io = new IntersectionObserver((entries) => {
15
- // this._checkVisibility2(entries)
16
- // })
17
9
  }
18
10
  connectedCallback() {
19
11
  console.debug(`${_a.name} ⚡️ connect`);
@@ -21,18 +13,13 @@ export class SidebarToggle extends HTMLElement {
21
13
  const entry = {
22
14
  target: this,
23
15
  };
24
- // NOTE: wait for config
25
- // Snapshot.waitReady.then(() => {
26
16
  observers.observe(this, debounce(_a.#handleMeasure, 100, true)); //this.#ro?.observe(this)
27
- // this.#io.observe(this)
28
17
  // @ts-expect-error
29
18
  _a.#handleMeasure([entry]);
30
- // })
31
19
  }
32
20
  disconnectedCallback() {
33
21
  console.debug(`${_a.name} ⚡️ disconnect`);
34
- observers.unobserve(this); //this.#ro?.disconnect()
35
- // this.#io.disconnect()
22
+ observers.unobserve(this);
36
23
  CleanupRegistry.unregister(this);
37
24
  }
38
25
  static query(target) {
@@ -42,62 +29,48 @@ export class SidebarToggle extends HTMLElement {
42
29
  // This triggers on show/hide of any of sidebar-toggle elements
43
30
  static #handleMeasure(entry) {
44
31
  console.debug(`${_a.name} ⚡️ measure`);
45
- const target = entry?.target;
32
+ const { target } = entry ?? {};
33
+ if (!(target instanceof HTMLElement))
34
+ return;
46
35
  // set/remove css var/prop to parent based on shown/hidden
47
- // for (const { target } of entries) {
48
- const tv = target?.parentElement, width = target?.offsetWidth ?? 0;
49
- // const gapProp =
50
- // getComputedStyle(tg).getPropertyValue('--toolbar-col-gap') || '0',
51
- // gap = parseFloat(gapProp) * 1 //(gapProp.endsWith('rem')? parseFloat(getComputedStyle(document.documentElement).fontSize): 1)
52
- if (0 < width)
53
- $.prop('--sidebar-toggle-padding-inline-start', `${width}px`, tv); //tv?.style?.setProperty?.(Snapshot.config!['sidebar-toggle-padding-inline-start-css-prop'], `${width}px`)
54
- else
55
- $.prop('--sidebar-toggle-padding-inline-start', null, tv); //tv?.style?.removeProperty?.(Snapshot.config!['sidebar-toggle-padding-inline-start-css-prop'])
56
- // }
36
+ const tv = target.parentElement;
37
+ // inlineSize = entry.borderBoxSize?.at(0)?.inlineSize ?? entry.contentRect?.width ?? 0,
38
+ // blockSize = entry.borderBoxSize?.at(0)?.blockSize ?? entry.contentRect?.height ?? 0
39
+ self.requestAnimationFrame(() => {
40
+ const inlineSize = target.offsetWidth ?? 0;
41
+ if (0 < inlineSize)
42
+ tv?.style?.setProperty?.('--sidebar-toggle-padding-inline-start', `${inlineSize}px`); //$.prop('--sidebar-toggle-padding-inline-start', `${inlineSize}px`, tv) //
43
+ else
44
+ tv?.style?.removeProperty?.('--sidebar-toggle-padding-inline-start'); //$.prop('--sidebar-toggle-padding-inline-start', null, tv) //
45
+ });
57
46
  // auto close IF open
58
- // for (const { target } of entries) {
59
47
  const { container, sideBar } = _a.query(target); //target?.closest('navigation-split-view,tab-view')
60
- // const sideBar = container?.querySelector<HTMLDialogElement>(':scope > dialog[is=sidebar-view]')
61
48
  if (!sideBar?.open)
62
49
  return;
63
- switch (container?.tagName) {
64
- case 'NAVIGATION-SPLIT-VIEW':
65
- // const sideBar = target?.parentElement?.querySelector<HTMLDialogElement>(':scope > dialog[is=sidebar-view]')
66
- // if (!sideBar?.open) return
67
- if (0 < target.offsetWidth && 0 < target.offsetHeight)
68
- return;
69
- // sideBar?.close?.()
70
- break;
71
- case 'TAB-VIEW':
72
- // const tabBar = container.querySelector<HTMLDialogElement>(':scope > dialog[is=tab-bar]')
73
- // if (!tabBar?.open) return
74
- // scan all toggles for anyone that is visible, sign that sidebar should stay open
75
- if ([
76
- ...container.querySelectorAll(":scope > sidebar-toggle,:scope > [is=tab-bar] > sidebar-toggle,:scope > [is='sidebar-view'] > tool-bar > sidebar-toggle"),
77
- ].some(({ offsetWidth, offsetHeight }) => 0 < offsetWidth && 0 < offsetHeight))
78
- return;
79
- //tabBar?.close?.()
80
- break;
81
- }
82
- sideBar?.close?.();
83
- // }
50
+ self.requestAnimationFrame(() => {
51
+ switch (container?.tagName) {
52
+ case 'NAVIGATION-SPLIT-VIEW':
53
+ if (0 < target.offsetWidth && 0 < target.offsetHeight)
54
+ return;
55
+ break;
56
+ case 'TAB-VIEW':
57
+ // scan all toggles for anyone that is visible, sign that sidebar should stay open
58
+ if ([
59
+ ...container.querySelectorAll(':scope > sidebar-toggle,:scope > [is=tab-bar] > sidebar-toggle' //,:scope > [is='sidebar-view'] > tool-bar > sidebar-toggle" ?? FIXME: removed this bc/ inside sidebar!
60
+ ),
61
+ ].some(({ offsetWidth, offsetHeight }) => 0 < offsetWidth && 0 < offsetHeight))
62
+ return;
63
+ break;
64
+ }
65
+ sideBar?.close?.();
66
+ });
84
67
  }
85
- // _checkVisibility2(entries) {
86
- // entries.forEach((entry) => {
87
- // console.log('Visible in viewport?', entry.isIntersecting)
88
- // })
89
- // }
90
68
  #handleClick(evt) {
91
69
  console.debug(`${_a.name} ⚡️ ${evt?.type}`);
92
- const target = evt.target;
70
+ const { target } = evt;
93
71
  if (!target?.closest('button'))
94
72
  return;
95
73
  const { sideBar } = _a.query(target);
96
- // const container = target?.closest('tab-view,navigation-split-view')
97
- // const dialog =
98
- // 'TAB-VIEW' === container?.tagName
99
- // ? container.querySelector<TabBar>('dialog[is="tab-bar"]')
100
- // : container?.querySelector<SidebarView>('dialog[is="sidebar-view"]')
101
74
  if (!sideBar?.open)
102
75
  sideBar?.showModal();
103
76
  else
@@ -1,4 +1,7 @@
1
1
  import { DialogBase } from '../namespace-browser/base';
2
+ /**
3
+ * @summary A screen that lets users switch between different views using buttons, tabs, or other controls.
4
+ */
2
5
  export declare class TabBar extends DialogBase {
3
6
  #private;
4
7
  constructor();
@@ -1,6 +1,9 @@
1
1
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
2
2
  import { onoff, touchGlass } from '../internal/utils';
3
3
  import { DialogBase } from '../namespace-browser/base';
4
+ /**
5
+ * @summary A screen that lets users switch between different views using buttons, tabs, or other controls.
6
+ */
4
7
  export class TabBar extends DialogBase {
5
8
  constructor() {
6
9
  super();
@@ -62,7 +62,7 @@ export class TextField extends FormAssociatedBase {
62
62
  { types: 'beforeinput', listener: this.#handleInputBeforeinput },
63
63
  { types: 'paste', listener: this.#handleInputPaste },
64
64
  ], this.#input).on());
65
- CleanupRegistry.register(this, onoff('change', () => {
65
+ CleanupRegistry.register(this, onoff('localechange', () => {
66
66
  this.text = this.text;
67
67
  }, I18n.on).on());
68
68
  }