@xsynaptic/sonic-ui 0.1.0 → 0.2.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/README.md +8 -1
- package/dist/default/define/dial.js +7 -0
- package/dist/default/define/key.js +7 -0
- package/dist/default/define/meter.js +7 -0
- package/dist/default/define/segmented.js +7 -0
- package/dist/default/define/slider.js +7 -0
- package/dist/default/elements/define-once.js +7 -0
- package/dist/{elements → default/elements}/dial.js +15 -3
- package/dist/default/elements/form-element.js +56 -0
- package/dist/default/elements/key.js +144 -0
- package/dist/{elements → default/elements}/meter.js +27 -16
- package/dist/default/elements/range-element.js +410 -0
- package/dist/{elements → default/elements}/range-entry.js +5 -4
- package/dist/default/elements/segmented.js +173 -0
- package/dist/{elements → default/elements}/slider.js +23 -8
- package/dist/default/elements/sonic-element.js +153 -0
- package/dist/{index.js → default/index.js} +2 -1
- package/dist/default/lib/copy-node.js +30 -0
- package/dist/default/lib/percent.js +12 -0
- package/dist/default/lib/read-px-property.js +8 -0
- package/dist/{lib → default/lib}/render.js +9 -2
- package/dist/default/lib/taper.js +32 -0
- package/dist/default/lib/write-attribute.js +11 -0
- package/dist/dev/define/dial.js.map +1 -0
- package/dist/dev/define/key.js.map +1 -0
- package/dist/dev/define/meter.js.map +1 -0
- package/dist/dev/define/segmented.js.map +1 -0
- package/dist/dev/define/slider.js.map +1 -0
- package/dist/dev/define.js +5 -0
- package/dist/dev/elements/define-once.js.map +1 -0
- package/dist/{elements → dev/elements}/dial.d.ts +4 -0
- package/dist/dev/elements/dial.js +70 -0
- package/dist/dev/elements/dial.js.map +1 -0
- package/dist/dev/elements/form-element.d.ts +22 -0
- package/dist/dev/elements/form-element.js +57 -0
- package/dist/dev/elements/form-element.js.map +1 -0
- package/dist/dev/elements/key.d.ts +30 -0
- package/dist/{elements → dev/elements}/key.js +71 -25
- package/dist/dev/elements/key.js.map +1 -0
- package/dist/{elements → dev/elements}/meter.d.ts +6 -0
- package/dist/dev/elements/meter.js +131 -0
- package/dist/dev/elements/meter.js.map +1 -0
- package/dist/{elements → dev/elements}/range-element.d.ts +25 -5
- package/dist/{elements → dev/elements}/range-element.js +116 -53
- package/dist/dev/elements/range-element.js.map +1 -0
- package/dist/dev/elements/range-entry.js +75 -0
- package/dist/dev/elements/range-entry.js.map +1 -0
- package/dist/{elements → dev/elements}/segmented.d.ts +5 -2
- package/dist/dev/elements/segmented.js +174 -0
- package/dist/dev/elements/segmented.js.map +1 -0
- package/dist/{elements → dev/elements}/slider.d.ts +6 -0
- package/dist/dev/elements/slider.js +86 -0
- package/dist/dev/elements/slider.js.map +1 -0
- package/dist/dev/elements/sonic-element.d.ts +33 -0
- package/dist/dev/elements/sonic-element.js +169 -0
- package/dist/dev/elements/sonic-element.js.map +1 -0
- package/dist/{index.d.ts → dev/index.d.ts} +2 -1
- package/dist/dev/index.js +8 -0
- package/dist/dev/lib/copy-node.js +31 -0
- package/dist/dev/lib/copy-node.js.map +1 -0
- package/dist/dev/lib/percent.d.ts +5 -0
- package/dist/dev/lib/percent.js +13 -0
- package/dist/dev/lib/percent.js.map +1 -0
- package/dist/dev/lib/read-px-property.js.map +1 -0
- package/dist/dev/lib/render.js +27 -0
- package/dist/dev/lib/render.js.map +1 -0
- package/dist/dev/lib/taper.js.map +1 -0
- package/dist/dev/lib/write-attribute.js.map +1 -0
- package/dist/skins/ivory.css +3 -3
- package/dist/skins/slate.css +4 -4
- package/dist/styles/controls.css +0 -1
- package/dist/styles/dial.css +33 -13
- package/dist/styles/key.css +30 -10
- package/dist/styles/material.css +58 -10
- package/dist/styles/meter.css +25 -5
- package/dist/styles/screen.css +7 -1
- package/dist/styles/segmented.css +24 -7
- package/dist/styles/slider.css +17 -4
- package/package.json +13 -12
- package/dist/define/dial.js.map +0 -1
- package/dist/define/key.js.map +0 -1
- package/dist/define/meter.js.map +0 -1
- package/dist/define/segmented.js.map +0 -1
- package/dist/define/slider.js.map +0 -1
- package/dist/elements/define-once.js.map +0 -1
- package/dist/elements/dial.js.map +0 -1
- package/dist/elements/key.d.ts +0 -17
- package/dist/elements/key.js.map +0 -1
- package/dist/elements/meter.js.map +0 -1
- package/dist/elements/range-element.js.map +0 -1
- package/dist/elements/range-entry.js.map +0 -1
- package/dist/elements/segmented.js +0 -104
- package/dist/elements/segmented.js.map +0 -1
- package/dist/elements/slider.js.map +0 -1
- package/dist/elements/sonic-element.d.ts +0 -20
- package/dist/elements/sonic-element.js +0 -94
- package/dist/elements/sonic-element.js.map +0 -1
- package/dist/lib/read-px-property.js.map +0 -1
- package/dist/lib/render.js.map +0 -1
- package/dist/lib/taper.js.map +0 -1
- package/dist/lib/write-attribute.js.map +0 -1
- /package/dist/{define.js → default/define.js} +0 -0
- /package/dist/{define → dev/define}/dial.d.ts +0 -0
- /package/dist/{define → dev/define}/dial.js +0 -0
- /package/dist/{define → dev/define}/key.d.ts +0 -0
- /package/dist/{define → dev/define}/key.js +0 -0
- /package/dist/{define → dev/define}/meter.d.ts +0 -0
- /package/dist/{define → dev/define}/meter.js +0 -0
- /package/dist/{define → dev/define}/segmented.d.ts +0 -0
- /package/dist/{define → dev/define}/segmented.js +0 -0
- /package/dist/{define → dev/define}/slider.d.ts +0 -0
- /package/dist/{define → dev/define}/slider.js +0 -0
- /package/dist/{define.d.ts → dev/define.d.ts} +0 -0
- /package/dist/{elements → dev/elements}/define-once.js +0 -0
- /package/dist/{lib → dev/lib}/read-px-property.js +0 -0
- /package/dist/{lib → dev/lib}/taper.js +0 -0
- /package/dist/{lib → dev/lib}/write-attribute.js +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dial.js","names":[],"sources":["../../src/elements/dial.ts"],"sourcesContent":["import { SonicRangeElement } from '#elements/range-element.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { template } from '#lib/render.ts';\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-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-pointer\"></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\nconst fallbackTravelPx = 160;\n\nexport class SonicDial extends SonicRangeElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicRangeElement.observedAttributes,\n\t\t'modulation',\n\t];\n\n\treadonly #dial = renderDial();\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst dial = this.#dial;\n\n\t\tthis.appendOnce(dial);\n\t\tthis.render();\n\t\tthis.checkStyles(dial, 'dial.css');\n\t\tthis.bindGestures(dial, signal, () => ({\n\t\t\tposition: (event) => -event.clientY,\n\t\t\t// Once per gesture; a computed style read per move forces a style recalc\n\t\t\ttravelPx: readPxProperty(getComputedStyle(dial), '--_sonic-dial-travel', fallbackTravelPx),\n\t\t}));\n\t}\n\n\tprotected render(): void {\n\t\tconst dial = this.#dial;\n\t\tconst positions = this.positions();\n\t\tconst modulated = this.fraction(this.value + this.numberAttribute('modulation', 0));\n\t\tconst value = this.fraction(this.value);\n\n\t\tdial.style.setProperty('--_sonic-dial-value', String(value));\n\t\tdial.style.setProperty('--_sonic-dial-origin', String(this.originFraction()));\n\t\tdial.style.setProperty('--_sonic-dial-modulation-from', String(Math.min(value, modulated)));\n\t\tdial.style.setProperty('--_sonic-dial-modulation-to', String(Math.max(value, modulated)));\n\t\tif (positions === undefined) dial.style.removeProperty('--_sonic-dial-positions');\n\t\telse dial.style.setProperty('--_sonic-dial-positions', String(positions));\n\t\tthis.renderAria(dial);\n\t}\n}\n"],"mappings":";;;;;AAUA,MAAM,aAAa,SACP;;;;;;;;;;;;;;;;;;;IAoBX,cACD;AAEA,MAAM,mBAAmB;AAEzB,IAAa,YAAb,cAA+B,kBAAkB;CAChD,OAAyB,qBAAqB,CAC7C,GAAG,kBAAkB,oBACrB,YACD;CAEA,AAAS,QAAQ,WAAW;CAE5B,AAAU,QAAQ,QAA2B;EAC5C,MAAM,OAAO,KAAK;EAElB,KAAK,WAAW,IAAI;EACpB,KAAK,OAAO;EACZ,KAAK,YAAY,MAAM,UAAU;EACjC,KAAK,aAAa,MAAM,eAAe;GACtC,WAAW,UAAU,CAAC,MAAM;GAE5B,UAAU,eAAe,iBAAiB,IAAI,GAAG,wBAAwB,gBAAgB;EAC1F,EAAE;CACH;CAEA,AAAU,SAAe;EACxB,MAAM,OAAO,KAAK;EAClB,MAAM,YAAY,KAAK,UAAU;EACjC,MAAM,YAAY,KAAK,SAAS,KAAK,QAAQ,KAAK,gBAAgB,cAAc,CAAC,CAAC;EAClF,MAAM,QAAQ,KAAK,SAAS,KAAK,KAAK;EAEtC,KAAK,MAAM,YAAY,uBAAuB,OAAO,KAAK,CAAC;EAC3D,KAAK,MAAM,YAAY,wBAAwB,OAAO,KAAK,eAAe,CAAC,CAAC;EAC5E,KAAK,MAAM,YAAY,iCAAiC,OAAO,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC;EAC1F,KAAK,MAAM,YAAY,+BAA+B,OAAO,KAAK,IAAI,OAAO,SAAS,CAAC,CAAC;EACxF,IAAI,cAAc,QAAW,KAAK,MAAM,eAAe,yBAAyB;OAC3E,KAAK,MAAM,YAAY,2BAA2B,OAAO,SAAS,CAAC;EACxE,KAAK,WAAW,IAAI;CACrB;AACD"}
|
package/dist/elements/key.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { SonicElement } from "./sonic-element.js";
|
|
2
|
-
//#region src/elements/key.d.ts
|
|
3
|
-
declare global {
|
|
4
|
-
interface HTMLElementTagNameMap {
|
|
5
|
-
'sonic-key': SonicKey;
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
export declare class SonicKey extends SonicElement {
|
|
9
|
-
#private;
|
|
10
|
-
static readonly observedAttributes: string[];
|
|
11
|
-
get pressed(): boolean;
|
|
12
|
-
set pressed(isPressed: boolean);
|
|
13
|
-
attributeChangedCallback(name: string): void;
|
|
14
|
-
protected connect(signal: AbortSignal): void;
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
//# sourceMappingURL=key.d.ts.map
|
package/dist/elements/key.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"key.js","names":[],"sources":["../../src/elements/key.ts"],"sourcesContent":["import { SonicElement } from '#elements/sonic-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-key': SonicKey;\n\t}\n}\n\nconst renderKey = template(\n\t/* HTML */ `\n\t\t<button class=\"sonic-key\" type=\"button\"><span class=\"sonic-key-cap\"></span></button>\n\t`,\n\tHTMLButtonElement,\n);\n\nconst holdKeys = new Set([' ', 'Enter']);\n\nexport class SonicKey extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t'momentary',\n\t\t'pressed',\n\t\t'toggle',\n\t];\n\n\tget pressed(): boolean {\n\t\treturn this.hasAttribute('pressed');\n\t}\n\n\tset pressed(isPressed: boolean) {\n\t\tthis.toggleAttribute('pressed', isPressed);\n\t}\n\n\t// A pointer id or a key name\n\t#holder: number | string | undefined;\n\n\treadonly #key = renderKey();\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'disabled' && this.disabled) this.#release();\n\t\tthis.#render();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst key = this.#key;\n\t\tconst cap = requireChild(key, '.sonic-key-cap', HTMLSpanElement);\n\n\t\tthis.upgradeProperty('pressed');\n\t\tthis.adoptChildren(() => {\n\t\t\tconst arrivals = [...this.childNodes].filter((node) => node !== key);\n\n\t\t\t// Arrivals after a replacement take the icon's place\n\t\t\tif (key.parentNode === this) cap.append(...arrivals);\n\t\t\telse cap.replaceChildren(...arrivals);\n\t\t\tthis.appendOnce(key);\n\t\t}, signal);\n\t\tthis.#render();\n\t\tthis.checkStyles(key, 'key.css');\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#release();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\tkey.addEventListener(\n\t\t\t'click',\n\t\t\t() => {\n\t\t\t\tif (!this.hasAttribute('toggle')) return;\n\n\t\t\t\tthis.pressed = !this.pressed;\n\t\t\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindMomentary(key, signal);\n\t}\n\n\t// Not `click`, which lands a tap late by the finger's dwell\n\t#bindMomentary(key: HTMLButtonElement, signal: AbortSignal): void {\n\t\tconst releasePointer = (event: PointerEvent): void => {\n\t\t\tif (event.pointerId === this.#holder) this.#release();\n\t\t};\n\n\t\tkey.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.button !== 0 || !this.#isMomentary()) return;\n\n\t\t\t\tkey.setPointerCapture(event.pointerId);\n\t\t\t\tthis.#hold(event.pointerId);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tkey.addEventListener('lostpointercapture', releasePointer, { signal });\n\t\tkey.addEventListener('pointercancel', releasePointer, { signal });\n\t\tkey.addEventListener('pointerup', releasePointer, { signal });\n\t\tkey.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (event.repeat || !holdKeys.has(event.key) || !this.#isMomentary()) return;\n\n\t\t\t\tthis.#hold(event.key);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tkey.addEventListener(\n\t\t\t'keyup',\n\t\t\t(event) => {\n\t\t\t\tif (event.key === this.#holder) this.#release();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\t// Keyboard holds only; a finger on the next pad takes focus while this one is still held\n\t\tkey.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tif (typeof this.#holder === 'string') this.#release();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#hold(holder: number | string): void {\n\t\tif (this.disabled || this.#holder !== undefined) return;\n\n\t\tthis.#holder = holder;\n\t\tthis.pressed = true;\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#isMomentary(): boolean {\n\t\treturn this.hasAttribute('momentary') && !this.hasAttribute('toggle');\n\t}\n\n\t#release(): void {\n\t\tif (this.#holder === undefined) return;\n\n\t\tthis.#holder = undefined;\n\t\tthis.pressed = false;\n\t\tthis.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#render(): void {\n\t\tconst key = this.#key;\n\n\t\tkey.disabled = this.disabled;\n\t\tkey.removeAttribute('aria-pressed');\n\t\tif (this.hasAttribute('toggle')) key.setAttribute('aria-pressed', String(this.pressed));\n\t\tthis.forwardNaming(key, true);\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"meter.js","names":[],"sources":["../../src/elements/meter.ts"],"sourcesContent":["import { SonicElement } from '#elements/sonic-element.ts';\nimport { template } from '#lib/render.ts';\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-level\"></div>\n\t\t\t<div class=\"sonic-meter-clip\"></div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nconst releaseDecibelsPerSecond = 20;\nconst holdMs = 1500;\n\nfunction toDecibels(amplitude: number): number {\n\treturn 20 * Math.log10(Math.abs(amplitude));\n}\n\n// `level` is a linear amplitude; `min` and `max` are dBFS\nexport class SonicMeter extends SonicElement {\n\tstatic override readonly observedAttributes = ['disabled', 'max', 'min'];\n\n\tget level(): number {\n\t\treturn this.#level;\n\t}\n\n\tset level(amplitude: number) {\n\t\tif (!Number.isFinite(amplitude)) return;\n\n\t\tconst decibels = toDecibels(amplitude);\n\t\tconst now = performance.now();\n\n\t\tthis.#level = amplitude;\n\t\tthis.#target = decibels;\n\t\tthis.#bar = Math.max(this.#bar, decibels);\n\t\tif (decibels >= this.#peak) {\n\t\t\tthis.#peak = decibels;\n\t\t\tthis.#peakAt = now;\n\t\t}\n\t\tif (decibels >= 0) this.#clipAt = now;\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t}\n\n\t#bar = -Infinity;\n\n\t#clipAt = -Infinity;\n\n\t#frame: number | undefined;\n\n\t#lastFrame: number | undefined;\n\n\t#level = 0;\n\n\treadonly #meter = renderMeter();\n\n\t#peak = -Infinity;\n\n\t#peakAt = -Infinity;\n\n\t#target = -Infinity;\n\n\tattributeChangedCallback(): void {\n\t\tconst now = performance.now();\n\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst now = performance.now();\n\n\t\tthis.upgradeProperty('level');\n\t\tthis.appendOnce(this.#meter);\n\t\tthis.#render(now);\n\t\tthis.#schedule(now);\n\t\tthis.checkStyles(this.#meter, 'meter.css');\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}\n\n\t#fraction(decibels: number): number {\n\t\tconst min = this.#min();\n\t\tconst range = this.#max() - min;\n\n\t\treturn range > 0 ? Math.min(1, Math.max(0, (decibels - min) / range)) : 0;\n\t}\n\n\t#isSettled(now: number): boolean {\n\t\tconst floor = this.#min();\n\n\t\treturn (\n\t\t\tthis.#bar <= Math.max(this.#target, floor) &&\n\t\t\tthis.#peak <= Math.max(this.#bar, floor) &&\n\t\t\tnow - this.#clipAt >= holdMs\n\t\t);\n\t}\n\n\t#max(): number {\n\t\treturn this.numberAttribute('max', 0);\n\t}\n\n\t#min(): number {\n\t\treturn this.numberAttribute('min', -60);\n\t}\n\n\t#render(now: number): void {\n\t\tconst meter = this.#meter;\n\n\t\t// The sheet places its colour zones on the scale\n\t\tmeter.style.setProperty('--_sonic-meter-min', String(this.#min()));\n\t\tmeter.style.setProperty('--_sonic-meter-max', String(this.#max()));\n\n\t\t// Disabled puts the light out, as on the other controls; the ballistics keep running so it lights up mid-fall\n\t\tif (this.disabled) {\n\t\t\tmeter.style.setProperty('--_sonic-meter-level', '0');\n\t\t\tmeter.style.setProperty('--_sonic-meter-peak', '0');\n\t\t\tmeter.style.setProperty('--_sonic-meter-clipped', '0');\n\t\t\treturn;\n\t\t}\n\n\t\tmeter.style.setProperty('--_sonic-meter-level', String(this.#fraction(this.#bar)));\n\t\tmeter.style.setProperty('--_sonic-meter-peak', String(this.#fraction(this.#peak)));\n\t\tmeter.style.setProperty('--_sonic-meter-clipped', now - this.#clipAt < holdMs ? '1' : '0');\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// Timed by the frame rather than counted, so a 120Hz display falls at the same speed\n\treadonly #tick = (time: number): void => {\n\t\tconst fall = (releaseDecibelsPerSecond * Math.max(0, time - (this.#lastFrame ?? time))) / 1000;\n\n\t\tthis.#frame = undefined;\n\t\tthis.#lastFrame = time;\n\t\tthis.#bar = Math.max(this.#target, this.#min(), this.#bar - fall);\n\t\tif (time - this.#peakAt >= holdMs) this.#peak = Math.max(this.#bar, this.#peak - fall);\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":";;;;AASA,MAAM,cAAc,SACR;;;;;;IAOX,cACD;AAEA,MAAM,2BAA2B;AACjC,MAAM,SAAS;AAEf,SAAS,WAAW,WAA2B;CAC9C,OAAO,KAAK,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC;AAC3C;AAGA,IAAa,aAAb,cAAgC,aAAa;CAC5C,OAAyB,qBAAqB;EAAC;EAAY;EAAO;CAAK;CAEvE,IAAI,QAAgB;EACnB,OAAO,KAAK;CACb;CAEA,IAAI,MAAM,WAAmB;EAC5B,IAAI,CAAC,OAAO,SAAS,SAAS,GAAG;EAEjC,MAAM,WAAW,WAAW,SAAS;EACrC,MAAM,MAAM,YAAY,IAAI;EAE5B,KAAK,SAAS;EACd,KAAK,UAAU;EACf,KAAK,OAAO,KAAK,IAAI,KAAK,MAAM,QAAQ;EACxC,IAAI,YAAY,KAAK,OAAO;GAC3B,KAAK,QAAQ;GACb,KAAK,UAAU;EAChB;EACA,IAAI,YAAY,GAAG,KAAK,UAAU;EAClC,KAAK,QAAQ,GAAG;EAChB,KAAK,UAAU,GAAG;CACnB;CAEA,OAAO;CAEP,UAAU;CAEV;CAEA;CAEA,SAAS;CAET,AAAS,SAAS,YAAY;CAE9B,QAAQ;CAER,UAAU;CAEV,UAAU;CAEV,2BAAiC;EAChC,MAAM,MAAM,YAAY,IAAI;EAE5B,KAAK,QAAQ,GAAG;EAChB,KAAK,UAAU,GAAG;CACnB;CAEA,AAAU,QAAQ,QAA2B;EAC5C,MAAM,MAAM,YAAY,IAAI;EAE5B,KAAK,gBAAgB,OAAO;EAC5B,KAAK,WAAW,KAAK,MAAM;EAC3B,KAAK,QAAQ,GAAG;EAChB,KAAK,UAAU,GAAG;EAClB,KAAK,YAAY,KAAK,QAAQ,WAAW;EACzC,OAAO,iBACN,eACM;GACL,IAAI,KAAK,WAAW,QAAW,qBAAqB,KAAK,MAAM;GAC/D,KAAK,SAAS;GACd,KAAK,aAAa;EACnB,GACA,EAAE,MAAM,KAAK,CACd;CACD;CAEA,UAAU,UAA0B;EACnC,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,QAAQ,KAAK,KAAK,IAAI;EAE5B,OAAO,QAAQ,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,WAAW,OAAO,KAAK,CAAC,IAAI;CACzE;CAEA,WAAW,KAAsB;EAChC,MAAM,QAAQ,KAAK,KAAK;EAExB,OACC,KAAK,QAAQ,KAAK,IAAI,KAAK,SAAS,KAAK,KACzC,KAAK,SAAS,KAAK,IAAI,KAAK,MAAM,KAAK,KACvC,MAAM,KAAK,WAAW;CAExB;CAEA,OAAe;EACd,OAAO,KAAK,gBAAgB,OAAO,CAAC;CACrC;CAEA,OAAe;EACd,OAAO,KAAK,gBAAgB,OAAO,GAAG;CACvC;CAEA,QAAQ,KAAmB;EAC1B,MAAM,QAAQ,KAAK;EAGnB,MAAM,MAAM,YAAY,sBAAsB,OAAO,KAAK,KAAK,CAAC,CAAC;EACjE,MAAM,MAAM,YAAY,sBAAsB,OAAO,KAAK,KAAK,CAAC,CAAC;EAGjE,IAAI,KAAK,UAAU;GAClB,MAAM,MAAM,YAAY,wBAAwB,GAAG;GACnD,MAAM,MAAM,YAAY,uBAAuB,GAAG;GAClD,MAAM,MAAM,YAAY,0BAA0B,GAAG;GACrD;EACD;EAEA,MAAM,MAAM,YAAY,wBAAwB,OAAO,KAAK,UAAU,KAAK,IAAI,CAAC,CAAC;EACjF,MAAM,MAAM,YAAY,uBAAuB,OAAO,KAAK,UAAU,KAAK,KAAK,CAAC,CAAC;EACjF,MAAM,MAAM,YAAY,0BAA0B,MAAM,KAAK,UAAU,SAAS,MAAM,GAAG;CAC1F;CAEA,UAAU,KAAmB;EAC5B,IAAI,KAAK,WAAW,UAAa,CAAC,KAAK,eAAe,KAAK,WAAW,GAAG,GAAG;EAE5E,IAAI,KAAK,eAAe,QAAW,KAAK,aAAa;EACrD,KAAK,SAAS,sBAAsB,KAAK,KAAK;CAC/C;CAGA,AAAS,SAAS,SAAuB;EACxC,MAAM,OAAQ,2BAA2B,KAAK,IAAI,GAAG,QAAQ,KAAK,cAAc,KAAK,IAAK;EAE1F,KAAK,SAAS;EACd,KAAK,aAAa;EAClB,KAAK,OAAO,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,GAAG,KAAK,OAAO,IAAI;EAChE,IAAI,OAAO,KAAK,WAAW,QAAQ,KAAK,QAAQ,KAAK,IAAI,KAAK,MAAM,KAAK,QAAQ,IAAI;EACrF,KAAK,QAAQ,IAAI;EACjB,IAAI,KAAK,WAAW,IAAI,GAAG;GAC1B,KAAK,aAAa;GAClB;EACD;EAEA,KAAK,SAAS,sBAAsB,KAAK,KAAK;CAC/C;AACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"range-element.js","names":[],"sources":["../../src/elements/range-element.ts"],"sourcesContent":["import type { Taper } from '#lib/taper.ts';\n\nimport { RangeEntry } from '#elements/range-entry.ts';\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { requireChild } from '#lib/render.ts';\nimport { clampUnit, linearTaper, logTaper, skewTaper } from '#lib/taper.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\n// `position` grows with the value\nexport interface RangeAxis {\n\tposition: (event: PointerEvent) => number;\n\ttravelPx: number;\n}\n\ninterface RangeDrag extends RangeAxis {\n\tfromValue: number;\n\tisRevealed: boolean;\n\tlastPosition: number;\n\tpointerId: number;\n\t// Unstepped, or a slow drag rounds back to where it started\n\trawFraction: number;\n\trevealTimer: ReturnType<typeof setTimeout> | undefined;\n\tstartPosition: number;\n}\n\nconst fineScale = 0.1;\nconst keyRevealMs = 1000;\nconst pageSteps = 10;\nconst revealMs = 250;\nconst revealPx = 4;\n\n// happy-dom has no popover API\nconst canPopover = 'togglePopover' in HTMLElement.prototype;\n\n// A shared anchor name resolves to the last one on the page\nlet anchorCount = 0;\n\n// A Mac keyboard's Delete sends Backspace\nconst resetKeys = new Set(['Backspace', 'Delete']);\n\nconst keySteps = new Map([\n\t['ArrowDown', -1],\n\t['ArrowLeft', -1],\n\t['ArrowRight', 1],\n\t['ArrowUp', 1],\n\t['PageDown', -pageSteps],\n\t['PageUp', pageSteps],\n]);\n\n// A slider's children are presentational, so these come off while the entry is open\nconst sliderAttributes = [\n\t'aria-disabled',\n\t'aria-orientation',\n\t'aria-valuemax',\n\t'aria-valuemin',\n\t'aria-valuenow',\n\t'aria-valuetext',\n\t'role',\n\t'tabindex',\n];\n\n// As on `<input type=\"range\">`, the property never writes the `value` attribute back\nexport abstract class SonicRangeElement extends SonicElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicElement.observedAttributes,\n\t\t'max',\n\t\t'midpoint',\n\t\t'min',\n\t\t'notched',\n\t\t'origin',\n\t\t'readout',\n\t\t'step',\n\t\t'taper',\n\t\t'value',\n\t];\n\n\tget formatValue(): ((value: number) => string) | undefined {\n\t\treturn this.#formatValue;\n\t}\n\n\tset formatValue(format: ((value: number) => string) | undefined) {\n\t\tthis.#formatValue = format;\n\t\tthis.render();\n\t}\n\n\tget parseValue(): ((text: string) => number) | undefined {\n\t\treturn this.#parseValue;\n\t}\n\n\tset parseValue(parse: ((text: string) => number) | undefined) {\n\t\tthis.#parseValue = parse;\n\t}\n\n\tget value(): number {\n\t\treturn this.#value;\n\t}\n\n\t// A drag owns the value, so a timer's write can't pull the cap from under the pointer\n\tset value(next: number) {\n\t\tif (this.#drag) return;\n\n\t\tthis.#write(next);\n\t}\n\n\treadonly #anchor = `--sonic-readout-${String((anchorCount += 1))}`;\n\n\t#drag: RangeDrag | undefined;\n\n\t#entry: RangeEntry | undefined;\n\n\t#formatValue: ((value: number) => string) | undefined;\n\n\t// Re-read from the attribute until the property is set, since `max` may arrive after `value`\n\t#isDirty = false;\n\n\t#keyRevealTimer: ReturnType<typeof setTimeout> | undefined;\n\n\t#parseValue: ((text: string) => number) | undefined;\n\n\t#readout: undefined | { bubble: HTMLElement; text: HTMLElement };\n\n\t#value = 0;\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'value') {\n\t\t\tif (this.#drag) return;\n\n\t\t\tthis.#isDirty = false;\n\t\t}\n\t\tif (name === 'disabled' && this.disabled) {\n\t\t\tthis.#entry?.close(false);\n\t\t\tthis.#endDrag();\n\t\t}\n\n\t\tthis.#value = this.#clamp(\n\t\t\tthis.#isDirty ? this.#value : this.numberAttribute('value', this.min()),\n\t\t);\n\t\tthis.render();\n\t}\n\n\toverride connectedCallback(): void {\n\t\tthis.upgradeProperty('formatValue');\n\t\tthis.upgradeProperty('parseValue');\n\t\tthis.upgradeProperty('value');\n\t\tsuper.connectedCallback();\n\t}\n\n\tprotected bindGestures(\n\t\tcontrol: HTMLElement,\n\t\tsignal: AbortSignal,\n\t\tgrab: (event: PointerEvent) => RangeAxis | undefined,\n\t): void {\n\t\tconst entry = this.#bindEntry(control, signal);\n\n\t\tcontrol.addEventListener(\n\t\t\t'pointerdown',\n\t\t\t(event) => {\n\t\t\t\tif (event.button !== 0 || this.disabled || entry.isOpen) return;\n\n\t\t\t\tcontrol.focus();\n\t\t\t\tif (event.metaKey || event.ctrlKey) {\n\t\t\t\t\tthis.#reset();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst fromValue = this.#value;\n\t\t\t\tconst axis = grab(event);\n\t\t\t\tif (!axis) return;\n\n\t\t\t\tconst startPosition = axis.position(event);\n\t\t\t\tconst drag: RangeDrag = {\n\t\t\t\t\tfromValue,\n\t\t\t\t\tisRevealed: false,\n\t\t\t\t\tlastPosition: startPosition,\n\t\t\t\t\tpointerId: event.pointerId,\n\t\t\t\t\tposition: axis.position,\n\t\t\t\t\trawFraction: this.fraction(this.#value),\n\t\t\t\t\trevealTimer: undefined,\n\t\t\t\t\tstartPosition,\n\t\t\t\t\ttravelPx: axis.travelPx,\n\t\t\t\t};\n\n\t\t\t\tdrag.revealTimer = setTimeout(() => {\n\t\t\t\t\tthis.#reveal(drag);\n\t\t\t\t}, revealMs);\n\t\t\t\tthis.#drag = drag;\n\t\t\t\tcontrol.setPointerCapture(event.pointerId);\n\t\t\t\tthis.toggleState('dragging', true);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'pointermove',\n\t\t\t(event) => {\n\t\t\t\tconst drag = this.#drag;\n\t\t\t\tif (event.pointerId !== drag?.pointerId) return;\n\n\t\t\t\tconst position = drag.position(event);\n\t\t\t\tconst scale = event.shiftKey ? fineScale : 1;\n\n\t\t\t\t// Per move, so pressing Shift mid-drag changes pace without a jump\n\t\t\t\tdrag.rawFraction = clampUnit(\n\t\t\t\t\tdrag.rawFraction + ((position - drag.lastPosition) / drag.travelPx) * scale,\n\t\t\t\t);\n\t\t\t\tdrag.lastPosition = position;\n\t\t\t\tif (Math.abs(position - drag.startPosition) >= revealPx) this.#reveal(drag);\n\t\t\t\tthis.input(this.valueAt(drag.rawFraction));\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\tconst endDrag = (event: PointerEvent): void => {\n\t\t\tif (event.pointerId === this.#drag?.pointerId) this.#endDrag();\n\t\t};\n\n\t\tcontrol.addEventListener(\n\t\t\t'pointercancel',\n\t\t\t(event) => {\n\t\t\t\tendDrag(event);\n\t\t\t\tentry.forgetPress();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\t// Other code can release capture without a `pointerup`\n\t\tcontrol.addEventListener('lostpointercapture', endDrag, { signal });\n\t\tcontrol.addEventListener(\n\t\t\t'pointerup',\n\t\t\t(event) => {\n\t\t\t\tif (event.pointerId !== this.#drag?.pointerId) return;\n\n\t\t\t\tthis.#endDrag();\n\t\t\t\tif (!entry.press(event)) return;\n\n\t\t\t\tif (this.getAttribute('double-press') === 'reset') this.#reset();\n\t\t\t\telse entry.open();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tthis.#bindKeys(control, entry, signal);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.#endDrag();\n\t\t\t\tthis.#hideForKeys();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected clampRange(next: number): number {\n\t\treturn Math.min(this.max(), Math.max(this.min(), next));\n\t}\n\n\tprotected fraction(value: number): number {\n\t\treturn (this.#taper() ?? linearTaper(this.min(), this.max())).position(value);\n\t}\n\n\tprotected input(next: number): boolean {\n\t\tconst previous = this.#value;\n\n\t\tthis.#write(next);\n\t\tif (this.#value === previous) return false;\n\n\t\tthis.dispatchEvent(new Event('input', { bubbles: true, composed: true }));\n\n\t\treturn true;\n\t}\n\n\tprotected max(): number {\n\t\treturn Math.max(this.min(), this.numberAttribute('max', 100));\n\t}\n\n\tprotected min(): number {\n\t\treturn this.numberAttribute('min', 0);\n\t}\n\n\tprotected originFraction(): number {\n\t\treturn this.fraction(this.numberAttribute('origin', this.min()));\n\t}\n\n\tprotected positions(): number | undefined {\n\t\tconst positions = Math.round(this.range() / this.#step()) + 1;\n\n\t\tif (!this.hasAttribute('notched') || !Number.isFinite(positions) || positions < 2) return;\n\n\t\treturn positions;\n\t}\n\n\tprotected range(): number {\n\t\treturn this.max() - this.min();\n\t}\n\n\tprotected abstract render(): void;\n\n\tprotected renderAria(control: HTMLElement, orientation?: 'horizontal' | 'vertical'): void {\n\t\tif (this.#entry?.isOpen) {\n\t\t\tfor (const name of sliderAttributes) control.removeAttribute(name);\n\t\t\tthis.forwardNaming(control, false);\n\t\t\tthis.#renderReadout();\n\t\t\treturn;\n\t\t}\n\n\t\tcontrol.setAttribute('role', 'slider');\n\t\tcontrol.setAttribute('aria-valuemin', String(this.min()));\n\t\tcontrol.setAttribute('aria-valuemax', String(this.max()));\n\t\tcontrol.setAttribute('aria-valuenow', String(this.#value));\n\t\tif (orientation) control.setAttribute('aria-orientation', orientation);\n\t\twriteAttribute(control, 'aria-valuetext', this.#formatValue?.(this.#value));\n\t\tthis.forwardNaming(control, true);\n\t\tthis.#renderDisabled(control);\n\t\tthis.#renderReadout();\n\t}\n\n\tprotected valueAt(fraction: number): number {\n\t\treturn (this.#taper() ?? linearTaper(this.min(), this.max())).value(fraction);\n\t}\n\n\t#bindEntry(control: HTMLElement, signal: AbortSignal): RangeEntry {\n\t\tconst input = requireChild(control, 'input', HTMLInputElement);\n\t\tconst bubble = requireChild(control, '[popover]', HTMLElement);\n\n\t\tthis.#readout = { bubble, text: requireChild(bubble, 'span', HTMLElement) };\n\t\tcontrol.style.setProperty('anchor-name', this.#anchor);\n\t\tbubble.style.setProperty('position-anchor', this.#anchor);\n\n\t\tconst entry = new RangeEntry(input, {\n\t\t\tcommit: (text) => {\n\t\t\t\t// eslint-disable-next-line unicorn/prefer-number-coercion -- `Number('')` is 0; an emptied field should leave the value alone\n\t\t\t\tthis.#commit((this.#parseValue ?? Number.parseFloat)(text));\n\t\t\t},\n\t\t\t// `Number.parseFloat` reads \"5 kHz\" as 5\n\t\t\ttext: () => (this.#parseValue ? this.#valueText() : String(this.#value)),\n\t\t\ttoggle: (isOpen) => {\n\t\t\t\tif (isOpen) this.forwardNaming(input, true);\n\t\t\t\tthis.toggleState('editing', isOpen);\n\t\t\t\tthis.render();\n\t\t\t},\n\t\t});\n\n\t\tthis.#entry = entry;\n\t\tentry.bind(control, signal);\n\n\t\treturn entry;\n\t}\n\n\t#bindKeys(control: HTMLElement, entry: RangeEntry, signal: AbortSignal): void {\n\t\tcontrol.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (this.disabled || event.defaultPrevented || event.target !== control) return;\n\n\t\t\t\tif (event.key === 'Enter') {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tentry.open();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst next = this.#keyTarget(event.key);\n\t\t\t\tif (next === undefined) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#commit(next);\n\t\t\t\tthis.#revealForKeys();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.#hideForKeys();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#clamp(next: number): number {\n\t\tconst min = this.min();\n\t\tconst step = this.#step();\n\t\tconst stepped = step > 0 ? min + Math.round((next - min) / step) * step : next;\n\n\t\t// A step like 0.01 leaves float residue\n\t\treturn this.clampRange(Number(stepped.toPrecision(12)));\n\t}\n\n\t#commit(next: number): void {\n\t\tif (this.input(next)) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#endDrag(): void {\n\t\tconst drag = this.#drag;\n\t\tif (!drag) return;\n\n\t\tclearTimeout(drag.revealTimer);\n\t\tthis.#drag = undefined;\n\t\tthis.toggleState('dragging', false);\n\t\tthis.#renderReadout();\n\t\tif (this.#value !== drag.fromValue) this.dispatchEvent(new Event('change', { bubbles: true }));\n\t}\n\n\t#hideForKeys(): void {\n\t\tclearTimeout(this.#keyRevealTimer);\n\t\tif (this.#keyRevealTimer === undefined) return;\n\n\t\tthis.#keyRevealTimer = undefined;\n\t\tthis.#renderReadout();\n\t}\n\n\t#isValueShown(): boolean {\n\t\tconst isRevealed = this.#drag?.isRevealed === true || this.#keyRevealTimer !== undefined;\n\n\t\treturn isRevealed && this.hasAttribute('readout');\n\t}\n\n\t#keyTarget(key: string): number | undefined {\n\t\tif (key === 'Home') return this.min();\n\t\tif (key === 'End') return this.max();\n\t\tif (resetKeys.has(key)) return this.#resetValue();\n\n\t\tconst steps = keySteps.get(key);\n\t\tif (steps === undefined) return undefined;\n\n\t\tconst step = this.#step() > 0 ? this.#step() : this.range() / 100;\n\t\tconst taper = this.#taper();\n\t\tif (!taper) return this.#value + steps * step;\n\n\t\t// By travel, since steps at the wide end would take forever; at least one step, or the snap holds it in place\n\t\tconst next = this.#clamp(taper.value(taper.position(this.#value) + steps / 100));\n\n\t\treturn next === this.#value ? this.#value + Math.sign(steps) * step : next;\n\t}\n\n\t#renderDisabled(control: HTMLElement): void {\n\t\tif (this.disabled) {\n\t\t\tcontrol.setAttribute('aria-disabled', 'true');\n\t\t\tcontrol.removeAttribute('tabindex');\n\t\t\treturn;\n\t\t}\n\n\t\tcontrol.removeAttribute('aria-disabled');\n\t\tcontrol.setAttribute('tabindex', '0');\n\t}\n\n\t#renderReadout(): void {\n\t\tconst readout = this.#readout;\n\t\tif (!readout || !canPopover || !this.isConnected) return;\n\n\t\tconst isEditing = this.#entry?.isOpen === true;\n\n\t\treadout.text.hidden = isEditing;\n\t\tif (!isEditing) readout.text.textContent = this.#valueText();\n\t\treadout.bubble.togglePopover(isEditing || this.#isValueShown());\n\t}\n\n\t#reset(): void {\n\t\tthis.#commit(this.numberAttribute('default', NaN));\n\t}\n\n\t#resetValue(): number | undefined {\n\t\tconst value = this.numberAttribute('default', NaN);\n\n\t\treturn Number.isFinite(value) ? value : undefined;\n\t}\n\n\t#reveal(drag: RangeDrag): void {\n\t\tif (drag.isRevealed || this.#drag !== drag) return;\n\n\t\tdrag.isRevealed = true;\n\t\tthis.#renderReadout();\n\t}\n\n\t#revealForKeys(): void {\n\t\tclearTimeout(this.#keyRevealTimer);\n\t\tthis.#keyRevealTimer = setTimeout(() => {\n\t\t\tthis.#hideForKeys();\n\t\t}, keyRevealMs);\n\t\tthis.#renderReadout();\n\t}\n\n\t#step(): number {\n\t\treturn this.numberAttribute('step', 1);\n\t}\n\n\t// Notches are evenly spaced, so a notched control stays linear\n\t#taper(): Taper | undefined {\n\t\tif (this.hasAttribute('notched')) return undefined;\n\n\t\tconst min = this.min();\n\t\tconst max = this.max();\n\t\tconst log = this.getAttribute('taper') === 'log' ? logTaper(min, max) : undefined;\n\n\t\treturn log ?? skewTaper(min, max, this.numberAttribute('midpoint', NaN));\n\t}\n\n\t#valueText(): string {\n\t\treturn this.#formatValue?.(this.#value) ?? String(this.#value);\n\t}\n\n\t#write(next: number): void {\n\t\tif (!Number.isFinite(next)) return;\n\n\t\tthis.#isDirty = true;\n\t\tthis.#value = this.#clamp(next);\n\t\tthis.render();\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"range-entry.js","names":[],"sources":["../../src/elements/range-entry.ts"],"sourcesContent":["interface Press {\n\ttime: number;\n\tx: number;\n\ty: number;\n}\n\ninterface RangeEntryOptions {\n\tcommit: (text: string) => void;\n\ttext: () => string;\n\ttoggle: (isOpen: boolean) => void;\n}\n\n// Not `dblclick`, which iOS may not deliver under `touch-action: none`\nconst doublePressMs = 500;\nconst doublePressPx = 4;\n\nfunction isDoublePress(previous: Press | undefined, next: Press): boolean {\n\tif (!previous) return false;\n\n\treturn (\n\t\tnext.time - previous.time < doublePressMs &&\n\t\tMath.hypot(next.x - previous.x, next.y - previous.y) < doublePressPx\n\t);\n}\n\nexport class RangeEntry {\n\tget isOpen(): boolean {\n\t\treturn this.#isOpen;\n\t}\n\n\treadonly #input: HTMLInputElement;\n\n\t#isOpen = false;\n\n\t#lastPress: Press | undefined;\n\n\treadonly #options: RangeEntryOptions;\n\n\tconstructor(input: HTMLInputElement, options: RangeEntryOptions) {\n\t\tthis.#input = input;\n\t\tthis.#options = options;\n\t}\n\n\tbind(control: HTMLElement, signal: AbortSignal): void {\n\t\tconst input = this.#input;\n\n\t\tinput.addEventListener(\n\t\t\t'keydown',\n\t\t\t(event) => {\n\t\t\t\tif (event.isComposing || (event.key !== 'Enter' && event.key !== 'Escape')) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.close(event.key === 'Enter');\n\t\t\t\tcontrol.focus();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tinput.addEventListener(\n\t\t\t'blur',\n\t\t\t() => {\n\t\t\t\tthis.close(true);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\t// The field's own events would reach the host's listeners as if the value had moved\n\t\tfor (const type of ['change', 'input']) {\n\t\t\tinput.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tthis.close(false);\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t// Closes first, so the committed value renders with the control's role back in place\n\tclose(isCommitting: boolean): void {\n\t\tif (!this.#isOpen) return;\n\n\t\tthis.#isOpen = false;\n\t\tthis.#input.hidden = true;\n\t\tthis.#options.toggle(false);\n\t\tif (isCommitting) this.#options.commit(this.#input.value);\n\t}\n\n\tforgetPress(): void {\n\t\tthis.#lastPress = undefined;\n\t}\n\n\topen(): void {\n\t\tconst input = this.#input;\n\n\t\tthis.#isOpen = true;\n\t\tinput.value = this.#options.text();\n\t\tinput.hidden = false;\n\t\tthis.#options.toggle(true);\n\t\tinput.focus();\n\t\tinput.select();\n\t}\n\n\tpress(event: PointerEvent): boolean {\n\t\tconst press = { time: event.timeStamp, x: event.clientX, y: event.clientY };\n\t\tconst previous = this.#lastPress;\n\n\t\tthis.#lastPress = press;\n\t\tif (!isDoublePress(previous, press)) return false;\n\n\t\tthis.#lastPress = undefined;\n\n\t\treturn true;\n\t}\n}\n"],"mappings":";AAaA,MAAM,gBAAgB;AACtB,MAAM,gBAAgB;AAEtB,SAAS,cAAc,UAA6B,MAAsB;CACzE,IAAI,CAAC,UAAU,OAAO;CAEtB,OACC,KAAK,OAAO,SAAS,OAAO,iBAC5B,KAAK,MAAM,KAAK,IAAI,SAAS,GAAG,KAAK,IAAI,SAAS,CAAC,IAAI;AAEzD;AAEA,IAAa,aAAb,MAAwB;CACvB,IAAI,SAAkB;EACrB,OAAO,KAAK;CACb;CAEA,AAAS;CAET,UAAU;CAEV;CAEA,AAAS;CAET,YAAY,OAAyB,SAA4B;EAChE,KAAK,SAAS;EACd,KAAK,WAAW;CACjB;CAEA,KAAK,SAAsB,QAA2B;EACrD,MAAM,QAAQ,KAAK;EAEnB,MAAM,iBACL,YACC,UAAU;GACV,IAAI,MAAM,eAAgB,MAAM,QAAQ,WAAW,MAAM,QAAQ,UAAW;GAE5E,MAAM,eAAe;GACrB,KAAK,MAAM,MAAM,QAAQ,OAAO;GAChC,QAAQ,MAAM;EACf,GACA,EAAE,OAAO,CACV;EACA,MAAM,iBACL,cACM;GACL,KAAK,MAAM,IAAI;EAChB,GACA,EAAE,OAAO,CACV;EAGA,KAAK,MAAM,QAAQ,CAAC,UAAU,OAAO,GACpC,MAAM,iBACL,OACC,UAAU;GACV,MAAM,gBAAgB;EACvB,GACA,EAAE,OAAO,CACV;EAED,OAAO,iBACN,eACM;GACL,KAAK,MAAM,KAAK;EACjB,GACA,EAAE,MAAM,KAAK,CACd;CACD;CAGA,MAAM,cAA6B;EAClC,IAAI,CAAC,KAAK,SAAS;EAEnB,KAAK,UAAU;EACf,KAAK,OAAO,SAAS;EACrB,KAAK,SAAS,OAAO,KAAK;EAC1B,IAAI,cAAc,KAAK,SAAS,OAAO,KAAK,OAAO,KAAK;CACzD;CAEA,cAAoB;EACnB,KAAK,aAAa;CACnB;CAEA,OAAa;EACZ,MAAM,QAAQ,KAAK;EAEnB,KAAK,UAAU;EACf,MAAM,QAAQ,KAAK,SAAS,KAAK;EACjC,MAAM,SAAS;EACf,KAAK,SAAS,OAAO,IAAI;EACzB,MAAM,MAAM;EACZ,MAAM,OAAO;CACd;CAEA,MAAM,OAA8B;EACnC,MAAM,QAAQ;GAAE,MAAM,MAAM;GAAW,GAAG,MAAM;GAAS,GAAG,MAAM;EAAQ;EAC1E,MAAM,WAAW,KAAK;EAEtB,KAAK,aAAa;EAClB,IAAI,CAAC,cAAc,UAAU,KAAK,GAAG,OAAO;EAE5C,KAAK,aAAa;EAElB,OAAO;CACR;AACD"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { SonicElement } from "./sonic-element.js";
|
|
2
|
-
import { template } from "../lib/render.js";
|
|
3
|
-
|
|
4
|
-
//#region src/elements/segmented.ts
|
|
5
|
-
const renderSegmented = template(`<div class="sonic-segmented" role="radiogroup"></div>`, HTMLDivElement);
|
|
6
|
-
const renderSegment = template(`
|
|
7
|
-
<button class="sonic-segmented-segment" role="radio" type="button">
|
|
8
|
-
<span class="sonic-segmented-cap"></span>
|
|
9
|
-
</button>
|
|
10
|
-
`, HTMLButtonElement);
|
|
11
|
-
const keySteps = /* @__PURE__ */ new Map([
|
|
12
|
-
["ArrowDown", 1],
|
|
13
|
-
["ArrowLeft", -1],
|
|
14
|
-
["ArrowRight", 1],
|
|
15
|
-
["ArrowUp", -1]
|
|
16
|
-
]);
|
|
17
|
-
function segmentValue(segment) {
|
|
18
|
-
return segment.querySelector("[data-sonic-value]")?.dataset.sonicValue;
|
|
19
|
-
}
|
|
20
|
-
var SonicSegmented = class extends SonicElement {
|
|
21
|
-
static observedAttributes = [...SonicElement.observedAttributes, "value"];
|
|
22
|
-
get value() {
|
|
23
|
-
return this.#value;
|
|
24
|
-
}
|
|
25
|
-
set value(next) {
|
|
26
|
-
this.#value = next;
|
|
27
|
-
this.#render();
|
|
28
|
-
}
|
|
29
|
-
#segmented = renderSegmented();
|
|
30
|
-
#value = "";
|
|
31
|
-
attributeChangedCallback(name) {
|
|
32
|
-
if (name === "value") this.#value = this.getAttribute("value") ?? "";
|
|
33
|
-
this.#render();
|
|
34
|
-
}
|
|
35
|
-
connect(signal) {
|
|
36
|
-
const segmented = this.#segmented;
|
|
37
|
-
this.upgradeProperty("value");
|
|
38
|
-
this.adoptChildren(() => {
|
|
39
|
-
const segments = [...this.querySelectorAll(":scope > [data-sonic-value]")];
|
|
40
|
-
const isReplaced = segmented.parentNode !== this;
|
|
41
|
-
if (isReplaced) segmented.replaceChildren();
|
|
42
|
-
for (const child of segments) {
|
|
43
|
-
const segment = renderSegment();
|
|
44
|
-
segment.firstElementChild?.append(child);
|
|
45
|
-
segmented.append(segment);
|
|
46
|
-
}
|
|
47
|
-
this.appendOnce(segmented);
|
|
48
|
-
if (isReplaced || segments.length > 0) this.#render();
|
|
49
|
-
}, signal);
|
|
50
|
-
this.#render();
|
|
51
|
-
this.checkStyles(segmented, "segmented.css");
|
|
52
|
-
segmented.addEventListener("click", (event) => {
|
|
53
|
-
const segment = this.#segmentOf(event.target);
|
|
54
|
-
if (segment) this.#select(segment);
|
|
55
|
-
}, { signal });
|
|
56
|
-
segmented.addEventListener("keydown", (event) => {
|
|
57
|
-
if (event.defaultPrevented) return;
|
|
58
|
-
const segment = this.#segmentOf(event.target);
|
|
59
|
-
const next = segment && this.#keyTarget(segment, event.key);
|
|
60
|
-
if (!next) return;
|
|
61
|
-
event.preventDefault();
|
|
62
|
-
this.#select(next);
|
|
63
|
-
next.focus();
|
|
64
|
-
}, { signal });
|
|
65
|
-
}
|
|
66
|
-
#keyTarget(segment, key) {
|
|
67
|
-
const segments = this.#segments();
|
|
68
|
-
if (key === "Home") return segments[0];
|
|
69
|
-
if (key === "End") return segments.at(-1);
|
|
70
|
-
const step = keySteps.get(key);
|
|
71
|
-
if (step === void 0) return void 0;
|
|
72
|
-
return segments[(segments.indexOf(segment) + step + segments.length) % segments.length];
|
|
73
|
-
}
|
|
74
|
-
#render() {
|
|
75
|
-
const segmented = this.#segmented;
|
|
76
|
-
const segments = this.#segments();
|
|
77
|
-
const checked = segments.find((segment) => segmentValue(segment) === this.#value);
|
|
78
|
-
const focusable = checked ?? segments[0];
|
|
79
|
-
for (const segment of segments) {
|
|
80
|
-
segment.disabled = this.disabled;
|
|
81
|
-
segment.setAttribute("aria-checked", String(segment === checked));
|
|
82
|
-
segment.tabIndex = segment === focusable ? 0 : -1;
|
|
83
|
-
}
|
|
84
|
-
this.forwardNaming(segmented, true);
|
|
85
|
-
}
|
|
86
|
-
#segmentOf(target) {
|
|
87
|
-
if (!(target instanceof Element)) return void 0;
|
|
88
|
-
const segment = target.closest(".sonic-segmented-segment");
|
|
89
|
-
return segment instanceof HTMLButtonElement && segment.parentElement === this.#segmented ? segment : void 0;
|
|
90
|
-
}
|
|
91
|
-
#segments() {
|
|
92
|
-
return [...this.#segmented.children].filter((child) => child instanceof HTMLButtonElement);
|
|
93
|
-
}
|
|
94
|
-
#select(segment) {
|
|
95
|
-
const next = segmentValue(segment);
|
|
96
|
-
if (next === void 0 || next === this.#value) return;
|
|
97
|
-
this.value = next;
|
|
98
|
-
this.dispatchEvent(new Event("change", { bubbles: true }));
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
//#endregion
|
|
103
|
-
export { SonicSegmented };
|
|
104
|
-
//# sourceMappingURL=segmented.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"segmented.js","names":[],"sources":["../../src/elements/segmented.ts"],"sourcesContent":["import { SonicElement } from '#elements/sonic-element.ts';\nimport { template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-segmented': SonicSegmented;\n\t}\n}\n\nconst renderSegmented = template(\n\t/* HTML */ `<div class=\"sonic-segmented\" role=\"radiogroup\"></div>`,\n\tHTMLDivElement,\n);\n\nconst renderSegment = template(\n\t/* HTML */ `\n\t\t<button class=\"sonic-segmented-segment\" role=\"radio\" type=\"button\">\n\t\t\t<span class=\"sonic-segmented-cap\"></span>\n\t\t</button>\n\t`,\n\tHTMLButtonElement,\n);\n\n// RTL is not mirrored\nconst keySteps = new Map([\n\t['ArrowDown', 1],\n\t['ArrowLeft', -1],\n\t['ArrowRight', 1],\n\t['ArrowUp', -1],\n]);\n\nfunction segmentValue(segment: Element): string | undefined {\n\treturn segment.querySelector<HTMLElement | SVGElement>('[data-sonic-value]')?.dataset.sonicValue;\n}\n\nexport class SonicSegmented extends SonicElement {\n\tstatic override readonly observedAttributes = [...SonicElement.observedAttributes, 'value'];\n\n\t// As on the range, the property never writes the `value` attribute back\n\tget value(): string {\n\t\treturn this.#value;\n\t}\n\n\tset value(next: string) {\n\t\tthis.#value = next;\n\t\tthis.#render();\n\t}\n\n\treadonly #segmented = renderSegmented();\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 connect(signal: AbortSignal): void {\n\t\tconst segmented = this.#segmented;\n\n\t\tthis.upgradeProperty('value');\n\t\tthis.adoptChildren(() => {\n\t\t\tconst segments = [...this.querySelectorAll(':scope > [data-sonic-value]')];\n\t\t\tconst isReplaced = segmented.parentNode !== this;\n\n\t\t\t// Arrivals after a replacement take the old options' place\n\t\t\tif (isReplaced) segmented.replaceChildren();\n\t\t\tfor (const child of segments) {\n\t\t\t\tconst segment = renderSegment();\n\n\t\t\t\tsegment.firstElementChild?.append(child);\n\t\t\t\tsegmented.append(segment);\n\t\t\t}\n\t\t\tthis.appendOnce(segmented);\n\t\t\tif (isReplaced || segments.length > 0) this.#render();\n\t\t}, signal);\n\t\tthis.#render();\n\t\tthis.checkStyles(segmented, 'segmented.css');\n\n\t\tsegmented.addEventListener(\n\t\t\t'click',\n\t\t\t(event) => {\n\t\t\t\tconst segment = this.#segmentOf(event.target);\n\t\t\t\tif (segment) this.#select(segment);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tsegmented.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 segment = this.#segmentOf(event.target);\n\t\t\t\tconst next = segment && this.#keyTarget(segment, event.key);\n\t\t\t\tif (!next) return;\n\n\t\t\t\tevent.preventDefault();\n\t\t\t\tthis.#select(next);\n\t\t\t\tnext.focus();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#keyTarget(segment: HTMLButtonElement, key: string): HTMLButtonElement | undefined {\n\t\tconst segments = this.#segments();\n\n\t\tif (key === 'Home') return segments[0];\n\t\tif (key === 'End') return segments.at(-1);\n\n\t\tconst step = keySteps.get(key);\n\t\tif (step === undefined) return undefined;\n\n\t\tconst index = segments.indexOf(segment) + step;\n\n\t\treturn segments[(index + segments.length) % segments.length];\n\t}\n\n\t#render(): void {\n\t\tconst segmented = this.#segmented;\n\t\tconst segments = this.#segments();\n\t\tconst checked = segments.find((segment) => segmentValue(segment) === this.#value);\n\t\tconst focusable = checked ?? segments[0];\n\n\t\tfor (const segment of segments) {\n\t\t\tsegment.disabled = this.disabled;\n\t\t\tsegment.setAttribute('aria-checked', String(segment === checked));\n\t\t\tsegment.tabIndex = segment === focusable ? 0 : -1;\n\t\t}\n\t\tthis.forwardNaming(segmented, true);\n\t}\n\n\t#segmentOf(target: EventTarget | null): HTMLButtonElement | undefined {\n\t\tif (!(target instanceof Element)) return undefined;\n\n\t\tconst segment = target.closest('.sonic-segmented-segment');\n\n\t\treturn segment instanceof HTMLButtonElement && segment.parentElement === this.#segmented\n\t\t\t? segment\n\t\t\t: undefined;\n\t}\n\n\t#segments(): Array<HTMLButtonElement> {\n\t\treturn [...this.#segmented.children].filter(\n\t\t\t(child): child is HTMLButtonElement => child instanceof HTMLButtonElement,\n\t\t);\n\t}\n\n\t#select(segment: HTMLButtonElement): void {\n\t\tconst next = segmentValue(segment);\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"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"slider.js","names":[],"sources":["../../src/elements/slider.ts"],"sourcesContent":["import type { RangeAxis } from '#elements/range-element.ts';\n\nimport { SonicRangeElement } from '#elements/range-element.ts';\nimport { requireChild, template } from '#lib/render.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-slider': SonicSlider;\n\t}\n}\n\ninterface SliderAxis extends RangeAxis {\n\t// The cap's centre at the minimum, on the same scale as `position`\n\tstartPx: number;\n}\n\nconst renderSlider = template(\n\t/* HTML */ `\n\t\t<div class=\"sonic-slider\" role=\"slider\" tabindex=\"0\">\n\t\t\t<div class=\"sonic-slider-marks\"></div>\n\t\t\t<div class=\"sonic-slider-groove\"></div>\n\t\t\t<div class=\"sonic-slider-cap\"></div>\n\t\t\t<div class=\"sonic-slider-readout\" popover=\"manual\">\n\t\t\t\t<span></span>\n\t\t\t\t<input\n\t\t\t\t\tautocomplete=\"off\"\n\t\t\t\t\tclass=\"sonic-slider-entry\"\n\t\t\t\t\tenterkeyhint=\"done\"\n\t\t\t\t\thidden\n\t\t\t\t\tspellcheck=\"false\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t`,\n\tHTMLDivElement,\n);\n\nexport class SonicSlider extends SonicRangeElement {\n\tstatic override readonly observedAttributes = [\n\t\t...SonicRangeElement.observedAttributes,\n\t\t'orientation',\n\t];\n\n\treadonly #slider = renderSlider();\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst slider = this.#slider;\n\t\tconst cap = requireChild(slider, '.sonic-slider-cap', HTMLDivElement);\n\n\t\tthis.appendOnce(slider);\n\t\tthis.render();\n\t\tthis.checkStyles(slider, 'slider.css');\n\t\tthis.bindGestures(slider, signal, (event) => {\n\t\t\tconst axis = this.#axis(slider, cap);\n\t\t\tif (event.target instanceof Node && cap.contains(event.target)) return axis;\n\n\t\t\tif (this.getAttribute('groove-press') === 'none') return;\n\n\t\t\tthis.input(this.valueAt((axis.position(event) - axis.startPx) / axis.travelPx));\n\n\t\t\treturn axis;\n\t\t});\n\t}\n\n\tprotected render(): void {\n\t\tconst slider = this.#slider;\n\t\tconst positions = this.positions();\n\n\t\tslider.style.setProperty('--_sonic-slider-value', String(this.fraction(this.value)));\n\t\tslider.style.setProperty('--_sonic-slider-origin', String(this.originFraction()));\n\t\tif (positions === undefined) slider.style.removeProperty('--_sonic-slider-positions');\n\t\telse slider.style.setProperty('--_sonic-slider-positions', String(positions));\n\t\tthis.renderAria(slider, this.#isVertical() ? 'vertical' : 'horizontal');\n\t}\n\n\t#axis(slider: HTMLElement, cap: HTMLElement): SliderAxis {\n\t\tconst track = slider.getBoundingClientRect();\n\t\tconst capBox = cap.getBoundingClientRect();\n\n\t\tif (this.#isVertical()) {\n\t\t\treturn {\n\t\t\t\tposition: (event) => -event.clientY,\n\t\t\t\tstartPx: capBox.height / 2 - track.bottom,\n\t\t\t\ttravelPx: Math.max(1, track.height - capBox.height),\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tposition: (event) => event.clientX,\n\t\t\tstartPx: track.left + capBox.width / 2,\n\t\t\ttravelPx: Math.max(1, track.width - capBox.width),\n\t\t};\n\t}\n\n\t#isVertical(): boolean {\n\t\treturn this.getAttribute('orientation') === 'vertical';\n\t}\n}\n"],"mappings":";;;;AAgBA,MAAM,eAAe,SACT;;;;;;;;;;;;;;;;;IAkBX,cACD;AAEA,IAAa,cAAb,cAAiC,kBAAkB;CAClD,OAAyB,qBAAqB,CAC7C,GAAG,kBAAkB,oBACrB,aACD;CAEA,AAAS,UAAU,aAAa;CAEhC,AAAU,QAAQ,QAA2B;EAC5C,MAAM,SAAS,KAAK;EACpB,MAAM,MAAM,aAAa,QAAQ,qBAAqB,cAAc;EAEpE,KAAK,WAAW,MAAM;EACtB,KAAK,OAAO;EACZ,KAAK,YAAY,QAAQ,YAAY;EACrC,KAAK,aAAa,QAAQ,SAAS,UAAU;GAC5C,MAAM,OAAO,KAAK,MAAM,QAAQ,GAAG;GACnC,IAAI,MAAM,kBAAkB,QAAQ,IAAI,SAAS,MAAM,MAAM,GAAG,OAAO;GAEvE,IAAI,KAAK,aAAa,cAAc,MAAM,QAAQ;GAElD,KAAK,MAAM,KAAK,SAAS,KAAK,SAAS,KAAK,IAAI,KAAK,WAAW,KAAK,QAAQ,CAAC;GAE9E,OAAO;EACR,CAAC;CACF;CAEA,AAAU,SAAe;EACxB,MAAM,SAAS,KAAK;EACpB,MAAM,YAAY,KAAK,UAAU;EAEjC,OAAO,MAAM,YAAY,yBAAyB,OAAO,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC;EACnF,OAAO,MAAM,YAAY,0BAA0B,OAAO,KAAK,eAAe,CAAC,CAAC;EAChF,IAAI,cAAc,QAAW,OAAO,MAAM,eAAe,2BAA2B;OAC/E,OAAO,MAAM,YAAY,6BAA6B,OAAO,SAAS,CAAC;EAC5E,KAAK,WAAW,QAAQ,KAAK,YAAY,IAAI,aAAa,YAAY;CACvE;CAEA,MAAM,QAAqB,KAA8B;EACxD,MAAM,QAAQ,OAAO,sBAAsB;EAC3C,MAAM,SAAS,IAAI,sBAAsB;EAEzC,IAAI,KAAK,YAAY,GACpB,OAAO;GACN,WAAW,UAAU,CAAC,MAAM;GAC5B,SAAS,OAAO,SAAS,IAAI,MAAM;GACnC,UAAU,KAAK,IAAI,GAAG,MAAM,SAAS,OAAO,MAAM;EACnD;EAGD,OAAO;GACN,WAAW,UAAU,MAAM;GAC3B,SAAS,MAAM,OAAO,OAAO,QAAQ;GACrC,UAAU,KAAK,IAAI,GAAG,MAAM,QAAQ,OAAO,KAAK;EACjD;CACD;CAEA,cAAuB;EACtB,OAAO,KAAK,aAAa,aAAa,MAAM;CAC7C;AACD"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
//#region src/elements/sonic-element.d.ts
|
|
2
|
-
export declare abstract class SonicElement extends HTMLElement {
|
|
3
|
-
#private;
|
|
4
|
-
static readonly observedAttributes: string[];
|
|
5
|
-
get disabled(): boolean;
|
|
6
|
-
set disabled(isDisabled: boolean);
|
|
7
|
-
connectedCallback(): void;
|
|
8
|
-
connectedMoveCallback(): void;
|
|
9
|
-
disconnectedCallback(): void;
|
|
10
|
-
protected adoptChildren(adopt: () => void, signal: AbortSignal): void;
|
|
11
|
-
protected appendOnce(child: Element): void;
|
|
12
|
-
protected checkStyles(control: HTMLElement, sheet: string): void;
|
|
13
|
-
protected abstract connect(signal: AbortSignal): void;
|
|
14
|
-
protected forwardNaming(target: Element, isNamed: boolean): void;
|
|
15
|
-
protected numberAttribute(name: string, fallback: number): number;
|
|
16
|
-
protected toggleState(state: string, isOn: boolean): void;
|
|
17
|
-
protected upgradeProperty(name: keyof this): void;
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
//# sourceMappingURL=sonic-element.d.ts.map
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { writeAttribute } from "../lib/write-attribute.js";
|
|
2
|
-
|
|
3
|
-
//#region src/elements/sonic-element.ts
|
|
4
|
-
const namingAttributes = [
|
|
5
|
-
"aria-describedby",
|
|
6
|
-
"aria-label",
|
|
7
|
-
"aria-labelledby"
|
|
8
|
-
];
|
|
9
|
-
const checkedClasses = /* @__PURE__ */ new WeakSet();
|
|
10
|
-
var SonicElement = class extends HTMLElement {
|
|
11
|
-
static observedAttributes = [...namingAttributes, "disabled"];
|
|
12
|
-
get disabled() {
|
|
13
|
-
return this.hasAttribute("disabled");
|
|
14
|
-
}
|
|
15
|
-
set disabled(isDisabled) {
|
|
16
|
-
this.toggleAttribute("disabled", isDisabled);
|
|
17
|
-
}
|
|
18
|
-
#connection;
|
|
19
|
-
#internals = "attachInternals" in HTMLElement.prototype ? this.attachInternals() : void 0;
|
|
20
|
-
connectedCallback() {
|
|
21
|
-
this.upgradeProperty("disabled");
|
|
22
|
-
if (this.#connection) return;
|
|
23
|
-
const connection = new AbortController();
|
|
24
|
-
try {
|
|
25
|
-
this.connect(connection.signal);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
connection.abort();
|
|
28
|
-
throw error;
|
|
29
|
-
}
|
|
30
|
-
this.#connection = connection;
|
|
31
|
-
}
|
|
32
|
-
connectedMoveCallback() {}
|
|
33
|
-
disconnectedCallback() {
|
|
34
|
-
const connection = this.#connection;
|
|
35
|
-
if (!connection) return;
|
|
36
|
-
queueMicrotask(() => {
|
|
37
|
-
if (this.isConnected || this.#connection !== connection) return;
|
|
38
|
-
connection.abort();
|
|
39
|
-
this.#connection = void 0;
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
adoptChildren(adopt, signal) {
|
|
43
|
-
const observer = new MutationObserver(adopt);
|
|
44
|
-
adopt();
|
|
45
|
-
observer.observe(this, { childList: true });
|
|
46
|
-
signal.addEventListener("abort", () => {
|
|
47
|
-
observer.disconnect();
|
|
48
|
-
}, { once: true });
|
|
49
|
-
}
|
|
50
|
-
appendOnce(child) {
|
|
51
|
-
if (child.parentNode !== this) this.append(child);
|
|
52
|
-
}
|
|
53
|
-
checkStyles(control, sheet) {
|
|
54
|
-
const elementClass = this.constructor;
|
|
55
|
-
if (checkedClasses.has(elementClass)) return;
|
|
56
|
-
checkedClasses.add(elementClass);
|
|
57
|
-
requestAnimationFrame(() => {
|
|
58
|
-
if (!control.isConnected) {
|
|
59
|
-
checkedClasses.delete(elementClass);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const styles = getComputedStyle(control);
|
|
63
|
-
const missing = [styles.getPropertyValue("--_sonic-unlit") === "" ? "material.css" : "", styles.getPropertyValue("--_sonic-unit") === "" ? sheet : ""].filter(Boolean);
|
|
64
|
-
if (missing.length === 0) return;
|
|
65
|
-
console.warn(`<${this.localName}> draws blank without ${missing.map((file) => `@xsynaptic/sonic-ui/${file}`).join(" and ")} (or controls.css)`);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
forwardNaming(target, isNamed) {
|
|
69
|
-
for (const name of namingAttributes) {
|
|
70
|
-
const value = this.getAttribute(name);
|
|
71
|
-
writeAttribute(target, name, isNamed && value !== null ? value : void 0);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
numberAttribute(name, fallback) {
|
|
75
|
-
const attribute = this.getAttribute(name);
|
|
76
|
-
if (attribute === null) return fallback;
|
|
77
|
-
const parsed = Number(attribute);
|
|
78
|
-
return Number.isFinite(parsed) ? parsed : fallback;
|
|
79
|
-
}
|
|
80
|
-
toggleState(state, isOn) {
|
|
81
|
-
if (isOn) this.#internals?.states.add(state);
|
|
82
|
-
else this.#internals?.states.delete(state);
|
|
83
|
-
}
|
|
84
|
-
upgradeProperty(name) {
|
|
85
|
-
if (!Object.hasOwn(this, name)) return;
|
|
86
|
-
const value = this[name];
|
|
87
|
-
Reflect.deleteProperty(this, name);
|
|
88
|
-
this[name] = value;
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
//#endregion
|
|
93
|
-
export { SonicElement };
|
|
94
|
-
//# sourceMappingURL=sonic-element.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sonic-element.js","names":[],"sources":["../../src/elements/sonic-element.ts"],"sourcesContent":["import { writeAttribute } from '#lib/write-attribute.ts';\n\n// Forwarding another attribute is a public API decision\nconst namingAttributes = ['aria-describedby', 'aria-label', 'aria-labelledby'];\n\nconst checkedClasses = new WeakSet<object>();\n\n// eslint-disable-next-line wc/define-tag-after-class-definition -- abstract, so only its subclasses are ever defined\nexport abstract class SonicElement extends HTMLElement {\n\tstatic readonly observedAttributes = [...namingAttributes, 'disabled'];\n\n\tget disabled(): boolean {\n\t\treturn this.hasAttribute('disabled');\n\t}\n\n\tset disabled(isDisabled: boolean) {\n\t\tthis.toggleAttribute('disabled', isDisabled);\n\t}\n\n\t#connection: AbortController | undefined;\n\n\t// happy-dom has no `ElementInternals`\n\treadonly #internals =\n\t\t'attachInternals' in HTMLElement.prototype ? this.attachInternals() : undefined;\n\n\tconnectedCallback(): void {\n\t\tthis.upgradeProperty('disabled');\n\t\tif (this.#connection) return;\n\n\t\tconst connection = new AbortController();\n\n\t\t// A throw leaves nothing bound behind it\n\t\ttry {\n\t\t\tthis.connect(connection.signal);\n\t\t} catch (error) {\n\t\t\tconnection.abort();\n\t\t\tthrow error;\n\t\t}\n\n\t\tthis.#connection = connection;\n\t}\n\n\tconnectedMoveCallback(): void {\n\t\t// Defined so `moveBefore` from a persisting router keeps every binding\n\t}\n\n\t// Without `moveBefore` (Safari), a persisting router disconnects and reconnects in one task\n\tdisconnectedCallback(): void {\n\t\tconst connection = this.#connection;\n\t\tif (!connection) return;\n\n\t\tqueueMicrotask(() => {\n\t\t\tif (this.isConnected || this.#connection !== connection) return;\n\n\t\t\tconnection.abort();\n\t\t\tthis.#connection = undefined;\n\t\t});\n\t}\n\n\t// Replacing the host's children takes the control with them, so `adopt` runs on every change\n\tprotected adoptChildren(adopt: () => void, signal: AbortSignal): void {\n\t\tconst observer = new MutationObserver(adopt);\n\n\t\tadopt();\n\t\tobserver.observe(this, { childList: true });\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tobserver.disconnect();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected appendOnce(child: Element): void {\n\t\tif (child.parentNode !== this) this.append(child);\n\t}\n\n\t// A missing sheet draws the control blank without an error\n\tprotected checkStyles(control: HTMLElement, sheet: string): void {\n\t\tconst elementClass = this.constructor;\n\t\tif (checkedClasses.has(elementClass)) return;\n\n\t\tcheckedClasses.add(elementClass);\n\t\trequestAnimationFrame(() => {\n\t\t\t// A detached control computes no styles\n\t\t\tif (!control.isConnected) {\n\t\t\t\tcheckedClasses.delete(elementClass);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst styles = getComputedStyle(control);\n\t\t\tconst missing = [\n\t\t\t\tstyles.getPropertyValue('--_sonic-unlit') === '' ? 'material.css' : '',\n\t\t\t\tstyles.getPropertyValue('--_sonic-unit') === '' ? sheet : '',\n\t\t\t].filter(Boolean);\n\t\t\tif (missing.length === 0) return;\n\n\t\t\tconsole.warn(\n\t\t\t\t`<${this.localName}> draws blank without ${missing.map((file) => `@xsynaptic/sonic-ui/${file}`).join(' and ')} (or controls.css)`,\n\t\t\t);\n\t\t});\n\t}\n\n\tprotected abstract connect(signal: AbortSignal): void;\n\n\tprotected forwardNaming(target: Element, isNamed: boolean): void {\n\t\tfor (const name of namingAttributes) {\n\t\t\tconst value = this.getAttribute(name);\n\n\t\t\twriteAttribute(target, name, isNamed && value !== null ? value : undefined);\n\t\t}\n\t}\n\n\tprotected numberAttribute(name: string, fallback: number): number {\n\t\tconst attribute = this.getAttribute(name);\n\t\tif (attribute === null) return fallback;\n\n\t\tconst parsed = Number(attribute);\n\n\t\treturn Number.isFinite(parsed) ? parsed : fallback;\n\t}\n\n\tprotected toggleState(state: string, isOn: boolean): void {\n\t\tif (isOn) this.#internals?.states.add(state);\n\t\telse this.#internals?.states.delete(state);\n\t}\n\n\t// A property set before upgrade shadows its accessor\n\tprotected upgradeProperty(name: keyof this): void {\n\t\tif (!Object.hasOwn(this, name)) return;\n\n\t\tconst value = this[name];\n\n\t\tReflect.deleteProperty(this, name);\n\t\tthis[name] = value;\n\t}\n}\n"],"mappings":";;;AAGA,MAAM,mBAAmB;CAAC;CAAoB;CAAc;AAAiB;AAE7E,MAAM,iCAAiB,IAAI,QAAgB;AAG3C,IAAsB,eAAtB,cAA2C,YAAY;CACtD,OAAgB,qBAAqB,CAAC,GAAG,kBAAkB,UAAU;CAErE,IAAI,WAAoB;EACvB,OAAO,KAAK,aAAa,UAAU;CACpC;CAEA,IAAI,SAAS,YAAqB;EACjC,KAAK,gBAAgB,YAAY,UAAU;CAC5C;CAEA;CAGA,AAAS,aACR,qBAAqB,YAAY,YAAY,KAAK,gBAAgB,IAAI;CAEvE,oBAA0B;EACzB,KAAK,gBAAgB,UAAU;EAC/B,IAAI,KAAK,aAAa;EAEtB,MAAM,aAAa,IAAI,gBAAgB;EAGvC,IAAI;GACH,KAAK,QAAQ,WAAW,MAAM;EAC/B,SAAS,OAAO;GACf,WAAW,MAAM;GACjB,MAAM;EACP;EAEA,KAAK,cAAc;CACpB;CAEA,wBAA8B,CAE9B;CAGA,uBAA6B;EAC5B,MAAM,aAAa,KAAK;EACxB,IAAI,CAAC,YAAY;EAEjB,qBAAqB;GACpB,IAAI,KAAK,eAAe,KAAK,gBAAgB,YAAY;GAEzD,WAAW,MAAM;GACjB,KAAK,cAAc;EACpB,CAAC;CACF;CAGA,AAAU,cAAc,OAAmB,QAA2B;EACrE,MAAM,WAAW,IAAI,iBAAiB,KAAK;EAE3C,MAAM;EACN,SAAS,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;EAC1C,OAAO,iBACN,eACM;GACL,SAAS,WAAW;EACrB,GACA,EAAE,MAAM,KAAK,CACd;CACD;CAEA,AAAU,WAAW,OAAsB;EAC1C,IAAI,MAAM,eAAe,MAAM,KAAK,OAAO,KAAK;CACjD;CAGA,AAAU,YAAY,SAAsB,OAAqB;EAChE,MAAM,eAAe,KAAK;EAC1B,IAAI,eAAe,IAAI,YAAY,GAAG;EAEtC,eAAe,IAAI,YAAY;EAC/B,4BAA4B;GAE3B,IAAI,CAAC,QAAQ,aAAa;IACzB,eAAe,OAAO,YAAY;IAClC;GACD;GAEA,MAAM,SAAS,iBAAiB,OAAO;GACvC,MAAM,UAAU,CACf,OAAO,iBAAiB,gBAAgB,MAAM,KAAK,iBAAiB,IACpE,OAAO,iBAAiB,eAAe,MAAM,KAAK,QAAQ,EAC3D,CAAC,CAAC,OAAO,OAAO;GAChB,IAAI,QAAQ,WAAW,GAAG;GAE1B,QAAQ,KACP,IAAI,KAAK,UAAU,wBAAwB,QAAQ,KAAK,SAAS,uBAAuB,MAAM,CAAC,CAAC,KAAK,OAAO,EAAE,mBAC/G;EACD,CAAC;CACF;CAIA,AAAU,cAAc,QAAiB,SAAwB;EAChE,KAAK,MAAM,QAAQ,kBAAkB;GACpC,MAAM,QAAQ,KAAK,aAAa,IAAI;GAEpC,eAAe,QAAQ,MAAM,WAAW,UAAU,OAAO,QAAQ,MAAS;EAC3E;CACD;CAEA,AAAU,gBAAgB,MAAc,UAA0B;EACjE,MAAM,YAAY,KAAK,aAAa,IAAI;EACxC,IAAI,cAAc,MAAM,OAAO;EAE/B,MAAM,SAAS,OAAO,SAAS;EAE/B,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS;CAC3C;CAEA,AAAU,YAAY,OAAe,MAAqB;EACzD,IAAI,MAAM,KAAK,YAAY,OAAO,IAAI,KAAK;OACtC,KAAK,YAAY,OAAO,OAAO,KAAK;CAC1C;CAGA,AAAU,gBAAgB,MAAwB;EACjD,IAAI,CAAC,OAAO,OAAO,MAAM,IAAI,GAAG;EAEhC,MAAM,QAAQ,KAAK;EAEnB,QAAQ,eAAe,MAAM,IAAI;EACjC,KAAK,QAAQ;CACd;AACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"read-px-property.js","names":[],"sources":["../../src/lib/read-px-property.ts"],"sourcesContent":["// Only px; another unit needs a probe element to convert\nexport function readPxProperty(\n\tstyles: CSSStyleDeclaration,\n\tproperty: string,\n\tfallback: number,\n): number {\n\t// eslint-disable-next-line unicorn/prefer-number-coercion -- parsing has to stop at the unit\n\tconst parsed = Number.parseFloat(styles.getPropertyValue(property));\n\n\treturn Number.isFinite(parsed) ? parsed : fallback;\n}\n"],"mappings":";AACA,SAAgB,eACf,QACA,UACA,UACS;CAET,MAAM,SAAS,OAAO,WAAW,OAAO,iBAAiB,QAAQ,CAAC;CAElE,OAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC3C"}
|
package/dist/lib/render.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","names":[],"sources":["../../src/lib/render.ts"],"sourcesContent":["export function requireChild<Child extends Element>(\n\troot: Element,\n\tselector: string,\n\tchild: new () => Child,\n): Child {\n\tconst found = root.querySelector(selector);\n\tif (!(found instanceof child)) throw new Error(`A template is missing its ${selector}`);\n\n\treturn found;\n}\n\nexport function template<Root extends Element>(html: string, root: new () => Root): () => Root {\n\tconst parsed = document.createElement('template');\n\n\tparsed.innerHTML = html;\n\n\treturn () => {\n\t\tconst clone = document.importNode(parsed.content, true).firstElementChild;\n\t\tif (!(clone instanceof root)) throw new Error(`A template's root is not a ${root.name}`);\n\n\t\treturn clone;\n\t};\n}\n"],"mappings":";AAAA,SAAgB,aACf,MACA,UACA,OACQ;CACR,MAAM,QAAQ,KAAK,cAAc,QAAQ;CACzC,IAAI,EAAE,iBAAiB,QAAQ,MAAM,IAAI,MAAM,6BAA6B,UAAU;CAEtF,OAAO;AACR;AAEA,SAAgB,SAA+B,MAAc,MAAkC;CAC9F,MAAM,SAAS,SAAS,cAAc,UAAU;CAEhD,OAAO,YAAY;CAEnB,aAAa;EACZ,MAAM,QAAQ,SAAS,WAAW,OAAO,SAAS,IAAI,CAAC,CAAC;EACxD,IAAI,EAAE,iBAAiB,OAAO,MAAM,IAAI,MAAM,8BAA8B,KAAK,MAAM;EAEvF,OAAO;CACR;AACD"}
|
package/dist/lib/taper.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"taper.js","names":[],"sources":["../../src/lib/taper.ts"],"sourcesContent":["// Positions run 0 to 1 along the travel\nexport interface Taper {\n\tposition: (value: number) => number;\n\tvalue: (position: number) => number;\n}\n\nexport function clampUnit(fraction: number): number {\n\treturn Math.min(1, Math.max(0, fraction));\n}\n\nexport function linearTaper(min: number, max: number): Taper {\n\tconst range = max - min;\n\n\treturn {\n\t\tposition: (value) => (range > 0 ? clampUnit((value - min) / range) : 0),\n\t\tvalue: (position) => min + clampUnit(position) * range,\n\t};\n}\n\nexport function logTaper(min: number, max: number): Taper | undefined {\n\tif (min <= 0 || max <= min) return undefined;\n\n\tconst span = Math.log(max / min);\n\n\treturn {\n\t\tposition: (value) => clampUnit(Math.log(Math.max(min, value) / min) / span),\n\t\tvalue: (position) => min * Math.exp(clampUnit(position) * span),\n\t};\n}\n\n// JUCE's skew: a power curve through `midpoint` at half the travel\nexport function skewTaper(min: number, max: number, midpoint: number): Taper | undefined {\n\tconst range = max - min;\n\tconst proportion = (midpoint - min) / range;\n\n\tif (proportion === 0.5 || !(proportion > 0 && proportion < 1)) return undefined;\n\n\tconst skew = Math.log(0.5) / Math.log(proportion);\n\n\treturn {\n\t\tposition: (value) => clampUnit((value - min) / range) ** skew,\n\t\tvalue: (position) => min + range * clampUnit(position) ** (1 / skew),\n\t};\n}\n"],"mappings":";AAMA,SAAgB,UAAU,UAA0B;CACnD,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC;AACzC;AAEA,SAAgB,YAAY,KAAa,KAAoB;CAC5D,MAAM,QAAQ,MAAM;CAEpB,OAAO;EACN,WAAW,UAAW,QAAQ,IAAI,WAAW,QAAQ,OAAO,KAAK,IAAI;EACrE,QAAQ,aAAa,MAAM,UAAU,QAAQ,IAAI;CAClD;AACD;AAEA,SAAgB,SAAS,KAAa,KAAgC;CACrE,IAAI,OAAO,KAAK,OAAO,KAAK,OAAO;CAEnC,MAAM,OAAO,KAAK,IAAI,MAAM,GAAG;CAE/B,OAAO;EACN,WAAW,UAAU,UAAU,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,IAAI;EAC1E,QAAQ,aAAa,MAAM,KAAK,IAAI,UAAU,QAAQ,IAAI,IAAI;CAC/D;AACD;AAGA,SAAgB,UAAU,KAAa,KAAa,UAAqC;CACxF,MAAM,QAAQ,MAAM;CACpB,MAAM,cAAc,WAAW,OAAO;CAEtC,IAAI,eAAe,MAAO,EAAE,aAAa,KAAK,aAAa,IAAI,OAAO;CAEtE,MAAM,OAAO,KAAK,IAAI,EAAG,IAAI,KAAK,IAAI,UAAU;CAEhD,OAAO;EACN,WAAW,UAAU,WAAW,QAAQ,OAAO,KAAK,KAAK;EACzD,QAAQ,aAAa,MAAM,QAAQ,UAAU,QAAQ,MAAM,IAAI;CAChE;AACD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"write-attribute.js","names":[],"sources":["../../src/lib/write-attribute.ts"],"sourcesContent":["export function writeAttribute(element: Element, name: string, value: string | undefined): void {\n\tif (value === undefined) {\n\t\telement.removeAttribute(name);\n\t\treturn;\n\t}\n\n\telement.setAttribute(name, value);\n}\n"],"mappings":";AAAA,SAAgB,eAAe,SAAkB,MAAc,OAAiC;CAC/F,IAAI,UAAU,QAAW;EACxB,QAAQ,gBAAgB,IAAI;EAC5B;CACD;CAEA,QAAQ,aAAa,MAAM,KAAK;AACjC"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|