@xsynaptic/sonic-ui 0.24.1 → 0.26.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.
- package/CHANGELOG.md +42 -2
- package/README.md +6 -4
- package/dist/default/define/region.js +7 -0
- package/dist/default/define.js +1 -0
- package/dist/default/elements/button.js +3 -0
- package/dist/default/elements/dial.js +3 -0
- package/dist/default/elements/envelope.js +14 -11
- package/dist/default/elements/form-element.js +10 -0
- package/dist/default/elements/meter.js +3 -0
- package/dist/default/elements/number.js +3 -0
- package/dist/default/elements/radio-group.js +4 -1
- package/dist/default/elements/readout.js +1 -1
- package/dist/default/elements/region-layer.js +253 -0
- package/dist/default/elements/region.js +161 -0
- package/dist/default/elements/slider.js +3 -0
- package/dist/default/elements/sonic-element.js +7 -9
- package/dist/default/elements/split.js +2 -2
- package/dist/default/elements/toggle.js +4 -4
- package/dist/default/elements/value-element.js +21 -3
- package/dist/default/elements/value-gestures.js +7 -5
- package/dist/default/elements/wave-element.js +15 -3
- package/dist/default/elements/waveform.js +38 -2
- package/dist/default/elements/wavestrip.js +90 -17
- package/dist/default/elements/xy.js +6 -11
- package/dist/default/index.js +2 -1
- package/dist/default/lib/canvas-surface.js +22 -1
- package/dist/default/lib/clock.js +1 -0
- package/dist/default/lib/drag-step.js +1 -1
- package/dist/default/lib/key-press.js +2 -2
- package/dist/default/lib/marker-band.js +40 -29
- package/dist/default/lib/region-drag.js +48 -0
- package/dist/dev/define/region.d.ts +2 -0
- package/dist/dev/define/region.js +8 -0
- package/dist/dev/define/region.js.map +1 -0
- package/dist/dev/define.d.ts +3 -1
- package/dist/dev/define.js +1 -0
- package/dist/dev/elements/button.d.ts +1 -0
- package/dist/dev/elements/button.js +3 -0
- package/dist/dev/elements/button.js.map +1 -1
- package/dist/dev/elements/dial.d.ts +1 -0
- package/dist/dev/elements/dial.js +3 -0
- package/dist/dev/elements/dial.js.map +1 -1
- package/dist/dev/elements/envelope.d.ts +1 -0
- package/dist/dev/elements/envelope.js +14 -11
- package/dist/dev/elements/envelope.js.map +1 -1
- package/dist/dev/elements/form-element.d.ts +1 -0
- package/dist/dev/elements/form-element.js +10 -0
- package/dist/dev/elements/form-element.js.map +1 -1
- package/dist/dev/elements/meter.d.ts +1 -0
- package/dist/dev/elements/meter.js +3 -0
- package/dist/dev/elements/meter.js.map +1 -1
- package/dist/dev/elements/number.d.ts +1 -0
- package/dist/dev/elements/number.js +3 -0
- package/dist/dev/elements/number.js.map +1 -1
- package/dist/dev/elements/radio-group.d.ts +1 -0
- package/dist/dev/elements/radio-group.js +4 -1
- package/dist/dev/elements/radio-group.js.map +1 -1
- package/dist/dev/elements/readout.js +1 -1
- package/dist/dev/elements/readout.js.map +1 -1
- package/dist/dev/elements/region-layer.js +254 -0
- package/dist/dev/elements/region-layer.js.map +1 -0
- package/dist/dev/elements/region.d.ts +30 -0
- package/dist/dev/elements/region.js +169 -0
- package/dist/dev/elements/region.js.map +1 -0
- package/dist/dev/elements/slider.d.ts +1 -0
- package/dist/dev/elements/slider.js +3 -0
- package/dist/dev/elements/slider.js.map +1 -1
- package/dist/dev/elements/sonic-element.d.ts +2 -0
- package/dist/dev/elements/sonic-element.js +8 -10
- package/dist/dev/elements/sonic-element.js.map +1 -1
- package/dist/dev/elements/split.js +2 -2
- package/dist/dev/elements/split.js.map +1 -1
- package/dist/dev/elements/toggle.js +4 -4
- package/dist/dev/elements/toggle.js.map +1 -1
- package/dist/dev/elements/value-element.d.ts +3 -1
- package/dist/dev/elements/value-element.js +21 -3
- package/dist/dev/elements/value-element.js.map +1 -1
- package/dist/dev/elements/value-gestures.js +7 -5
- package/dist/dev/elements/value-gestures.js.map +1 -1
- package/dist/dev/elements/wave-element.d.ts +8 -1
- package/dist/dev/elements/wave-element.js +15 -3
- package/dist/dev/elements/wave-element.js.map +1 -1
- package/dist/dev/elements/waveform.d.ts +7 -0
- package/dist/dev/elements/waveform.js +38 -2
- package/dist/dev/elements/waveform.js.map +1 -1
- package/dist/dev/elements/wavestrip.d.ts +17 -1
- package/dist/dev/elements/wavestrip.js +90 -17
- package/dist/dev/elements/wavestrip.js.map +1 -1
- package/dist/dev/elements/xy.d.ts +1 -0
- package/dist/dev/elements/xy.js +6 -11
- package/dist/dev/elements/xy.js.map +1 -1
- package/dist/dev/index.d.ts +3 -2
- package/dist/dev/index.js +2 -1
- package/dist/dev/lib/canvas-surface.js +22 -1
- package/dist/dev/lib/canvas-surface.js.map +1 -1
- package/dist/dev/lib/clock.js +1 -0
- package/dist/dev/lib/clock.js.map +1 -1
- package/dist/dev/lib/drag-step.js +1 -1
- package/dist/dev/lib/drag-step.js.map +1 -1
- package/dist/dev/lib/key-press.js +2 -2
- package/dist/dev/lib/key-press.js.map +1 -1
- package/dist/dev/lib/marker-band.d.ts +7 -0
- package/dist/dev/lib/marker-band.js +40 -29
- package/dist/dev/lib/marker-band.js.map +1 -1
- package/dist/dev/lib/region-drag.js +49 -0
- package/dist/dev/lib/region-drag.js.map +1 -0
- package/dist/styles/button.css +23 -13
- package/dist/styles/controls.css +1 -0
- package/dist/styles/dial.css +41 -24
- package/dist/styles/envelope.css +12 -5
- package/dist/styles/led.css +8 -8
- package/dist/styles/material/arc.css +2 -2
- package/dist/styles/material/cap.css +34 -13
- package/dist/styles/material/glass.css +1 -1
- package/dist/styles/material/groove.css +3 -3
- package/dist/styles/material/keycap.css +2 -2
- package/dist/styles/material/lens.css +1 -1
- package/dist/styles/material/pane.css +20 -6
- package/dist/styles/material/readout.css +14 -5
- package/dist/styles/material/{bracket.css → reticle.css} +15 -8
- package/dist/styles/material/well.css +2 -2
- package/dist/styles/material.css +1 -1
- package/dist/styles/meter.css +29 -16
- package/dist/styles/number.css +3 -2
- package/dist/styles/panel.css +3 -3
- package/dist/styles/region.css +94 -0
- package/dist/styles/ring.css +2 -1
- package/dist/styles/screen.css +7 -5
- package/dist/styles/segmented.css +5 -4
- package/dist/styles/slider.css +24 -15
- package/dist/styles/spectrum.css +9 -1
- package/dist/styles/switch.css +9 -2
- package/dist/styles/toggle.css +5 -4
- package/dist/styles/waveform.css +10 -3
- package/dist/styles/wavestrip.css +17 -8
- package/dist/styles/xy.css +24 -11
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { createModulation } from '#elements/modulation.ts';\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-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-dial-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 SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t/** How far a modulation source can push the value, in value units; signed */\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\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\t/** Where the value is right now with modulation applied; a property only, cheap to write every frame, fires nothing */\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.#modulation.value();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.#modulation.write(value, this.isBound() ? this.mapping() : undefined);\n\t}\n\n\treadonly #dial = renderDial();\n\n\treadonly #modulation = createModulation(this.#dial, 'dial', (isModulated) => {\n\t\tthis.toggleState('modulated', isModulated);\n\t});\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: dial, marks: requireChild(dial, '.sonic-dial-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(dial, 'dial.css', { property: 'margin-bottom', selector: '[popover]' });\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(dial, '--_sonic-dial-travel'),\n\t\t}));\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#modulation.draw({\n\t\t\tmapping: this.mapping(),\n\t\t\tmodulation: this.endless ? 0 : this.modulation,\n\t\t\torigin: this.originValue(),\n\t\t\tvalue: this.value,\n\t\t});\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n\n\tprotected override isWrapping(): boolean {\n\t\treturn this.endless;\n\t}\n}\n"],"mappings":";;;;;;AAaA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;;IAqBX,cACD;AAEA,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;CACD;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;;CAIA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;;CAIA,IAAI,kBAAsC;EACzC,OAAO,KAAK,YAAY,MAAM;CAC/B;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,YAAY,MAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAS;CAC1E;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAS,cAAc,iBAAiB,KAAK,OAAO,SAAS,gBAAgB;EAC5E,KAAK,YAAY,aAAa,WAAW;CAC1C,CAAC;CAED,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,WAAW,mBAAmB,YAAY;EACjE,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,UACC,MACA;GAAE,SAAS;GAAM,OAAO,aAAa,MAAM,qBAAqB,cAAc;EAAE,GAChF,MACD;EACA,KAAK,OAAO;EAEX,KAAK,YAAY,MAAM,YAAY;GAAE,UAAU;GAAiB,UAAU;EAAY,CAAC;EACxF,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,MAAM,sBAAsB;EACrD,EAAE;CACH;CAEA,AAAU,OAAa;EACtB,KAAK,YAAY,KAAK;GACrB,SAAS,KAAK,QAAQ;GACtB,YAAY,KAAK,UAAU,IAAI,KAAK;GACpC,QAAQ,KAAK,YAAY;GACzB,OAAO,KAAK;EACb,CAAC;CACF;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,aAAsB;EACxC,OAAO,KAAK;CACb;AACD"}
|
|
1
|
+
{"version":3,"file":"dial.js","names":[],"sources":["../../../src/elements/dial.ts"],"sourcesContent":["import { createModulation } from '#elements/modulation.ts';\nimport { bindScale } from '#elements/scale.ts';\nimport { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-dial': SonicDial;\n\t}\n}\n\nconst renderDial = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-dial\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-dial-scale\" aria-hidden=\"true\"></div>\n\t\t\t<div class=\"sonic-dial-notches\"></div>\n\t\t\t<div class=\"sonic-dial-ring\"></div>\n\t\t\t<div class=\"sonic-dial-modulation\"></div>\n\t\t\t<div class=\"sonic-dial-cap\"></div>\n\t\t\t<div class=\"sonic-dial-indicator\"></div>\n\t\t\t<div class=\"sonic-dial-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-dial-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 SonicDial extends SonicValueElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicValueElement.observedAttributes,\n\t\t'endless',\n\t\t'modulation',\n\t];\n\n\tget endless(): boolean {\n\t\treturn this.hasAttribute('endless');\n\t}\n\n\tset endless(isEndless: boolean) {\n\t\tthis.reflect('endless', isEndless);\n\t}\n\n\t/** How far a modulation source can push the value, in value units; signed */\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\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\t/** Where the value is right now with modulation applied; a property only, cheap to write every frame, fires nothing */\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget modulationValue(): number | undefined {\n\t\treturn this.#modulation.value();\n\t}\n\n\tset modulationValue(value: null | number | undefined) {\n\t\tthis.#modulation.write(value, this.isBound() ? this.mapping() : undefined);\n\t}\n\n\treadonly #dial = renderDial();\n\n\treadonly #modulation = createModulation(this.#dial, 'dial', (isModulated) => {\n\t\tthis.toggleState('modulated', isModulated);\n\t});\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('endless', 'modulationValue', 'modulation');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tbindScale(\n\t\t\tthis,\n\t\t\t{ control: dial, marks: requireChild(dial, '.sonic-dial-scale', HTMLDivElement) },\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(dial, 'dial.css', { property: 'margin-bottom', selector: '[popover]' });\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(dial, '--_sonic-dial-travel'),\n\t\t}));\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#modulation.draw({\n\t\t\tmapping: this.mapping(),\n\t\t\tmodulation: this.endless ? 0 : this.modulation,\n\t\t\torigin: this.originValue(),\n\t\t\tvalue: this.value,\n\t\t});\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n\n\tprotected override isWrapping(): boolean {\n\t\treturn this.endless;\n\t}\n\n\tprotected override stateTarget(): HTMLElement {\n\t\treturn this.#dial;\n\t}\n}\n"],"mappings":";;;;;;AAaA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;;IAqBX,cACD;AAEA,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB;EAC7C,GAAG,kBAAkB;EACrB;EACA;CACD;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,aAAa,SAAS;CACnC;CAEA,IAAI,QAAQ,WAAoB;EAC/B,KAAK,QAAQ,WAAW,SAAS;CAClC;;CAIA,IAAI,aAAqB;EACxB,OAAO,KAAK,gBAAgB,cAAc,CAAC;CAC5C;CAEA,IAAI,WAAW,QAA4B;EAC1C,KAAK,QAAQ,cAAc,MAAM;CAClC;;CAIA,IAAI,kBAAsC;EACzC,OAAO,KAAK,YAAY,MAAM;CAC/B;CAEA,IAAI,gBAAgB,OAAkC;EACrD,KAAK,YAAY,MAAM,OAAO,KAAK,QAAQ,IAAI,KAAK,QAAQ,IAAI,MAAS;CAC1E;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAS,cAAc,iBAAiB,KAAK,OAAO,SAAS,gBAAgB;EAC5E,KAAK,YAAY,aAAa,WAAW;CAC1C,CAAC;CAED,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,WAAW,mBAAmB,YAAY;EACjE,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,UACC,MACA;GAAE,SAAS;GAAM,OAAO,aAAa,MAAM,qBAAqB,cAAc;EAAE,GAChF,MACD;EACA,KAAK,OAAO;EAEX,KAAK,YAAY,MAAM,YAAY;GAAE,UAAU;GAAiB,UAAU;EAAY,CAAC;EACxF,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,MAAM,sBAAsB;EACrD,EAAE;CACH;CAEA,AAAU,OAAa;EACtB,KAAK,YAAY,KAAK;GACrB,SAAS,KAAK,QAAQ;GACtB,YAAY,KAAK,UAAU,IAAI,KAAK;GACpC,QAAQ,KAAK,YAAY;GACzB,OAAO,KAAK;EACb,CAAC;CACF;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,aAAsB;EACxC,OAAO,KAAK;CACb;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;AACD"}
|
|
@@ -32,6 +32,7 @@ export declare class SonicEnvelope extends SonicElement {
|
|
|
32
32
|
attributeChangedCallback(name: string): void;
|
|
33
33
|
connectedCallback(): void;
|
|
34
34
|
protected connect(signal: AbortSignal): void;
|
|
35
|
+
protected stateTarget(): HTMLElement;
|
|
35
36
|
}
|
|
36
37
|
//#endregion
|
|
37
38
|
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -36,7 +36,7 @@ function stageParts(envelope, hook, stages) {
|
|
|
36
36
|
}
|
|
37
37
|
const renderEnvelope = template(`
|
|
38
38
|
<div class="sonic-envelope" aria-hidden="true">
|
|
39
|
-
<div class="sonic-envelope-
|
|
39
|
+
<div class="sonic-envelope-reticle"></div>
|
|
40
40
|
<svg class="sonic-envelope-graph" preserveAspectRatio="none" viewBox="0 0 1 1">
|
|
41
41
|
<path class="sonic-envelope-fill" />
|
|
42
42
|
<rect class="sonic-envelope-fill sonic-envelope-floor" y="1" />
|
|
@@ -123,11 +123,10 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
123
123
|
this.reflect("sustain", id);
|
|
124
124
|
}
|
|
125
125
|
#bindings = /* @__PURE__ */ new Map();
|
|
126
|
-
#envelope = renderEnvelope();
|
|
127
|
-
#bracket = requireChild(this.#envelope, ".sonic-envelope-bracket", HTMLDivElement);
|
|
128
126
|
#claim = new ReadoutClaim(() => {
|
|
129
127
|
this.#renderReadout();
|
|
130
128
|
});
|
|
129
|
+
#envelope = renderEnvelope();
|
|
131
130
|
#curveHandles = stageParts(this.#envelope, "sonic-envelope-curve", curveStages);
|
|
132
131
|
#fill = requireChild(this.#envelope, ".sonic-envelope-fill", SVGElement);
|
|
133
132
|
#floor = requireChild(this.#envelope, ".sonic-envelope-floor", SVGElement);
|
|
@@ -138,6 +137,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
138
137
|
#isDriving = false;
|
|
139
138
|
#line = requireChild(this.#envelope, ".sonic-envelope-line", SVGElement);
|
|
140
139
|
#readout = new Readout(requireChild(this.#envelope, ".sonic-envelope-readout", HTMLDivElement));
|
|
140
|
+
#reticle = requireChild(this.#envelope, ".sonic-envelope-reticle", HTMLDivElement);
|
|
141
141
|
#shape = adsrShape({});
|
|
142
142
|
#waiting = /* @__PURE__ */ new Set();
|
|
143
143
|
#warned = /* @__PURE__ */ new Set();
|
|
@@ -168,6 +168,9 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
168
168
|
this.#unbind();
|
|
169
169
|
}, { once: true });
|
|
170
170
|
}
|
|
171
|
+
stateTarget() {
|
|
172
|
+
return this.#envelope;
|
|
173
|
+
}
|
|
171
174
|
#bind() {
|
|
172
175
|
this.#gesture?.end();
|
|
173
176
|
this.#unbind();
|
|
@@ -233,15 +236,9 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
233
236
|
at: shape.curveHandles.find((point) => point.stage === stage),
|
|
234
237
|
drives: this.#curveDrives(stage)
|
|
235
238
|
});
|
|
236
|
-
this.#
|
|
239
|
+
this.#drawReticle();
|
|
237
240
|
this.#renderReadout();
|
|
238
241
|
}
|
|
239
|
-
#drawBracket() {
|
|
240
|
-
const part = this.#held;
|
|
241
|
-
if (!part) return;
|
|
242
|
-
for (const name of ["--_sonic-envelope-x", "--_sonic-envelope-y"]) this.#bracket.style.setProperty(name, part.style.getPropertyValue(name));
|
|
243
|
-
writeAttribute(this.#bracket, "data-sonic-part", part.classList.contains("sonic-envelope-curve") ? "curve" : void 0);
|
|
244
|
-
}
|
|
245
242
|
#drawPoint(part, point) {
|
|
246
243
|
const { at, drives } = point;
|
|
247
244
|
writeAttribute(part, "hidden", at ? void 0 : "");
|
|
@@ -252,6 +249,12 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
252
249
|
writeAttribute(part, "data-sonic-axes", drives.map(({ axis }) => axis).join(""));
|
|
253
250
|
writeAttribute(part, "data-sonic-disabled", isStill ? "" : void 0);
|
|
254
251
|
}
|
|
252
|
+
#drawReticle() {
|
|
253
|
+
const part = this.#held;
|
|
254
|
+
if (!part) return;
|
|
255
|
+
for (const name of ["--_sonic-envelope-x", "--_sonic-envelope-y"]) this.#reticle.style.setProperty(name, part.style.getPropertyValue(name));
|
|
256
|
+
writeAttribute(this.#reticle, "data-sonic-part", part.classList.contains("sonic-envelope-curve") ? "curve" : void 0);
|
|
257
|
+
}
|
|
255
258
|
#drives(stage) {
|
|
256
259
|
const x = this.#bindings.get(stage);
|
|
257
260
|
const y = stage === "decay" ? this.#bindings.get("sustain") : void 0;
|
|
@@ -267,7 +270,7 @@ var SonicEnvelope = class extends SonicElement {
|
|
|
267
270
|
const pressed = this.#pressed(event);
|
|
268
271
|
if (!pressed || pressed.drives.length === 0) return void 0;
|
|
269
272
|
this.#held = pressed.part;
|
|
270
|
-
this.#
|
|
273
|
+
this.#drawReticle();
|
|
271
274
|
const box = this.#graph.getBoundingClientRect();
|
|
272
275
|
const travel = {
|
|
273
276
|
x: Math.max(1, box.width * this.#shape.share),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope.js","names":[],"sources":["../../../src/elements/envelope.ts"],"sourcesContent":["import type { FieldGesture, FieldHold } from '#elements/field-gesture.ts';\nimport type { ValueLink } from '#elements/value-element.ts';\nimport type { AdsrCurves, AdsrProportions, AdsrShape } from '#lib/adsr-shape.ts';\nimport type { FieldAxis, FieldPoint } from '#lib/field.ts';\n\nimport { bindFieldGesture } from '#elements/field-gesture.ts';\nimport { ReadoutClaim } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { linkValue, SonicValueElement } from '#elements/value-element.ts';\nimport { adsrPath, adsrShape, adsrStages, timeStages } from '#lib/adsr-shape.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-envelope': SonicEnvelope;\n\t}\n}\n\ninterface Binding {\n\telement: SonicValueElement;\n\tlink: ValueLink;\n\tunwatch: () => void;\n}\n\ninterface Drive {\n\taxis: FieldAxis;\n\tbinding: Binding;\n}\n\ninterface HandleHold extends FieldHold {\n\tdrives: Array<Drive>;\n\tisFlipped: boolean;\n\tpart: HTMLElement;\n}\n\nconst curveStages = ['attack', 'decay', 'release'] as const;\n\nconst bindable = [...adsrStages, 'attack-curve', 'decay-curve', 'release-curve'] as const;\n\nconst bindableNames = new Set<string>(bindable);\n\nfunction isParent(node: Node): node is Node & ParentNode {\n\treturn 'querySelector' in node;\n}\n\nconst emptyPath = { fill: '', floor: { from: 0, to: 0 }, stroke: '' };\n\nfunction stageParts<Stage extends string>(\n\tenvelope: Element,\n\thook: string,\n\tstages: ReadonlyArray<Stage>,\n): Map<Stage, HTMLDivElement> {\n\treturn new Map(\n\t\tstages.map((stage) => [\n\t\t\tstage,\n\t\t\trequireChild(envelope, `.${hook}[data-sonic-stage=\"${stage}\"]`, HTMLDivElement),\n\t\t]),\n\t);\n}\n\nconst renderEnvelope = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-envelope\" aria-hidden=\"true\">\n\t\t\t<div class=\"sonic-envelope-bracket\"></div>\n\t\t\t<svg class=\"sonic-envelope-graph\" preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">\n\t\t\t\t<path class=\"sonic-envelope-fill\" />\n\t\t\t\t<rect class=\"sonic-envelope-fill sonic-envelope-floor\" y=\"1\" />\n\t\t\t\t<path class=\"sonic-envelope-line\" />\n\t\t\t</svg>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"delay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"hold\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-readout\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicEnvelope extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t...bindable,\n\t\t'readout',\n\t];\n\n\tget attack(): string {\n\t\treturn this.getAttribute('attack') ?? '';\n\t}\n\n\tset attack(id: string | undefined) {\n\t\tthis.reflect('attack', id);\n\t}\n\n\tget attackCurve(): string {\n\t\treturn this.getAttribute('attack-curve') ?? '';\n\t}\n\n\tset attackCurve(id: string | undefined) {\n\t\tthis.reflect('attack-curve', id);\n\t}\n\n\tget decay(): string {\n\t\treturn this.getAttribute('decay') ?? '';\n\t}\n\n\tset decay(id: string | undefined) {\n\t\tthis.reflect('decay', id);\n\t}\n\n\tget decayCurve(): string {\n\t\treturn this.getAttribute('decay-curve') ?? '';\n\t}\n\n\tset decayCurve(id: string | undefined) {\n\t\tthis.reflect('decay-curve', id);\n\t}\n\n\tget delay(): string {\n\t\treturn this.getAttribute('delay') ?? '';\n\t}\n\n\tset delay(id: string | undefined) {\n\t\tthis.reflect('delay', id);\n\t}\n\n\tget hold(): string {\n\t\treturn this.getAttribute('hold') ?? '';\n\t}\n\n\tset hold(id: string | undefined) {\n\t\tthis.reflect('hold', id);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget pointerType(): string | undefined {\n\t\treturn this.#gesture?.pointerType();\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isOn: boolean) {\n\t\tthis.reflect('readout', isOn);\n\t}\n\n\tget release(): string {\n\t\treturn this.getAttribute('release') ?? '';\n\t}\n\n\tset release(id: string | undefined) {\n\t\tthis.reflect('release', id);\n\t}\n\n\tget releaseCurve(): string {\n\t\treturn this.getAttribute('release-curve') ?? '';\n\t}\n\n\tset releaseCurve(id: string | undefined) {\n\t\tthis.reflect('release-curve', id);\n\t}\n\n\tget sustain(): string {\n\t\treturn this.getAttribute('sustain') ?? '';\n\t}\n\n\tset sustain(id: string | undefined) {\n\t\tthis.reflect('sustain', id);\n\t}\n\n\treadonly #bindings = new Map<string, Binding>();\n\n\treadonly #envelope = renderEnvelope();\n\n\treadonly #bracket = requireChild(this.#envelope, '.sonic-envelope-bracket', HTMLDivElement);\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\treadonly #curveHandles = stageParts(this.#envelope, 'sonic-envelope-curve', curveStages);\n\n\treadonly #fill = requireChild(this.#envelope, '.sonic-envelope-fill', SVGElement);\n\n\treadonly #floor = requireChild(this.#envelope, '.sonic-envelope-floor', SVGElement);\n\n\t#gesture: FieldGesture<HandleHold> | undefined;\n\n\treadonly #graph = requireChild(this.#envelope, '.sonic-envelope-graph', SVGElement);\n\n\treadonly #handles = stageParts(this.#envelope, 'sonic-envelope-handle', timeStages);\n\n\t#held: HTMLElement | undefined;\n\n\t#isDriving = false;\n\n\treadonly #line = requireChild(this.#envelope, '.sonic-envelope-line', SVGElement);\n\n\treadonly #readout = new Readout(\n\t\trequireChild(this.#envelope, '.sonic-envelope-readout', HTMLDivElement),\n\t);\n\n\t#shape: AdsrShape = adsrShape({});\n\n\treadonly #waiting = new Set<unknown>();\n\n\treadonly #warned = new Set<string>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (!this.isBound()) return;\n\t\tif (name === 'disabled') {\n\t\t\tthis.#gesture?.end();\n\t\t\tthis.#draw();\n\t\t\treturn;\n\t\t}\n\t\tif (bindableNames.has(name)) this.#bind();\n\t\telse this.#renderReadout();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(...adsrStages, 'attackCurve', 'decayCurve', 'readout', 'releaseCurve');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst envelope = this.#envelope;\n\n\t\tthis.keepControl(envelope, signal);\n\t\tthis.#bind();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(envelope, 'envelope.css', {\n\t\t\t\tproperty: 'margin-bottom',\n\t\t\t\tselector: '[popover]',\n\t\t\t});\n\t\tthis.#bindPointer(envelope, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#unbind();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#bind(): void {\n\t\tthis.#gesture?.end();\n\t\tthis.#unbind();\n\t\tfor (const name of bindable) {\n\t\t\tconst element = this.#resolve(name);\n\t\t\tif (!element) continue;\n\n\t\t\tconst link = linkValue(element);\n\t\t\tconst unwatch = link.model.watch(() => {\n\t\t\t\tif (!this.#isDriving) this.#draw();\n\t\t\t});\n\n\t\t\tthis.#bindings.set(name, { element, link, unwatch });\n\t\t}\n\t\tthis.#draw();\n\t}\n\n\t#bindPointer(envelope: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#gesture = bindFieldGesture(\n\t\t\tenvelope,\n\t\t\t{\n\t\t\t\tclaim: this.#claim,\n\t\t\t\tgrab: (event) => this.#grab(event),\n\t\t\t\tinput: ({ drives }, next) => {\n\t\t\t\t\tthis.#isDriving = true;\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tconst value = next[axis];\n\n\t\t\t\t\t\tif (value !== undefined) binding.link.input(value);\n\t\t\t\t\t}\n\t\t\t\t\tthis.#isDriving = false;\n\t\t\t\t\tthis.#draw();\n\t\t\t\t},\n\t\t\t\trelease: ({ drives }, moved) => {\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tif (!moved.includes(axis)) continue;\n\n\t\t\t\t\t\tbinding.link.change();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\tthis.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#curveDrives(stage: string): Array<Drive> {\n\t\tconst binding = this.#bindings.get(`${stage}-curve`);\n\n\t\treturn binding ? [{ axis: 'y', binding }] : [];\n\t}\n\n\t#draw(): void {\n\t\tconst shape = this.#readShape();\n\t\tconst path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);\n\n\t\tthis.#shape = shape;\n\t\twriteAttribute(this.#line, 'd', path.stroke);\n\t\twriteAttribute(this.#fill, 'd', path.fill);\n\t\twriteAttribute(this.#floor, 'x', String(path.floor.from));\n\t\twriteAttribute(this.#floor, 'width', String(path.floor.to - path.floor.from));\n\t\tfor (const [stage, part] of this.#handles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.handles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#drives(stage),\n\t\t\t});\n\t\t}\n\t\tfor (const [stage, part] of this.#curveHandles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.curveHandles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#curveDrives(stage),\n\t\t\t});\n\t\t}\n\t\tthis.#drawBracket();\n\t\tthis.#renderReadout();\n\t}\n\n\t#drawBracket(): void {\n\t\tconst part = this.#held;\n\t\tif (!part) return;\n\n\t\tfor (const name of ['--_sonic-envelope-x', '--_sonic-envelope-y']) {\n\t\t\tthis.#bracket.style.setProperty(name, part.style.getPropertyValue(name));\n\t\t}\n\t\twriteAttribute(\n\t\t\tthis.#bracket,\n\t\t\t'data-sonic-part',\n\t\t\tpart.classList.contains('sonic-envelope-curve') ? 'curve' : undefined,\n\t\t);\n\t}\n\n\t#drawPoint(\n\t\tpart: HTMLDivElement,\n\t\tpoint: { at: FieldPoint | undefined; drives: Array<Drive> },\n\t): void {\n\t\tconst { at, drives } = point;\n\n\t\twriteAttribute(part, 'hidden', at ? undefined : '');\n\t\tif (!at) return;\n\n\t\tconst isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());\n\n\t\tpart.style.setProperty('--_sonic-envelope-x', String(at.x));\n\t\tpart.style.setProperty('--_sonic-envelope-y', String(at.y));\n\t\twriteAttribute(part, 'data-sonic-axes', drives.map(({ axis }) => axis).join(''));\n\t\twriteAttribute(part, 'data-sonic-disabled', isStill ? '' : undefined);\n\t}\n\n\t#drives(stage: string): Array<Drive> {\n\t\tconst x = this.#bindings.get(stage);\n\t\tconst y = stage === 'decay' ? this.#bindings.get('sustain') : undefined;\n\n\t\treturn [\n\t\t\t...(x ? [{ axis: 'x' as const, binding: x }] : []),\n\t\t\t...(y ? [{ axis: 'y' as const, binding: y }] : []),\n\t\t];\n\t}\n\n\t#grab(event: PointerEvent): HandleHold | undefined {\n\t\tconst pressed = this.#pressed(event);\n\t\tif (!pressed || pressed.drives.length === 0) return undefined;\n\n\t\tthis.#held = pressed.part;\n\t\tthis.#drawBracket();\n\n\t\tconst box = this.#graph.getBoundingClientRect();\n\t\tconst travel = { x: Math.max(1, box.width * this.#shape.share), y: Math.max(1, box.height) };\n\t\tconst axes: HandleHold['axes'] = {};\n\n\t\tfor (const { axis, binding } of pressed.drives) {\n\t\t\taxes[axis] = { model: binding.link.model, travelPx: travel[axis] };\n\t\t}\n\n\t\treturn { ...pressed, axes };\n\t}\n\n\t#pressed(event: PointerEvent): Omit<HandleHold, 'axes'> | undefined {\n\t\tconst part =\n\t\t\tevent.target instanceof Element\n\t\t\t\t? event.target.closest<HTMLElement>('.sonic-envelope-handle, .sonic-envelope-curve')\n\t\t\t\t: undefined;\n\t\tconst stage = part?.dataset.sonicStage;\n\t\tif (!part || stage === undefined || this.isDisabled()) return undefined;\n\n\t\tconst isCurveHandle = part.classList.contains('sonic-envelope-curve');\n\t\tconst drives = isCurveHandle ? this.#curveDrives(stage) : this.#drives(stage);\n\n\t\treturn {\n\t\t\tdrives: drives.filter(({ binding }) => !binding.link.isDisabled()),\n\t\t\tisFlipped: isCurveHandle && stage === 'attack',\n\t\t\tpart,\n\t\t};\n\t}\n\n\t#readShape(): AdsrShape {\n\t\tconst proportions: AdsrProportions = {};\n\t\tconst curves: AdsrCurves = {};\n\n\t\tfor (const stage of adsrStages) {\n\t\t\tconst model = this.#bindings.get(stage)?.link.model;\n\n\t\t\tproportions[stage] = model?.mapping().proportionOf(model.value);\n\t\t}\n\t\tfor (const stage of curveStages) {\n\t\t\tcurves[stage] = this.#bindings.get(`${stage}-curve`)?.link.model.value;\n\t\t}\n\n\t\treturn adsrShape(proportions, curves);\n\t}\n\n\t#rebindOnce(key: unknown, arrives: (rebind: () => void) => void): void {\n\t\tif (this.#waiting.has(key)) return;\n\n\t\tthis.#waiting.add(key);\n\t\tarrives(() => {\n\t\t\tthis.#waiting.delete(key);\n\t\t\tif (this.isBound()) this.#bind();\n\t\t});\n\t}\n\n\t#renderReadout(): void {\n\t\tconst drag = this.#gesture?.current();\n\n\t\tif (!drag) {\n\t\t\tthis.#readout.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#readout.show({\n\t\t\tanchor: drag.part,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: drag.drives.map(({ binding }) => binding.element.valueText).join(', '),\n\t\t});\n\t}\n\n\t#resolve(name: string): SonicValueElement | undefined {\n\t\tconst id = this.getAttribute(name);\n\t\tconst root = this.getRootNode();\n\t\tif (!id || !isParent(root)) return undefined;\n\n\t\tconst found = root.querySelector(`#${CSS.escape(id)}`);\n\t\tif (found instanceof SonicValueElement) return found;\n\n\t\tthis.#wait(id, found);\n\n\t\treturn undefined;\n\t}\n\n\t#unbind(): void {\n\t\tfor (const { unwatch } of this.#bindings.values()) unwatch();\n\n\t\tthis.#bindings.clear();\n\t}\n\n\t#wait(id: string, found: Element | null): void {\n\t\tconst tag = found?.localName;\n\t\tconst { ownerDocument } = this;\n\n\t\tif (tag?.includes('-') && !customElements.get(tag)) {\n\t\t\tthis.#rebindOnce(tag, (rebind) => {\n\t\t\t\tvoid customElements.whenDefined(tag).then(rebind);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!found && ownerDocument.readyState === 'loading') {\n\t\t\tthis.#rebindOnce(ownerDocument, (rebind) => {\n\t\t\t\townerDocument.addEventListener('DOMContentLoaded', rebind, { once: true });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#warn(id);\n\t}\n\n\t#warn(id: string): void {\n\t\tif (!__DEV__) return;\n\t\tif (this.#warned.has(id)) return;\n\n\t\tthis.#warned.add(id);\n\t\tconsole.warn(`<sonic-envelope> found no dial, slider or number box with the id \"${id}\"`);\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"envelope.js","names":[],"sources":["../../../src/elements/envelope.ts"],"sourcesContent":["import type { FieldGesture, FieldHold } from '#elements/field-gesture.ts';\nimport type { ValueLink } from '#elements/value-element.ts';\nimport type { AdsrCurves, AdsrProportions, AdsrShape } from '#lib/adsr-shape.ts';\nimport type { FieldAxis, FieldPoint } from '#lib/field.ts';\n\nimport { bindFieldGesture } from '#elements/field-gesture.ts';\nimport { ReadoutClaim } from '#elements/readout-claim.ts';\nimport { Readout } from '#elements/readout.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { linkValue, SonicValueElement } from '#elements/value-element.ts';\nimport { adsrPath, adsrShape, adsrStages, timeStages } from '#lib/adsr-shape.ts';\nimport { requireChild, template } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-envelope': SonicEnvelope;\n\t}\n}\n\ninterface Binding {\n\telement: SonicValueElement;\n\tlink: ValueLink;\n\tunwatch: () => void;\n}\n\ninterface Drive {\n\taxis: FieldAxis;\n\tbinding: Binding;\n}\n\ninterface HandleHold extends FieldHold {\n\tdrives: Array<Drive>;\n\tisFlipped: boolean;\n\tpart: HTMLElement;\n}\n\nconst curveStages = ['attack', 'decay', 'release'] as const;\n\nconst bindable = [...adsrStages, 'attack-curve', 'decay-curve', 'release-curve'] as const;\n\nconst bindableNames = new Set<string>(bindable);\n\nfunction isParent(node: Node): node is Node & ParentNode {\n\treturn 'querySelector' in node;\n}\n\nconst emptyPath = { fill: '', floor: { from: 0, to: 0 }, stroke: '' };\n\nfunction stageParts<Stage extends string>(\n\tenvelope: Element,\n\thook: string,\n\tstages: ReadonlyArray<Stage>,\n): Map<Stage, HTMLDivElement> {\n\treturn new Map(\n\t\tstages.map((stage) => [\n\t\t\tstage,\n\t\t\trequireChild(envelope, `.${hook}[data-sonic-stage=\"${stage}\"]`, HTMLDivElement),\n\t\t]),\n\t);\n}\n\nconst renderEnvelope = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-envelope\" aria-hidden=\"true\">\n\t\t\t<div class=\"sonic-envelope-reticle\"></div>\n\t\t\t<svg class=\"sonic-envelope-graph\" preserveAspectRatio=\"none\" viewBox=\"0 0 1 1\">\n\t\t\t\t<path class=\"sonic-envelope-fill\" />\n\t\t\t\t<rect class=\"sonic-envelope-fill sonic-envelope-floor\" y=\"1\" />\n\t\t\t\t<path class=\"sonic-envelope-line\" />\n\t\t\t</svg>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"delay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"hold\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-handle\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"attack\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"decay\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-curve\" data-sonic-stage=\"release\" hidden></div>\n\t\t\t<div class=\"sonic-envelope-readout\" popover=\"manual\"><span></span></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicEnvelope extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t...bindable,\n\t\t'readout',\n\t];\n\n\tget attack(): string {\n\t\treturn this.getAttribute('attack') ?? '';\n\t}\n\n\tset attack(id: string | undefined) {\n\t\tthis.reflect('attack', id);\n\t}\n\n\tget attackCurve(): string {\n\t\treturn this.getAttribute('attack-curve') ?? '';\n\t}\n\n\tset attackCurve(id: string | undefined) {\n\t\tthis.reflect('attack-curve', id);\n\t}\n\n\tget decay(): string {\n\t\treturn this.getAttribute('decay') ?? '';\n\t}\n\n\tset decay(id: string | undefined) {\n\t\tthis.reflect('decay', id);\n\t}\n\n\tget decayCurve(): string {\n\t\treturn this.getAttribute('decay-curve') ?? '';\n\t}\n\n\tset decayCurve(id: string | undefined) {\n\t\tthis.reflect('decay-curve', id);\n\t}\n\n\tget delay(): string {\n\t\treturn this.getAttribute('delay') ?? '';\n\t}\n\n\tset delay(id: string | undefined) {\n\t\tthis.reflect('delay', id);\n\t}\n\n\tget hold(): string {\n\t\treturn this.getAttribute('hold') ?? '';\n\t}\n\n\tset hold(id: string | undefined) {\n\t\tthis.reflect('hold', id);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per property\n\tget pointerType(): string | undefined {\n\t\treturn this.#gesture?.pointerType();\n\t}\n\n\tget readout(): boolean {\n\t\treturn this.hasAttribute('readout');\n\t}\n\n\tset readout(isOn: boolean) {\n\t\tthis.reflect('readout', isOn);\n\t}\n\n\tget release(): string {\n\t\treturn this.getAttribute('release') ?? '';\n\t}\n\n\tset release(id: string | undefined) {\n\t\tthis.reflect('release', id);\n\t}\n\n\tget releaseCurve(): string {\n\t\treturn this.getAttribute('release-curve') ?? '';\n\t}\n\n\tset releaseCurve(id: string | undefined) {\n\t\tthis.reflect('release-curve', id);\n\t}\n\n\tget sustain(): string {\n\t\treturn this.getAttribute('sustain') ?? '';\n\t}\n\n\tset sustain(id: string | undefined) {\n\t\tthis.reflect('sustain', id);\n\t}\n\n\treadonly #bindings = new Map<string, Binding>();\n\n\treadonly #claim = new ReadoutClaim(() => {\n\t\tthis.#renderReadout();\n\t});\n\n\treadonly #envelope = renderEnvelope();\n\n\treadonly #curveHandles = stageParts(this.#envelope, 'sonic-envelope-curve', curveStages);\n\n\treadonly #fill = requireChild(this.#envelope, '.sonic-envelope-fill', SVGElement);\n\n\treadonly #floor = requireChild(this.#envelope, '.sonic-envelope-floor', SVGElement);\n\n\t#gesture: FieldGesture<HandleHold> | undefined;\n\n\treadonly #graph = requireChild(this.#envelope, '.sonic-envelope-graph', SVGElement);\n\n\treadonly #handles = stageParts(this.#envelope, 'sonic-envelope-handle', timeStages);\n\n\t#held: HTMLElement | undefined;\n\n\t#isDriving = false;\n\n\treadonly #line = requireChild(this.#envelope, '.sonic-envelope-line', SVGElement);\n\n\treadonly #readout = new Readout(\n\t\trequireChild(this.#envelope, '.sonic-envelope-readout', HTMLDivElement),\n\t);\n\n\treadonly #reticle = requireChild(this.#envelope, '.sonic-envelope-reticle', HTMLDivElement);\n\n\t#shape: AdsrShape = adsrShape({});\n\n\treadonly #waiting = new Set<unknown>();\n\n\treadonly #warned = new Set<string>();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (!this.isBound()) return;\n\t\tif (name === 'disabled') {\n\t\t\tthis.#gesture?.end();\n\t\t\tthis.#draw();\n\t\t\treturn;\n\t\t}\n\t\tif (bindableNames.has(name)) this.#bind();\n\t\telse this.#renderReadout();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties(...adsrStages, 'attackCurve', 'decayCurve', 'readout', 'releaseCurve');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst envelope = this.#envelope;\n\n\t\tthis.keepControl(envelope, signal);\n\t\tthis.#bind();\n\t\tif (__DEV__)\n\t\t\tthis.checkStyles(envelope, 'envelope.css', {\n\t\t\t\tproperty: 'margin-bottom',\n\t\t\t\tselector: '[popover]',\n\t\t\t});\n\t\tthis.#bindPointer(envelope, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#unbind();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected override stateTarget(): HTMLElement {\n\t\treturn this.#envelope;\n\t}\n\n\t#bind(): void {\n\t\tthis.#gesture?.end();\n\t\tthis.#unbind();\n\t\tfor (const name of bindable) {\n\t\t\tconst element = this.#resolve(name);\n\t\t\tif (!element) continue;\n\n\t\t\tconst link = linkValue(element);\n\t\t\tconst unwatch = link.model.watch(() => {\n\t\t\t\tif (!this.#isDriving) this.#draw();\n\t\t\t});\n\n\t\t\tthis.#bindings.set(name, { element, link, unwatch });\n\t\t}\n\t\tthis.#draw();\n\t}\n\n\t#bindPointer(envelope: HTMLElement, signal: AbortSignal): void {\n\t\tthis.#gesture = bindFieldGesture(\n\t\t\tenvelope,\n\t\t\t{\n\t\t\t\tclaim: this.#claim,\n\t\t\t\tgrab: (event) => this.#grab(event),\n\t\t\t\tinput: ({ drives }, next) => {\n\t\t\t\t\tthis.#isDriving = true;\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tconst value = next[axis];\n\n\t\t\t\t\t\tif (value !== undefined) binding.link.input(value);\n\t\t\t\t\t}\n\t\t\t\t\tthis.#isDriving = false;\n\t\t\t\t\tthis.#draw();\n\t\t\t\t},\n\t\t\t\trelease: ({ drives }, moved) => {\n\t\t\t\t\tfor (const { axis, binding } of drives) {\n\t\t\t\t\t\tif (!moved.includes(axis)) continue;\n\n\t\t\t\t\t\tbinding.link.change();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\ttoggle: (isDragging) => {\n\t\t\t\t\tthis.toggleState('dragging', isDragging);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#curveDrives(stage: string): Array<Drive> {\n\t\tconst binding = this.#bindings.get(`${stage}-curve`);\n\n\t\treturn binding ? [{ axis: 'y', binding }] : [];\n\t}\n\n\t#draw(): void {\n\t\tconst shape = this.#readShape();\n\t\tconst path = this.#bindings.size === 0 ? emptyPath : adsrPath(shape);\n\n\t\tthis.#shape = shape;\n\t\twriteAttribute(this.#line, 'd', path.stroke);\n\t\twriteAttribute(this.#fill, 'd', path.fill);\n\t\twriteAttribute(this.#floor, 'x', String(path.floor.from));\n\t\twriteAttribute(this.#floor, 'width', String(path.floor.to - path.floor.from));\n\t\tfor (const [stage, part] of this.#handles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.handles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#drives(stage),\n\t\t\t});\n\t\t}\n\t\tfor (const [stage, part] of this.#curveHandles) {\n\t\t\tthis.#drawPoint(part, {\n\t\t\t\tat: shape.curveHandles.find((point) => point.stage === stage),\n\t\t\t\tdrives: this.#curveDrives(stage),\n\t\t\t});\n\t\t}\n\t\tthis.#drawReticle();\n\t\tthis.#renderReadout();\n\t}\n\n\t#drawPoint(\n\t\tpart: HTMLDivElement,\n\t\tpoint: { at: FieldPoint | undefined; drives: Array<Drive> },\n\t): void {\n\t\tconst { at, drives } = point;\n\n\t\twriteAttribute(part, 'hidden', at ? undefined : '');\n\t\tif (!at) return;\n\n\t\tconst isStill = this.isDisabled() || drives.every(({ binding }) => binding.link.isDisabled());\n\n\t\tpart.style.setProperty('--_sonic-envelope-x', String(at.x));\n\t\tpart.style.setProperty('--_sonic-envelope-y', String(at.y));\n\t\twriteAttribute(part, 'data-sonic-axes', drives.map(({ axis }) => axis).join(''));\n\t\twriteAttribute(part, 'data-sonic-disabled', isStill ? '' : undefined);\n\t}\n\n\t#drawReticle(): void {\n\t\tconst part = this.#held;\n\t\tif (!part) return;\n\n\t\tfor (const name of ['--_sonic-envelope-x', '--_sonic-envelope-y']) {\n\t\t\tthis.#reticle.style.setProperty(name, part.style.getPropertyValue(name));\n\t\t}\n\t\twriteAttribute(\n\t\t\tthis.#reticle,\n\t\t\t'data-sonic-part',\n\t\t\tpart.classList.contains('sonic-envelope-curve') ? 'curve' : undefined,\n\t\t);\n\t}\n\n\t#drives(stage: string): Array<Drive> {\n\t\tconst x = this.#bindings.get(stage);\n\t\tconst y = stage === 'decay' ? this.#bindings.get('sustain') : undefined;\n\n\t\treturn [\n\t\t\t...(x ? [{ axis: 'x' as const, binding: x }] : []),\n\t\t\t...(y ? [{ axis: 'y' as const, binding: y }] : []),\n\t\t];\n\t}\n\n\t#grab(event: PointerEvent): HandleHold | undefined {\n\t\tconst pressed = this.#pressed(event);\n\t\tif (!pressed || pressed.drives.length === 0) return undefined;\n\n\t\tthis.#held = pressed.part;\n\t\tthis.#drawReticle();\n\n\t\tconst box = this.#graph.getBoundingClientRect();\n\t\tconst travel = { x: Math.max(1, box.width * this.#shape.share), y: Math.max(1, box.height) };\n\t\tconst axes: HandleHold['axes'] = {};\n\n\t\tfor (const { axis, binding } of pressed.drives) {\n\t\t\taxes[axis] = { model: binding.link.model, travelPx: travel[axis] };\n\t\t}\n\n\t\treturn { ...pressed, axes };\n\t}\n\n\t#pressed(event: PointerEvent): Omit<HandleHold, 'axes'> | undefined {\n\t\tconst part =\n\t\t\tevent.target instanceof Element\n\t\t\t\t? event.target.closest<HTMLElement>('.sonic-envelope-handle, .sonic-envelope-curve')\n\t\t\t\t: undefined;\n\t\tconst stage = part?.dataset.sonicStage;\n\t\tif (!part || stage === undefined || this.isDisabled()) return undefined;\n\n\t\tconst isCurveHandle = part.classList.contains('sonic-envelope-curve');\n\t\tconst drives = isCurveHandle ? this.#curveDrives(stage) : this.#drives(stage);\n\n\t\treturn {\n\t\t\tdrives: drives.filter(({ binding }) => !binding.link.isDisabled()),\n\t\t\tisFlipped: isCurveHandle && stage === 'attack',\n\t\t\tpart,\n\t\t};\n\t}\n\n\t#readShape(): AdsrShape {\n\t\tconst proportions: AdsrProportions = {};\n\t\tconst curves: AdsrCurves = {};\n\n\t\tfor (const stage of adsrStages) {\n\t\t\tconst model = this.#bindings.get(stage)?.link.model;\n\n\t\t\tproportions[stage] = model?.mapping().proportionOf(model.value);\n\t\t}\n\t\tfor (const stage of curveStages) {\n\t\t\tcurves[stage] = this.#bindings.get(`${stage}-curve`)?.link.model.value;\n\t\t}\n\n\t\treturn adsrShape(proportions, curves);\n\t}\n\n\t#rebindOnce(key: unknown, arrives: (rebind: () => void) => void): void {\n\t\tif (this.#waiting.has(key)) return;\n\n\t\tthis.#waiting.add(key);\n\t\tarrives(() => {\n\t\t\tthis.#waiting.delete(key);\n\t\t\tif (this.isBound()) this.#bind();\n\t\t});\n\t}\n\n\t#renderReadout(): void {\n\t\tconst drag = this.#gesture?.current();\n\n\t\tif (!drag) {\n\t\t\tthis.#readout.close();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#readout.show({\n\t\t\tanchor: drag.part,\n\t\t\tisOpen: this.#claim.isRevealed && this.readout,\n\t\t\ttext: drag.drives.map(({ binding }) => binding.element.valueText).join(', '),\n\t\t});\n\t}\n\n\t#resolve(name: string): SonicValueElement | undefined {\n\t\tconst id = this.getAttribute(name);\n\t\tconst root = this.getRootNode();\n\t\tif (!id || !isParent(root)) return undefined;\n\n\t\tconst found = root.querySelector(`#${CSS.escape(id)}`);\n\t\tif (found instanceof SonicValueElement) return found;\n\n\t\tthis.#wait(id, found);\n\n\t\treturn undefined;\n\t}\n\n\t#unbind(): void {\n\t\tfor (const { unwatch } of this.#bindings.values()) unwatch();\n\n\t\tthis.#bindings.clear();\n\t}\n\n\t#wait(id: string, found: Element | null): void {\n\t\tconst tag = found?.localName;\n\t\tconst { ownerDocument } = this;\n\n\t\tif (tag?.includes('-') && !customElements.get(tag)) {\n\t\t\tthis.#rebindOnce(tag, (rebind) => {\n\t\t\t\tvoid customElements.whenDefined(tag).then(rebind);\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tif (!found && ownerDocument.readyState === 'loading') {\n\t\t\tthis.#rebindOnce(ownerDocument, (rebind) => {\n\t\t\t\townerDocument.addEventListener('DOMContentLoaded', rebind, { once: true });\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#warn(id);\n\t}\n\n\t#warn(id: string): void {\n\t\tif (!__DEV__) return;\n\t\tif (this.#warned.has(id)) return;\n\n\t\tthis.#warned.add(id);\n\t\tconsole.warn(`<sonic-envelope> found no dial, slider or number box with the id \"${id}\"`);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ export declare abstract class SonicFormElement extends SonicElement {
|
|
|
16
16
|
protected namingLabels(): Array<Element>;
|
|
17
17
|
protected abstract render(): void;
|
|
18
18
|
protected abstract restoreState(state: string): void;
|
|
19
|
+
protected writeFormFields(fields: ReadonlyArray<[string, number]>, state: string): void;
|
|
19
20
|
protected writeFormValue(value: FormData | null | string, state: string): void;
|
|
20
21
|
}
|
|
21
22
|
//#endregion
|
|
@@ -47,6 +47,16 @@ var SonicFormElement = class extends SonicElement {
|
|
|
47
47
|
if (!internals || this.hasAttribute("aria-label") || this.hasAttribute("aria-labelledby")) return [];
|
|
48
48
|
return [...internals.labels].filter((label) => label instanceof Element);
|
|
49
49
|
}
|
|
50
|
+
writeFormFields(fields, state) {
|
|
51
|
+
const { name } = this;
|
|
52
|
+
if (name === "") {
|
|
53
|
+
this.writeFormValue(null, state);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const data = new FormData();
|
|
57
|
+
for (const [field, value] of fields) data.set(`${name}.${field}`, String(value));
|
|
58
|
+
this.writeFormValue(data, state);
|
|
59
|
+
}
|
|
50
60
|
writeFormValue(value, state) {
|
|
51
61
|
this.internals?.setFormValue(value, `${this.localName}:${state}`);
|
|
52
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-element.js","names":[],"sources":["../../../src/elements/form-element.ts"],"sourcesContent":["import { SonicElement } from '#elements/sonic-element.ts';\n\nexport abstract class SonicFormElement extends SonicElement {\n\tstatic readonly formAssociated = true;\n\n\tget form(): HTMLFormElement | null {\n\t\t// eslint-disable-next-line unicorn/no-null -- as on native inputs\n\t\treturn this.internals?.form ?? null;\n\t}\n\n\tget labels(): NodeList | null {\n\t\t// eslint-disable-next-line unicorn/no-null -- as on native inputs\n\t\treturn this.internals?.labels ?? null;\n\t}\n\n\tget name(): string {\n\t\treturn this.getAttribute('name') ?? '';\n\t}\n\n\tset name(value: string | undefined) {\n\t\tthis.reflect('name', value);\n\t}\n\n\tconstructor() {\n\t\tsuper();\n\n\t\t// Otherwise the host takes the label's name and shows as a named generic beside the control\n\t\tif (this.internals) this.internals.role = 'none';\n\n\t\t// A click inside the control bubbles here with its own target; only a label's click targets the host\n\t\tthis.addEventListener('click', (event) => {\n\t\t\tif (event.target === this) this.activate();\n\t\t});\n\n\t\t// No event fires when a label arrives, so focus picks it up\n\t\tthis.addEventListener('focusin', () => {\n\t\t\tthis.render();\n\t\t});\n\t}\n\n\tabstract attributeChangedCallback(name: string): void;\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('name');\n\t\tsuper.connectedCallback();\n\t}\n\n\t// Never writes the attribute, or re-enabling the fieldset would leave the control disabled\n\tformDisabledCallback(): void {\n\t\tthis.attributeChangedCallback('disabled');\n\t}\n\n\tformResetCallback(): void {\n\t\tthis.attributeChangedCallback('value');\n\t}\n\n\t// Firefox hands a restored state to the wrong control when tags upgrade out of document order\n\tformStateRestoreCallback(state: File | FormData | null | string): void {\n\t\tconst prefix = `${this.localName}:`;\n\n\t\tif (typeof state === 'string' && state.startsWith(prefix)) {\n\t\t\tthis.restoreState(state.slice(prefix.length));\n\t\t}\n\t}\n\n\tprotected activate(): void {\n\t\tthis.focus();\n\t}\n\n\tprotected override namingLabels(): Array<Element> {\n\t\tconst internals = this.internals;\n\t\tif (!internals || this.hasAttribute('aria-label') || this.hasAttribute('aria-labelledby')) {\n\t\t\treturn [];\n\t\t}\n\n\t\treturn [...internals.labels].filter((label) => label instanceof Element);\n\t}\n\n\tprotected abstract render(): void;\n\n\tprotected abstract restoreState(state: string): void;\n\n\tprotected writeFormValue(value: FormData | null | string, state: string): void {\n\t\tthis.internals?.setFormValue(value, `${this.localName}:${state}`);\n\t}\n}\n"],"mappings":";;;AAEA,IAAsB,mBAAtB,cAA+C,aAAa;CAC3D,OAAgB,iBAAiB;CAEjC,IAAI,OAA+B;EAElC,OAAO,KAAK,WAAW,QAAQ;CAChC;CAEA,IAAI,SAA0B;EAE7B,OAAO,KAAK,WAAW,UAAU;CAClC;CAEA,IAAI,OAAe;EAClB,OAAO,KAAK,aAAa,MAAM,KAAK;CACrC;CAEA,IAAI,KAAK,OAA2B;EACnC,KAAK,QAAQ,QAAQ,KAAK;CAC3B;CAEA,cAAc;EACb,MAAM;EAGN,IAAI,KAAK,WAAW,KAAK,UAAU,OAAO;EAG1C,KAAK,iBAAiB,UAAU,UAAU;GACzC,IAAI,MAAM,WAAW,MAAM,KAAK,SAAS;EAC1C,CAAC;EAGD,KAAK,iBAAiB,iBAAiB;GACtC,KAAK,OAAO;EACb,CAAC;CACF;CAIA,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,MAAM;EAC7B,MAAM,kBAAkB;CACzB;CAGA,uBAA6B;EAC5B,KAAK,yBAAyB,UAAU;CACzC;CAEA,oBAA0B;EACzB,KAAK,yBAAyB,OAAO;CACtC;CAGA,yBAAyB,OAA8C;EACtE,MAAM,SAAS,GAAG,KAAK,UAAU;EAEjC,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,MAAM,GACvD,KAAK,aAAa,MAAM,MAAM,OAAO,MAAM,CAAC;CAE9C;CAEA,AAAU,WAAiB;EAC1B,KAAK,MAAM;CACZ;CAEA,AAAmB,eAA+B;EACjD,MAAM,YAAY,KAAK;EACvB,IAAI,CAAC,aAAa,KAAK,aAAa,YAAY,KAAK,KAAK,aAAa,iBAAiB,GACvF,OAAO,CAAC;EAGT,OAAO,CAAC,GAAG,UAAU,MAAM,CAAC,CAAC,QAAQ,UAAU,iBAAiB,OAAO;CACxE;CAMA,AAAU,eAAe,OAAiC,OAAqB;EAC9E,KAAK,WAAW,aAAa,OAAO,GAAG,KAAK,UAAU,GAAG,OAAO;CACjE;AACD"}
|
|
1
|
+
{"version":3,"file":"form-element.js","names":[],"sources":["../../../src/elements/form-element.ts"],"sourcesContent":["import { SonicElement } from '#elements/sonic-element.ts';\n\nexport abstract class SonicFormElement extends SonicElement {\n\tstatic readonly formAssociated = true;\n\n\tget form(): HTMLFormElement | null {\n\t\t// eslint-disable-next-line unicorn/no-null -- as on native inputs\n\t\treturn this.internals?.form ?? null;\n\t}\n\n\tget labels(): NodeList | null {\n\t\t// eslint-disable-next-line unicorn/no-null -- as on native inputs\n\t\treturn this.internals?.labels ?? null;\n\t}\n\n\tget name(): string {\n\t\treturn this.getAttribute('name') ?? '';\n\t}\n\n\tset name(value: string | undefined) {\n\t\tthis.reflect('name', value);\n\t}\n\n\tconstructor() {\n\t\tsuper();\n\n\t\t// Otherwise the host takes the label's name and shows as a named generic beside the control\n\t\tif (this.internals) this.internals.role = 'none';\n\n\t\t// A click inside the control bubbles here with its own target; only a label's click targets the host\n\t\tthis.addEventListener('click', (event) => {\n\t\t\tif (event.target === this) this.activate();\n\t\t});\n\n\t\t// No event fires when a label arrives, so focus picks it up\n\t\tthis.addEventListener('focusin', () => {\n\t\t\tthis.render();\n\t\t});\n\t}\n\n\tabstract attributeChangedCallback(name: string): void;\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('name');\n\t\tsuper.connectedCallback();\n\t}\n\n\t// Never writes the attribute, or re-enabling the fieldset would leave the control disabled\n\tformDisabledCallback(): void {\n\t\tthis.attributeChangedCallback('disabled');\n\t}\n\n\tformResetCallback(): void {\n\t\tthis.attributeChangedCallback('value');\n\t}\n\n\t// Firefox hands a restored state to the wrong control when tags upgrade out of document order\n\tformStateRestoreCallback(state: File | FormData | null | string): void {\n\t\tconst prefix = `${this.localName}:`;\n\n\t\tif (typeof state === 'string' && state.startsWith(prefix)) {\n\t\t\tthis.restoreState(state.slice(prefix.length));\n\t\t}\n\t}\n\n\tprotected activate(): void {\n\t\tthis.focus();\n\t}\n\n\tprotected override namingLabels(): Array<Element> {\n\t\tconst internals = this.internals;\n\t\tif (!internals || this.hasAttribute('aria-label') || this.hasAttribute('aria-labelledby')) {\n\t\t\treturn [];\n\t\t}\n\n\t\treturn [...internals.labels].filter((label) => label instanceof Element);\n\t}\n\n\tprotected abstract render(): void;\n\n\tprotected abstract restoreState(state: string): void;\n\n\tprotected writeFormFields(fields: ReadonlyArray<[string, number]>, state: string): void {\n\t\tconst { name } = this;\n\n\t\tif (name === '') {\n\t\t\t// eslint-disable-next-line unicorn/no-null -- `null` submits nothing\n\t\t\tthis.writeFormValue(null, state);\n\t\t\treturn;\n\t\t}\n\n\t\tconst data = new FormData();\n\n\t\tfor (const [field, value] of fields) {\n\t\t\tdata.set(`${name}.${field}`, String(value));\n\t\t}\n\t\tthis.writeFormValue(data, state);\n\t}\n\n\tprotected writeFormValue(value: FormData | null | string, state: string): void {\n\t\tthis.internals?.setFormValue(value, `${this.localName}:${state}`);\n\t}\n}\n"],"mappings":";;;AAEA,IAAsB,mBAAtB,cAA+C,aAAa;CAC3D,OAAgB,iBAAiB;CAEjC,IAAI,OAA+B;EAElC,OAAO,KAAK,WAAW,QAAQ;CAChC;CAEA,IAAI,SAA0B;EAE7B,OAAO,KAAK,WAAW,UAAU;CAClC;CAEA,IAAI,OAAe;EAClB,OAAO,KAAK,aAAa,MAAM,KAAK;CACrC;CAEA,IAAI,KAAK,OAA2B;EACnC,KAAK,QAAQ,QAAQ,KAAK;CAC3B;CAEA,cAAc;EACb,MAAM;EAGN,IAAI,KAAK,WAAW,KAAK,UAAU,OAAO;EAG1C,KAAK,iBAAiB,UAAU,UAAU;GACzC,IAAI,MAAM,WAAW,MAAM,KAAK,SAAS;EAC1C,CAAC;EAGD,KAAK,iBAAiB,iBAAiB;GACtC,KAAK,OAAO;EACb,CAAC;CACF;CAIA,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,MAAM;EAC7B,MAAM,kBAAkB;CACzB;CAGA,uBAA6B;EAC5B,KAAK,yBAAyB,UAAU;CACzC;CAEA,oBAA0B;EACzB,KAAK,yBAAyB,OAAO;CACtC;CAGA,yBAAyB,OAA8C;EACtE,MAAM,SAAS,GAAG,KAAK,UAAU;EAEjC,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,MAAM,GACvD,KAAK,aAAa,MAAM,MAAM,OAAO,MAAM,CAAC;CAE9C;CAEA,AAAU,WAAiB;EAC1B,KAAK,MAAM;CACZ;CAEA,AAAmB,eAA+B;EACjD,MAAM,YAAY,KAAK;EACvB,IAAI,CAAC,aAAa,KAAK,aAAa,YAAY,KAAK,KAAK,aAAa,iBAAiB,GACvF,OAAO,CAAC;EAGT,OAAO,CAAC,GAAG,UAAU,MAAM,CAAC,CAAC,QAAQ,UAAU,iBAAiB,OAAO;CACxE;CAMA,AAAU,gBAAgB,QAAyC,OAAqB;EACvF,MAAM,EAAE,SAAS;EAEjB,IAAI,SAAS,IAAI;GAEhB,KAAK,eAAe,MAAM,KAAK;GAC/B;EACD;EAEA,MAAM,OAAO,IAAI,SAAS;EAE1B,KAAK,MAAM,CAAC,OAAO,UAAU,QAC5B,KAAK,IAAI,GAAG,KAAK,GAAG,SAAS,OAAO,KAAK,CAAC;EAE3C,KAAK,eAAe,MAAM,KAAK;CAChC;CAEA,AAAU,eAAe,OAAiC,OAAqB;EAC9E,KAAK,WAAW,aAAa,OAAO,GAAG,KAAK,UAAU,GAAG,OAAO;CACjE;AACD"}
|
|
@@ -34,6 +34,7 @@ export declare class SonicMeter extends SonicElement {
|
|
|
34
34
|
attributeChangedCallback(name: string): void;
|
|
35
35
|
resetPeak(): void;
|
|
36
36
|
protected connect(signal: AbortSignal): void;
|
|
37
|
+
protected stateTarget(): HTMLElement;
|
|
37
38
|
}
|
|
38
39
|
//#endregion
|
|
39
40
|
//# sourceMappingURL=meter.d.ts.map
|
|
@@ -149,6 +149,9 @@ var SonicMeter = class extends SonicElement {
|
|
|
149
149
|
}, { once: true });
|
|
150
150
|
this.checkStyles(this.#meter, "meter.css");
|
|
151
151
|
}
|
|
152
|
+
stateTarget() {
|
|
153
|
+
return this.#meter;
|
|
154
|
+
}
|
|
152
155
|
#isSettled(now) {
|
|
153
156
|
if (this.#isValueShown) return true;
|
|
154
157
|
if (now - this.#clipAt < holdMs) return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meter.js","names":[],"sources":["../../../src/elements/meter.ts"],"sourcesContent":["import type { Decibels } from '#lib/units.ts';\n\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { ballisticsRates, fall, rise, stepNeedle } from '#lib/ballistics.ts';\nimport { toNumber } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { template } from '#lib/render.ts';\nimport { linearTaper } from '#lib/taper.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-meter': SonicMeter;\n\t}\n}\n\nconst renderMeter = template(\n\t/* HTML */ `\n\t\t<div aria-hidden=\"true\" class=\"sonic-meter\">\n\t\t\t<div class=\"sonic-meter-segments\"></div>\n\t\t\t<div class=\"sonic-meter-bar\"></div>\n\t\t\t<div class=\"sonic-meter-clip\"></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nconst holdMs = 1500;\n\nconst ballisticsModes = ['peak', 'ppm-1', 'ppm-2', 'vu'] as const;\n\nconst settledDecibels = 0.01;\nconst settledAmplitude = 1e-4;\nconst settledPerSecond = 1e-2;\n\nfunction toDecibels(amplitude: number): number {\n\treturn 20 * Math.log10(Math.abs(amplitude));\n}\n\nfunction toAmplitude(decibels: number): number {\n\treturn 10 ** (decibels / 20);\n}\n\n// A `calc()` rather than a count, so the zone's token stays live\nfunction segmentsBelow(segments: Array<number>, zone: 'clip' | 'hot'): string {\n\tconst terms = segments.map(\n\t\t(threshold) => `sign(max(0, var(--_sonic-meter-${zone}-from) - (${String(threshold)})))`,\n\t);\n\n\treturn `calc(${terms.join(' + ')})`;\n}\n\nexport class SonicMeter extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t'ballistics',\n\t\t'disabled',\n\t\t'max',\n\t\t'min',\n\t\t'origin',\n\t\t'segments',\n\t\t'value',\n\t];\n\n\tget ballistics(): (typeof ballisticsModes)[number] {\n\t\tconst name = this.getAttribute('ballistics');\n\n\t\treturn ballisticsModes.find((mode) => mode === name) ?? 'peak';\n\t}\n\n\tset ballistics(mode: (typeof ballisticsModes)[number] | undefined) {\n\t\tthis.reflect('ballistics', mode);\n\t}\n\n\t/** Linear peak amplitude, 1 at full scale; the meter converts to decibels and applies its ballistics */\n\tget level(): number {\n\t\treturn this.#level;\n\t}\n\n\tset level(level: number) {\n\t\tconst amplitude = toNumber(level);\n\t\tif (!Number.isFinite(amplitude)) return;\n\n\t\tconst decibels = toDecibels(amplitude);\n\n\t\tthis.#level = amplitude;\n\t\tthis.#target = decibels;\n\t\tif (this.#isValueShown) return;\n\n\t\tconst now = performance.now();\n\n\t\tthis.#peak = Math.max(this.#peak, decibels);\n\t\tif (decibels >= 0) this.#clipAt = now;\n\t\tthis.#step(now, 0);\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t}\n\n\tget max(): Decibels {\n\t\treturn this.numberAttribute('max', 0);\n\t}\n\n\tset max(decibels: number | undefined) {\n\t\tthis.reflect('max', decibels);\n\t}\n\n\tget min(): Decibels {\n\t\treturn this.numberAttribute('min', -60);\n\t}\n\n\tset min(decibels: number | undefined) {\n\t\tthis.reflect('min', decibels);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'horizontal' ? 'horizontal' : 'vertical';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\t/** Highest level since `resetPeak()`; `-Infinity` before any signal */\n\tget peak(): Decibels {\n\t\treturn this.#peak;\n\t}\n\n\t/** Ladder thresholds, one per segment; unset draws a bar */\n\tget segments(): Array<Decibels> | undefined {\n\t\treturn parseNumberList(this.getAttribute('segments'));\n\t}\n\n\tset segments(thresholds: Array<Decibels> | undefined) {\n\t\tthis.reflect('segments', thresholds?.join(' '));\n\t}\n\n\t/** A reading in the meter's own units, drawn as given with no ballistics; takes over from `level` while set */\n\tget value(): number | undefined {\n\t\treturn this.optionalNumberAttribute('value');\n\t}\n\n\tset value(value: number | undefined) {\n\t\tthis.reflect('value', value);\n\t}\n\n\t#bar = -Infinity;\n\n\t#carryMs = 0;\n\n\t#clipAt = -Infinity;\n\n\t#frame: number | undefined;\n\n\t#isValueShown = false;\n\n\t#lastFrame: number | undefined;\n\n\t#level = 0;\n\n\treadonly #meter = renderMeter();\n\n\t#peak = -Infinity;\n\n\t#peakHold = -Infinity;\n\n\t#peakHoldAt = -Infinity;\n\n\t#segments: Array<number> | undefined;\n\n\t#target = -Infinity;\n\n\t#velocity = 0;\n\n\tattributeChangedCallback(name: string): void {\n\t\tconst now = performance.now();\n\n\t\tif (name === 'ballistics') this.#velocity = 0;\n\t\telse if (name === 'value') this.#readValue(now);\n\t\tthis.#segments = this.segments;\n\t\tif (name === 'segments') this.#renderSegments();\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t}\n\n\tresetPeak(): void {\n\t\tthis.#peak = -Infinity;\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst now = performance.now();\n\n\t\tthis.upgradeProperties(\n\t\t\t'ballistics',\n\t\t\t'max',\n\t\t\t'min',\n\t\t\t'orientation',\n\t\t\t'origin',\n\t\t\t'segments',\n\t\t\t'value',\n\t\t\t'level',\n\t\t);\n\t\tthis.keepControl(this.#meter, signal);\n\t\tthis.#renderSegments();\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tif (this.#frame !== undefined) cancelAnimationFrame(this.#frame);\n\t\t\t\tthis.#frame = undefined;\n\t\t\t\tthis.#lastFrame = undefined;\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t\tif (__DEV__) this.checkStyles(this.#meter, 'meter.css');\n\t}\n\n\t#isSettled(now: number): boolean {\n\t\tif (this.#isValueShown) return true;\n\t\tif (now - this.#clipAt < holdMs) return false;\n\t\tif (this.ballistics === 'vu') return this.#bar === this.#target && this.#velocity === 0;\n\n\t\tconst floor = this.min;\n\n\t\treturn (\n\t\t\tthis.#bar >= this.#target &&\n\t\t\tthis.#bar <= Math.max(this.#target, floor) &&\n\t\t\tthis.#peakHold <= Math.max(this.#bar, floor)\n\t\t);\n\t}\n\n\t#proportionOf(level: number): number {\n\t\tconst segments = this.#segments;\n\t\tif (segments) {\n\t\t\treturn segments.filter((threshold) => threshold <= level).length / segments.length;\n\t\t}\n\n\t\treturn linearTaper(this.min, this.max).proportionOf(level);\n\t}\n\n\t#readValue(now: number): void {\n\t\tconst value = this.value;\n\t\tconst isValueShown = value !== undefined;\n\n\t\tif (isValueShown !== this.#isValueShown) {\n\t\t\tthis.#isValueShown = isValueShown;\n\t\t\tthis.resetPeak();\n\t\t\tif (!isValueShown) this.#step(now, 0);\n\t\t}\n\t\tif (value !== undefined) this.#peak = Math.max(this.#peak, value);\n\t}\n\n\t#render(now: number): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst meter = this.#meter;\n\n\t\tconst origin = this.origin === undefined ? 0 : this.#proportionOf(this.origin);\n\n\t\tmeter.style.setProperty('--_sonic-meter-min', String(this.min));\n\t\tmeter.style.setProperty('--_sonic-meter-max', String(this.max));\n\t\tmeter.style.setProperty('--_sonic-meter-origin', String(origin));\n\n\t\tif (this.disabled) {\n\t\t\tthis.#renderStill(origin);\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = this.value;\n\n\t\tif (value !== undefined) {\n\t\t\tthis.#renderStill(this.#proportionOf(value));\n\t\t\treturn;\n\t\t}\n\n\t\tconst peakHold = this.ballistics === 'vu' ? 0 : this.#proportionOf(this.#peakHold);\n\n\t\tmeter.style.setProperty('--_sonic-meter-bar', String(this.#proportionOf(this.#bar)));\n\t\tmeter.style.setProperty('--_sonic-meter-peak-hold', String(peakHold));\n\t\tmeter.style.setProperty('--_sonic-meter-clipped', now - this.#clipAt < holdMs ? '1' : '0');\n\t}\n\n\t#renderSegments(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst meter = this.#meter;\n\t\tconst segments = this.#segments;\n\n\t\tthis.toggleState('ladder', segments !== undefined);\n\t\tif (!segments) {\n\t\t\tfor (const name of ['count', 'hot-segments', 'clip-segments']) {\n\t\t\t\tmeter.style.removeProperty(`--_sonic-meter-${name}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tmeter.style.setProperty('--_sonic-meter-count', String(segments.length));\n\t\tmeter.style.setProperty('--_sonic-meter-hot-segments', segmentsBelow(segments, 'hot'));\n\t\tmeter.style.setProperty('--_sonic-meter-clip-segments', segmentsBelow(segments, 'clip'));\n\t}\n\n\t#renderStill(bar: number): void {\n\t\tconst { style } = this.#meter;\n\n\t\tstyle.setProperty('--_sonic-meter-bar', String(bar));\n\t\tstyle.setProperty('--_sonic-meter-peak-hold', '0');\n\t\tstyle.setProperty('--_sonic-meter-clipped', '0');\n\t}\n\n\t#risen(timeConstantMs: number, elapsedMs: number): number {\n\t\tconst target = toAmplitude(this.#target);\n\t\tconst risen = toDecibels(rise(toAmplitude(this.#bar), { target, timeConstantMs }, elapsedMs));\n\n\t\treturn this.#target - risen < settledDecibels ? this.#target : risen;\n\t}\n\n\t#schedule(now: number): void {\n\t\tif (this.#frame !== undefined || !this.isConnected || this.#isSettled(now)) return;\n\n\t\tif (this.#lastFrame === undefined) this.#lastFrame = now;\n\t\tthis.#frame = requestAnimationFrame(this.#tick);\n\t}\n\n\t#step(now: number, elapsedMs: number): void {\n\t\tconst mode = this.ballistics;\n\n\t\tif (mode === 'vu') {\n\t\t\tthis.#stepNeedle(elapsedMs);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { fallDecibelsPerSecond, riseTimeConstantMs } = ballisticsRates[mode];\n\t\tconst floor = Math.max(this.#target, this.min);\n\n\t\tthis.#bar =\n\t\t\tthis.#target > this.#bar\n\t\t\t\t? this.#risen(riseTimeConstantMs, elapsedMs)\n\t\t\t\t: fall(this.#bar, { fallDecibelsPerSecond, floor }, elapsedMs);\n\t\tif (this.#bar >= this.#peakHold) {\n\t\t\tthis.#peakHold = this.#bar;\n\t\t\tthis.#peakHoldAt = now;\n\t\t}\n\n\t\tconst pastHoldMs = now - this.#peakHoldAt - holdMs;\n\n\t\tif (pastHoldMs >= 0) {\n\t\t\tthis.#peakHold = fall(\n\t\t\t\tthis.#peakHold,\n\t\t\t\t{ fallDecibelsPerSecond, floor: this.#bar },\n\t\t\t\tMath.min(elapsedMs, pastHoldMs),\n\t\t\t);\n\t\t}\n\t}\n\n\t#stepNeedle(elapsedMs: number): void {\n\t\tconst target = toAmplitude(this.#target);\n\t\tconst needle = stepNeedle(\n\t\t\t{ carryMs: this.#carryMs, position: toAmplitude(this.#bar), velocity: this.#velocity },\n\t\t\ttarget,\n\t\t\telapsedMs,\n\t\t);\n\t\tconst isLanded =\n\t\t\tMath.abs(needle.position - target) < settledAmplitude &&\n\t\t\tMath.abs(needle.velocity) < settledPerSecond;\n\n\t\tthis.#carryMs = needle.carryMs;\n\t\tthis.#velocity = isLanded ? 0 : needle.velocity;\n\t\tthis.#bar = isLanded ? this.#target : toDecibels(Math.max(0, needle.position));\n\t}\n\n\treadonly #tick = (time: number): void => {\n\t\tconst elapsedMs = Math.max(0, time - (this.#lastFrame ?? time));\n\n\t\tthis.#frame = undefined;\n\t\tthis.#lastFrame = time;\n\t\tthis.#step(time, elapsedMs);\n\t\tthis.#render(time);\n\t\tif (this.#isSettled(time)) {\n\t\t\tthis.#lastFrame = undefined;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#frame = requestAnimationFrame(this.#tick);\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"meter.js","names":[],"sources":["../../../src/elements/meter.ts"],"sourcesContent":["import type { Decibels } from '#lib/units.ts';\n\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { ballisticsRates, fall, rise, stepNeedle } from '#lib/ballistics.ts';\nimport { toNumber } from '#lib/math.ts';\nimport { parseNumberList } from '#lib/number-list.ts';\nimport { template } from '#lib/render.ts';\nimport { linearTaper } from '#lib/taper.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-meter': SonicMeter;\n\t}\n}\n\nconst renderMeter = template(\n\t/* HTML */ `\n\t\t<div aria-hidden=\"true\" class=\"sonic-meter\">\n\t\t\t<div class=\"sonic-meter-segments\"></div>\n\t\t\t<div class=\"sonic-meter-bar\"></div>\n\t\t\t<div class=\"sonic-meter-clip\"></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nconst holdMs = 1500;\n\nconst ballisticsModes = ['peak', 'ppm-1', 'ppm-2', 'vu'] as const;\n\nconst settledDecibels = 0.01;\nconst settledAmplitude = 1e-4;\nconst settledPerSecond = 1e-2;\n\nfunction toDecibels(amplitude: number): number {\n\treturn 20 * Math.log10(Math.abs(amplitude));\n}\n\nfunction toAmplitude(decibels: number): number {\n\treturn 10 ** (decibels / 20);\n}\n\n// A `calc()` rather than a count, so the zone's token stays live\nfunction segmentsBelow(segments: Array<number>, zone: 'clip' | 'hot'): string {\n\tconst terms = segments.map(\n\t\t(threshold) => `sign(max(0, var(--_sonic-meter-${zone}-from) - (${String(threshold)})))`,\n\t);\n\n\treturn `calc(${terms.join(' + ')})`;\n}\n\nexport class SonicMeter extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t'ballistics',\n\t\t'disabled',\n\t\t'max',\n\t\t'min',\n\t\t'origin',\n\t\t'segments',\n\t\t'value',\n\t];\n\n\tget ballistics(): (typeof ballisticsModes)[number] {\n\t\tconst name = this.getAttribute('ballistics');\n\n\t\treturn ballisticsModes.find((mode) => mode === name) ?? 'peak';\n\t}\n\n\tset ballistics(mode: (typeof ballisticsModes)[number] | undefined) {\n\t\tthis.reflect('ballistics', mode);\n\t}\n\n\t/** Linear peak amplitude, 1 at full scale; the meter converts to decibels and applies its ballistics */\n\tget level(): number {\n\t\treturn this.#level;\n\t}\n\n\tset level(level: number) {\n\t\tconst amplitude = toNumber(level);\n\t\tif (!Number.isFinite(amplitude)) return;\n\n\t\tconst decibels = toDecibels(amplitude);\n\n\t\tthis.#level = amplitude;\n\t\tthis.#target = decibels;\n\t\tif (this.#isValueShown) return;\n\n\t\tconst now = performance.now();\n\n\t\tthis.#peak = Math.max(this.#peak, decibels);\n\t\tif (decibels >= 0) this.#clipAt = now;\n\t\tthis.#step(now, 0);\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t}\n\n\tget max(): Decibels {\n\t\treturn this.numberAttribute('max', 0);\n\t}\n\n\tset max(decibels: number | undefined) {\n\t\tthis.reflect('max', decibels);\n\t}\n\n\tget min(): Decibels {\n\t\treturn this.numberAttribute('min', -60);\n\t}\n\n\tset min(decibels: number | undefined) {\n\t\tthis.reflect('min', decibels);\n\t}\n\n\tget orientation(): 'horizontal' | 'vertical' {\n\t\treturn this.getAttribute('orientation') === 'horizontal' ? 'horizontal' : 'vertical';\n\t}\n\n\tset orientation(direction: 'horizontal' | 'vertical' | undefined) {\n\t\tthis.reflect('orientation', direction);\n\t}\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget origin(): number | undefined {\n\t\treturn this.optionalNumberAttribute('origin');\n\t}\n\n\tset origin(value: number | undefined) {\n\t\tthis.reflect('origin', value);\n\t}\n\n\t/** Highest level since `resetPeak()`; `-Infinity` before any signal */\n\tget peak(): Decibels {\n\t\treturn this.#peak;\n\t}\n\n\t/** Ladder thresholds, one per segment; unset draws a bar */\n\tget segments(): Array<Decibels> | undefined {\n\t\treturn parseNumberList(this.getAttribute('segments'));\n\t}\n\n\tset segments(thresholds: Array<Decibels> | undefined) {\n\t\tthis.reflect('segments', thresholds?.join(' '));\n\t}\n\n\t/** A reading in the meter's own units, drawn as given with no ballistics; takes over from `level` while set */\n\tget value(): number | undefined {\n\t\treturn this.optionalNumberAttribute('value');\n\t}\n\n\tset value(value: number | undefined) {\n\t\tthis.reflect('value', value);\n\t}\n\n\t#bar = -Infinity;\n\n\t#carryMs = 0;\n\n\t#clipAt = -Infinity;\n\n\t#frame: number | undefined;\n\n\t#isValueShown = false;\n\n\t#lastFrame: number | undefined;\n\n\t#level = 0;\n\n\treadonly #meter = renderMeter();\n\n\t#peak = -Infinity;\n\n\t#peakHold = -Infinity;\n\n\t#peakHoldAt = -Infinity;\n\n\t#segments: Array<number> | undefined;\n\n\t#target = -Infinity;\n\n\t#velocity = 0;\n\n\tattributeChangedCallback(name: string): void {\n\t\tconst now = performance.now();\n\n\t\tif (name === 'ballistics') this.#velocity = 0;\n\t\telse if (name === 'value') this.#readValue(now);\n\t\tthis.#segments = this.segments;\n\t\tif (name === 'segments') this.#renderSegments();\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t}\n\n\tresetPeak(): void {\n\t\tthis.#peak = -Infinity;\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst now = performance.now();\n\n\t\tthis.upgradeProperties(\n\t\t\t'ballistics',\n\t\t\t'max',\n\t\t\t'min',\n\t\t\t'orientation',\n\t\t\t'origin',\n\t\t\t'segments',\n\t\t\t'value',\n\t\t\t'level',\n\t\t);\n\t\tthis.keepControl(this.#meter, signal);\n\t\tthis.#renderSegments();\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tif (this.#frame !== undefined) cancelAnimationFrame(this.#frame);\n\t\t\t\tthis.#frame = undefined;\n\t\t\t\tthis.#lastFrame = undefined;\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t\tif (__DEV__) this.checkStyles(this.#meter, 'meter.css');\n\t}\n\n\tprotected override stateTarget(): HTMLElement {\n\t\treturn this.#meter;\n\t}\n\n\t#isSettled(now: number): boolean {\n\t\tif (this.#isValueShown) return true;\n\t\tif (now - this.#clipAt < holdMs) return false;\n\t\tif (this.ballistics === 'vu') return this.#bar === this.#target && this.#velocity === 0;\n\n\t\tconst floor = this.min;\n\n\t\treturn (\n\t\t\tthis.#bar >= this.#target &&\n\t\t\tthis.#bar <= Math.max(this.#target, floor) &&\n\t\t\tthis.#peakHold <= Math.max(this.#bar, floor)\n\t\t);\n\t}\n\n\t#proportionOf(level: number): number {\n\t\tconst segments = this.#segments;\n\t\tif (segments) {\n\t\t\treturn segments.filter((threshold) => threshold <= level).length / segments.length;\n\t\t}\n\n\t\treturn linearTaper(this.min, this.max).proportionOf(level);\n\t}\n\n\t#readValue(now: number): void {\n\t\tconst value = this.value;\n\t\tconst isValueShown = value !== undefined;\n\n\t\tif (isValueShown !== this.#isValueShown) {\n\t\t\tthis.#isValueShown = isValueShown;\n\t\t\tthis.resetPeak();\n\t\t\tif (!isValueShown) this.#step(now, 0);\n\t\t}\n\t\tif (value !== undefined) this.#peak = Math.max(this.#peak, value);\n\t}\n\n\t#render(now: number): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst meter = this.#meter;\n\n\t\tconst origin = this.origin === undefined ? 0 : this.#proportionOf(this.origin);\n\n\t\tmeter.style.setProperty('--_sonic-meter-min', String(this.min));\n\t\tmeter.style.setProperty('--_sonic-meter-max', String(this.max));\n\t\tmeter.style.setProperty('--_sonic-meter-origin', String(origin));\n\n\t\tif (this.disabled) {\n\t\t\tthis.#renderStill(origin);\n\t\t\treturn;\n\t\t}\n\n\t\tconst value = this.value;\n\n\t\tif (value !== undefined) {\n\t\t\tthis.#renderStill(this.#proportionOf(value));\n\t\t\treturn;\n\t\t}\n\n\t\tconst peakHold = this.ballistics === 'vu' ? 0 : this.#proportionOf(this.#peakHold);\n\n\t\tmeter.style.setProperty('--_sonic-meter-bar', String(this.#proportionOf(this.#bar)));\n\t\tmeter.style.setProperty('--_sonic-meter-peak-hold', String(peakHold));\n\t\tmeter.style.setProperty('--_sonic-meter-clipped', now - this.#clipAt < holdMs ? '1' : '0');\n\t}\n\n\t#renderSegments(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst meter = this.#meter;\n\t\tconst segments = this.#segments;\n\n\t\tthis.toggleState('ladder', segments !== undefined);\n\t\tif (!segments) {\n\t\t\tfor (const name of ['count', 'hot-segments', 'clip-segments']) {\n\t\t\t\tmeter.style.removeProperty(`--_sonic-meter-${name}`);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tmeter.style.setProperty('--_sonic-meter-count', String(segments.length));\n\t\tmeter.style.setProperty('--_sonic-meter-hot-segments', segmentsBelow(segments, 'hot'));\n\t\tmeter.style.setProperty('--_sonic-meter-clip-segments', segmentsBelow(segments, 'clip'));\n\t}\n\n\t#renderStill(bar: number): void {\n\t\tconst { style } = this.#meter;\n\n\t\tstyle.setProperty('--_sonic-meter-bar', String(bar));\n\t\tstyle.setProperty('--_sonic-meter-peak-hold', '0');\n\t\tstyle.setProperty('--_sonic-meter-clipped', '0');\n\t}\n\n\t#risen(timeConstantMs: number, elapsedMs: number): number {\n\t\tconst target = toAmplitude(this.#target);\n\t\tconst risen = toDecibels(rise(toAmplitude(this.#bar), { target, timeConstantMs }, elapsedMs));\n\n\t\treturn this.#target - risen < settledDecibels ? this.#target : risen;\n\t}\n\n\t#schedule(now: number): void {\n\t\tif (this.#frame !== undefined || !this.isConnected || this.#isSettled(now)) return;\n\n\t\tif (this.#lastFrame === undefined) this.#lastFrame = now;\n\t\tthis.#frame = requestAnimationFrame(this.#tick);\n\t}\n\n\t#step(now: number, elapsedMs: number): void {\n\t\tconst mode = this.ballistics;\n\n\t\tif (mode === 'vu') {\n\t\t\tthis.#stepNeedle(elapsedMs);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { fallDecibelsPerSecond, riseTimeConstantMs } = ballisticsRates[mode];\n\t\tconst floor = Math.max(this.#target, this.min);\n\n\t\tthis.#bar =\n\t\t\tthis.#target > this.#bar\n\t\t\t\t? this.#risen(riseTimeConstantMs, elapsedMs)\n\t\t\t\t: fall(this.#bar, { fallDecibelsPerSecond, floor }, elapsedMs);\n\t\tif (this.#bar >= this.#peakHold) {\n\t\t\tthis.#peakHold = this.#bar;\n\t\t\tthis.#peakHoldAt = now;\n\t\t}\n\n\t\tconst pastHoldMs = now - this.#peakHoldAt - holdMs;\n\n\t\tif (pastHoldMs >= 0) {\n\t\t\tthis.#peakHold = fall(\n\t\t\t\tthis.#peakHold,\n\t\t\t\t{ fallDecibelsPerSecond, floor: this.#bar },\n\t\t\t\tMath.min(elapsedMs, pastHoldMs),\n\t\t\t);\n\t\t}\n\t}\n\n\t#stepNeedle(elapsedMs: number): void {\n\t\tconst target = toAmplitude(this.#target);\n\t\tconst needle = stepNeedle(\n\t\t\t{ carryMs: this.#carryMs, position: toAmplitude(this.#bar), velocity: this.#velocity },\n\t\t\ttarget,\n\t\t\telapsedMs,\n\t\t);\n\t\tconst isLanded =\n\t\t\tMath.abs(needle.position - target) < settledAmplitude &&\n\t\t\tMath.abs(needle.velocity) < settledPerSecond;\n\n\t\tthis.#carryMs = needle.carryMs;\n\t\tthis.#velocity = isLanded ? 0 : needle.velocity;\n\t\tthis.#bar = isLanded ? this.#target : toDecibels(Math.max(0, needle.position));\n\t}\n\n\treadonly #tick = (time: number): void => {\n\t\tconst elapsedMs = Math.max(0, time - (this.#lastFrame ?? time));\n\n\t\tthis.#frame = undefined;\n\t\tthis.#lastFrame = time;\n\t\tthis.#step(time, elapsedMs);\n\t\tthis.#render(time);\n\t\tif (this.#isSettled(time)) {\n\t\t\tthis.#lastFrame = undefined;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#frame = requestAnimationFrame(this.#tick);\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -14,6 +14,7 @@ export declare class SonicNumber extends SonicValueElement {
|
|
|
14
14
|
protected controlRole(): 'spinbutton';
|
|
15
15
|
protected draw(): void;
|
|
16
16
|
protected focusTarget(): HTMLElement;
|
|
17
|
+
protected stateTarget(): HTMLElement;
|
|
17
18
|
protected tapTarget(): number | undefined;
|
|
18
19
|
}
|
|
19
20
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number.js","names":[],"sources":["../../../src/elements/number.ts"],"sourcesContent":["import { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-number': SonicNumber;\n\t}\n}\n\nconst renderNumber = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-number\" role=\"spinbutton\" tabindex=\"0\">\n\t\t\t<span aria-hidden=\"true\" class=\"sonic-number-value\"></span>\n\t\t\t<input\n\t\t\t\tautocomplete=\"off\"\n\t\t\t\tclass=\"sonic-number-entry\"\n\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\thidden\n\t\t\t\tspellcheck=\"false\"\n\t\t\t\ttype=\"text\"\n\t\t\t/>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicNumber extends SonicValueElement {\n\tget press(): 'none' | 'step' {\n\t\treturn this.getAttribute('press') === 'step' ? 'step' : 'none';\n\t}\n\n\tset press(gesture: 'none' | 'step' | undefined) {\n\t\tthis.reflect('press', gesture);\n\t}\n\n\treadonly #number = renderNumber();\n\n\treadonly #digits = requireChild(this.#number, '.sonic-number-value', HTMLSpanElement);\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('press');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst number = this.#number;\n\n\t\tthis.keepControl(number, signal);\n\t\tthis.render();\n\t\tif (__DEV__) this.checkStyles(number, 'number.css');\n\t\tthis.bindGestures(number, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(number, '--_sonic-number-travel'),\n\t\t}));\n\t}\n\n\tprotected override controlRole(): 'spinbutton' {\n\t\treturn 'spinbutton';\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#digits.textContent = this.valueText;\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#number;\n\t}\n\n\tprotected override tapTarget(): number | undefined {\n\t\tif (this.press !== 'step') return undefined;\n\n\t\tconst mapping = this.mapping();\n\t\tconst next = mapping.keyTarget('ArrowUp', this.value);\n\n\t\treturn next === undefined || next === this.value ? mapping.bounds[0] : next;\n\t}\n}\n"],"mappings":";;;;AAWA,MAAM,eAAe,SACT;;;;;;;;;;;;IAaX,cACD;AAEA,IAAa,cAAb,cAAiC,kBAAkB;CAClD,IAAI,QAAyB;EAC5B,OAAO,KAAK,aAAa,OAAO,MAAM,SAAS,SAAS;CACzD;CAEA,IAAI,MAAM,SAAsC;EAC/C,KAAK,QAAQ,SAAS,OAAO;CAC9B;CAEA,AAAS,UAAU,aAAa;CAEhC,AAAS,UAAU,aAAa,KAAK,SAAS,uBAAuB,eAAe;CAEpF,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,OAAO;EAC9B,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,SAAS,KAAK;EAEpB,KAAK,YAAY,QAAQ,MAAM;EAC/B,KAAK,OAAO;EACC,KAAK,YAAY,QAAQ,YAAY;EAClD,KAAK,aAAa,QAAQ,eAAe;GACxC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,QAAQ,wBAAwB;EACzD,EAAE;CACH;CAEA,AAAmB,cAA4B;EAC9C,OAAO;CACR;CAEA,AAAU,OAAa;EACtB,KAAK,QAAQ,cAAc,KAAK;CACjC;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,YAAgC;EAClD,IAAI,KAAK,UAAU,QAAQ,OAAO;EAElC,MAAM,UAAU,KAAK,QAAQ;EAC7B,MAAM,OAAO,QAAQ,UAAU,WAAW,KAAK,KAAK;EAEpD,OAAO,SAAS,UAAa,SAAS,KAAK,QAAQ,QAAQ,OAAO,KAAK;CACxE;AACD"}
|
|
1
|
+
{"version":3,"file":"number.js","names":[],"sources":["../../../src/elements/number.ts"],"sourcesContent":["import { SonicValueElement } from '#elements/value-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare const __DEV__: boolean;\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-number': SonicNumber;\n\t}\n}\n\nconst renderNumber = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-number\" role=\"spinbutton\" tabindex=\"0\">\n\t\t\t<span aria-hidden=\"true\" class=\"sonic-number-value\"></span>\n\t\t\t<input\n\t\t\t\tautocomplete=\"off\"\n\t\t\t\tclass=\"sonic-number-entry\"\n\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\thidden\n\t\t\t\tspellcheck=\"false\"\n\t\t\t\ttype=\"text\"\n\t\t\t/>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicNumber extends SonicValueElement {\n\tget press(): 'none' | 'step' {\n\t\treturn this.getAttribute('press') === 'step' ? 'step' : 'none';\n\t}\n\n\tset press(gesture: 'none' | 'step' | undefined) {\n\t\tthis.reflect('press', gesture);\n\t}\n\n\treadonly #number = renderNumber();\n\n\treadonly #digits = requireChild(this.#number, '.sonic-number-value', HTMLSpanElement);\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperties('press');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst number = this.#number;\n\n\t\tthis.keepControl(number, signal);\n\t\tthis.render();\n\t\tif (__DEV__) this.checkStyles(number, 'number.css');\n\t\tthis.bindGestures(number, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\ttravelPx: this.travelPx(number, '--_sonic-number-travel'),\n\t\t}));\n\t}\n\n\tprotected override controlRole(): 'spinbutton' {\n\t\treturn 'spinbutton';\n\t}\n\n\tprotected draw(): void {\n\t\tthis.#digits.textContent = this.valueText;\n\t}\n\n\tprotected override focusTarget(): HTMLElement {\n\t\treturn this.#number;\n\t}\n\n\tprotected override stateTarget(): HTMLElement {\n\t\treturn this.#number;\n\t}\n\n\tprotected override tapTarget(): number | undefined {\n\t\tif (this.press !== 'step') return undefined;\n\n\t\tconst mapping = this.mapping();\n\t\tconst next = mapping.keyTarget('ArrowUp', this.value);\n\n\t\treturn next === undefined || next === this.value ? mapping.bounds[0] : next;\n\t}\n}\n"],"mappings":";;;;AAWA,MAAM,eAAe,SACT;;;;;;;;;;;;IAaX,cACD;AAEA,IAAa,cAAb,cAAiC,kBAAkB;CAClD,IAAI,QAAyB;EAC5B,OAAO,KAAK,aAAa,OAAO,MAAM,SAAS,SAAS;CACzD;CAEA,IAAI,MAAM,SAAsC;EAC/C,KAAK,QAAQ,SAAS,OAAO;CAC9B;CAEA,AAAS,UAAU,aAAa;CAEhC,AAAS,UAAU,aAAa,KAAK,SAAS,uBAAuB,eAAe;CAEpF,AAAS,oBAA0B;EAClC,KAAK,kBAAkB,OAAO;EAC9B,MAAM,kBAAkB;CACzB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,SAAS,KAAK;EAEpB,KAAK,YAAY,QAAQ,MAAM;EAC/B,KAAK,OAAO;EACC,KAAK,YAAY,QAAQ,YAAY;EAClD,KAAK,aAAa,QAAQ,eAAe;GACxC,WAAW,UAAU,CAAC,MAAM;GAC5B,UAAU,KAAK,SAAS,QAAQ,wBAAwB;EACzD,EAAE;CACH;CAEA,AAAmB,cAA4B;EAC9C,OAAO;CACR;CAEA,AAAU,OAAa;EACtB,KAAK,QAAQ,cAAc,KAAK;CACjC;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,cAA2B;EAC7C,OAAO,KAAK;CACb;CAEA,AAAmB,YAAgC;EAClD,IAAI,KAAK,UAAU,QAAQ,OAAO;EAElC,MAAM,UAAU,KAAK,QAAQ;EAC7B,MAAM,OAAO,QAAQ,UAAU,WAAW,KAAK,KAAK;EAEpD,OAAO,SAAS,UAAa,SAAS,KAAK,QAAQ,QAAQ,OAAO,KAAK;CACxE;AACD"}
|
|
@@ -27,6 +27,7 @@ export declare abstract class SonicRadioGroupElement extends SonicFormElement {
|
|
|
27
27
|
protected abstract renderOption(): HTMLButtonElement;
|
|
28
28
|
protected restoreState(state: string): void;
|
|
29
29
|
protected select(index: number): void;
|
|
30
|
+
protected stateTarget(): HTMLElement;
|
|
30
31
|
protected stepFrom(from: number, step: number): number | undefined;
|
|
31
32
|
}
|
|
32
33
|
//#endregion
|
|
@@ -139,6 +139,9 @@ var SonicRadioGroupElement = class extends SonicFormElement {
|
|
|
139
139
|
this.value = next;
|
|
140
140
|
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
141
141
|
}
|
|
142
|
+
stateTarget() {
|
|
143
|
+
return this.group;
|
|
144
|
+
}
|
|
142
145
|
stepFrom(from, step) {
|
|
143
146
|
return stepFrom(this.positions(), from, {
|
|
144
147
|
isWrapping: this.isWrapping(),
|
|
@@ -192,7 +195,7 @@ var SonicRadioGroupElement = class extends SonicFormElement {
|
|
|
192
195
|
if (focused) this.#refocus(focused, focusedValue);
|
|
193
196
|
}
|
|
194
197
|
#markPressed(pressed) {
|
|
195
|
-
for (const [index, option] of this.options().entries()) option.toggleAttribute("data-sonic-
|
|
198
|
+
for (const [index, option] of this.options().entries()) option.toggleAttribute("data-sonic-active", index === pressed);
|
|
196
199
|
}
|
|
197
200
|
#optionBoxes() {
|
|
198
201
|
const positions = this.positions();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.js","names":[],"sources":["../../../src/elements/radio-group.ts"],"sourcesContent":["import type { Position } from '#lib/positions.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { bindKeyPress } from '#lib/key-press.ts';\nimport { mirrorChildren } from '#lib/mirror-children.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { keyTarget, stepFrom } from '#lib/positions.ts';\nimport { placeChildren } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ninterface Press {\n\tboxes: Array<[number, DOMRect]>;\n\tindex: number | undefined;\n}\n\nfunction isInside(box: DOMRect, x: number, y: number): boolean {\n\treturn x >= box.left && x < box.right && y >= box.top && y < box.bottom;\n}\n\nexport function isUnder(option: Element, x: number, y: number): boolean {\n\treturn isInside(option.getBoundingClientRect(), x, y);\n}\n\nexport function optionValue(option: Element): string | undefined {\n\treturn option.querySelector<HTMLElement | SVGElement>('[data-sonic-value]')?.dataset.sonicValue;\n}\n\nexport abstract class SonicRadioGroupElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [...SonicFormElement.observedAttributes, 'value'];\n\n\tget value(): string {\n\t\treturn this.#value;\n\t}\n\n\tset value(next: string) {\n\t\tif (next === this.#value) return;\n\n\t\tthis.#value = next;\n\t\tthis.render();\n\t}\n\n\tprotected abstract readonly group: HTMLElement;\n\n\t#value = '';\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value') this.#value = this.getAttribute('value') ?? '';\n\t\tthis.render();\n\t}\n\n\tprotected chooseByClick(index: number): void {\n\t\tthis.select(index);\n\t}\n\n\tprotected chooseByKey(index: number, _event: KeyboardEvent): void {\n\t\tthis.select(index);\n\t\tthis.options()[index]?.focus();\n\t}\n\n\tprotected claimPress(_index: number, _event: PointerEvent): boolean {\n\t\treturn false;\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst group = this.group;\n\n\t\tthis.upgradeProperties('value');\n\t\tmirrorChildren(\n\t\t\tthis,\n\t\t\t{\n\t\t\t\tcontrol: group,\n\t\t\t\tcopy: copyNode,\n\t\t\t\tisCopied: (child) => this.isMirrored(child),\n\t\t\t\tplace: (copies) => {\n\t\t\t\t\tthis.placeCopies(copies);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\n\t\tgroup.addEventListener(\n\t\t\t'click',\n\t\t\t(event) => {\n\t\t\t\tif (event instanceof PointerEvent && event.pointerType !== '') return;\n\n\t\t\t\tconst index = this.indexOf(event.target);\n\t\t\t\tif (index !== -1) this.chooseByClick(index);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindPress(group, signal);\n\t\tbindKeyPress(group, signal);\n\t\tgroup.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (event.defaultPrevented) return;\n\n\t\t\t\tconst index = this.indexOf(event.target);\n\t\t\t\tconst next =\n\t\t\t\t\tindex === -1\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: keyTarget(this.positions(), index, { isWrapping: this.isWrapping(), key: event.key });\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.chooseByKey(next, event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected override focusTarget(): HTMLElement | undefined {\n\t\treturn this.options().find((option) => option.tabIndex === 0);\n\t}\n\n\tprotected indexOf(target: EventTarget | null): number {\n\t\tconst option = this.optionOf(target);\n\n\t\treturn option ? this.options().indexOf(option) : -1;\n\t}\n\n\tprotected isMirrored(child: Node): boolean {\n\t\treturn child instanceof Element && child.matches('[data-sonic-value]');\n\t}\n\n\tprotected isOptionDisabled(index: number): boolean {\n\t\treturn this.positions()[index]?.isDisabled !== false;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn true;\n\t}\n\n\tprotected optionOf(target: EventTarget | null): HTMLButtonElement | undefined {\n\t\tif (!(target instanceof Element)) return undefined;\n\n\t\tconst option = target.closest('[role=\"radio\"]');\n\n\t\treturn option instanceof HTMLButtonElement && option.parentElement === this.group\n\t\t\t? option\n\t\t\t: undefined;\n\t}\n\n\tprotected options(): Array<HTMLButtonElement> {\n\t\treturn [...this.group.children].filter(\n\t\t\t(child): child is HTMLButtonElement =>\n\t\t\t\tchild instanceof HTMLButtonElement && child.getAttribute('role') === 'radio',\n\t\t);\n\t}\n\n\tprotected placeCopies(copies: Array<Node>): void {\n\t\tthis.#copyOptions(copies);\n\t}\n\n\tprotected positions(): Array<Position> {\n\t\treturn this.options().map((option) => {\n\t\t\tconst child = option.querySelector('[data-sonic-value]');\n\n\t\t\treturn {\n\t\t\t\tisDisabled: child?.hasAttribute('data-sonic-disabled') === true,\n\t\t\t\tisMomentary: child?.hasAttribute('data-sonic-momentary') === true,\n\t\t\t};\n\t\t});\n\t}\n\n\tprotected refocusAt(index: number): void {\n\t\tif (this.group.matches(':focus-within')) this.options()[index]?.focus();\n\t}\n\n\tprotected releaseTarget(index: number): number | undefined {\n\t\treturn index;\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst options = this.options();\n\t\tconst positions = this.positions();\n\t\tconst checked = options.find((option) => optionValue(option) === this.#value);\n\t\tconst enabled = this.isDisabled()\n\t\t\t? []\n\t\t\t: options.filter((_option, index) => positions[index]?.isDisabled === false);\n\t\tconst focusable = enabled.find((option) => option === checked) ?? enabled[0];\n\n\t\tfor (const option of options) {\n\t\t\twriteAttribute(option, 'disabled', enabled.includes(option) ? undefined : '');\n\t\t\twriteAttribute(option, 'aria-checked', String(option === checked));\n\t\t\twriteAttribute(option, 'tabindex', option === focusable ? '0' : '-1');\n\t\t}\n\t\tthis.forwardNaming(this.group, true);\n\t\t// eslint-disable-next-line unicorn/no-null -- `null` submits nothing\n\t\tthis.writeFormValue(checked ? this.#value : null, this.#value);\n\t}\n\n\tprotected abstract renderOption(): HTMLButtonElement;\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = state;\n\t}\n\n\tprotected select(index: number): void {\n\t\tconst option = this.options()[index];\n\t\tconst next = option && optionValue(option);\n\t\tif (next === undefined || next === this.#value) return;\n\n\t\tthis.value = next;\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\tprotected stepFrom(from: number, step: number): number | undefined {\n\t\treturn stepFrom(this.positions(), from, { isWrapping: this.isWrapping(), step });\n\t}\n\n\t#bindPress(group: HTMLElement, signal: AbortSignal): void {\n\t\tbindDrag<Press>(\n\t\t\tgroup,\n\t\t\t{\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tconst index = this.indexOf(event.target);\n\t\t\t\t\tif (index === -1 || this.isDisabled() || this.isOptionDisabled(index)) return;\n\t\t\t\t\tif (this.claimPress(index, event)) return;\n\n\t\t\t\t\tconst boxes = this.#optionBoxes();\n\n\t\t\t\t\tthis.#markPressed(index);\n\n\t\t\t\t\treturn { boxes, index };\n\t\t\t\t},\n\t\t\t\tlift: (press) => {\n\t\t\t\t\tif (press.index === undefined || this.isDisabled()) return;\n\n\t\t\t\t\tconst index = this.releaseTarget(press.index);\n\t\t\t\t\tif (index === undefined) return;\n\n\t\t\t\t\tthis.select(index);\n\t\t\t\t\tthis.refocusAt(index);\n\t\t\t\t},\n\t\t\t\tmove: (press, event) => {\n\t\t\t\t\tpress.index = press.boxes.find(([, box]) =>\n\t\t\t\t\t\tisInside(box, event.clientX, event.clientY),\n\t\t\t\t\t)?.[0];\n\t\t\t\t\tthis.#markPressed(press.index);\n\t\t\t\t},\n\t\t\t\trelease: () => {\n\t\t\t\t\tthis.#markPressed(undefined);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#copyOptions(copies: Array<Node>): void {\n\t\tconst group = this.group;\n\t\tconst options = this.options();\n\t\tconst focused = this.optionOf(group.querySelector(':scope > :focus'));\n\t\tconst focusedValue = focused && optionValue(focused);\n\n\t\tfor (const [index, copy] of copies.entries()) {\n\t\t\tlet option = options[index];\n\t\t\tif (!option) {\n\t\t\t\toption = this.renderOption();\n\t\t\t\tgroup.append(option);\n\t\t\t}\n\n\t\t\tif (option.firstElementChild) placeChildren(option.firstElementChild, [copy]);\n\t\t}\n\t\tfor (const option of options.slice(copies.length)) option.remove();\n\t\tthis.render();\n\t\tif (focused) this.#refocus(focused, focusedValue);\n\t}\n\n\t#markPressed(pressed: number | undefined): void {\n\t\tfor (const [index, option] of this.options().entries()) {\n\t\t\toption.toggleAttribute('data-sonic-pressed', index === pressed);\n\t\t}\n\t}\n\n\t#optionBoxes(): Array<[number, DOMRect]> {\n\t\tconst positions = this.positions();\n\n\t\treturn this.options().flatMap((option, index) =>\n\t\t\tpositions[index]?.isDisabled === false\n\t\t\t\t? [[index, option.getBoundingClientRect()] satisfies [number, DOMRect]]\n\t\t\t\t: [],\n\t\t);\n\t}\n\n\t#refocus(focused: HTMLButtonElement, value: string | undefined): void {\n\t\tconst options = this.options();\n\t\tconst next =\n\t\t\toptions.find((option) => optionValue(option) === value) ??\n\t\t\toptions.find((option) => option.tabIndex === 0);\n\n\t\tif (next !== focused) next?.focus();\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"radio-group.js","names":[],"sources":["../../../src/elements/radio-group.ts"],"sourcesContent":["import type { Position } from '#lib/positions.ts';\n\nimport { SonicFormElement } from '#elements/form-element.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { bindKeyPress } from '#lib/key-press.ts';\nimport { mirrorChildren } from '#lib/mirror-children.ts';\nimport { bindDrag } from '#lib/pointer-drag.ts';\nimport { keyTarget, stepFrom } from '#lib/positions.ts';\nimport { placeChildren } from '#lib/render.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ninterface Press {\n\tboxes: Array<[number, DOMRect]>;\n\tindex: number | undefined;\n}\n\nfunction isInside(box: DOMRect, x: number, y: number): boolean {\n\treturn x >= box.left && x < box.right && y >= box.top && y < box.bottom;\n}\n\nexport function isUnder(option: Element, x: number, y: number): boolean {\n\treturn isInside(option.getBoundingClientRect(), x, y);\n}\n\nexport function optionValue(option: Element): string | undefined {\n\treturn option.querySelector<HTMLElement | SVGElement>('[data-sonic-value]')?.dataset.sonicValue;\n}\n\nexport abstract class SonicRadioGroupElement extends SonicFormElement {\n\tstatic override readonly observedAttributes = [...SonicFormElement.observedAttributes, 'value'];\n\n\tget value(): string {\n\t\treturn this.#value;\n\t}\n\n\tset value(next: string) {\n\t\tif (next === this.#value) return;\n\n\t\tthis.#value = next;\n\t\tthis.render();\n\t}\n\n\tprotected abstract readonly group: HTMLElement;\n\n\t#value = '';\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value') this.#value = this.getAttribute('value') ?? '';\n\t\tthis.render();\n\t}\n\n\tprotected chooseByClick(index: number): void {\n\t\tthis.select(index);\n\t}\n\n\tprotected chooseByKey(index: number, _event: KeyboardEvent): void {\n\t\tthis.select(index);\n\t\tthis.options()[index]?.focus();\n\t}\n\n\tprotected claimPress(_index: number, _event: PointerEvent): boolean {\n\t\treturn false;\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst group = this.group;\n\n\t\tthis.upgradeProperties('value');\n\t\tmirrorChildren(\n\t\t\tthis,\n\t\t\t{\n\t\t\t\tcontrol: group,\n\t\t\t\tcopy: copyNode,\n\t\t\t\tisCopied: (child) => this.isMirrored(child),\n\t\t\t\tplace: (copies) => {\n\t\t\t\t\tthis.placeCopies(copies);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\n\t\tgroup.addEventListener(\n\t\t\t'click',\n\t\t\t(event) => {\n\t\t\t\tif (event instanceof PointerEvent && event.pointerType !== '') return;\n\n\t\t\t\tconst index = this.indexOf(event.target);\n\t\t\t\tif (index !== -1) this.chooseByClick(index);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindPress(group, signal);\n\t\tbindKeyPress(group, signal);\n\t\tgroup.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (event.defaultPrevented) return;\n\n\t\t\t\tconst index = this.indexOf(event.target);\n\t\t\t\tconst next =\n\t\t\t\t\tindex === -1\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: keyTarget(this.positions(), index, { isWrapping: this.isWrapping(), key: event.key });\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.chooseByKey(next, event);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\tprotected override focusTarget(): HTMLElement | undefined {\n\t\treturn this.options().find((option) => option.tabIndex === 0);\n\t}\n\n\tprotected indexOf(target: EventTarget | null): number {\n\t\tconst option = this.optionOf(target);\n\n\t\treturn option ? this.options().indexOf(option) : -1;\n\t}\n\n\tprotected isMirrored(child: Node): boolean {\n\t\treturn child instanceof Element && child.matches('[data-sonic-value]');\n\t}\n\n\tprotected isOptionDisabled(index: number): boolean {\n\t\treturn this.positions()[index]?.isDisabled !== false;\n\t}\n\n\tprotected isWrapping(): boolean {\n\t\treturn true;\n\t}\n\n\tprotected optionOf(target: EventTarget | null): HTMLButtonElement | undefined {\n\t\tif (!(target instanceof Element)) return undefined;\n\n\t\tconst option = target.closest('[role=\"radio\"]');\n\n\t\treturn option instanceof HTMLButtonElement && option.parentElement === this.group\n\t\t\t? option\n\t\t\t: undefined;\n\t}\n\n\tprotected options(): Array<HTMLButtonElement> {\n\t\treturn [...this.group.children].filter(\n\t\t\t(child): child is HTMLButtonElement =>\n\t\t\t\tchild instanceof HTMLButtonElement && child.getAttribute('role') === 'radio',\n\t\t);\n\t}\n\n\tprotected placeCopies(copies: Array<Node>): void {\n\t\tthis.#copyOptions(copies);\n\t}\n\n\tprotected positions(): Array<Position> {\n\t\treturn this.options().map((option) => {\n\t\t\tconst child = option.querySelector('[data-sonic-value]');\n\n\t\t\treturn {\n\t\t\t\tisDisabled: child?.hasAttribute('data-sonic-disabled') === true,\n\t\t\t\tisMomentary: child?.hasAttribute('data-sonic-momentary') === true,\n\t\t\t};\n\t\t});\n\t}\n\n\tprotected refocusAt(index: number): void {\n\t\tif (this.group.matches(':focus-within')) this.options()[index]?.focus();\n\t}\n\n\tprotected releaseTarget(index: number): number | undefined {\n\t\treturn index;\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tconst options = this.options();\n\t\tconst positions = this.positions();\n\t\tconst checked = options.find((option) => optionValue(option) === this.#value);\n\t\tconst enabled = this.isDisabled()\n\t\t\t? []\n\t\t\t: options.filter((_option, index) => positions[index]?.isDisabled === false);\n\t\tconst focusable = enabled.find((option) => option === checked) ?? enabled[0];\n\n\t\tfor (const option of options) {\n\t\t\twriteAttribute(option, 'disabled', enabled.includes(option) ? undefined : '');\n\t\t\twriteAttribute(option, 'aria-checked', String(option === checked));\n\t\t\twriteAttribute(option, 'tabindex', option === focusable ? '0' : '-1');\n\t\t}\n\t\tthis.forwardNaming(this.group, true);\n\t\t// eslint-disable-next-line unicorn/no-null -- `null` submits nothing\n\t\tthis.writeFormValue(checked ? this.#value : null, this.#value);\n\t}\n\n\tprotected abstract renderOption(): HTMLButtonElement;\n\n\tprotected restoreState(state: string): void {\n\t\tthis.value = state;\n\t}\n\n\tprotected select(index: number): void {\n\t\tconst option = this.options()[index];\n\t\tconst next = option && optionValue(option);\n\t\tif (next === undefined || next === this.#value) return;\n\n\t\tthis.value = next;\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\tprotected override stateTarget(): HTMLElement {\n\t\treturn this.group;\n\t}\n\n\tprotected stepFrom(from: number, step: number): number | undefined {\n\t\treturn stepFrom(this.positions(), from, { isWrapping: this.isWrapping(), step });\n\t}\n\n\t#bindPress(group: HTMLElement, signal: AbortSignal): void {\n\t\tbindDrag<Press>(\n\t\t\tgroup,\n\t\t\t{\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tconst index = this.indexOf(event.target);\n\t\t\t\t\tif (index === -1 || this.isDisabled() || this.isOptionDisabled(index)) return;\n\t\t\t\t\tif (this.claimPress(index, event)) return;\n\n\t\t\t\t\tconst boxes = this.#optionBoxes();\n\n\t\t\t\t\tthis.#markPressed(index);\n\n\t\t\t\t\treturn { boxes, index };\n\t\t\t\t},\n\t\t\t\tlift: (press) => {\n\t\t\t\t\tif (press.index === undefined || this.isDisabled()) return;\n\n\t\t\t\t\tconst index = this.releaseTarget(press.index);\n\t\t\t\t\tif (index === undefined) return;\n\n\t\t\t\t\tthis.select(index);\n\t\t\t\t\tthis.refocusAt(index);\n\t\t\t\t},\n\t\t\t\tmove: (press, event) => {\n\t\t\t\t\tpress.index = press.boxes.find(([, box]) =>\n\t\t\t\t\t\tisInside(box, event.clientX, event.clientY),\n\t\t\t\t\t)?.[0];\n\t\t\t\t\tthis.#markPressed(press.index);\n\t\t\t\t},\n\t\t\t\trelease: () => {\n\t\t\t\t\tthis.#markPressed(undefined);\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#copyOptions(copies: Array<Node>): void {\n\t\tconst group = this.group;\n\t\tconst options = this.options();\n\t\tconst focused = this.optionOf(group.querySelector(':scope > :focus'));\n\t\tconst focusedValue = focused && optionValue(focused);\n\n\t\tfor (const [index, copy] of copies.entries()) {\n\t\t\tlet option = options[index];\n\t\t\tif (!option) {\n\t\t\t\toption = this.renderOption();\n\t\t\t\tgroup.append(option);\n\t\t\t}\n\n\t\t\tif (option.firstElementChild) placeChildren(option.firstElementChild, [copy]);\n\t\t}\n\t\tfor (const option of options.slice(copies.length)) option.remove();\n\t\tthis.render();\n\t\tif (focused) this.#refocus(focused, focusedValue);\n\t}\n\n\t#markPressed(pressed: number | undefined): void {\n\t\tfor (const [index, option] of this.options().entries()) {\n\t\t\toption.toggleAttribute('data-sonic-active', index === pressed);\n\t\t}\n\t}\n\n\t#optionBoxes(): Array<[number, DOMRect]> {\n\t\tconst positions = this.positions();\n\n\t\treturn this.options().flatMap((option, index) =>\n\t\t\tpositions[index]?.isDisabled === false\n\t\t\t\t? [[index, option.getBoundingClientRect()] satisfies [number, DOMRect]]\n\t\t\t\t: [],\n\t\t);\n\t}\n\n\t#refocus(focused: HTMLButtonElement, value: string | undefined): void {\n\t\tconst options = this.options();\n\t\tconst next =\n\t\t\toptions.find((option) => optionValue(option) === value) ??\n\t\t\toptions.find((option) => option.tabIndex === 0);\n\n\t\tif (next !== focused) next?.focus();\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { requireChild } from "../lib/render.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/elements/readout.ts
|
|
4
4
|
const canPopover = "togglePopover" in HTMLElement.prototype;
|
|
5
|
-
const isUnanchored =
|
|
5
|
+
const isUnanchored = !CSS.supports("anchor-name", "--a");
|
|
6
6
|
let readoutCount = 0;
|
|
7
7
|
var Readout = class {
|
|
8
8
|
#anchor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readout.js","names":[],"sources":["../../../src/elements/readout.ts"],"sourcesContent":["import { requireChild } from '#lib/render.ts';\n\ninterface ReadoutView {\n\tanchor: HTMLElement;\n\tisOpen: boolean;\n\ttext: string | undefined;\n}\n\n// happy-dom has no popover API\nconst canPopover = 'togglePopover' in HTMLElement.prototype;\n\n// Without anchor positioning the bubble opens away from its control\nexport const isUnanchored =
|
|
1
|
+
{"version":3,"file":"readout.js","names":[],"sources":["../../../src/elements/readout.ts"],"sourcesContent":["import { requireChild } from '#lib/render.ts';\n\ninterface ReadoutView {\n\tanchor: HTMLElement;\n\tisOpen: boolean;\n\ttext: string | undefined;\n}\n\n// happy-dom has no popover API\nconst canPopover = 'togglePopover' in HTMLElement.prototype;\n\n// Without anchor positioning the bubble opens away from its control\nexport const isUnanchored = !CSS.supports('anchor-name', '--a');\n\nlet readoutCount = 0;\n\nexport class Readout {\n\t#anchor: HTMLElement | undefined;\n\n\treadonly #bubble: HTMLElement;\n\n\treadonly #name = `--sonic-readout-${String((readoutCount += 1))}`;\n\n\treadonly #text: HTMLElement;\n\n\tconstructor(bubble: HTMLElement) {\n\t\tthis.#bubble = bubble;\n\t\tthis.#text = requireChild(bubble, 'span', HTMLElement);\n\t\tbubble.style.setProperty('position-anchor', this.#name);\n\t}\n\n\tclose(): void {\n\t\tthis.#toggle(false);\n\t}\n\n\tshow({ anchor, isOpen, text }: ReadoutView): void {\n\t\tif (anchor !== this.#anchor) {\n\t\t\tthis.#anchor?.style.removeProperty('anchor-name');\n\t\t\tanchor.style.setProperty('anchor-name', this.#name);\n\t\t\tthis.#anchor = anchor;\n\t\t}\n\n\t\tthis.#text.hidden = text === undefined;\n\t\tif (isOpen && text !== undefined && this.#text.textContent !== text) {\n\t\t\tthis.#text.textContent = text;\n\t\t}\n\t\tthis.#toggle(isOpen);\n\t}\n\n\t#toggle(isOpen: boolean): void {\n\t\tif (canPopover && !isUnanchored && this.#bubble.isConnected) this.#bubble.togglePopover(isOpen);\n\t}\n}\n"],"mappings":";;;AASA,MAAM,aAAa,mBAAmB,YAAY;AAGlD,MAAa,eAAe,CAAC,IAAI,SAAS,eAAe,KAAK;AAE9D,IAAI,eAAe;AAEnB,IAAa,UAAb,MAAqB;CACpB;CAEA,AAAS;CAET,AAAS,QAAQ,mBAAmB,OAAQ,gBAAgB,CAAE;CAE9D,AAAS;CAET,YAAY,QAAqB;EAChC,KAAK,UAAU;EACf,KAAK,QAAQ,aAAa,QAAQ,QAAQ,WAAW;EACrD,OAAO,MAAM,YAAY,mBAAmB,KAAK,KAAK;CACvD;CAEA,QAAc;EACb,KAAK,QAAQ,KAAK;CACnB;CAEA,KAAK,EAAE,QAAQ,QAAQ,QAA2B;EACjD,IAAI,WAAW,KAAK,SAAS;GAC5B,KAAK,SAAS,MAAM,eAAe,aAAa;GAChD,OAAO,MAAM,YAAY,eAAe,KAAK,KAAK;GAClD,KAAK,UAAU;EAChB;EAEA,KAAK,MAAM,SAAS,SAAS;EAC7B,IAAI,UAAU,SAAS,UAAa,KAAK,MAAM,gBAAgB,MAC9D,KAAK,MAAM,cAAc;EAE1B,KAAK,QAAQ,MAAM;CACpB;CAEA,QAAQ,QAAuB;EAC9B,IAAI,cAAc,CAAC,gBAAgB,KAAK,QAAQ,aAAa,KAAK,QAAQ,cAAc,MAAM;CAC/F;AACD"}
|