@vollowx/seele 0.9.1 → 0.10.0

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.
@@ -38,6 +38,13 @@
38
38
  "default": "false",
39
39
  "attribute": "error",
40
40
  "reflects": true
41
+ },
42
+ {
43
+ "kind": "field",
44
+ "name": "$ripple",
45
+ "type": {
46
+ "text": "M3Ripple"
47
+ }
41
48
  }
42
49
  ],
43
50
  "attributes": [
@@ -120,7 +127,7 @@
120
127
  "type": {
121
128
  "text": "| 'surface'\r\n | 'primary-container'\r\n | 'secondary-container'\r\n | 'tertiary-container'\r\n | 'primary'\r\n | 'secondary'\r\n | 'tertiary'"
122
129
  },
123
- "default": "'surface'",
130
+ "default": "'primary'",
124
131
  "attribute": "color",
125
132
  "reflects": true
126
133
  }
@@ -139,7 +146,7 @@
139
146
  "type": {
140
147
  "text": "| 'surface'\r\n | 'primary-container'\r\n | 'secondary-container'\r\n | 'tertiary-container'\r\n | 'primary'\r\n | 'secondary'\r\n | 'tertiary'"
141
148
  },
142
- "default": "'surface'",
149
+ "default": "'primary'",
143
150
  "fieldName": "color"
144
151
  }
145
152
  ],
@@ -850,33 +857,27 @@
850
857
  },
851
858
  {
852
859
  "kind": "field",
853
- "name": "#handleKeyDown",
854
- "privacy": "private"
860
+ "name": "handleKeyDown"
855
861
  },
856
862
  {
857
863
  "kind": "field",
858
- "name": "#handleKeyUp",
859
- "privacy": "private"
864
+ "name": "handleKeyUp"
860
865
  },
861
866
  {
862
867
  "kind": "field",
863
- "name": "#handlePointerEnter",
864
- "privacy": "private"
868
+ "name": "handlePointerEnter"
865
869
  },
866
870
  {
867
871
  "kind": "field",
868
- "name": "#handlePointerLeave",
869
- "privacy": "private"
872
+ "name": "handlePointerLeave"
870
873
  },
871
874
  {
872
875
  "kind": "field",
873
- "name": "#handlePointerDown",
874
- "privacy": "private"
876
+ "name": "handlePointerDown"
875
877
  },
876
878
  {
877
879
  "kind": "field",
878
- "name": "#handlePointerUp",
879
- "privacy": "private"
880
+ "name": "handlePointerUp"
880
881
  },
881
882
  {
882
883
  "kind": "method",
@@ -900,8 +901,7 @@
900
901
  },
901
902
  {
902
903
  "kind": "method",
903
- "name": "#calculateRipple",
904
- "privacy": "private",
904
+ "name": "calculateRipple",
905
905
  "parameters": [
906
906
  {
907
907
  "name": "e",
@@ -1013,6 +1013,74 @@
1013
1013
  }
1014
1014
  ]
1015
1015
  },
