@swiftwc/ui 0.0.0-dev.98 → 0.0.0-dev.99

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.
@@ -2,7 +2,7 @@ var _a;
2
2
  import { CleanupRegistry } from '../internal/class/cleanup-registry';
3
3
  import { FormAssociatedBase, getInternals } from '../internal/class/form-associated-base';
4
4
  import { MutationObserverSet } from '../internal/class/mutation-observer-set';
5
- import { $, devFlags, kebabCase, onoff } from '../internal/utils';
5
+ import { $, devFlags, kebabCase, onoff, touchGlass } from '../internal/utils';
6
6
  import { queryMorph } from '../morphdom';
7
7
  import { html } from '../tpl';
8
8
  const toggleStyles = ['switch', 'button'];
@@ -24,7 +24,6 @@ export class ToggleView extends FormAssociatedBase {
24
24
  * @type {boolean}
25
25
  */
26
26
  'is-on',
27
- 'keyboard-type',
28
27
  /**
29
28
  * @type {boolean}
30
29
  */
@@ -42,8 +41,7 @@ export class ToggleView extends FormAssociatedBase {
42
41
  <slot name="label"></slot>
43
42
  </div>
44
43
  <div part="root toggle-input-stack">
45
- <!--<input type="checkbox" part="root input toggle-form-input">-->
46
- <div part="root toggle-form-input" tabindex="0"></div>
44
+ <div part="root toggle-track" tabindex="0"></div>
47
45
  </div>
48
46
  <slot name="validity-options" hidden></slot>
49
47
  </label>`));
@@ -57,10 +55,30 @@ export class ToggleView extends FormAssociatedBase {
57
55
  #customValidity = '';
58
56
  #slots = new Map();
59
57
  #validityObservers = new MutationObserverSet(this.#renderValidityMsgs);
60
- #input;
58
+ #track;
61
59
  #value = 'on';
62
60
  #isOn = false;
63
- #connected = false;
61
+ #didDrag = false;
62
+ #trackWidth = 0;
63
+ #dotSize = 0;
64
+ get #trackMinX() {
65
+ return 0;
66
+ }
67
+ get #trackMaxX() {
68
+ return this.#trackWidth - this.#dotSize;
69
+ }
70
+ #handleMeasure = ([{ target, borderBoxSize }]) => {
71
+ if (devFlags.debug)
72
+ console.debug(`${_a.name} ⚡️ measure`);
73
+ if (!(target instanceof HTMLElement))
74
+ return;
75
+ this.#trackWidth = borderBoxSize.at(0)?.inlineSize ?? 0;
76
+ this.#dotSize = borderBoxSize.at(0)?.blockSize ?? 0;
77
+ };
78
+ #resizeObserver = new ResizeObserver(this.#handleMeasure);
79
+ get #isDragging() {
80
+ return '0ms' === this.#track?.style.getPropertyValue('--toggle--dot-transition-duration');
81
+ }
64
82
  get #internals() {
65
83
  return getInternals(this);
66
84
  }
@@ -75,14 +93,18 @@ export class ToggleView extends FormAssociatedBase {
75
93
  });
76
94
  CleanupRegistry.unregister(this, 'validities');
77
95
  CleanupRegistry.register(this, onoff('slotchange', this.#handleValiditiesSlotchange, this.#slots?.get('validity-options')).on(), 'validities');
78
- this.#input = this.#shadowRoot.querySelector('[part*=toggle-form-input]') ?? undefined;
79
- this.#input?.addEventListener('click', () => {
80
- this.isOn = !this.isOn;
81
- this.#sendValueToForm(this.#connected);
82
- });
96
+ this.#track = this.#shadowRoot.querySelector('[part*=toggle-track]') ?? undefined;
83
97
  CleanupRegistry.register(this, onoff([{ types: 'focusin', listener: this.#handleFocusin }], this).on());
84
- CleanupRegistry.register(this, onoff([{ types: 'keydown', listener: this.#handleInputKeydown }], this.#input).on());
85
- // CleanupRegistry.register(this, onoff([{ types: 'input', listener: this.#handleInputInput }], this.#input).on())
98
+ CleanupRegistry.register(this, onoff([
99
+ { types: 'keydown', listener: this.#handleTrackKeydown },
100
+ { types: 'click', listener: this.#handleTrackClick },
101
+ { types: 'pointerdown', listener: this.#handleTrackPointerdown },
102
+ { types: 'pointerup', listener: this.#handleTrackPointerup },
103
+ { types: 'pointercancel', listener: this.#handleTrackPointercancel },
104
+ { types: 'pointermove', listener: this.#handleTrackPointermove },
105
+ { types: 'lostpointercapture', listener: this.#handleTrackPointercancel },
106
+ ], this.#track).on());
107
+ CleanupRegistry.register(this, onoff([{ types: 'blur', listener: this.#handleWindowBlur }], self).on());
86
108
  }
87
109
  attributeChangedCallback(name, oldValue, newValue) {
88
110
  if (devFlags.debug)
@@ -90,18 +112,18 @@ export class ToggleView extends FormAssociatedBase {
90
112
  switch (name) {
91
113
  case 'value':
92
114
  this.value = String(newValue); // this.#input?.setAttribute('checked', newValue ?? '')
93
- this.#sendValueToForm(this.#connected);
115
+ this.#sendValueToForm(false);
94
116
  break;
95
117
  case 'is-on':
96
118
  this.isOn = newValue !== null; // this.#input?.setAttribute('checked', newValue ?? '')
97
- this.#sendValueToForm(this.#connected);
119
+ this.#sendValueToForm(false);
98
120
  break;
99
121
  case 'required':
100
122
  // this.#input?.setAttribute(name, newValue ?? '')
101
- this.#sendValueToForm(this.#connected);
123
+ this.#sendValueToForm(false);
102
124
  break;
103
125
  case 'name':
104
- this.#sendValueToForm(this.#connected);
126
+ this.#sendValueToForm(false);
105
127
  break;
106
128
  case 'label':
107
129
  queryMorph('[slot=label]', html `<label-view slot="label">${newValue ? html `<span>${newValue}</span>` : null}</label-view>`, this);
@@ -114,16 +136,18 @@ export class ToggleView extends FormAssociatedBase {
114
136
  }
115
137
  disconnectedCallback() {
116
138
  super.disconnectedCallback();
139
+ if (this.#track)
140
+ this.#resizeObserver.unobserve(this.#track);
117
141
  this.#validityObservers.unobserveAll();
118
142
  }
119
143
  connectedCallback() {
120
144
  super.connectedCallback();
121
- // finally
122
- // if (this.hasAttribute('value')) this.value = this.getAttribute('value') ?? ''
123
145
  if (this.hasAttribute('is-on'))
124
146
  this.isOn = this.hasAttribute('is-on');
125
147
  this.#sendValueToForm(false);
126
- this.#connected = true;
148
+ if (this.#track)
149
+ this.#resizeObserver.observe(this.#track);
150
+ CleanupRegistry.register(this, onoff(touchGlass(this, (t) => t, ({ clientX, clientY }) => this.#measurePointerIsOverDot(clientX, clientY) ?? false), this).on());
127
151
  }
128
152
  get toggleStyle() {
129
153
  return toggleStyles.includes(this.getAttribute('toggle-style') ?? '') ? this.getAttribute('toggle-style') : 'switch';
@@ -149,7 +173,116 @@ export class ToggleView extends FormAssociatedBase {
149
173
  if (devFlags.debug)
150
174
  console.debug(`${_a.name} ⚡️ ${type}`);
151
175
  if (target === this)
152
- this.#input?.focus();
176
+ this.#track?.focus();
177
+ };
178
+ #handleTrackClick = ({ type }) => {
179
+ if (devFlags.debug)
180
+ console.debug(`${_a.name} ⚡️ ${type}`);
181
+ if (this.#didDrag)
182
+ return (this.#didDrag = false);
183
+ this.#applyIsOnWithFlip(!this.isOn);
184
+ this.#sendValueToForm(true);
185
+ };
186
+ #measurePointerIsOverDot = (clientX, clientY) => {
187
+ if (!this.#track)
188
+ return;
189
+ const { left, top, height } = this.#track.getBoundingClientRect();
190
+ const dotLeft = left + parseFloat(self.getComputedStyle(this.#track, '::before').left);
191
+ const dotCenterX = dotLeft + this.#dotSize / 2;
192
+ const dotCenterY = top + height / 2;
193
+ return Math.hypot(clientX - dotCenterX, clientY - dotCenterY) <= this.#dotSize / 2;
194
+ };
195
+ #dragOffsetX = 0;
196
+ get #baseX() {
197
+ return this.isOn ? this.#trackMaxX : this.#trackMinX;
198
+ }
199
+ #handleTrackPointerdown = ({ type, pointerId, clientX, clientY }) => {
200
+ if (devFlags.debug)
201
+ console.debug(`${_a.name} ⚡️ ${type}`);
202
+ if (!this.#track)
203
+ return;
204
+ if (!this.#measurePointerIsOverDot(clientX, clientY))
205
+ return; // ignore taps elsewhere on the track
206
+ this.#didDrag = true; // dot was engaged — settle() will own the outcome, click must be swallowed
207
+ const currentDelta = parseFloat(self.getComputedStyle(this.#track).getPropertyValue('--toggle--dot-x')) || 0;
208
+ const currentAbsoluteX = this.#baseX + currentDelta;
209
+ this.#dragOffsetX = clientX - this.#track.getBoundingClientRect().left - currentAbsoluteX;
210
+ this.#track.style.setProperty('--toggle--dot-transition-duration', '0ms');
211
+ this.#track.setPointerCapture(pointerId);
212
+ // this.#updateFromEvent(clientX)
213
+ };
214
+ #handleTrackPointermove = ({ type, clientX }) => {
215
+ if (devFlags.debug)
216
+ console.debug(`${_a.name} ⚡️ ${type}`);
217
+ if (!this.#isDragging)
218
+ return;
219
+ this.#updateFromEvent(clientX);
220
+ };
221
+ #handleTrackPointerup = ({ type, pointerId }) => {
222
+ if (devFlags.debug)
223
+ console.debug(`${_a.name} ⚡️ ${type}`);
224
+ if (!this.#isDragging)
225
+ return; // pointerdown never engaged (missed the dot) — plain click, nothing to settle
226
+ this.#track?.style.removeProperty('--toggle--dot-transition-duration');
227
+ this.#track?.releasePointerCapture(pointerId);
228
+ this.#settle();
229
+ };
230
+ #handleTrackPointercancel = ({ type }) => {
231
+ if (devFlags.debug)
232
+ console.debug(`${_a.name} ⚡️ ${type}`);
233
+ if (!this.#isDragging)
234
+ return; // already settled via pointerup, or nothing to settle
235
+ this.#track?.style.removeProperty('--toggle--dot-transition-duration');
236
+ this.#settle();
237
+ };
238
+ #handleWindowBlur = () => {
239
+ if (!this.#isDragging)
240
+ return;
241
+ this.#track?.style.removeProperty('--toggle--dot-transition-duration');
242
+ this.#settle();
243
+ };
244
+ #updateFromEvent = (clientX) => {
245
+ if (!this.#track)
246
+ return;
247
+ const { left } = this.#track.getBoundingClientRect();
248
+ const absoluteX = Math.min(Math.max(clientX - left - this.#dragOffsetX, this.#trackMinX), this.#trackMaxX);
249
+ this.#track.style.setProperty('--toggle--dot-x', `${Math.round(absoluteX - this.#baseX)}px`);
250
+ };
251
+ #settle = () => {
252
+ if (!this.#track)
253
+ return;
254
+ const deltaX = parseFloat(self.getComputedStyle(this.#track).getPropertyValue('--toggle--dot-x')) || 0;
255
+ const absoluteX = this.#baseX + deltaX;
256
+ const nextIsOn = absoluteX > this.#trackMaxX / 2;
257
+ if (nextIsOn === this.isOn)
258
+ return this.#track.style.removeProperty('--toggle--dot-x'); // pure transform animation back to rest, inset untouched
259
+ this.#applyIsOnWithFlip(nextIsOn);
260
+ this.#sendValueToForm(true);
261
+ };
262
+ #handleTrackKeydown = (evt) => {
263
+ if (devFlags.debug)
264
+ console.debug(`${_a.name} ⚡️ ${evt?.type}`);
265
+ if (' ' !== evt.key)
266
+ return;
267
+ evt.preventDefault();
268
+ this.#applyIsOnWithFlip(!this.isOn);
269
+ this.#sendValueToForm(true);
270
+ };
271
+ #applyIsOnWithFlip = (nextIsOn) => {
272
+ if (!this.#track || nextIsOn === this.isOn)
273
+ return;
274
+ // capture current visual position under the OLD base, before flipping state
275
+ const deltaX = parseFloat(self.getComputedStyle(this.#track).getPropertyValue('--toggle--dot-x')) || 0, oldAbsoluteX = this.#baseX + deltaX;
276
+ this.isOn = nextIsOn; // changes --toggle--dot-inset-inline-start/-end via aria-checked
277
+ const compensationX = oldAbsoluteX - this.#baseX; // baseX now reflects the NEW state
278
+ // make inset + transform jump together, instantly, so nothing is visibly out of sync
279
+ this.#track.style.setProperty('--toggle--dot-transition-duration', '0ms');
280
+ this.#track.style.setProperty('--toggle--dot-x', `${Math.round(compensationX)}px`);
281
+ void this.#track.offsetWidth; // force reflow so the instant jump is committed before re-enabling transitions
282
+ this.#track.style.removeProperty('--toggle--dot-transition-duration');
283
+ self.requestAnimationFrame(() => {
284
+ this.#track?.style.removeProperty('--toggle--dot-x'); // only transform animates from here — single source of motion
285
+ });
153
286
  };
154
287
  #sendValueToForm = (dispatchEvent = true) => {
155
288
  // input.value has already been updated/synced !!
@@ -158,9 +291,8 @@ export class ToggleView extends FormAssociatedBase {
158
291
  if (this.hasAttribute('required') && !this.isOn) {
159
292
  this.setValidity({ valueMissing: true });
160
293
  }
161
- else {
294
+ else
162
295
  this.setValidity({});
163
- }
164
296
  const entries = new FormData();
165
297
  if (this.value)
166
298
  entries.append(this.name, this.value);
@@ -168,17 +300,6 @@ export class ToggleView extends FormAssociatedBase {
168
300
  if (dispatchEvent)
169
301
  this.dispatchEvent(new CustomEvent('toggle:change', { detail: { value: this.value, isOn: this.isOn }, bubbles: true, composed: true }));
170
302
  };
171
- #handleInputKeydown = (evt) => {
172
- if (devFlags.debug)
173
- console.debug(`${_a.name} ⚡️ ${evt?.type}`);
174
- this.isOn = !this.isOn;
175
- this.#sendValueToForm();
176
- };
177
- #handleInputInput = (evt) => {
178
- if (devFlags.debug)
179
- console.debug(`${_a.name} ⚡️ ${evt?.type}`);
180
- this.#sendValueToForm();
181
- };
182
303
  /**
183
304
  * Form participation property
184
305
  */
@@ -222,7 +343,7 @@ export class ToggleView extends FormAssociatedBase {
222
343
  }
223
344
  if (devFlags.debug)
224
345
  console.debug(`${_a.name} ⚡️ validity-change`);
225
- return this.#internals.setValidity(flags, this.#customValidity || message, anchor ?? this.#input);
346
+ return this.#internals.setValidity(flags, this.#customValidity || message, anchor ?? this.#track);
226
347
  };
227
348
  setCustomValidity = (message) => {
228
349
  this.#customValidity = message;
@@ -1138,6 +1138,7 @@
1138
1138
  display: grid;
1139
1139
  }
1140
1140
  :where(labeled-content)::part(labeled-content-container),
1141
+ :where(labeled-content)::part(labeled-content-container),
1141
1142
  :where(labeled-content)::part(labeled-content-label-stack),
1142
1143
  :where(labeled-content)::part(labeled-content-value-stack),
1143
1144
  :where(progress-view)::part(progress-control),
@@ -3440,7 +3441,15 @@
3440
3441
  :where(toggle-view) {
3441
3442
  grid-template-columns: minmax(0, 1fr);
3442
3443
  grid-template-rows: minmax(0, 1fr);
3443
- --toggle--trigger-bg: lightgray;
3444
+ --toggle--track-padding: 0.2rem;
3445
+ --toggle--track-bg: lightgray;
3446
+ --toggle--dot-size: 1.4rem;
3447
+ --toggle--dot-inset-inline-start: 0;
3448
+ --toggle--dot-inset-inline-end: 100%;
3449
+ --toggle--dot-x: 0px;
3450
+ --toggle--dot-transition-duration: 200ms;
3451
+ --toggle--dot-scale: scale(1);
3452
+ --toogle--track-brightness: brightness(1);
3444
3453
  }
3445
3454
  :where(toggle-view)::part(toggle-stack) {
3446
3455
  grid-template-columns: minmax(0, 1fr) auto;
@@ -3451,36 +3460,51 @@
3451
3460
  align-items: center;
3452
3461
  justify-items: end;
3453
3462
  place-content: stretch;
3463
+ align-self: flex-start;
3464
+ cursor: pointer;
3465
+ padding-inline: var(--toggle--track-padding);
3466
+ padding-block: var(--toggle--track-padding);
3467
+ border-radius: var(--pill-border-radius);
3468
+ background: var(--toggle--track-bg);
3469
+ transition: background-color var(--toggle--dot-transition-duration) ease-out;
3454
3470
  }
3455
- :where(toggle-view)::part(toggle-form-input):focus {
3456
- outline: blue solid 5px;
3457
- }
3458
- :where(toggle-view)::part(toggle-form-input) {
3471
+ :where(toggle-view)::part(toggle-track) {
3472
+ container: toggle-track/size;
3459
3473
  text-align: end;
3460
3474
  position: relative;
3461
- display: grid;
3462
- outline: red solid 3px;
3463
- border-radius: 2rem;
3475
+ outline: none;
3476
+ border-radius: var(--pill-border-radius);
3464
3477
  inline-size: 4rem;
3465
- block-size: 1rem;
3466
- background: var(--toggle--trigger-bg);
3478
+ block-size: var(--toggle--dot-size);
3479
+ filter: var(--toogle--track-brightness, );
3480
+ transition: background-color var(--toggle--dot-transition-duration) ease-out;
3481
+ touch-action: none;
3482
+ }
3483
+ :where(toggle-view)::part(toggle-track):focus {
3484
+ --toogle--track-brightness: brightness(0.9);
3467
3485
  }
3468
- :where(toggle-view)::part(toggle-form-input)::before {
3486
+ :where(toggle-view)::part(toggle-track)::before {
3469
3487
  content: "";
3470
3488
  display: grid;
3471
3489
  position: absolute;
3472
- inset: 0 auto auto 0;
3473
- inline-size: 1rem;
3474
- block-size: 1rem;
3475
- border-radius: 1rem;
3490
+ inset: 0 var(--toggle--dot-inset-inline-end) auto var(--toggle--dot-inset-inline-start);
3491
+ inline-size: var(--toggle--dot-size);
3492
+ block-size: var(--toggle--dot-size);
3493
+ border-radius: var(--pill-border-radius);
3476
3494
  backdrop-filter: var(--liquid-glass-backdrop-filter);
3477
3495
  box-shadow: var(--liquid-glass-box-shadow);
3496
+ transition: inset-inline-start var(--toggle--dot-transition-duration) ease-out, inset-inline-end var(--toggle--dot-transition-duration) ease-out, transform var(--toggle--dot-transition-duration) ease-out, scale var(--toggle--dot-press-duration) ease-out;
3497
+ transform: translateX(var(--toggle--dot-x));
3498
+ scale: var(--toggle--dot-scale, );
3499
+ transform-origin: center;
3478
3500
  }
3479
3501
  :where(form toggle-view)::part(toggle-label-stack) {
3480
3502
  color: var(--secondary);
3481
3503
  }
3482
3504
  :where(toggle-view[aria-checked=true]) {
3483
- --toggle--trigger-bg: green;
3505
+ --toggle--track-bg: green;
3506
+ --toggle--dot-inset-inline-start: calc(100% - var(--toggle--dot-size));
3507
+ --toggle--dot-inset-inline-end: 0;
3484
3508
  }
3485
3509
  :where(toggle-view > :nth-child(n+2 of [slot=label])) {
3486
3510
  --label-font: callout;
@@ -10528,6 +10552,10 @@
10528
10552
  } */
10529
10553
  }
10530
10554
  @layer sw-final {
10555
+ :where(toggle-view[touch-glass]) {
10556
+ --toggle--dot-scale: 1.22;
10557
+ --toggle--dot-press-duration: 100ms;
10558
+ }
10531
10559
  @media (pointer: coarse) {
10532
10560
  :where([is=glass-button][touch-glass],
10533
10561
  [is=glass-prominent-button][touch-glass]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swiftwc/ui",
3
- "version": "0.0.0-dev.98",
3
+ "version": "0.0.0-dev.99",
4
4
  "description": "Elegant SwiftUI-inspired web components for standalone web apps and web extensions.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -56,6 +56,7 @@
56
56
  }
57
57
  }
58
58
 
59
+ :where(labeled-content)::part(labeled-content-container),
59
60
  :where(labeled-content)::part(labeled-content-container),
60
61
  :where(labeled-content)::part(labeled-content-label-stack),
61
62
  :where(labeled-content)::part(labeled-content-value-stack),
@@ -9,7 +9,19 @@
9
9
 
10
10
  grid-template-rows: minmax(0, 1fr);
11
11
 
12
- --toggle--trigger-bg: lightgray;
12
+ --toggle--track-padding: 0.2rem;
13
+ --toggle--track-bg: lightgray;
14
+
15
+ --toggle--dot-size: 1.4rem;
16
+
17
+ --toggle--dot-inset-inline-start: 0;
18
+ --toggle--dot-inset-inline-end: 100%;
19
+
20
+ --toggle--dot-x: 0px;
21
+ --toggle--dot-transition-duration: 200ms;
22
+ --toggle--dot-scale: scale(1);
23
+
24
+ --toogle--track-brightness: brightness(1);
13
25
 
14
26
  &::part(toggle-stack) {
15
27
  grid-template-columns: minmax(0, 1fr) auto;
@@ -30,50 +42,73 @@
30
42
  justify-items: end;
31
43
 
32
44
  place-content: stretch;
33
- }
45
+ align-self: flex-start;
46
+
47
+ cursor: pointer;
34
48
 
35
- &::part(toggle-form-input):focus {
36
- outline: blue solid 5px;
49
+ padding-inline: var(--toggle--track-padding);
50
+ padding-block: var(--toggle--track-padding);
51
+ border-radius: var(--pill-border-radius);
52
+ background: var(--toggle--track-bg);
53
+
54
+ transition: background-color var(--toggle--dot-transition-duration) ease-out;
37
55
  }
38
56
 
39
- &::part(toggle-form-input) {
57
+ &::part(toggle-track) {
58
+ container: toggle-track / size;
59
+
40
60
  text-align: end;
41
61
 
42
62
  position: relative;
43
63
 
44
- display: grid;
45
- outline: red solid 3px;
46
- border-radius: 2rem;
64
+ outline: none;
65
+
66
+ // display: grid;
67
+ // outline: red solid 3px;
68
+ border-radius: var(--pill-border-radius);
47
69
  inline-size: 4rem;
48
- block-size: 1rem;
49
- background: var(--toggle--trigger-bg);
70
+ block-size: var(--toggle--dot-size);
71
+ // background: var(--toggle--track-bg);
50
72
 
51
- // appearance: none;
73
+ filter: var(--toogle--track-brightness,);
52
74
 
53
- // border: 0;
75
+ transition: background-color var(--toggle--dot-transition-duration) ease-out;
54
76
 
55
- // outline: 0;
77
+ touch-action: none; // prevent scrolling while dragging
56
78
 
57
- // padding: 0;
79
+ &:focus {
80
+ // outline: blue solid 5px;
81
+ // --toggle--track-bg: gray;
58
82
 
59
- // background-color: transparent;
60
- }
83
+ --toogle--track-brightness: brightness(0.9);
84
+ }
85
+
86
+ &::before {
87
+ content: '';
61
88
 
62
- &::part(toggle-form-input)::before {
63
- content: '';
89
+ display: grid;
64
90
 
65
- display: grid;
91
+ position: absolute;
92
+ inset: 0 var(--toggle--dot-inset-inline-end) auto var(--toggle--dot-inset-inline-start);
93
+ inline-size: var(--toggle--dot-size);
94
+ block-size: var(--toggle--dot-size);
95
+ border-radius: var(--pill-border-radius);
66
96
 
67
- position: absolute;
68
- inset: 0 auto auto 0;
69
- inline-size: 1rem;
70
- block-size: 1rem;
71
- border-radius: 1rem;
72
- // background: yellow;
97
+ backdrop-filter: var(--liquid-glass-backdrop-filter);
73
98
 
74
- backdrop-filter: var(--liquid-glass-backdrop-filter);
99
+ box-shadow: var(--liquid-glass-box-shadow);
75
100
 
76
- box-shadow: var(--liquid-glass-box-shadow);
101
+ transition:
102
+ inset-inline-start var(--toggle--dot-transition-duration) ease-out,
103
+ inset-inline-end var(--toggle--dot-transition-duration) ease-out,
104
+ transform var(--toggle--dot-transition-duration) ease-out,
105
+ scale var(--toggle--dot-press-duration) ease-out;
106
+
107
+ transform: translateX(var(--toggle--dot-x));
108
+ scale: var(--toggle--dot-scale,);
109
+
110
+ transform-origin: center;
111
+ }
77
112
  }
78
113
  }
79
114
 
@@ -85,7 +120,10 @@
85
120
 
86
121
  &[aria-checked='true'] {
87
122
  :where(&) {
88
- --toggle--trigger-bg: green;
123
+ --toggle--track-bg: green;
124
+
125
+ --toggle--dot-inset-inline-start: calc(100% - var(--toggle--dot-size));
126
+ --toggle--dot-inset-inline-end: 0;
89
127
  }
90
128
  }
91
129
 
@@ -2,6 +2,13 @@
2
2
  @use '../mixins';
3
3
 
4
4
  @layer #{vars.$final-layer} {
5
+ toggle-view {
6
+ :where(&[touch-glass]) {
7
+ --toggle--dot-scale: 1.22;
8
+ --toggle--dot-press-duration: 100ms;
9
+ }
10
+ }
11
+
5
12
  @media (pointer: coarse) {
6
13
  [is='glass-button'],
7
14
  [is='glass-prominent-button'] {
@@ -882,9 +882,6 @@
882
882
  "name": "is-on",
883
883
  "description": "Value Type: boolean"
884
884
  },
885
- {
886
- "name": "keyboard-type"
887
- },
888
885
  {
889
886
  "name": "required",
890
887
  "description": "Value Type: boolean"
@@ -900,7 +897,7 @@
900
897
  "description": "Value Type: boolean"
901
898
  }
902
899
  ],
903
- "description": "A control that switches between on and off states.\n---\n\n```ts\ninterface ToggleViewSignature {\n Attributes: {\n label?: string\n name?: string\n value?: string\n 'is-on'?: boolean\n 'keyboard-type'?: string\n required?: boolean\n disabled?: boolean\n }\n\n Slots: {\n label: HTMLElement[]\n 'validity-options': HTMLOptionElement[]\n }\n}\n\nclass ToggleView extends HTMLElement<ToggleViewSignature> {\n static formAssociated = true\n\n readonly toggleStyle: 'button' | 'switch'\n isOn: boolean\n readonly name: string // Form participation property\n value: string | null\n\n setValidity(): void\n setCustomValidity(): void\n}\n\ninterface GlobalEventMap<\n Targets = HTMLElementEventMap | DocumentEventMap | WindowEventMap,\n> {\n 'toggle:change': CustomEvent // User toggled the control\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'toggle-view': ToggleView // <toggle-view></toggle-view>\n }\n}\n\n```\n"
900
+ "description": "A control that switches between on and off states.\n---\n\n```ts\ninterface ToggleViewSignature {\n Attributes: {\n label?: string\n name?: string\n value?: string\n 'is-on'?: boolean\n required?: boolean\n disabled?: boolean\n }\n\n Slots: {\n label: HTMLElement[]\n 'validity-options': HTMLOptionElement[]\n }\n}\n\nclass ToggleView extends HTMLElement<ToggleViewSignature> {\n static formAssociated = true\n\n readonly toggleStyle: 'button' | 'switch'\n isOn: boolean\n readonly name: string // Form participation property\n value: string | null\n\n setValidity(): void\n setCustomValidity(): void\n}\n\ninterface GlobalEventMap<\n Targets = HTMLElementEventMap | DocumentEventMap | WindowEventMap,\n> {\n 'toggle:change': CustomEvent // User toggled the control\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'toggle-view': ToggleView // <toggle-view></toggle-view>\n }\n}\n\n```\n"
904
901
  },
905
902
  {
906
903
  "name": "tool-bar-item-group",