@xsynaptic/sonic-ui 0.6.0 → 0.6.1

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 (59) hide show
  1. package/dist/default/elements/button.js +3 -2
  2. package/dist/default/elements/dial.js +5 -1
  3. package/dist/default/elements/envelope.js +14 -2
  4. package/dist/default/elements/meter.js +1 -1
  5. package/dist/default/elements/radio-group.js +3 -2
  6. package/dist/default/elements/readout-claim.js +0 -29
  7. package/dist/default/elements/scale.js +43 -0
  8. package/dist/default/elements/slider.js +16 -3
  9. package/dist/default/elements/sonic-element.js +2 -67
  10. package/dist/default/elements/switch.js +1 -1
  11. package/dist/default/elements/value-element.js +35 -83
  12. package/dist/default/elements/wavestrip.js +42 -4
  13. package/dist/default/elements/xy.js +2 -1
  14. package/dist/default/lib/mirror-children.js +64 -0
  15. package/dist/default/lib/owned-control.js +10 -0
  16. package/dist/default/lib/time-regions.js +4 -1
  17. package/dist/dev/elements/button.js +3 -2
  18. package/dist/dev/elements/button.js.map +1 -1
  19. package/dist/dev/elements/dial.js +5 -1
  20. package/dist/dev/elements/dial.js.map +1 -1
  21. package/dist/dev/elements/envelope.js +14 -2
  22. package/dist/dev/elements/envelope.js.map +1 -1
  23. package/dist/dev/elements/meter.js +1 -1
  24. package/dist/dev/elements/radio-group.js +3 -2
  25. package/dist/dev/elements/radio-group.js.map +1 -1
  26. package/dist/dev/elements/readout-claim.js +0 -29
  27. package/dist/dev/elements/readout-claim.js.map +1 -1
  28. package/dist/dev/elements/scale.js +44 -0
  29. package/dist/dev/elements/scale.js.map +1 -0
  30. package/dist/dev/elements/slider.d.ts +3 -0
  31. package/dist/dev/elements/slider.js +16 -3
  32. package/dist/dev/elements/slider.js.map +1 -1
  33. package/dist/dev/elements/sonic-element.d.ts +0 -8
  34. package/dist/dev/elements/sonic-element.js +3 -90
  35. package/dist/dev/elements/sonic-element.js.map +1 -1
  36. package/dist/dev/elements/switch.js +1 -1
  37. package/dist/dev/elements/value-element.d.ts +3 -5
  38. package/dist/dev/elements/value-element.js +35 -83
  39. package/dist/dev/elements/value-element.js.map +1 -1
  40. package/dist/dev/elements/wavestrip.d.ts +4 -0
  41. package/dist/dev/elements/wavestrip.js +42 -4
  42. package/dist/dev/elements/wavestrip.js.map +1 -1
  43. package/dist/dev/elements/xy.js +2 -1
  44. package/dist/dev/elements/xy.js.map +1 -1
  45. package/dist/dev/lib/mirror-children.js +80 -0
  46. package/dist/dev/lib/mirror-children.js.map +1 -0
  47. package/dist/dev/lib/owned-control.js +23 -0
  48. package/dist/dev/lib/owned-control.js.map +1 -0
  49. package/dist/dev/lib/time-regions.js +4 -1
  50. package/dist/dev/lib/time-regions.js.map +1 -1
  51. package/dist/skins/flat.css +2 -1
  52. package/dist/styles/button.css +21 -0
  53. package/dist/styles/envelope.css +30 -30
  54. package/dist/styles/led.css +18 -2
  55. package/dist/styles/material.css +72 -26
  56. package/dist/styles/panel.css +2 -1
  57. package/dist/styles/switch.css +10 -7
  58. package/dist/styles/xy.css +14 -30
  59. package/package.json +1 -1
@@ -6,10 +6,7 @@ var ReadoutClaim = class {
6
6
  return this.#isDragging || this.#keyTimer !== void 0;
7
7
  }
8
8
  #holdTimer;
9
- #hover;
10
- #isDismissed = false;
11
9
  #isDragging = false;
12
- #isEditing = false;
13
10
  #keyTimer;
14
11
  #onLapse;