1016
+ {
1017
+ "kind": "javascript-module",
1018
+ "path": "src/m3/slider.ts",
1019
+ "declarations": [
1020
+ {
1021
+ "kind": "class",
1022
+ "description": "",
1023
+ "name": "M3Slider",
1024
+ "members": [
1025
+ {
1026
+ "kind": "method",
1027
+ "name": "renderTrack",
1028
+ "privacy": "private"
1029
+ },
1030
+ {
1031
+ "kind": "method",
1032
+ "name": "renderLabel",
1033
+ "privacy": "private",
1034
+ "parameters": [
1035
+ {
1036
+ "name": "value",
1037
+ "type": {
1038
+ "text": "string"
1039
+ }
1040
+ }
1041
+ ]
1042
+ },
1043
+ {
1044
+ "kind": "method",
1045
+ "name": "renderHandle",
1046
+ "privacy": "private",
1047
+ "parameters": [
1048
+ {
1049
+ "name": "{ start, label }",
1050
+ "type": {
1051
+ "text": "{ start: boolean; label: string }"
1052
+ }
1053
+ }
1054
+ ]
1055
+ }
1056
+ ],
1057
+ "superclass": {
1058
+ "name": "Slider",
1059
+ "module": "/src/base/slider.js"
1060
+ },
1061
+ "tagName": "md-slider",
1062
+ "customElement": true
1063
+ }
1064
+ ],
1065
+ "exports": [
1066
+ {
1067
+ "kind": "js",
1068
+ "name": "M3Slider",
1069
+ "declaration": {
1070
+ "name": "M3Slider",
1071
+ "module": "src/m3/slider.ts"
1072
+ }
1073
+ },
1074
+ {
1075
+ "kind": "custom-element-definition",
1076
+ "name": "md-slider",
1077
+ "declaration": {
1078
+ "name": "M3Slider",
1079
+ "module": "src/m3/slider.ts"
1080
+ }
1081
+ }
1082
+ ]
1083
+ },
1016
1084
  {
1017
1085
  "kind": "javascript-module",
1018
1086
  "path": "src/m3/switch.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vollowx/seele",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Standard Extensible Elements. A web components library that can be styled and extended freely, pre-providing components in Material Design 3.",
5
5
  "author": "vollowx",
6
6
  "license": "MIT",
package/src/all.js CHANGED
@@ -14,6 +14,7 @@ export { M3Menu } from './m3/menu.js';
14
14
  export { M3MenuItem } from './m3/menu-item.js';
15
15
  export { M3Option } from './m3/option.js';
16
16
  export { M3Ripple } from './m3/ripple.js';
17
+ export { M3Slider } from './m3/slider.js';
17
18
  export { M3Switch } from './m3/switch.js';
18
19
  export { M3Tooltip } from './m3/tooltip.js';
19
20
  export { M3Toolbar } from './m3/toolbar.js';
@@ -0,0 +1,444 @@
1
+ import { __decorate } from "tslib";
2
+ import { LitElement, html, nothing } from 'lit';
3
+ import { property, query, state } from 'lit/decorators.js';
4
+ import { classMap } from 'lit/directives/class-map.js';
5
+ import { FormAssociated } from './mixins/form-associated.js';
6
+ import { InternalsAttached, internals } from './mixins/internals-attached.js';
7
+ const Base = FormAssociated(InternalsAttached(LitElement));
8
+ export class Slider extends Base {
9
+ static { this.shadowRootOptions = {
10
+ ...LitElement.shadowRootOptions,
11
+ delegatesFocus: true,
12
+ }; }
13
+ get nameStart() {
14
+ return this.getAttribute('name-start') ?? this.name ?? '';
15
+ }
16
+ set nameStart(name) {
17
+ this.setAttribute('name-start', name);
18
+ }
19
+ get nameEnd() {
20
+ return this.getAttribute('name-end') ?? this.nameStart;
21
+ }
22
+ set nameEnd(name) {
23
+ this.setAttribute('name-end', name);
24
+ }
25
+ get renderAriaLabelStart() {
26
+ return (this.ariaLabelStart ||
27
+ (this.ariaLabel && `${this.ariaLabel} start`) ||
28
+ this.valueLabelStart ||
29
+ String(this.valueStart));
30
+ }
31
+ get renderAriaValueTextStart() {
32
+ return (this.ariaValueTextStart || this.valueLabelStart || String(this.valueStart));
33
+ }
34
+ get renderAriaLabelEnd() {
35
+ if (this.range) {
36
+ return (this.ariaLabelEnd ||
37
+ (this.ariaLabel && `${this.ariaLabel} end`) ||
38
+ this.valueLabelEnd ||
39
+ String(this.valueEnd));
40
+ }
41
+ return this.ariaLabel || this.valueLabel || String(this.value);
42
+ }
43
+ get renderAriaValueTextEnd() {
44
+ if (this.range) {
45
+ return (this.ariaValueTextEnd || this.valueLabelEnd || String(this.valueEnd));
46
+ }
47
+ return this.ariaValueText || this.valueLabel || String(this.value);
48
+ }
49
+ renderInput({ start, value, ariaLabel, ariaValueText, ariaMin, ariaMax, }) {
50
+ const name = start ? `start` : `end`;
51
+ return html `<input
52
+ type="range"
53
+ class="${classMap({
54
+ start,
55
+ end: !start,
56
+ })}"
57
+ @focus=${this.handleFocus}
58
+ @pointerdown=${this.handleDown}
59
+ @pointerup=${this.handleUp}
60
+ @keydown=${this.handleKeydown}
61
+ @keyup=${this.handleKeyup}
62
+ @input=${this.handleInput}
63
+ @change=${this.handleChange}
64
+ id=${name}
65
+ .disabled=${this.disabled}
66
+ .min=${String(this.min)}
67
+ aria-valuemin=${ariaMin}
68
+ .max=${String(this.max)}
69
+ aria-valuemax=${ariaMax}
70
+ .step=${String(this.step)}
71
+ .value=${String(value)}
72
+ .tabIndex=${start ? 1 : 0}
73
+ aria-label=${ariaLabel || nothing}
74
+ aria-valuetext=${ariaValueText}
75
+ />`;
76
+ }
77
+ constructor() {
78
+ super();
79
+ this.min = 0;
80
+ this.max = 100;
81
+ this.valueLabel = '';
82
+ this.valueLabelStart = '';
83
+ this.valueLabelEnd = '';
84
+ this.ariaLabelStart = '';
85
+ this.ariaValueTextStart = '';
86
+ this.ariaLabelEnd = '';
87
+ this.ariaValueTextEnd = '';
88
+ this.step = 1;
89
+ this.ticks = false;
90
+ this.labeled = false;
91
+ this.range = false;
92
+ this.startOnTop = false;
93
+ this.handlesOverlapping = false;
94
+ this.isRedispatchingEvent = false;
95
+ // @TODO: Delegate ARIA attributes to inputs, automatically convert from
96
+ // aria-* to data-aria-*.
97
+ this.ariaLabel = null;
98
+ this.ariaValueText = null;
99
+ // Activation click handling usually goes here if needed
100
+ this.addEventListener('click', (event) => {
101
+ if (!isActivationClick(event) || !this.inputEnd) {
102
+ return;
103
+ }
104
+ this.focus();
105
+ dispatchActivationClick(this.inputEnd);
106
+ });
107
+ }
108
+ focus() {
109
+ this.inputEnd?.focus();
110
+ }
111
+ willUpdate(changed) {
112
+ this.renderValueStart = changed.has('valueStart')
113
+ ? this.valueStart
114
+ : this.inputStart?.valueAsNumber;
115
+ const endValueChanged = (changed.has('valueEnd') && this.range) || changed.has('value');
116
+ this.renderValueEnd = endValueChanged
117
+ ? this.range
118
+ ? this.valueEnd
119
+ : this.value
120
+ : this.inputEnd?.valueAsNumber;
121
+ }
122
+ updated(changed) {
123
+ if (this.range) {
124
+ this.renderValueStart = this.inputStart.valueAsNumber;
125
+ }
126
+ this.renderValueEnd = this.inputEnd.valueAsNumber;
127
+ if (this.range) {
128
+ const segment = (this.max - this.min) / 3;
129
+ if (this.valueStart === undefined) {
130
+ this.inputStart.valueAsNumber = this.min + segment;
131
+ const v = this.inputStart.valueAsNumber;
132
+ this.valueStart = this.renderValueStart = v;
133
+ }
134
+ if (this.valueEnd === undefined) {
135
+ this.inputEnd.valueAsNumber = this.min + 2 * segment;
136
+ const v = this.inputEnd.valueAsNumber;
137
+ this.valueEnd = this.renderValueEnd = v;
138
+ }
139
+ }
140
+ else {
141
+ this.value ??= this.renderValueEnd;
142
+ }
143
+ if (changed.has('range') ||
144
+ changed.has('renderValueStart') ||
145
+ changed.has('renderValueEnd') ||
146
+ this.isUpdatePending) {
147
+ const startNub = this.handleStart?.querySelector('.handleNub');
148
+ const endNub = this.handleEnd?.querySelector('.handleNub');
149
+ this.handlesOverlapping = isOverlapping(startNub, endNub);
150
+ }
151
+ this.updateFormValue();
152
+ this.performUpdate();
153
+ }
154
+ // Event Handlers - Changed to protected for subclass template usage
155
+ handleFocus(event) {
156
+ this.updateOnTop(event.target);
157
+ }
158
+ handleKeydown(event) {
159
+ this.startAction(event);
160
+ }
161
+ handleKeyup(event) {
162
+ this.finishAction(event);
163
+ }
164
+ handleDown(event) {
165
+ this.startAction(event);
166
+ }
167
+ async handleUp(event) {
168
+ if (!this.action) {
169
+ return;
170
+ }
171
+ const { target, values, flipped } = this.action;
172
+ await new Promise(requestAnimationFrame);
173
+ if (target !== undefined) {
174
+ target.focus();
175
+ if (flipped && target.valueAsNumber !== values.get(target)) {
176
+ target.dispatchEvent(new Event('change', { bubbles: true }));
177
+ }
178
+ }
179
+ this.finishAction(event);
180
+ }
181
+ handleInput(event) {
182
+ if (this.isRedispatchingEvent) {
183
+ return;
184
+ }
185
+ let stopPropagation = false;
186
+ let redispatch = false;
187
+ if (this.range) {
188
+ if (this.isActionFlipped()) {
189
+ stopPropagation = true;
190
+ redispatch = this.flipAction();
191
+ }
192
+ if (this.clampAction()) {
193
+ stopPropagation = true;
194
+ redispatch = false;
195
+ }
196
+ }
197
+ const target = event.target;
198
+ this.updateOnTop(target);
199
+ if (this.range) {
200
+ this.valueStart = this.inputStart.valueAsNumber;
201
+ this.valueEnd = this.inputEnd.valueAsNumber;
202
+ }
203
+ else {
204
+ this.value = this.inputEnd.valueAsNumber;
205
+ }
206
+ if (stopPropagation) {
207
+ event.stopPropagation();
208
+ }
209
+ if (redispatch) {
210
+ this.isRedispatchingEvent = true;
211
+ redispatchEvent(target, event);
212
+ this.isRedispatchingEvent = false;
213
+ }
214
+ }
215
+ handleChange(event) {
216
+ const changeTarget = event.target;
217
+ const { target, values } = this.action ?? {};
218
+ const squelch = target && target.valueAsNumber === values.get(changeTarget);
219
+ if (!squelch) {
220
+ redispatchEvent(this, event);
221
+ }
222
+ this.finishAction(event);
223
+ }
224
+ // Logic helpers
225
+ startAction(event) {
226
+ const target = event.target;
227
+ const fixed = target === this.inputStart ? this.inputEnd : this.inputStart;
228
+ this.action = {
229
+ canFlip: event.type === 'pointerdown',
230
+ flipped: false,
231
+ target,
232
+ fixed,
233
+ values: new Map([
234
+ [target, target.valueAsNumber],
235
+ [fixed, fixed?.valueAsNumber],
236
+ ]),
237
+ };
238
+ }
239
+ finishAction(event) {
240
+ this.action = undefined;
241
+ }
242
+ updateOnTop(input) {
243
+ this.startOnTop = input.classList.contains('start');
244
+ }
245
+ needsClamping() {
246
+ if (!this.action) {
247
+ return false;
248
+ }
249
+ const { target, fixed } = this.action;
250
+ const isStart = target === this.inputStart;
251
+ return isStart
252
+ ? target.valueAsNumber > fixed.valueAsNumber
253
+ : target.valueAsNumber < fixed.valueAsNumber;
254
+ }
255
+ isActionFlipped() {
256
+ const { action } = this;
257
+ if (!action) {
258
+ return false;
259
+ }
260
+ const { target, fixed, values } = action;
261
+ if (action.canFlip) {
262
+ const coincident = values.get(target) === values.get(fixed);
263
+ if (coincident && this.needsClamping()) {
264
+ action.canFlip = false;
265
+ action.flipped = true;
266
+ action.target = fixed;
267
+ action.fixed = target;
268
+ }
269
+ }
270
+ return action.flipped;
271
+ }
272
+ flipAction() {
273
+ if (!this.action) {
274
+ return false;
275
+ }
276
+ const { target, fixed, values } = this.action;
277
+ const changed = target.valueAsNumber !== fixed.valueAsNumber;
278
+ target.valueAsNumber = fixed.valueAsNumber;
279
+ fixed.valueAsNumber = values.get(fixed);
280
+ return changed;
281
+ }
282
+ clampAction() {
283
+ if (!this.needsClamping() || !this.action) {
284
+ return false;
285
+ }
286
+ const { target, fixed } = this.action;
287
+ target.valueAsNumber = fixed.valueAsNumber;
288
+ return true;
289
+ }
290
+ // Form associated callbacks
291
+ formResetCallback() {
292
+ if (this.range) {
293
+ const valueStart = this.getAttribute('value-start');
294
+ this.valueStart = valueStart !== null ? Number(valueStart) : undefined;
295
+ const valueEnd = this.getAttribute('value-end');
296
+ this.valueEnd = valueEnd !== null ? Number(valueEnd) : undefined;
297
+ return;
298
+ }
299
+ const value = this.getAttribute('value');
300
+ this.value = value !== null ? Number(value) : undefined;
301
+ }
302
+ formStateRestoreCallback(state) {
303
+ if (Array.isArray(state)) {
304
+ const [[, valueStart], [, valueEnd]] = state;
305
+ this.valueStart = Number(valueStart);
306
+ this.valueEnd = Number(valueEnd);
307
+ this.range = true;
308
+ return;
309
+ }
310
+ this.value = Number(state);
311
+ this.range = false;
312
+ }
313
+ // Use Internals for form value
314
+ updateFormValue() {
315
+ if (this.range) {
316
+ const data = new FormData();
317
+ data.append(this.nameStart, String(this.valueStart));
318
+ data.append(this.nameEnd, String(this.valueEnd));
319
+ this[internals].setFormValue(data);
320
+ }
321
+ else {
322
+ this[internals].setFormValue(String(this.value));
323
+ }
324
+ }
325
+ }
326
+ __decorate([
327
+ property({ type: Number })
328
+ ], Slider.prototype, "min", void 0);
329
+ __decorate([
330
+ property({ type: Number })
331
+ ], Slider.prototype, "max", void 0);
332
+ __decorate([
333
+ property({ type: Number })
334
+ ], Slider.prototype, "value", void 0);
335
+ __decorate([
336
+ property({ type: Number, attribute: 'value-start' })
337
+ ], Slider.prototype, "valueStart", void 0);
338
+ __decorate([
339
+ property({ type: Number, attribute: 'value-end' })
340
+ ], Slider.prototype, "valueEnd", void 0);
341
+ __decorate([
342
+ property({ attribute: 'value-label' })
343
+ ], Slider.prototype, "valueLabel", void 0);
344
+ __decorate([
345
+ property({ attribute: 'value-label-start' })
346
+ ], Slider.prototype, "valueLabelStart", void 0);
347
+ __decorate([
348
+ property({ attribute: 'value-label-end' })
349
+ ], Slider.prototype, "valueLabelEnd", void 0);
350
+ __decorate([
351
+ property({ attribute: 'aria-label-start' })
352
+ ], Slider.prototype, "ariaLabelStart", void 0);
353
+ __decorate([
354
+ property({ attribute: 'aria-valuetext-start' })
355
+ ], Slider.prototype, "ariaValueTextStart", void 0);
356
+ __decorate([
357
+ property({ attribute: 'aria-label-end' })
358
+ ], Slider.prototype, "ariaLabelEnd", void 0);
359
+ __decorate([
360
+ property({ attribute: 'aria-valuetext-end' })
361
+ ], Slider.prototype, "ariaValueTextEnd", void 0);
362
+ __decorate([
363
+ property({ type: Number })
364
+ ], Slider.prototype, "step", void 0);
365
+ __decorate([
366
+ property({ type: Boolean })
367
+ ], Slider.prototype, "ticks", void 0);
368
+ __decorate([
369
+ property({ type: Boolean })
370
+ ], Slider.prototype, "labeled", void 0);
371
+ __decorate([
372
+ property({ type: Boolean })
373
+ ], Slider.prototype, "range", void 0);
374
+ __decorate([
375
+ query('input.start')
376
+ ], Slider.prototype, "inputStart", void 0);
377
+ __decorate([
378
+ query('input.end')
379
+ ], Slider.prototype, "inputEnd", void 0);
380
+ __decorate([
381
+ query('.handle.start')
382
+ ], Slider.prototype, "handleStart", void 0);
383
+ __decorate([
384
+ query('.handle.end')
385
+ ], Slider.prototype, "handleEnd", void 0);
386
+ __decorate([
387
+ state()
388
+ ], Slider.prototype, "startOnTop", void 0);
389
+ __decorate([
390
+ state()
391
+ ], Slider.prototype, "handlesOverlapping", void 0);
392
+ __decorate([
393
+ state()
394
+ ], Slider.prototype, "renderValueStart", void 0);
395
+ __decorate([
396
+ state()
397
+ ], Slider.prototype, "renderValueEnd", void 0);
398
+ __decorate([
399
+ property({ attribute: 'data-aria-label' })
400
+ ], Slider.prototype, "ariaLabel", void 0);
401
+ __decorate([
402
+ property({ attribute: 'data-aria-valuetext' })
403
+ ], Slider.prototype, "ariaValueText", void 0);
404
+ function isOverlapping(elA, elB) {
405
+ if (!(elA && elB)) {
406
+ return false;
407
+ }
408
+ const a = elA.getBoundingClientRect();
409
+ const b = elB.getBoundingClientRect();
410
+ return !(a.top > b.bottom ||
411
+ a.right < b.left ||
412
+ a.bottom < b.top ||
413
+ a.left > b.right);
414
+ }
415
+ function isActivationClick(event) {
416
+ // Simple check for now, simplified version of material web's check
417
+ // If it's a mouse event, primary button, bubbles etc.
418
+ // Or just assume true if it reached here for now as placeholder description
419
+ // In real impl this checks if the event invalidates activation (like bubbling from a label)
420
+ return event.type === 'click';
421
+ }
422
+ function dispatchActivationClick(element) {
423
+ const event = new MouseEvent('click', {
424
+ bubbles: true,
425
+ cancelable: true,
426
+ view: window,
427
+ });
428
+ element.dispatchEvent(event);
429
+ }
430
+ function redispatchEvent(element, event) {
431
+ if (event.bubbles && (event.composed || event.defaultPrevented)) {
432
+ const copy = new event.constructor(event.type, event);
433
+ element.dispatchEvent(copy);
434
+ return true;
435
+ }
436
+ const copy = new event.constructor(event.type, {
437
+ bubbles: event.bubbles,
438
+ cancelable: event.cancelable,
439
+ composed: event.composed,
440
+ // detail: (event as any).detail,
441
+ });
442
+ element.dispatchEvent(copy);
443
+ return true;
444
+ }
@@ -1,9 +1,8 @@
1
1
  import { __decorate } from "tslib";
2
2
  import { html } from 'lit';
3
- import { customElement, property } from 'lit/decorators.js';
3
+ import { customElement, property, query } from 'lit/decorators.js';
4
4
  import { Checkbox } from '../base/checkbox.js';
5
5
  import './focus-ring.js';
6
- import './ripple.js';
7
6
  import { checkboxStyles } from './checkbox-styles.css.js';
8
7
  import { targetStyles } from './target-styles.css.js';
9
8
  /**
@@ -36,10 +35,20 @@ let M3Checkbox = class M3Checkbox extends Checkbox {
36
35
  </svg>
37
36
  `;
38
37
  }
38
+ connectedCallback() {
39
+ super.connectedCallback();
40
+ // SSR'd <md-checkbox> components don't have their labels set up on time
41
+ setTimeout(() => {
42
+ this.$ripple.attach(this);
43
+ });
44
+ }
39
45
  };
40
46
  __decorate([
41
47
  property({ type: Boolean, reflect: true })
42
48
  ], M3Checkbox.prototype, "error", void 0);
49
+ __decorate([
50
+ query('md-ripple')
51
+ ], M3Checkbox.prototype, "$ripple", void 0);
43
52
  M3Checkbox = __decorate([
44
53
  customElement('md-checkbox')
45
54
  ], M3Checkbox);
@@ -1,2 +1,2 @@
1
1
  import { css } from 'lit';
2
- export const fabStyles = css `:host{--md-focus-ring-shape:var(--_border-radius);--_text-color:var(--md-sys-color-primary);--_background-color:var(--md-sys-color-surface-container-high);--_size:56px;--_border-radius:16px;--_icon-size:24px;background-color:color-mix(in srgb,var(--_background-color)var(--_background-opacity,100%),transparent);border-radius:var(--_border-radius);box-shadow:var(--md-sys-elevation-shadow-3);box-sizing:border-box;color:color-mix(in srgb,var(--_text-color)var(--_text-opacity,100%),transparent);cursor:pointer;font:var(--md-sys-typography-label-large);height:var(--_size);min-width:var(--_size);padding-inline:calc((var(--_size) - var(--_icon-size))/2);-webkit-tap-highlight-color:transparent;transition:box-shadow var(--md-sys-motion-std-effects-slow-duration)var(--md-sys-motion-std-effects-slow);-webkit-user-select:none;user-select:none;vertical-align:middle;outline:0;justify-content:center;align-items:center;gap:8px;display:inline-flex;position:relative}:host([lowered]){--_background-color:var(--md-sys-color-surface-container-low);box-shadow:var(--md-sys-elevation-shadow-1)}:host([size=medium]){--_size:80px;--_border-radius:20px;--_icon-size:28px}:host([size=large]){--_size:96px;--_border-radius:28px;--_icon-size:36px}:host([color=primary-container]){--_text-color:var(--md-sys-color-on-primary-container);--_background-color:var(--md-sys-color-primary-container)}:host([color=secondary-container]){--_text-color:var(--md-sys-color-on-secondary-container);--_background-color:var(--md-sys-color-secondary-container)}:host([color=tertiary-container]){--_text-color:var(--md-sys-color-on-tertiary-container);--_background-color:var(--md-sys-color-tertiary-container)}:host([color=primary]){--_text-color:var(--md-sys-color-on-primary);--_background-color:var(--md-sys-color-primary)}:host([color=secondary]){--_text-color:var(--md-sys-color-on-secondary);--_background-color:var(--md-sys-color-secondary)}:host([color=tertiary]){--_text-color:var(--md-sys-color-on-tertiary);--_background-color:var(--md-sys-color-tertiary)}:host(:disabled){--_background-color:var(--md-sys-color-on-surface);--_background-opacity:12%;--_text-color:var(--md-sys-color-on-surface);--_text-opacity:38%;box-shadow:none;cursor:default;pointer-events:none}@media (hover:hover) and (pointer:fine){:host(:hover:not(:active)){box-shadow:var(--md-sys-elevation-shadow-4)}}::slotted(:not([slot=label])){fill:currentColor;block-size:1em;font-size:var(--_icon-size);inline-size:1em;display:inline-flex}::slotted([slot=label]){margin-inline:4px}`;
2
+ export const fabStyles = css `:host{--md-focus-ring-shape:var(--_border-radius);--_text-color:var(--md-sys-color-on-primary);--_background-color:var(--md-sys-color-primary);--_size:56px;--_border-radius:16px;--_icon-size:24px;background-color:color-mix(in srgb,var(--_background-color)var(--_background-opacity,100%),transparent);border-radius:var(--_border-radius);box-shadow:var(--md-sys-elevation-shadow-3);box-sizing:border-box;color:color-mix(in srgb,var(--_text-color)var(--_text-opacity,100%),transparent);cursor:pointer;font:var(--md-sys-typography-label-large);height:var(--_size);min-width:var(--_size);padding-inline:calc((var(--_size) - var(--_icon-size))/2);-webkit-tap-highlight-color:transparent;transition:box-shadow var(--md-sys-motion-std-effects-slow-duration)var(--md-sys-motion-std-effects-slow);-webkit-user-select:none;user-select:none;vertical-align:middle;outline:0;justify-content:center;align-items:center;gap:8px;display:inline-flex;position:relative}:host([lowered]){--_background-color:var(--md-sys-color-surface-container-low);box-shadow:var(--md-sys-elevation-shadow-1)}:host([size=medium]){--_size:80px;--_border-radius:20px;--_icon-size:28px}:host([size=large]){--_size:96px;--_border-radius:28px;--_icon-size:36px}:host([color=primary-container]){--_text-color:var(--md-sys-color-on-primary-container);--_background-color:var(--md-sys-color-primary-container)}:host([color=secondary-container]){--_text-color:var(--md-sys-color-on-secondary-container);--_background-color:var(--md-sys-color-secondary-container)}:host([color=tertiary-container]){--_text-color:var(--md-sys-color-on-tertiary-container);--_background-color:var(--md-sys-color-tertiary-container)}:host([color=secondary]){--_text-color:var(--md-sys-color-on-secondary);--_background-color:var(--md-sys-color-secondary)}:host([color=tertiary]){--_text-color:var(--md-sys-color-on-tertiary);--_background-color:var(--md-sys-color-tertiary)}:host([color=surface]){--_text-color:var(--md-sys-color-primary);--_background-color:var(--md-sys-color-surface-container-high)}:host(:disabled){--_background-color:var(--md-sys-color-on-surface);--_background-opacity:12%;--_text-color:var(--md-sys-color-on-surface);--_text-opacity:38%;box-shadow:none;cursor:default;pointer-events:none}@media (hover:hover) and (pointer:fine){:host(:hover:not(:active)){box-shadow:var(--md-sys-elevation-shadow-4)}}::slotted(:not([slot=label])){fill:currentColor;block-size:1em;font-size:var(--_icon-size);inline-size:1em;display:inline-flex}::slotted([slot=label]){margin-inline:4px}`;
package/src/m3/fab.js CHANGED
@@ -19,7 +19,7 @@ let M3FAB = class M3FAB extends Button {
19
19
  constructor() {
20
20
  super(...arguments);
21
21
  this.size = 'default';
22
- this.color = 'surface';
22
+ this.color = 'primary';
23
23
  }
24
24
  static { this.styles = [...super.styles, targetStyles, fabStyles]; }
25
25
  render() {
package/src/m3/ripple.js CHANGED
@@ -27,7 +27,7 @@ let M3Ripple = class M3Ripple extends Attachable(InternalsAttached(LitElement))
27
27
  this.#spaceKeyDown = false;
28
28
  this.#pointerDown = false;
29
29
  this.#lastTime = 0;
30
- this.#handleKeyDown = (e) => {
30
+ this.handleKeyDown = (e) => {
31
31
  if ((e.key === 'Enter' && this.enterBehavior === 'always') ||
32
32
  (e.key === ' ' && this.spaceBehavior === 'always')) {
33
33
  this.addRipple();
@@ -39,42 +39,42 @@ let M3Ripple = class M3Ripple extends Attachable(InternalsAttached(LitElement))
39
39
  this.#spaceKeyDown = true;
40
40
  }
41
41
  };
42
- this.#handleKeyUp = (e) => {
42
+ this.handleKeyUp = (e) => {
43
43
  if (e.key === ' ' && this.spaceBehavior === 'once') {
44
44
  this.#spaceKeyDown = false;
45
45
  this.removeRippleAll();
46
46
  }
47
47
  };
48
- this.#handlePointerEnter = (e) => {
48
+ this.handlePointerEnter = (e) => {
49
49
  if (e.pointerType === 'touch')
50
50
  return;
51
51
  this[internals].states.add('hover');
52
52
  if (this.#pointerDown && this.clickBehavior === 'always')
53
53
  this.addRipple(e);
54
54
  };
55
- this.#handlePointerLeave = () => {
55
+ this.handlePointerLeave = () => {
56
56
  this[internals].states.delete('hover');
57
57
  if (this.#pointerDown && this.clickBehavior === 'always')
58
58
  this.removeRippleAll();
59
59
  };
60
- this.#handlePointerDown = (e) => {
60
+ this.handlePointerDown = (e) => {
61
61
  if (e.pointerType === 'mouse')
62
62
  this.#pointerDown = true;
63
- document.addEventListener('pointerup', this.#handlePointerUp);
64
- document.addEventListener('touchcancel', this.#handlePointerUp);
65
- document.addEventListener('touchend', this.#handlePointerUp);
66
- document.addEventListener('touchmove', this.#handlePointerUp);
63
+ document.addEventListener('pointerup', this.handlePointerUp);
64
+ document.addEventListener('touchcancel', this.handlePointerUp);
65
+ document.addEventListener('touchend', this.handlePointerUp);
66
+ document.addEventListener('touchmove', this.handlePointerUp);
67
67
  if (e.button === 2)
68
68
  return;
69
69
  if (this.clickBehavior === 'always')
70
70
  this.addRipple(e);
71
71
  };
72
- this.#handlePointerUp = () => {
72
+ this.handlePointerUp = () => {
73
73
  this.#pointerDown = false;
74
- document.removeEventListener('pointerup', this.#handlePointerUp);
75
- document.removeEventListener('touchcancel', this.#handlePointerUp);
76
- document.removeEventListener('touchend', this.#handlePointerUp);
77
- document.removeEventListener('touchmove', this.#handlePointerUp);
74
+ document.removeEventListener('pointerup', this.handlePointerUp);
75
+ document.removeEventListener('touchcancel', this.handlePointerUp);
76
+ document.removeEventListener('touchend', this.handlePointerUp);
77
+ document.removeEventListener('touchmove', this.handlePointerUp);
78
78
  this.removeRippleAll();
79
79
  };
80
80
  this[internals].ariaHidden = 'true';
@@ -82,19 +82,13 @@ let M3Ripple = class M3Ripple extends Attachable(InternalsAttached(LitElement))
82
82
  #spaceKeyDown;
83
83
  #pointerDown;
84
84
  #lastTime;
85
- #handleKeyDown;
86
- #handleKeyUp;
87
- #handlePointerEnter;
88
- #handlePointerLeave;
89
- #handlePointerDown;
90
- #handlePointerUp;
91
85
  handleControlChange(prev = null, next = null) {
92
86
  const eventHandlers = {
93
- keydown: this.#handleKeyDown,
94
- keyup: this.#handleKeyUp,
95
- pointerenter: this.#handlePointerEnter,
96
- pointerleave: this.#handlePointerLeave,
97
- pointerdown: this.#handlePointerDown,
87
+ keydown: this.handleKeyDown,
88
+ keyup: this.handleKeyUp,
89
+ pointerenter: this.handlePointerEnter,
90
+ pointerleave: this.handlePointerLeave,
91
+ pointerdown: this.handlePointerDown,
98
92
  };
99
93
  Object.keys(eventHandlers).forEach((eventName) => {
100
94
  // @ts-ignore
@@ -116,7 +110,7 @@ let M3Ripple = class M3Ripple extends Attachable(InternalsAttached(LitElement))
116
110
  }
117
111
  });
118
112
  }
119
- #calculateRipple(e = null) {
113
+ calculateRipple(e = null) {
120
114
  const containerRect = this.getBoundingClientRect();
121
115
  const containerMiddle = {
122
116
  x: containerRect.width / 2,
@@ -139,7 +133,7 @@ let M3Ripple = class M3Ripple extends Attachable(InternalsAttached(LitElement))
139
133
  return { startCenter, endCenter, radius };
140
134
  }
141
135
  addRipple(e = null) {
142
- const { startCenter, endCenter, radius } = this.#calculateRipple(e);
136
+ const { startCenter, endCenter, radius } = this.calculateRipple(e);
143
137
  const diameter = radius * 2 + 'px';
144
138
  const translateStart = `${startCenter.x - radius}px ${startCenter.y - radius}px`;
145
139
  const translateEnd = `${endCenter.x - radius}px ${endCenter.y - radius}px`;
@@ -0,0 +1,2 @@
1
+ import { css } from 'lit';
2
+ export const sliderStyles = css `:host{--_target-size:48px;--_half-target:calc(var(--_target-size)/2);--_handle-width-start:var(--md-slider-handle-width,4px);--_handle-width-end:var(--md-slider-handle-width,4px);--_handle-height:var(--md-slider-handle-height,44px);--_handle-shape:var(--md-slider-handle-shape,20px);--_handle-margin:6px;--_tick-size:var(--md-slider-tick-size,2px);--_track-shape:8px;--_track-height:var(--md-slider-track-height,16px);vertical-align:middle;min-inline-size:200px;display:inline-flex}md-focus-ring{width:16px;height:56px;inset:unset}:host([disabled]){opacity:.38}:host([disabled]) input{cursor:default;pointer-events:none}.container{--_start-px:calc(100%*var(--_start));--_end-px:calc(100%*var(--_end));--_inv-start-px:calc(100%*(1 - var(--_start)));--_inv-end-px:calc(100%*(1 - var(--_end)));--_mask-offset-start:calc(var(--_handle-width-start)/2 + var(--_handle-margin));--_mask-offset-end:calc(var(--_handle-width-end)/2 + var(--_handle-margin));--_cut-start:calc(var(--_start-px) + var(--_mask-offset-start));--_cut-end:calc(var(--_inv-end-px) + var(--_mask-offset-end));--_cut-inv-start:calc(var(--_inv-start-px) + var(--_mask-offset-start));--_cut-inv-end:calc(var(--_end-px) + var(--_mask-offset-end));block-size:var(--_target-size);pointer-events:none;touch-action:none;user-select:none;flex:1;align-items:center;display:flex;position:relative}.track,.tickmarks{block-size:var(--_track-height);align-items:center;display:flex;position:absolute;inset-inline:0}.track div,.tickmarks div{position:absolute;inset:0}.track div{border-radius:var(--_track-shape)}.track .inactive-before,.track .inactive-after{background:var(--md-sys-color-secondary-container)}.track .active{background:var(--md-sys-color-primary)}:host([disabled]) .track .inactive-before,:host([disabled]) .track .inactive-after{background:var(--md-sys-color-on-surface);opacity:.315789}:host([disabled]) .track .active{background:var(--md-sys-color-on-surface)}.tickmarks div{--_tick-color:var(--md-sys-color-on-secondary-container);background-size:calc((100% - var(--_tick-size)*2)/var(--_ticks))100%;background-image:radial-gradient(circle at var(--_tick-size)center,var(--_tick-color)0,var(--_tick-color)calc(var(--_tick-size)/2),transparent calc(var(--_tick-size)/2))}.tickmarks .active{--_tick-color:var(--md-sys-color-on-primary)}:host([disabled]) .tickmarks div{--_tick-color:var(--md-sys-color-on-surface)}:host([disabled]) .tickmarks .active{--_tick-color:var(--md-sys-color-inverse-on-surface)}.inactive-before{clip-path:inset(0 var(--_cut-inv-start)0 0 round 2px)}.inactive-after{clip-path:inset(0 0 0 var(--_cut-inv-end)round 2px)}.active{clip-path:inset(0 var(--_cut-end)0 0 round 2px)}.ranged .active{clip-path:inset(0 var(--_cut-end)0 var(--_cut-start)round 2px)}:host(:dir(rtl)) .inactive-before{clip-path:inset(0 0 0 var(--_cut-inv-start)round 2px)}:host(:dir(rtl)) .inactive-after{clip-path:inset(0 var(--_cut-inv-end)0 0 round 2px)}:host(:dir(rtl)) .active{clip-path:inset(0 0 0 var(--_cut-end)round 2px)}:host(:dir(rtl)) .ranged .active{clip-path:inset(0 var(--_cut-start)0 var(--_cut-end)round 2px)}.handles{block-size:100%;inline-size:100%;inline-size:calc(var(--_end-px) - var(--_start-px));position:relative;inset-block:0;inset-inline-start:var(--_start-px)}.handle{--md-ripple-color:var(--md-sys-color-primary);block-size:var(--_target-size);inline-size:var(--_target-size);border-radius:var(--_handle-shape);place-content:center;place-items:center;display:flex;position:absolute}.handle.start{inset-inline-start:calc(0px - var(--_half-target))}.handle.end{inset-inline-end:calc(0px - var(--_half-target))}.nub{height:var(--_handle-height);width:var(--_handle-width-end);border-radius:var(--_handle-shape);background:var(--md-sys-color-primary);transition:width var(--md-sys-motion-std-effects-fast-duration)var(--md-sys-motion-std-effects-fast);position:absolute}.start .nub{width:var(--_handle-width-start)}:host([disabled]) .nub{background:var(--md-sys-color-on-surface)}.onTop.isOverlapping .nub{border:1px solid var(--md-sys-color-surface)}.label{box-sizing:border-box;color:var(--md-sys-color-inverse-on-surface);background:var(--md-sys-color-inverse-surface);font:var(--md-sys-typography-label-large);opacity:0;border-radius:9999px;place-content:center;place-items:center;min-block-size:44px;min-inline-size:48px;padding:0 16px;transition:opacity 67ms linear;display:flex;position:absolute;inset-block-end:100%;transform:translateY(-2px)}:host(:not(:disabled):hover) .label,:host(:not(:disabled)) :where(:has(input:active)) .label,:host(:not(:disabled)) :where(:has(input:focus-visible)) .label{opacity:1}input[type=range]{opacity:0;-webkit-tap-highlight-color:transparent;box-sizing:border-box;cursor:pointer;pointer-events:auto;appearance:none;width:100%;height:100%;margin:0;position:absolute}input[type=range]:focus{outline:none}:host(:not([disabled])) :has(input.start:active){--_handle-width-start:2px}:host(:not([disabled])) :has(input.end:active){--_handle-width-end:2px}::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;block-size:var(--_handle-height);inline-size:var(--_handle-width-end);opacity:0;z-index:2}::-moz-range-thumb{appearance:none;block-size:var(--_handle-height);inline-size:var(--_handle-width-end);opacity:0;z-index:2;transform:scaleX(0)}input.start::-webkit-slider-thumb{--_pad:calc((var(--_target-size) - var(--_handle-width-start))/2);transform:translateX(calc(var(--_pad) - 2*var(--_start)*var(--_pad)))}input.end::-webkit-slider-thumb{--_pad:calc((var(--_target-size) - var(--_handle-width-end))/2);transform:translateX(calc(var(--_pad) - 2*var(--_end)*var(--_pad)))}input:dir(rtl)::-webkit-slider-thumb{transform:translateX(calc(-1*(var(--_pad) - 2*var(--frac)*var(--_pad))))}.ranged input{--track-len:calc(100% - var(--_target-size));--mid-frac:calc(var(--_start) + ((var(--_end) - var(--_start))/2));--clip-px:calc(var(--_half-target) + var(--track-len)*var(--mid-frac))}.ranged input.start{clip-path:inset(0 calc(100% - var(--clip-px))0 0)}.ranged input.end{clip-path:inset(0 0 0 var(--clip-px))}.ranged input.start:dir(rtl){clip-path:inset(0 0 0 calc(100% - var(--clip-px)))}.ranged input.end:dir(rtl){clip-path:inset(0 var(--clip-px)0 0)}.onTop{z-index:1}`;
@@ -0,0 +1,111 @@
1
+ import { __decorate } from "tslib";
2
+ import { html, nothing } from 'lit';
3
+ import { customElement } from 'lit/decorators.js';
4
+ import { classMap } from 'lit/directives/class-map.js';
5
+ import { styleMap } from 'lit/directives/style-map.js';
6
+ import { when } from 'lit/directives/when.js';
7
+ import { Slider } from '../base/slider.js';
8
+ import './focus-ring.js';
9
+ import { sliderStyles } from './slider-styles.css.js';
10
+ /**
11
+ * @TODO: Add size variants
12
+ * @TODO: Add orientation: vertical
13
+ */
14
+ let M3Slider = class M3Slider extends Slider {
15
+ static { this.styles = [sliderStyles]; }
16
+ render() {
17
+ const step = this.step === 0 ? 1 : this.step;
18
+ const range = Math.max(this.max - this.min, step);
19
+ const startFraction = this.range
20
+ ? ((this.renderValueStart ?? this.min) - this.min) / range
21
+ : 0;
22
+ const endFraction = ((this.renderValueEnd ?? this.min) - this.min) / range;
23
+ const containerStyles = {
24
+ '--_start': String(startFraction),
25
+ '--_end': String(endFraction),
26
+ '--_ticks': String(range / step),
27
+ };
28
+ const containerClasses = { ranged: this.range };
29
+ const labelStart = this.valueLabelStart || String(this.renderValueStart);
30
+ const labelEnd = (this.range ? this.valueLabelEnd : this.valueLabel) ||
31
+ String(this.renderValueEnd);
32
+ const inputStartProps = {
33
+ start: true,
34
+ value: this.renderValueStart,
35
+ ariaLabel: this.renderAriaLabelStart,
36
+ ariaValueText: this.renderAriaValueTextStart,
37
+ ariaMin: this.min,
38
+ ariaMax: this.valueEnd ?? this.max,
39
+ };
40
+ const inputEndProps = {
41
+ start: false,
42
+ value: this.renderValueEnd,
43
+ ariaLabel: this.renderAriaLabelEnd,
44
+ ariaValueText: this.renderAriaValueTextEnd,
45
+ ariaMin: this.range ? (this.valueStart ?? this.min) : this.min,
46
+ ariaMax: this.max,
47
+ };
48
+ const handleStartProps = {
49
+ start: true,
50
+ label: labelStart,
51
+ };
52
+ const handleEndProps = {
53
+ start: false,
54
+ label: labelEnd,
55
+ };
56
+ return html ` <div
57
+ class="container ${classMap(containerClasses)}"
58
+ style=${styleMap(containerStyles)}
59
+ >
60
+ ${when(this.range, () => this.renderInput(inputStartProps))}
61
+ ${this.renderInput(inputEndProps)} ${this.renderTrack()}
62
+ <div class="handles">
63
+ ${when(this.range, () => this.renderHandle(handleStartProps))}
64
+ ${this.renderHandle(handleEndProps)}
65
+ </div>
66
+ </div>`;
67
+ }
68
+ renderTrack() {
69
+ return html `
70
+ <div class="track">
71
+ <div class="inactive-before"></div>
72
+ <div class="active"></div>
73
+ <div class="inactive-after"></div>
74
+ </div>
75
+ ${this.ticks
76
+ ? html `
77
+ <div class="tickmarks">
78
+ <div class="inactive-before"></div>
79
+ <div class="active"></div>
80
+ <div class="inactive-after"></div>
81
+ </div>
82
+ `
83
+ : nothing}
84
+ `;
85
+ }
86
+ renderLabel(value) {
87
+ return html `<div class="label" aria-hidden="true">
88
+ <span part="label">${value}</span>
89
+ </div>`;
90
+ }
91
+ renderHandle({ start, label }) {
92
+ const onTop = !this.disabled && start === this.startOnTop;
93
+ const isOverlapping = !this.disabled && this.handlesOverlapping;
94
+ const name = start ? 'start' : 'end';
95
+ return html `<div
96
+ class="handle ${classMap({
97
+ [name]: true,
98
+ onTop,
99
+ isOverlapping,
100
+ })}"
101
+ >
102
+ <md-focus-ring part="focus-ring" for=${name}></md-focus-ring>
103
+ <div class="nub"></div>
104
+ ${when(this.labeled, () => this.renderLabel(label))}
105
+ </div>`;
106
+ }
107
+ };
108
+ M3Slider = __decorate([
109
+ customElement('md-slider')
110
+ ], M3Slider);
111
+ export { M3Slider };
package/src/m3/switch.js CHANGED
@@ -87,7 +87,7 @@ let M3Switch = class M3Switch extends Switch {
87
87
  }
88
88
  connectedCallback() {
89
89
  super.connectedCallback();
90
- this.updateComplete.then(() => {
90
+ setTimeout(() => {
91
91
  this.$ripple.attach(this);
92
92
  });
93
93
  this.addEventListener('pointerdown', this.#handlePointerDown);