15
12
  constructor(onLapse) {
@@ -27,21 +24,6 @@ var ReadoutClaim = class {
27
24
  this.#keyTimer = void 0;
28
25
  return wasTimed;
29
26
  }
30
- dismiss() {
31
- const wasHovered = this.#hover !== void 0;
32
- this.#hover = void 0;
33
- this.#isDismissed = wasHovered || this.#isDismissed;
34
- return wasHovered;
35
- }
36
- edit(isOpen) {
37
- this.#isEditing = isOpen;
38
- if (isOpen) this.#hover = void 0;
39
- }
40
- hover(value) {
41
- if (value === void 0) this.#isDismissed = false;
42
- if (this.#isDismissed || this.#isEditing || this.isRevealed) return;
43
- this.#hover = value;
44
- }
45
27
  press(isDown) {
46
28
  this.conceal("drag");
47
29
  if (!isDown) {
@@ -54,7 +36,6 @@ var ReadoutClaim = class {
54
36
  }, 250);
55
37
  }
56
38
  reveal(by) {
57
- this.#hover = void 0;
58
39
  if (by === "drag") {
59
40
  this.#isDragging = true;
60
41
  return;
@@ -65,16 +46,6 @@ var ReadoutClaim = class {
65
46
  this.#onLapse();
66
47
  }, keyRevealMs);
67
48
  }
68
- shown(current, isReadout) {
69
- if (this.#isEditing) return {
70
- isOpen: true,
71
- value: void 0
72
- };
73
- return {
74
- isOpen: (this.isRevealed || this.#hover !== void 0) && isReadout,
75
- value: this.#hover ?? current
76
- };
77
- }
78
49
  };
79
50
 
80
51
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"readout-claim.js","names":[],"sources":["../../../src/elements/readout-claim.ts"],"sourcesContent":["const keyRevealMs = 1000;\n\nexport const revealMs = 250;\n\nexport class ReadoutClaim {\n\tget isRevealed(): boolean {\n\t\treturn this.#isDragging || this.#keyTimer !== undefined;\n\t}\n\n\t#holdTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#hover: number | undefined;\n\n\t#isDismissed = false;\n\n\t#isDragging = false;\n\n\t#isEditing = false;\n\n\t#keyTimer: ReturnType<typeof setTimeout> | undefined;\n\n\treadonly #onLapse: () => void;\n\n\tconstructor(onLapse: () => void) {\n\t\tthis.#onLapse = onLapse;\n\t}\n\n\tconceal(by: 'drag' | 'keys'): boolean {\n\t\tif (by === 'drag') {\n\t\t\tconst wasDragging = this.#isDragging;\n\n\t\t\tclearTimeout(this.#holdTimer);\n\t\t\tthis.#isDragging = false;\n\n\t\t\treturn wasDragging;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\n\t\tconst wasTimed = this.#keyTimer !== undefined;\n\n\t\tthis.#keyTimer = undefined;\n\n\t\treturn wasTimed;\n\t}\n\n\t// Stays dismissed until the hover ends, or the next pointer move would bring it straight back\n\tdismiss(): boolean {\n\t\tconst wasHovered = this.#hover !== undefined;\n\n\t\tthis.#hover = undefined;\n\t\tthis.#isDismissed = wasHovered || this.#isDismissed;\n\n\t\treturn wasHovered;\n\t}\n\n\tedit(isOpen: boolean): void {\n\t\tthis.#isEditing = isOpen;\n\t\tif (isOpen) this.#hover = undefined;\n\t}\n\n\thover(value?: number): void {\n\t\tif (value === undefined) this.#isDismissed = false;\n\t\tif (this.#isDismissed || this.#isEditing || this.isRevealed) return;\n\n\t\tthis.#hover = value;\n\t}\n\n\tpress(isDown: boolean): void {\n\t\tthis.conceal('drag');\n\t\tif (!isDown) {\n\t\t\tthis.#onLapse();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#holdTimer = setTimeout(() => {\n\t\t\tthis.reveal('drag');\n\t\t\tthis.#onLapse();\n\t\t}, revealMs);\n\t}\n\n\treveal(by: 'drag' | 'keys'): void {\n\t\tthis.#hover = undefined;\n\t\tif (by === 'drag') {\n\t\t\tthis.#isDragging = true;\n\t\t\treturn;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\t\tthis.#keyTimer = setTimeout(() => {\n\t\t\tthis.#keyTimer = undefined;\n\t\t\tthis.#onLapse();\n\t\t}, keyRevealMs);\n\t}\n\n\tshown(current: number, isReadout: boolean): { isOpen: boolean; value: number | undefined } {\n\t\tif (this.#isEditing) return { isOpen: true, value: undefined };\n\n\t\tconst isValueShown = this.isRevealed || this.#hover !== undefined;\n\n\t\treturn { isOpen: isValueShown && isReadout, value: this.#hover ?? current };\n\t}\n}\n"],"mappings":";AAAA,MAAM,cAAc;AAEpB,MAAa,WAAW;AAExB,IAAa,eAAb,MAA0B;CACzB,IAAI,aAAsB;EACzB,OAAO,KAAK,eAAe,KAAK,cAAc;CAC/C;CAEA;CAEA;CAEA,eAAe;CAEf,cAAc;CAEd,aAAa;CAEb;CAEA,AAAS;CAET,YAAY,SAAqB;EAChC,KAAK,WAAW;CACjB;CAEA,QAAQ,IAA8B;EACrC,IAAI,OAAO,QAAQ;GAClB,MAAM,cAAc,KAAK;GAEzB,aAAa,KAAK,UAAU;GAC5B,KAAK,cAAc;GAEnB,OAAO;EACR;EAEA,aAAa,KAAK,SAAS;EAE3B,MAAM,WAAW,KAAK,cAAc;EAEpC,KAAK,YAAY;EAEjB,OAAO;CACR;CAGA,UAAmB;EAClB,MAAM,aAAa,KAAK,WAAW;EAEnC,KAAK,SAAS;EACd,KAAK,eAAe,cAAc,KAAK;EAEvC,OAAO;CACR;CAEA,KAAK,QAAuB;EAC3B,KAAK,aAAa;EAClB,IAAI,QAAQ,KAAK,SAAS;CAC3B;CAEA,MAAM,OAAsB;EAC3B,IAAI,UAAU,QAAW,KAAK,eAAe;EAC7C,IAAI,KAAK,gBAAgB,KAAK,cAAc,KAAK,YAAY;EAE7D,KAAK,SAAS;CACf;CAEA,MAAM,QAAuB;EAC5B,KAAK,QAAQ,MAAM;EACnB,IAAI,CAAC,QAAQ;GACZ,KAAK,SAAS;GACd;EACD;EAEA,KAAK,aAAa,iBAAiB;GAClC,KAAK,OAAO,MAAM;GAClB,KAAK,SAAS;EACf,MAAW;CACZ;CAEA,OAAO,IAA2B;EACjC,KAAK,SAAS;EACd,IAAI,OAAO,QAAQ;GAClB,KAAK,cAAc;GACnB;EACD;EAEA,aAAa,KAAK,SAAS;EAC3B,KAAK,YAAY,iBAAiB;GACjC,KAAK,YAAY;GACjB,KAAK,SAAS;EACf,GAAG,WAAW;CACf;CAEA,MAAM,SAAiB,WAAoE;EAC1F,IAAI,KAAK,YAAY,OAAO;GAAE,QAAQ;GAAM,OAAO;EAAU;EAI7D,OAAO;GAAE,SAFY,KAAK,cAAc,KAAK,WAAW,WAEvB;GAAW,OAAO,KAAK,UAAU;EAAQ;CAC3E;AACD"}
1
+ {"version":3,"file":"readout-claim.js","names":[],"sources":["../../../src/elements/readout-claim.ts"],"sourcesContent":["const keyRevealMs = 1000;\n\nexport const revealMs = 250;\n\nexport class ReadoutClaim {\n\tget isRevealed(): boolean {\n\t\treturn this.#isDragging || this.#keyTimer !== undefined;\n\t}\n\n\t#holdTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#isDragging = false;\n\n\t#keyTimer: ReturnType<typeof setTimeout> | undefined;\n\n\treadonly #onLapse: () => void;\n\n\tconstructor(onLapse: () => void) {\n\t\tthis.#onLapse = onLapse;\n\t}\n\n\tconceal(by: 'drag' | 'keys'): boolean {\n\t\tif (by === 'drag') {\n\t\t\tconst wasDragging = this.#isDragging;\n\n\t\t\tclearTimeout(this.#holdTimer);\n\t\t\tthis.#isDragging = false;\n\n\t\t\treturn wasDragging;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\n\t\tconst wasTimed = this.#keyTimer !== undefined;\n\n\t\tthis.#keyTimer = undefined;\n\n\t\treturn wasTimed;\n\t}\n\n\tpress(isDown: boolean): void {\n\t\tthis.conceal('drag');\n\t\tif (!isDown) {\n\t\t\tthis.#onLapse();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#holdTimer = setTimeout(() => {\n\t\t\tthis.reveal('drag');\n\t\t\tthis.#onLapse();\n\t\t}, revealMs);\n\t}\n\n\treveal(by: 'drag' | 'keys'): void {\n\t\tif (by === 'drag') {\n\t\t\tthis.#isDragging = true;\n\t\t\treturn;\n\t\t}\n\n\t\tclearTimeout(this.#keyTimer);\n\t\tthis.#keyTimer = setTimeout(() => {\n\t\t\tthis.#keyTimer = undefined;\n\t\t\tthis.#onLapse();\n\t\t}, keyRevealMs);\n\t}\n}\n"],"mappings":";AAAA,MAAM,cAAc;AAEpB,MAAa,WAAW;AAExB,IAAa,eAAb,MAA0B;CACzB,IAAI,aAAsB;EACzB,OAAO,KAAK,eAAe,KAAK,cAAc;CAC/C;CAEA;CAEA,cAAc;CAEd;CAEA,AAAS;CAET,YAAY,SAAqB;EAChC,KAAK,WAAW;CACjB;CAEA,QAAQ,IAA8B;EACrC,IAAI,OAAO,QAAQ;GAClB,MAAM,cAAc,KAAK;GAEzB,aAAa,KAAK,UAAU;GAC5B,KAAK,cAAc;GAEnB,OAAO;EACR;EAEA,aAAa,KAAK,SAAS;EAE3B,MAAM,WAAW,KAAK,cAAc;EAEpC,KAAK,YAAY;EAEjB,OAAO;CACR;CAEA,MAAM,QAAuB;EAC5B,KAAK,QAAQ,MAAM;EACnB,IAAI,CAAC,QAAQ;GACZ,KAAK,SAAS;GACd;EACD;EAEA,KAAK,aAAa,iBAAiB;GAClC,KAAK,OAAO,MAAM;GAClB,KAAK,SAAS;EACf,MAAW;CACZ;CAEA,OAAO,IAA2B;EACjC,IAAI,OAAO,QAAQ;GAClB,KAAK,cAAc;GACnB;EACD;EAEA,aAAa,KAAK,SAAS;EAC3B,KAAK,YAAY,iBAAiB;GACjC,KAAK,YAAY;GACjB,KAAK,SAAS;EACf,GAAG,WAAW;CACf;AACD"}
@@ -0,0 +1,44 @@
1
+ import { copyNode } from "../lib/copy-node.js";
2
+ import { placeChildren } from "../lib/render.js";
3
+ import { mirrorChildren } from "../lib/mirror-children.js";
4
+ import { linkValue } from "./value-element.js";
5
+
6
+ //#region src/elements/scale.ts
7
+ function scaleValue(mark) {
8
+ const text = mark.dataset.sonicValue?.trim();
9
+ return text ? Number(text) : NaN;
10
+ }
11
+ function scaleMark(original) {
12
+ const mark = copyNode(original);
13
+ if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return void 0;
14
+ if (!Number.isFinite(scaleValue(mark))) return void 0;
15
+ const isTick = mark.childElementCount === 0 && mark.textContent.trim() === "";
16
+ mark.classList.add(isTick ? "sonic-scale-tick" : "sonic-scale-label");
17
+ return mark;
18
+ }
19
+ function bindScale(element, { control, marks }, signal) {
20
+ const { mapping, watch } = linkValue(element);
21
+ const render = () => {
22
+ const { proportionOf } = mapping();
23
+ for (const mark of marks.children) {
24
+ if (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;
25
+ const at = String(proportionOf(scaleValue(mark)));
26
+ if (mark.style.getPropertyValue("--_sonic-scale-at") !== at) mark.style.setProperty("--_sonic-scale-at", at);
27
+ }
28
+ };
29
+ mirrorChildren(element, {
30
+ control,
31
+ copy: scaleMark,
32
+ isCopied: (child) => child instanceof Element && child.matches("[data-sonic-value]"),
33
+ isPassed: (child) => child instanceof Element && child.matches(".sonic-led"),
34
+ place: (copies) => {
35
+ placeChildren(marks, copies);
36
+ render();
37
+ }
38
+ }, signal);
39
+ signal.addEventListener("abort", watch(render), { once: true });
40
+ }
41
+
42
+ //#endregion
43
+ export { bindScale };
44
+ //# sourceMappingURL=scale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scale.js","names":[],"sources":["../../../src/elements/scale.ts"],"sourcesContent":["import type { SonicValueElement } from '#elements/value-element.ts';\n\nimport { linkValue } from '#elements/value-element.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { mirrorChildren } from '#lib/mirror-children.ts';\nimport { placeChildren } from '#lib/render.ts';\n\ntype ScaleMark = HTMLElement | SVGElement;\n\nfunction scaleValue(mark: ScaleMark): number {\n\tconst text = mark.dataset.sonicValue?.trim();\n\n\treturn text ? Number(text) : NaN;\n}\n\nfunction scaleMark(original: ChildNode): ScaleMark | undefined {\n\tconst mark = copyNode(original);\n\tif (!(mark instanceof HTMLElement || mark instanceof SVGElement)) return undefined;\n\tif (!Number.isFinite(scaleValue(mark))) return undefined;\n\n\tconst isTick = mark.childElementCount === 0 && mark.textContent.trim() === '';\n\n\tmark.classList.add(isTick ? 'sonic-scale-tick' : 'sonic-scale-label');\n\n\treturn mark;\n}\n\nexport function bindScale(\n\telement: SonicValueElement,\n\t{ control, marks }: { control: HTMLElement; marks: HTMLElement },\n\tsignal: AbortSignal,\n): void {\n\tconst { mapping, watch } = linkValue(element);\n\tconst render = (): void => {\n\t\tconst { proportionOf } = mapping();\n\n\t\tfor (const mark of marks.children) {\n\t\t\tif (!(mark instanceof HTMLElement || mark instanceof SVGElement)) continue;\n\n\t\t\tconst at = String(proportionOf(scaleValue(mark)));\n\n\t\t\tif (mark.style.getPropertyValue('--_sonic-scale-at') !== at) {\n\t\t\t\tmark.style.setProperty('--_sonic-scale-at', at);\n\t\t\t}\n\t\t}\n\t};\n\n\tmirrorChildren(\n\t\telement,\n\t\t{\n\t\t\tcontrol,\n\t\t\tcopy: scaleMark,\n\t\t\tisCopied: (child) => child instanceof Element && child.matches('[data-sonic-value]'),\n\t\t\tisPassed: (child) => child instanceof Element && child.matches('.sonic-led'),\n\t\t\tplace: (copies) => {\n\t\t\t\tplaceChildren(marks, copies);\n\t\t\t\trender();\n\t\t\t},\n\t\t},\n\t\tsignal,\n\t);\n\tsignal.addEventListener('abort', watch(render), { once: true });\n}\n"],"mappings":";;;;;;AASA,SAAS,WAAW,MAAyB;CAC5C,MAAM,OAAO,KAAK,QAAQ,YAAY,KAAK;CAE3C,OAAO,OAAO,OAAO,IAAI,IAAI;AAC9B;AAEA,SAAS,UAAU,UAA4C;CAC9D,MAAM,OAAO,SAAS,QAAQ;CAC9B,IAAI,EAAE,gBAAgB,eAAe,gBAAgB,aAAa,OAAO;CACzE,IAAI,CAAC,OAAO,SAAS,WAAW,IAAI,CAAC,GAAG,OAAO;CAE/C,MAAM,SAAS,KAAK,sBAAsB,KAAK,KAAK,YAAY,KAAK,MAAM;CAE3E,KAAK,UAAU,IAAI,SAAS,qBAAqB,mBAAmB;CAEpE,OAAO;AACR;AAEA,SAAgB,UACf,SACA,EAAE,SAAS,SACX,QACO;CACP,MAAM,EAAE,SAAS,UAAU,UAAU,OAAO;CAC5C,MAAM,eAAqB;EAC1B,MAAM,EAAE,iBAAiB,QAAQ;EAEjC,KAAK,MAAM,QAAQ,MAAM,UAAU;GAClC,IAAI,EAAE,gBAAgB,eAAe,gBAAgB,aAAa;GAElE,MAAM,KAAK,OAAO,aAAa,WAAW,IAAI,CAAC,CAAC;GAEhD,IAAI,KAAK,MAAM,iBAAiB,mBAAmB,MAAM,IACxD,KAAK,MAAM,YAAY,qBAAqB,EAAE;EAEhD;CACD;CAEA,eACC,SACA;EACC;EACA,MAAM;EACN,WAAW,UAAU,iBAAiB,WAAW,MAAM,QAAQ,oBAAoB;EACnF,WAAW,UAAU,iBAAiB,WAAW,MAAM,QAAQ,YAAY;EAC3E,QAAQ,WAAW;GAClB,cAAc,OAAO,MAAM;GAC3B,OAAO;EACR;CACD,GACA,MACD;CACA,OAAO,iBAAiB,SAAS,MAAM,MAAM,GAAG,EAAE,MAAM,KAAK,CAAC;AAC/D"}
@@ -1,4 +1,5 @@
1
1
  import { SonicValueElement } from "./value-element.js";
2
+ import { TimeRegions } from "../lib/time-regions.js";
2
3
  //#region src/elements/slider.d.ts
3
4
  declare global {
4
5
  interface HTMLElementTagNameMap {
@@ -8,6 +9,8 @@ declare global {
8
9
  export declare class SonicSlider extends SonicValueElement {
9
10
  #private;
10
11
  static readonly observedAttributes: string[];
12
+ get buffered(): Array<[number, number]>;
13
+ set buffered(regions: TimeRegions | undefined);
11
14
  get groovePress(): 'jump' | 'none';
12
15
  set groovePress(gesture: 'jump' | 'none' | undefined);
13
16
  get modulation(): number;
@@ -1,5 +1,7 @@
1
1
  import { requireChild, template } from "../lib/render.js";
2
2
  import { SonicValueElement } from "./value-element.js";
3
+ import { bindScale } from "./scale.js";
4
+ import { sortedRegions } from "../lib/time-regions.js";
3
5
 
4
6
  //#region src/elements/slider.ts
5
7
  function grooveStop(proportion) {
@@ -34,6 +36,13 @@ var SonicSlider = class extends SonicValueElement {
34
36
  "orientation",
35
37
  "spring"
36
38
  ];
39
+ get buffered() {
40
+ return this.#buffered.map(([start, end]) => [start, end]);
41
+ }
42
+ set buffered(regions) {
43
+ this.#buffered = sortedRegions(regions);
44
+ this.render();
45
+ }
37
46
  get groovePress() {
38
47
  return this.getAttribute("groove-press") === "none" ? "none" : "jump";
39
48
  }
@@ -64,15 +73,19 @@ var SonicSlider = class extends SonicValueElement {
64
73
  set spring(isSpring) {
65
74
  this.reflect("spring", isSpring);
66
75
  }
76
+ #buffered = [];
67
77
  #slider = renderSlider();
68
78
  connectedCallback() {
69
- this.upgradeProperties("groovePress", "modulationValue", "modulation", "orientation", "spring");
79
+ this.upgradeProperties("buffered", "groovePress", "modulationValue", "modulation", "orientation", "spring");
70
80
  super.connectedCallback();
71
81
  }
72
82
  connect(signal) {
73
83
  const slider = this.#slider;
74
84
  const cap = requireChild(slider, ".sonic-slider-cap", HTMLDivElement);
75
- this.bindScale(slider, requireChild(slider, ".sonic-slider-scale", HTMLDivElement), signal);
85
+ bindScale(this, {
86
+ control: slider,
87
+ marks: requireChild(slider, ".sonic-slider-scale", HTMLDivElement)
88
+ }, signal);
76
89
  slider.addEventListener("transitionend", (event) => {
77
90
  if (event.propertyName === "--_sonic-slider-value") this.toggleState("springing", false);
78
91
  }, { signal });
@@ -113,7 +126,7 @@ var SonicSlider = class extends SonicValueElement {
113
126
  };
114
127
  }
115
128
  #renderBuffered(slider) {
116
- const buffered = this.buffered;
129
+ const buffered = this.#buffered;
117
130
  if (buffered.length === 0) {
118
131
  slider.style.removeProperty("--_sonic-slider-buffered-regions");
119
132
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"slider.js","names":[],"sources":["../../../src/elements/slider.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-element.ts';\n\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends ValueAxis {\n\tstartPx: number;\n}\n\nfunction grooveStop(proportion: number): string {\n\tif (proportion <= 0) return '0%';\n\tif (proportion >= 1) return '100%';\n\n\treturn `calc(${String(proportion)} * var(--_sonic-slider-travel) + var(--_sonic-slider-groove) / 2)`;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-slider-notches\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-modulation\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-slider-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicSlider extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'modulation',\n\t\t'orientation',\n\t\t'spring',\n\t];\n\n\tget groovePress(): 'jump' | 'none' {\n\t\treturn this.getAttribute('groove-press') === 'none' ? 'none' : 'jump';\n\t}\n\n\tset groovePress(gesture: 'jump' | 'none' | undefined) {\n\t\tthis.reflect('groove-press', gesture);\n\t}\n\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.writeModulationValue(this.#slider, 'slider', value);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\tget spring(): boolean {\n\t\treturn this.hasAttribute('spring');\n\t}\n\n\tset spring(isSpring: boolean) {\n\t\tthis.reflect('spring', isSpring);\n\t}\n\n\treadonly #slider = renderSlider();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('groovePress', 'modulationValue', 'modulation', 'orientation', 'spring');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tthis.bindScale(slider, requireChild(slider, '.sonic-slider-scale', HTMLDivElement), signal);\n\t\tslider.addEventListener(\n\t\t\t'transitionend',\n\t\t\t(event) => {\n\t\t\t\tif (event.propertyName === '--_sonic-slider-value') this.toggleState('springing', false);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.groovePress === 'none') return;\n\n\t\t\tthis.input(this.mapping().valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected override controlOrientation(): 'horizontal' | 'vertical' {\n\t\treturn this.orientation;\n\t}\n\n\tprotected draw(): void {\n\t\tthis.writeProportions(this.#slider, 'slider', this.modulation);\n\t\tthis.#renderBuffered(this.#slider);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#slider;\n\t}\n\n\tprotected override springTarget(): number | undefined {\n\t\treturn this.spring ? this.originValue() : undefined;\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst box = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\treturn this.orientation === 'vertical'\n\t\t\t? {\n\t\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\t\tstartPx: capBox.height / 2 - box.bottom,\n\t\t\t\t\ttravelPx: Math.max(1, box.height - capBox.height),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tposition: (event) => event.clientX,\n\t\t\t\t\tstartPx: box.left + capBox.width / 2,\n\t\t\t\t\ttravelPx: Math.max(1, box.width - capBox.width),\n\t\t\t\t};\n\t}\n\n\t#renderBuffered(slider: HTMLElement): void {\n\t\tconst buffered = this.buffered;\n\n\t\tif (buffered.length === 0) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-buffered-regions');\n\t\t\treturn;\n\t\t}\n\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst stops = buffered.flatMap(([start, end]) => {\n\t\t\tconst from = grooveStop(proportionOf(Math.min(start, end)));\n\t\t\tconst to = grooveStop(proportionOf(Math.max(start, end)));\n\n\t\t\treturn [\n\t\t\t\t`transparent ${from}`,\n\t\t\t\t`var(--_sonic-slider-buffered) ${from} ${to}`,\n\t\t\t\t`transparent ${to}`,\n\t\t\t];\n\t\t});\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-buffered-regions',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), ${stops.join(', ')})`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"slider.js","names":[],"sources":["../../../src/elements/slider.ts"],"sourcesContent":["import type { ValueAxis } from '#elements/value-element.ts';\nimport type { TimeRegions } from '#lib/time-regions.ts';\n\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { sortedRegions } from '#lib/time-regions.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends ValueAxis {\n\tstartPx: number;\n}\n\nfunction grooveStop(proportion: number): string {\n\tif (proportion <= 0) return '0%';\n\tif (proportion >= 1) return '100%';\n\n\treturn `calc(${String(proportion)} * var(--_sonic-slider-travel) + var(--_sonic-slider-groove) / 2)`;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-slider-notches\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-modulation\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-slider-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicSlider extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'modulation',\n\t\t'orientation',\n\t\t'spring',\n\t];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget buffered(): Array<[number, number]> {\n\t\treturn this.#buffered.map(([start, end]) => [start, end]);\n\t}\n\n\tset buffered(regions: TimeRegions | undefined) {\n\t\tthis.#buffered = sortedRegions(regions);\n\t\tthis.render();\n\t}\n\n\tget groovePress(): 'jump' | 'none' {\n\t\treturn this.getAttribute('groove-press') === 'none' ? 'none' : 'jump';\n\t}\n\n\tset groovePress(gesture: 'jump' | 'none' | undefined) {\n\t\tthis.reflect('groove-press', gesture);\n\t}\n\n\tget modulation(): number {\n\t\treturn this.numberAttribute('modulation', 0);\n\t}\n\n\tset modulation(amount: number | undefined) {\n\t\tthis.reflect('modulation', amount);\n\t}\n\n\tget modulationValue(): number | undefined {\n\t\treturn this.readModulationValue();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.writeModulationValue(this.#slider, 'slider', value);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'vertical' ? 'vertical' : 'horizontal';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\tget spring(): boolean {\n\t\treturn this.hasAttribute('spring');\n\t}\n\n\tset spring(isSpring: boolean) {\n\t\tthis.reflect('spring', isSpring);\n\t}\n\n\t#buffered: Array<[number, number]> = [];\n\n\treadonly #slider = renderSlider();\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(\n\t\t\t'buffered',\n\t\t\t'groovePress',\n\t\t\t'modulationValue',\n\t\t\t'modulation',\n\t\t\t'orientation',\n\t\t\t'spring',\n\t\t);\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: slider, marks: requireChild(slider, '.sonic-slider-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tslider.addEventListener(\n\t\t\t'transitionend',\n\t\t\t(event) => {\n\t\t\t\tif (event.propertyName === '--_sonic-slider-value') this.toggleState('springing', false);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.groovePress === 'none') return;\n\n\t\t\tthis.input(this.mapping().valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected override controlOrientation(): 'horizontal' | 'vertical' {\n\t\treturn this.orientation;\n\t}\n\n\tprotected draw(): void {\n\t\tthis.writeProportions(this.#slider, 'slider', this.modulation);\n\t\tthis.#renderBuffered(this.#slider);\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#slider;\n\t}\n\n\tprotected override springTarget(): number | undefined {\n\t\treturn this.spring ? this.originValue() : undefined;\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst box = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\treturn this.orientation === 'vertical'\n\t\t\t? {\n\t\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\t\tstartPx: capBox.height / 2 - box.bottom,\n\t\t\t\t\ttravelPx: Math.max(1, box.height - capBox.height),\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tposition: (event) => event.clientX,\n\t\t\t\t\tstartPx: box.left + capBox.width / 2,\n\t\t\t\t\ttravelPx: Math.max(1, box.width - capBox.width),\n\t\t\t\t};\n\t}\n\n\t#renderBuffered(slider: HTMLElement): void {\n\t\tconst buffered = this.#buffered;\n\n\t\tif (buffered.length === 0) {\n\t\t\tslider.style.removeProperty('--_sonic-slider-buffered-regions');\n\t\t\treturn;\n\t\t}\n\n\t\tconst { proportionOf } = this.mapping();\n\t\tconst stops = buffered.flatMap(([start, end]) => {\n\t\t\tconst from = grooveStop(proportionOf(Math.min(start, end)));\n\t\t\tconst to = grooveStop(proportionOf(Math.max(start, end)));\n\n\t\t\treturn [\n\t\t\t\t`transparent ${from}`,\n\t\t\t\t`var(--_sonic-slider-buffered) ${from} ${to}`,\n\t\t\t\t`transparent ${to}`,\n\t\t\t];\n\t\t});\n\n\t\tslider.style.setProperty(\n\t\t\t'--_sonic-slider-buffered-regions',\n\t\t\t`linear-gradient(var(--_sonic-slider-toward), ${stops.join(', ')})`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
@@ -1,11 +1,4 @@
1
1
  //#region src/elements/sonic-element.d.ts
2
- interface Mirror {
3
- control: Element;
4
- copy: (original: ChildNode) => Node | undefined;
5
- isCopied?: (child: Node) => boolean;
6
- isPassed?: (child: Node) => boolean;
7
- place: (copies: Array<Node>) => void;
8
- }
9
2
  export declare abstract class SonicElement extends HTMLElement {
10
3
  #private;
11
4
  static readonly observedAttributes: string[];
@@ -24,7 +17,6 @@ export declare abstract class SonicElement extends HTMLElement {
24
17
  protected isBound(): boolean;
25
18
  protected isDisabled(): boolean;
26
19
  protected keepControl(control: Element, signal: AbortSignal): void;
27
- protected mirrorChildren({ control, copy, isCopied, isPassed, place }: Mirror, signal: AbortSignal): void;
28
20
  protected namingLabels(): Array<Element>;
29
21
  protected numberAttribute(name: string, fallback: number): number;
30
22
  protected optionalNumberAttribute(name: string): number | undefined;
@@ -1,5 +1,5 @@
1
1
  import { toNumber } from "../lib/math.js";
2
- import { attachSlots } from "../lib/render.js";
2
+ import { appendOnce, checkChildren } from "../lib/owned-control.js";
3
3
  import { writeAttribute } from "../lib/write-attribute.js";
4
4
 
5
5
  //#region src/elements/sonic-element.ts
@@ -9,22 +9,11 @@ const namingAttributes = [
9
9
  "aria-labelledby"
10
10
  ];
11
11
  const checkedSheets = /* @__PURE__ */ new WeakMap();
12
- const checkedMirrors = /* @__PURE__ */ new WeakSet();
13
- const checkedChildren = /* @__PURE__ */ new WeakSet();
14
- const interactive = "a[href], button, input, select, textarea, [tabindex]";
15
12
  function writeLabelledBy(target, labels) {
16
13
  const current = target.ariaLabelledByElements;
17
14
  if (current?.length === labels.length && labels.every((label, index) => label === current[index])) return;
18
15
  target.ariaLabelledByElements = labels;
19
16
  }
20
- function unmirrorable(original) {
21
- if (!(original instanceof Element)) return void 0;
22
- return [original, ...original.querySelectorAll("*")].find((element) => element.matches(interactive) || element.localName.includes("-"));
23
- }
24
- function isContent(child) {
25
- if (child instanceof Element) return true;
26
- return child instanceof Text && child.data.trim() !== "";
27
- }
28
17
  var SonicElement = class extends HTMLElement {
29
18
  static observedAttributes = [...namingAttributes, "disabled"];
30
19
  get disabled() {
@@ -35,7 +24,6 @@ var SonicElement = class extends HTMLElement {
35
24
  }
36
25
  internals = "attachInternals" in HTMLElement.prototype ? this.attachInternals() : void 0;
37
26
  #connection;
38
- #slots;
39
27
  blur() {
40
28
  this.#focused()?.blur();
41
29
  }
@@ -121,8 +109,8 @@ var SonicElement = class extends HTMLElement {
121
109
  }
122
110
  keepControl(control, signal) {
123
111
  const keep = () => {
124
- this.#appendOnce(control);
125
- this.#checkChildren([...this.childNodes].filter((child) => child !== control));
112
+ appendOnce(this, control);
113
+ checkChildren(this, [...this.childNodes].filter((child) => child !== control));
126
114
  };
127
115
  const observer = new MutationObserver(keep);
128
116
  keep();
@@ -131,50 +119,6 @@ var SonicElement = class extends HTMLElement {
131
119
  observer.disconnect();
132
120
  }, { once: true });
133
121
  }
134
- mirrorChildren({ control, copy, isCopied = () => true, isPassed = () => false, place }, signal) {
135
- const slots = this.#slots ?? attachSlots(this);
136
- let copies = /* @__PURE__ */ new Map();
137
- this.#slots = slots;
138
- const mirror = (touched) => {
139
- this.#appendOnce(control);
140
- const children = [...this.childNodes].filter((child) => child !== control);
141
- const slotted = children.filter((child) => child instanceof Element || child instanceof Text);
142
- const kept = copies;
143
- const originals = children.filter((child) => isCopied(child));
144
- this.#checkMirrored(originals);
145
- this.#checkChildren(children.filter((child) => !isCopied(child) && !isPassed(child)));
146
- copies = new Map(originals.map((original) => [original, touched && !touched.has(original) && kept.has(original) ? kept.get(original) : copy(original)]));
147
- place([...copies.values()].filter((made) => made !== void 0));
148
- slots.shown.assign(...slotted.filter((child) => !isCopied(child)), control);
149
- slots.kept.assign(...slotted.filter((child) => isCopied(child)));
150
- };
151
- const watch = () => {
152
- observer.disconnect();
153
- observer.observe(this, { childList: true });
154
- for (const child of this.childNodes) {
155
- if (child === control) continue;
156
- observer.observe(child, {
157
- attributes: true,
158
- characterData: true,
159
- childList: true,
160
- subtree: true
161
- });
162
- }
163
- };
164
- const observer = new MutationObserver((records) => {
165
- const changes = records.filter((record) => this.#isCopiedChange(record, control, isCopied));
166
- if (changes.length === 0) return;
167
- if (changes.some((record) => record.target === this)) {
168
- mirror();
169
- watch();
170
- } else mirror(new Set(changes.map((record) => this.#childHolding(record.target))));
171
- });
172
- mirror();
173
- watch();
174
- signal.addEventListener("abort", () => {
175
- observer.disconnect();
176
- }, { once: true });
177
- }
178
122
  namingLabels() {
179
123
  return [];
180
124
  }
@@ -205,42 +149,11 @@ var SonicElement = class extends HTMLElement {
205
149
  this[name] = value;
206
150
  }
207
151
  }
208
- #appendOnce(control) {
209
- const [hook] = control.classList;
210
- const stale = [...this.children].filter((child) => child !== control && hook !== void 0 && child.classList.contains(hook));
211
- for (const child of stale) child.remove();
212
- if (control.parentNode !== this) this.append(control);
213
- }
214
- #checkChildren(undocumented) {
215
- if (checkedChildren.has(this.constructor)) return;
216
- const found = undocumented.find((child) => isContent(child));
217
- if (!found) return;
218
- checkedChildren.add(this.constructor);
219
- console.warn(`<${this.localName}> uses only the children it documents, so the ${found instanceof Element ? `<${found.localName}>` : "text"} inside it is unsupported`);
220
- }
221
- #checkMirrored(originals) {
222
- if (checkedMirrors.has(this.constructor)) return;
223
- const [found] = originals.flatMap((original) => unmirrorable(original) ?? []);
224
- if (!found) return;
225
- checkedMirrors.add(this.constructor);
226
- console.warn(`<${this.localName}> copies its children into the control, so the <${found.localName}> inside it loses its listeners and state; keep mirrored children static`);
227
- }
228
- #childHolding(node) {
229
- let current = node;
230
- while (current && current.parentNode !== this) current = current.parentNode;
231
- return current ?? void 0;
232
- }
233
152
  #focused() {
234
153
  const root = this.getRootNode();
235
154
  const active = root instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;
236
155
  return active instanceof HTMLElement && this.contains(active) ? active : void 0;
237
156
  }
238
- #isCopiedChange(record, control, isCopied) {
239
- if (record.target === this) return record.removedNodes.length > 0 || [...record.addedNodes].some((node) => node !== control);
240
- const child = this.#childHolding(record.target);
241
- if (child === void 0) return false;
242
- return isCopied(child) || record.type === "attributes" && record.target === child;
243
- }
244
157
  };
245
158
 
246
159
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"sonic-element.js","names":[],"sources":["../../../src/elements/sonic-element.ts"],"sourcesContent":["import type { MirrorSlots } from '#lib/render.ts';\n\nimport { toNumber } from '#lib/math.ts';\nimport { attachSlots } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// eslint-disable-next-line unicorn/consistent-boolean-name -- the name bundlers and other kits use\ndeclare const __DEV__: boolean;\n\nconst namingAttributes = ['aria-describedby', 'aria-label', 'aria-labelledby'];\n\nconst checkedSheets = new WeakMap<object, Set<string>>();\n\nconst checkedMirrors = new WeakSet<object>();\n\nconst checkedChildren = new WeakSet<object>();\n\nconst interactive = 'a[href], button, input, select, textarea, [tabindex]';\n\ninterface StyleProbe {\n\tselector?: string;\n\tsheet: string;\n\ttoken: string;\n}\n\nfunction writeLabelledBy(target: Element, labels: Array<Element>): void {\n\tconst current = target.ariaLabelledByElements;\n\tif (\n\t\tcurrent?.length === labels.length &&\n\t\tlabels.every((label, index) => label === current[index])\n\t) {\n\t\treturn;\n\t}\n\n\ttarget.ariaLabelledByElements = labels;\n}\n\nfunction unmirrorable(original: Node): Element | undefined {\n\tif (!(original instanceof Element)) return undefined;\n\n\treturn [original, ...original.querySelectorAll('*')].find(\n\t\t(element) => element.matches(interactive) || element.localName.includes('-'),\n\t);\n}\n\nfunction isContent(child: Node): boolean {\n\tif (child instanceof Element) return true;\n\n\treturn child instanceof Text && child.data.trim() !== '';\n}\n\ninterface Mirror {\n\tcontrol: Element;\n\tcopy: (original: ChildNode) => Node | undefined;\n\tisCopied?: (child: Node) => boolean;\n\tisPassed?: (child: Node) => boolean;\n\tplace: (copies: Array<Node>) => void;\n}\n\n// eslint-disable-next-line wc/define-tag-after-class-definition -- abstract; only subclasses are defined\nexport abstract class SonicElement extends HTMLElement {\n\tstatic readonly observedAttributes = [...namingAttributes, 'disabled'];\n\n\tget disabled(): boolean {\n\t\treturn this.hasAttribute('disabled');\n\t}\n\n\tset disabled(isDisabled: boolean) {\n\t\tthis.reflect('disabled', isDisabled);\n\t}\n\n\t// happy-dom has no `ElementInternals`\n\tprotected readonly internals =\n\t\t'attachInternals' in HTMLElement.prototype ? this.attachInternals() : undefined;\n\n\t#connection: AbortController | undefined;\n\n\t#slots: MirrorSlots | undefined;\n\n\toverride blur(): void {\n\t\tthis.#focused()?.blur();\n\t}\n\n\tconnectedCallback(): void {\n\t\tthis.upgradeProperties('disabled');\n\t\tif (this.#connection) return;\n\n\t\tconst connection = new AbortController();\n\n\t\tthis.#connection = connection;\n\t\ttry {\n\t\t\tthis.connect(connection.signal);\n\t\t} catch (error) {\n\t\t\tconnection.abort();\n\t\t\tthis.#connection = undefined;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tconnectedMoveCallback(): void {\n\t\t// Defined so `moveBefore` from a persisting router keeps every binding\n\t}\n\n\t// Without `moveBefore` (Safari), a persisting router disconnects and reconnects in one task\n\tdisconnectedCallback(): void {\n\t\tconst connection = this.#connection;\n\t\tif (!connection) return;\n\n\t\tqueueMicrotask(() => {\n\t\t\tif (this.isConnected || this.#connection !== connection) return;\n\n\t\t\tconnection.abort();\n\t\t\tthis.#connection = undefined;\n\t\t});\n\t}\n\n\toverride focus(options?: FocusOptions): void {\n\t\tconst target = this.focusTarget();\n\t\tif (!target) {\n\t\t\tsuper.focus(options);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.isDisabled() && !this.#focused()) target.focus(options);\n\t}\n\n\tprotected checkStyles(control: HTMLElement, sheet: string): void {\n\t\tif (!__DEV__) return;\n\n\t\tconst elementClass = this.constructor;\n\t\tconst checked = checkedSheets.get(elementClass) ?? new Set<string>();\n\t\tconst part = ({ selector }: StyleProbe): Element | undefined =>\n\t\t\tselector === undefined ? control : (this.querySelector(selector) ?? undefined);\n\t\t// An LED inherits `--_sonic-unit` from its control, so a token of its own probes it\n\t\tconst probes: Array<StyleProbe> = [\n\t\t\t{ sheet: 'material.css', token: '--_sonic-unlit' },\n\t\t\t{ sheet, token: '--_sonic-unit' },\n\t\t\t{ selector: '.sonic-led', sheet: 'led.css', token: '--_sonic-led-lens-ratio' },\n\t\t].filter((probe) => !checked.has(probe.sheet) && part(probe) !== undefined);\n\t\tif (probes.length === 0) return;\n\n\t\tcheckedSheets.set(elementClass, checked);\n\t\tfor (const probe of probes) checked.add(probe.sheet);\n\t\trequestAnimationFrame(() => {\n\t\t\tconst missing: Array<string> = [];\n\n\t\t\tfor (const probe of probes) {\n\t\t\t\tconst drawn = control.isConnected ? part(probe) : undefined;\n\n\t\t\t\tif (!drawn) checked.delete(probe.sheet);\n\t\t\t\telse if (getComputedStyle(drawn).getPropertyValue(probe.token) === '') {\n\t\t\t\t\tmissing.push(`@xsynaptic/sonic-ui/${probe.sheet}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (missing.length === 0) return;\n\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> draws blank without ${missing.join(' and ')} (or controls.css)`,\n\t\t\t);\n\t\t});\n\t}\n\n\tprotected abstract connect(signal: AbortSignal): void;\n\n\tprotected focusTarget(): HTMLElement | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected forwardNaming(target: Element, isNamed: boolean): void {\n\t\tconst labels = isNamed ? this.namingLabels() : [];\n\n\t\tfor (const name of namingAttributes) {\n\t\t\t// `ariaLabelledByElements` holds the attribute, empty\n\t\t\tif (name === 'aria-labelledby' && labels.length > 0) continue;\n\n\t\t\tconst value = this.getAttribute(name);\n\n\t\t\twriteAttribute(target, name, isNamed && value !== null ? value : undefined);\n\t\t}\n\t\tif (labels.length > 0) writeLabelledBy(target, labels);\n\t}\n\n\t// happy-dom matches `:disabled` on no custom element, and an element not form-associated never matches\n\tprotected isBound(): boolean {\n\t\treturn this.#connection !== undefined;\n\t}\n\n\tprotected isDisabled(): boolean {\n\t\treturn this.disabled || this.matches(':disabled');\n\t}\n\n\t// A morph against server HTML, or a stray `innerHTML`, deletes the control\n\tprotected keepControl(control: Element, signal: AbortSignal): void {\n\t\tconst keep = (): void => {\n\t\t\tthis.#appendOnce(control);\n\t\t\tthis.#checkChildren([...this.childNodes].filter((child) => child !== control));\n\t\t};\n\t\tconst observer = new MutationObserver(keep);\n\n\t\tkeep();\n\t\tobserver.observe(this, { childList: true });\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tobserver.disconnect();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected mirrorChildren(\n\t\t{ control, copy, isCopied = () => true, isPassed = () => false, place }: Mirror,\n\t\tsignal: AbortSignal,\n\t): void {\n\t\tconst slots = this.#slots ?? attachSlots(this);\n\t\tlet copies = new Map<ChildNode, Node | undefined>();\n\n\t\tthis.#slots = slots;\n\t\tconst mirror = (touched?: Set<Node | undefined>): void => {\n\t\t\tthis.#appendOnce(control);\n\n\t\t\tconst children = [...this.childNodes].filter((child) => child !== control);\n\t\t\tconst slotted = children.filter(\n\t\t\t\t(child): child is Element | Text => child instanceof Element || child instanceof Text,\n\t\t\t);\n\t\t\tconst kept = copies;\n\t\t\tconst originals = children.filter((child) => isCopied(child));\n\n\t\t\tthis.#checkMirrored(originals);\n\t\t\tthis.#checkChildren(children.filter((child) => !isCopied(child) && !isPassed(child)));\n\t\t\tcopies = new Map(\n\t\t\t\toriginals.map((original) => [\n\t\t\t\t\toriginal,\n\t\t\t\t\ttouched && !touched.has(original) && kept.has(original)\n\t\t\t\t\t\t? kept.get(original)\n\t\t\t\t\t\t: copy(original),\n\t\t\t\t]),\n\t\t\t);\n\t\t\tplace([...copies.values()].filter((made) => made !== undefined));\n\t\t\tslots.shown.assign(...slotted.filter((child) => !isCopied(child)), control);\n\t\t\tslots.kept.assign(...slotted.filter((child) => isCopied(child)));\n\t\t};\n\t\t// Each child is watched, never the control, so a value write wakes nothing\n\t\tconst watch = (): void => {\n\t\t\tobserver.disconnect();\n\t\t\tobserver.observe(this, { childList: true });\n\t\t\tfor (const child of this.childNodes) {\n\t\t\t\tif (child === control) continue;\n\n\t\t\t\tobserver.observe(child, {\n\t\t\t\t\tattributes: true,\n\t\t\t\t\tcharacterData: true,\n\t\t\t\t\tchildList: true,\n\t\t\t\t\tsubtree: true,\n\t\t\t\t});\n\t\t\t}\n\t\t};\n\t\tconst observer = new MutationObserver((records) => {\n\t\t\tconst changes = records.filter((record) => this.#isCopiedChange(record, control, isCopied));\n\t\t\tif (changes.length === 0) return;\n\n\t\t\tif (changes.some((record) => record.target === this)) {\n\t\t\t\tmirror();\n\t\t\t\twatch();\n\t\t\t} else mirror(new Set(changes.map((record) => this.#childHolding(record.target))));\n\t\t});\n\n\t\tmirror();\n\t\twatch();\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tobserver.disconnect();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected namingLabels(): Array<Element> {\n\t\treturn [];\n\t}\n\n\tprotected numberAttribute(name: string, fallback: number): number {\n\t\tconst parsed = toNumber(this.getAttribute(name));\n\n\t\treturn Number.isFinite(parsed) ? parsed : fallback;\n\t}\n\n\tprotected optionalNumberAttribute(name: string): number | undefined {\n\t\tconst value = this.numberAttribute(name, NaN);\n\n\t\treturn Number.isNaN(value) ? undefined : value;\n\t}\n\n\t// Never compared; a host attribute set again resets the value a gesture left dirty\n\tprotected reflect(name: string, value: boolean | null | number | string | undefined): void {\n\t\tif (typeof value !== 'number' && typeof value !== 'string' && value !== true) {\n\t\t\tthis.removeAttribute(name);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setAttribute(name, value === true ? '' : value.toString());\n\t}\n\n\tprotected toggleState(state: string, isOn: boolean): void {\n\t\tif (isOn) this.internals?.states.add(state);\n\t\telse this.internals?.states.delete(state);\n\t}\n\n\t// A property set before upgrade shadows its accessor\n\tprotected upgradeProperties(...names: Array<keyof this>): void {\n\t\tfor (const name of names) {\n\t\t\tif (!Object.hasOwn(this, name)) continue;\n\n\t\t\tconst value = this[name];\n\n\t\t\tReflect.deleteProperty(this, name);\n\t\t\tthis[name] = value;\n\t\t}\n\t}\n\n\t#appendOnce(control: Element): void {\n\t\tconst [hook] = control.classList;\n\t\tconst stale = [...this.children].filter(\n\t\t\t(child) => child !== control && hook !== undefined && child.classList.contains(hook),\n\t\t);\n\n\t\tfor (const child of stale) child.remove();\n\t\tif (control.parentNode !== this) this.append(control);\n\t}\n\n\t#checkChildren(undocumented: Array<Node>): void {\n\t\tif (!__DEV__ || checkedChildren.has(this.constructor)) return;\n\n\t\tconst found = undocumented.find((child) => isContent(child));\n\t\tif (!found) return;\n\n\t\tcheckedChildren.add(this.constructor);\n\t\tconsole.warn(\n\t\t\t`<${this.localName}> uses only the children it documents, so the ${\n\t\t\t\tfound instanceof Element ? `<${found.localName}>` : 'text'\n\t\t\t} inside it is unsupported`,\n\t\t);\n\t}\n\n\t#checkMirrored(originals: Array<Node>): void {\n\t\tif (!__DEV__ || checkedMirrors.has(this.constructor)) return;\n\n\t\tconst [found] = originals.flatMap((original) => unmirrorable(original) ?? []);\n\t\tif (!found) return;\n\n\t\tcheckedMirrors.add(this.constructor);\n\t\tconsole.warn(\n\t\t\t`<${this.localName}> copies its children into the control, so the <${found.localName}> inside it loses its listeners and state; keep mirrored children static`,\n\t\t);\n\t}\n\n\t#childHolding(node: Node): Node | undefined {\n\t\tlet current: Node | null = node;\n\n\t\twhile (current && current.parentNode !== this) current = current.parentNode;\n\n\t\treturn current ?? undefined;\n\t}\n\n\t#focused(): HTMLElement | undefined {\n\t\tconst root = this.getRootNode();\n\t\tconst active =\n\t\t\troot instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;\n\n\t\treturn active instanceof HTMLElement && this.contains(active) ? active : undefined;\n\t}\n\n\t#isCopiedChange(\n\t\trecord: MutationRecord,\n\t\tcontrol: Element,\n\t\tisCopied: (child: Node) => boolean,\n\t): boolean {\n\t\tif (record.target === this) {\n\t\t\treturn (\n\t\t\t\trecord.removedNodes.length > 0 || [...record.addedNodes].some((node) => node !== control)\n\t\t\t);\n\t\t}\n\n\t\tconst child = this.#childHolding(record.target);\n\n\t\tif (child === undefined) return false;\n\n\t\treturn isCopied(child) || (record.type === 'attributes' && record.target === child);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sonic-element.js","names":[],"sources":["../../../src/elements/sonic-element.ts"],"sourcesContent":["import { toNumber } from '#lib/math.ts';\nimport { appendOnce, checkChildren } from '#lib/owned-control.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// eslint-disable-next-line unicorn/consistent-boolean-name -- the name bundlers and other kits use\ndeclare const __DEV__: boolean;\n\nconst namingAttributes = ['aria-describedby', 'aria-label', 'aria-labelledby'];\n\nconst checkedSheets = new WeakMap<object, Set<string>>();\n\ninterface StyleProbe {\n\tselector?: string;\n\tsheet: string;\n\ttoken: string;\n}\n\nfunction writeLabelledBy(target: Element, labels: Array<Element>): void {\n\tconst current = target.ariaLabelledByElements;\n\tif (\n\t\tcurrent?.length === labels.length &&\n\t\tlabels.every((label, index) => label === current[index])\n\t) {\n\t\treturn;\n\t}\n\n\ttarget.ariaLabelledByElements = labels;\n}\n\n// eslint-disable-next-line wc/define-tag-after-class-definition -- abstract; only subclasses are defined\nexport abstract class SonicElement extends HTMLElement {\n\tstatic readonly observedAttributes = [...namingAttributes, 'disabled'];\n\n\tget disabled(): boolean {\n\t\treturn this.hasAttribute('disabled');\n\t}\n\n\tset disabled(isDisabled: boolean) {\n\t\tthis.reflect('disabled', isDisabled);\n\t}\n\n\t// happy-dom has no `ElementInternals`\n\tprotected readonly internals =\n\t\t'attachInternals' in HTMLElement.prototype ? this.attachInternals() : undefined;\n\n\t#connection: AbortController | undefined;\n\n\toverride blur(): void {\n\t\tthis.#focused()?.blur();\n\t}\n\n\tconnectedCallback(): void {\n\t\tthis.upgradeProperties('disabled');\n\t\tif (this.#connection) return;\n\n\t\tconst connection = new AbortController();\n\n\t\tthis.#connection = connection;\n\t\ttry {\n\t\t\tthis.connect(connection.signal);\n\t\t} catch (error) {\n\t\t\tconnection.abort();\n\t\t\tthis.#connection = undefined;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tconnectedMoveCallback(): void {\n\t\t// Defined so `moveBefore` from a persisting router keeps every binding\n\t}\n\n\t// Without `moveBefore` (Safari), a persisting router disconnects and reconnects in one task\n\tdisconnectedCallback(): void {\n\t\tconst connection = this.#connection;\n\t\tif (!connection) return;\n\n\t\tqueueMicrotask(() => {\n\t\t\tif (this.isConnected || this.#connection !== connection) return;\n\n\t\t\tconnection.abort();\n\t\t\tthis.#connection = undefined;\n\t\t});\n\t}\n\n\toverride focus(options?: FocusOptions): void {\n\t\tconst target = this.focusTarget();\n\t\tif (!target) {\n\t\t\tsuper.focus(options);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.isDisabled() && !this.#focused()) target.focus(options);\n\t}\n\n\tprotected checkStyles(control: HTMLElement, sheet: string): void {\n\t\tif (!__DEV__) return;\n\n\t\tconst elementClass = this.constructor;\n\t\tconst checked = checkedSheets.get(elementClass) ?? new Set<string>();\n\t\tconst part = ({ selector }: StyleProbe): Element | undefined =>\n\t\t\tselector === undefined ? control : (this.querySelector(selector) ?? undefined);\n\t\t// An LED inherits `--_sonic-unit` from its control, so a token of its own probes it\n\t\tconst probes: Array<StyleProbe> = [\n\t\t\t{ sheet: 'material.css', token: '--_sonic-unlit' },\n\t\t\t{ sheet, token: '--_sonic-unit' },\n\t\t\t{ selector: '.sonic-led', sheet: 'led.css', token: '--_sonic-led-lens-ratio' },\n\t\t].filter((probe) => !checked.has(probe.sheet) && part(probe) !== undefined);\n\t\tif (probes.length === 0) return;\n\n\t\tcheckedSheets.set(elementClass, checked);\n\t\tfor (const probe of probes) checked.add(probe.sheet);\n\t\trequestAnimationFrame(() => {\n\t\t\tconst missing: Array<string> = [];\n\n\t\t\tfor (const probe of probes) {\n\t\t\t\tconst drawn = control.isConnected ? part(probe) : undefined;\n\n\t\t\t\tif (!drawn) checked.delete(probe.sheet);\n\t\t\t\telse if (getComputedStyle(drawn).getPropertyValue(probe.token) === '') {\n\t\t\t\t\tmissing.push(`@xsynaptic/sonic-ui/${probe.sheet}`);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (missing.length === 0) return;\n\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> draws blank without ${missing.join(' and ')} (or controls.css)`,\n\t\t\t);\n\t\t});\n\t}\n\n\tprotected abstract connect(signal: AbortSignal): void;\n\n\tprotected focusTarget(): HTMLElement | undefined {\n\t\treturn undefined;\n\t}\n\n\tprotected forwardNaming(target: Element, isNamed: boolean): void {\n\t\tconst labels = isNamed ? this.namingLabels() : [];\n\n\t\tfor (const name of namingAttributes) {\n\t\t\t// `ariaLabelledByElements` holds the attribute, empty\n\t\t\tif (name === 'aria-labelledby' && labels.length > 0) continue;\n\n\t\t\tconst value = this.getAttribute(name);\n\n\t\t\twriteAttribute(target, name, isNamed && value !== null ? value : undefined);\n\t\t}\n\t\tif (labels.length > 0) writeLabelledBy(target, labels);\n\t}\n\n\t// happy-dom matches `:disabled` on no custom element, and an element not form-associated never matches\n\tprotected isBound(): boolean {\n\t\treturn this.#connection !== undefined;\n\t}\n\n\tprotected isDisabled(): boolean {\n\t\treturn this.disabled || this.matches(':disabled');\n\t}\n\n\t// A morph against server HTML, or a stray `innerHTML`, deletes the control\n\tprotected keepControl(control: Element, signal: AbortSignal): void {\n\t\tconst keep = (): void => {\n\t\t\tappendOnce(this, control);\n\t\t\tif (__DEV__)\n\t\t\t\tcheckChildren(\n\t\t\t\t\tthis,\n\t\t\t\t\t[...this.childNodes].filter((child) => child !== control),\n\t\t\t\t);\n\t\t};\n\t\tconst observer = new MutationObserver(keep);\n\n\t\tkeep();\n\t\tobserver.observe(this, { childList: true });\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tobserver.disconnect();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected namingLabels(): Array<Element> {\n\t\treturn [];\n\t}\n\n\tprotected numberAttribute(name: string, fallback: number): number {\n\t\tconst parsed = toNumber(this.getAttribute(name));\n\n\t\treturn Number.isFinite(parsed) ? parsed : fallback;\n\t}\n\n\tprotected optionalNumberAttribute(name: string): number | undefined {\n\t\tconst value = this.numberAttribute(name, NaN);\n\n\t\treturn Number.isNaN(value) ? undefined : value;\n\t}\n\n\t// Never compared; a host attribute set again resets the value a gesture left dirty\n\tprotected reflect(name: string, value: boolean | null | number | string | undefined): void {\n\t\tif (typeof value !== 'number' && typeof value !== 'string' && value !== true) {\n\t\t\tthis.removeAttribute(name);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.setAttribute(name, value === true ? '' : value.toString());\n\t}\n\n\tprotected toggleState(state: string, isOn: boolean): void {\n\t\tif (isOn) this.internals?.states.add(state);\n\t\telse this.internals?.states.delete(state);\n\t}\n\n\t// A property set before upgrade shadows its accessor\n\tprotected upgradeProperties(...names: Array<keyof this>): void {\n\t\tfor (const name of names) {\n\t\t\tif (!Object.hasOwn(this, name)) continue;\n\n\t\t\tconst value = this[name];\n\n\t\t\tReflect.deleteProperty(this, name);\n\t\t\tthis[name] = value;\n\t\t}\n\t}\n\n\t#focused(): HTMLElement | undefined {\n\t\tconst root = this.getRootNode();\n\t\tconst active =\n\t\t\troot instanceof ShadowRoot ? root.activeElement : this.ownerDocument.activeElement;\n\n\t\treturn active instanceof HTMLElement && this.contains(active) ? active : undefined;\n\t}\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { clamp } from "../lib/math.js";
2
- import { requireChild, template } from "../lib/render.js";
3
2
  import { writeAttribute } from "../lib/write-attribute.js";
4
3
  import { bindHold } from "../lib/hold.js";
4
+ import { requireChild, template } from "../lib/render.js";
5
5
  import { bindDrag } from "../lib/pointer-drag.js";
6
6
  import { SonicRadioGroupElement, isUnder, optionValue } from "./radio-group.js";
7
7
 
@@ -1,5 +1,4 @@
1
1
  import { SonicFormElement } from "./form-element.js";
2
- import { TimeRegions } from "../lib/time-regions.js";
3
2
  import { ValueMapping } from "../lib/value-mapping.js";
4
3
  //#region src/elements/value-element.d.ts
5
4
  export interface ValueAxis {
@@ -12,8 +11,6 @@ export interface ValueAxis {
12
11
  export declare abstract class SonicValueElement extends SonicFormElement {
13
12
  #private;
14
13
  static readonly observedAttributes: string[];
15
- get buffered(): Array<[number, number]>;
16
- set buffered(regions: TimeRegions | undefined);
17
14
  get default(): number | undefined;
18
15
  set default(value: number | undefined);
19
16
  get detent(): number | undefined;
@@ -54,7 +51,6 @@ export declare abstract class SonicValueElement extends SonicFormElement {
54
51
  attributeChangedCallback(name: string): void;
55
52
  connectedCallback(): void;
56
53
  protected bindGestures(control: HTMLElement, signal: AbortSignal, grab: (event: PointerEvent) => undefined | ValueAxis): void;
57
- protected bindScale(control: HTMLElement, marks: HTMLElement, signal: AbortSignal): void;
58
54
  protected controlOrientation(): 'horizontal' | 'vertical' | undefined;
59
55
  protected controlRole(): 'slider' | 'spinbutton';
60
56
  protected abstract draw(): void;
@@ -62,16 +58,18 @@ export declare abstract class SonicValueElement extends SonicFormElement {
62
58
  protected heldFrom(): number | undefined;
63
59
  protected heldWrite(_next: number): void;
64
60
  protected holdChanged(): void;
65
- protected hoverReadout(value: number | undefined): void;
66
61
  protected input(next: number): boolean;
62
+ protected isEditing(): boolean;
67
63
  protected isRevealed(): boolean;
68
64
  protected isWrapping(): boolean;
69
65
  protected mapping(): ValueMapping;
70
66
  protected originValue(): number;
67
+ protected previewValue(): number | undefined;
71
68
  protected proportionsChanged(): void;
72
69
  protected readModulationValue(): number | undefined;
73
70
  protected readoutValue(): number;
74
71
  protected render(): void;
72
+ protected renderReadout(): void;
75
73
  protected restoreState(state: string): void;
76
74
  protected scrubsKeyRepeat(): boolean;
77
75
  protected springTarget(): number | undefined;