@xsynaptic/sonic-ui 0.28.0 → 0.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @xsynaptic/sonic-ui
2
2
 
3
+ ## 0.28.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Ticker: setting `text` to the text it already shows no longer sends the tape back to the start, and `disabled` now holds the ticker at rest with no scrolling and no drag.
8
+
3
9
  ## 0.28.0
4
10
 
5
11
  ### Minor Changes
@@ -17,7 +17,7 @@ const fadePx = 12;
17
17
  const holdMs = 2e3;
18
18
  const ratePxPerSecond = 40;
19
19
  var SonicTicker = class extends SonicElement {
20
- static observedAttributes = ["paused"];
20
+ static observedAttributes = ["disabled", "paused"];
21
21
  get cancellable() {
22
22
  return this.hasAttribute("cancellable");
23
23
  }
@@ -34,12 +34,15 @@ var SonicTicker = class extends SonicElement {
34
34
  return this.#originals().map((original) => original.textContent).join("");
35
35
  }
36
36
  set text(text) {
37
+ const next = text ?? "";
38
+ if (this.#holds(next)) return;
37
39
  for (const original of this.#originals()) original.remove();
38
- if (text !== void 0 && text !== "") this.prepend(text);
40
+ if (next !== "") this.prepend(next);
39
41
  }
40
42
  control = renderTicker();
41
43
  sheet = "ticker.css";
42
44
  sheetToken = "--_sonic-ticker-fade";
45
+ #drag;
43
46
  #fade = fadePx;
44
47
  #heading = 1;
45
48
  #isClickSwallowed = false;
@@ -52,22 +55,31 @@ var SonicTicker = class extends SonicElement {
52
55
  #position = 0;
53
56
  #reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
54
57
  #running;
58
+ #tape = requireChild(this.control, ".sonic-ticker-tape", HTMLSpanElement);
55
59
  #travel = {
56
60
  distance: 0,
57
61
  hold: holdMs,
58
62
  rate: ratePxPerSecond
59
63
  };
60
- attributeChangedCallback() {
64
+ attributeChangedCallback(name) {
65
+ if (name === "disabled" && this.isBound()) {
66
+ this.#drag?.end();
67
+ this.#run(this.#tape, {
68
+ from: 0,
69
+ heading: 1
70
+ });
71
+ }
61
72
  this.render();
62
73
  }
63
74
  connect(signal) {
64
75
  const control = this.control;
65
- const tape = requireChild(control, ".sonic-ticker-tape", HTMLSpanElement);
76
+ const tape = this.#tape;
66
77
  const measure = () => {
67
78
  this.#measure(tape);
68
79
  };
69
80
  const observer = new ResizeObserver(measure);
70
81
  const drag = this.#bindDrag(tape, signal);
82
+ this.#drag = drag;
71
83
  this.upgradeProperties("cancellable", "paused", "text");
72
84
  mirrorChildren(this, {
73
85
  canCopyLinks: true,
@@ -117,7 +129,7 @@ var SonicTicker = class extends SonicElement {
117
129
  }, { signal });
118
130
  return bindDrag(control, {
119
131
  grab: (event) => {
120
- if (this.#travel.distance <= 0) return;
132
+ if (this.#travel.distance <= 0 || this.isDisabled()) return;
121
133
  return {
122
134
  from: 0,
123
135
  isDragging: false,
@@ -193,6 +205,12 @@ var SonicTicker = class extends SonicElement {
193
205
  view.disconnect();
194
206
  }, { once: true });
195
207
  }
208
+ #holds(text) {
209
+ const [only, ...rest] = this.#originals();
210
+ if (only === void 0) return text === "";
211
+ if (rest.length > 0 || !(only instanceof Text)) return false;
212
+ return only.data === text;
213
+ }
196
214
  #isPaused() {
197
215
  return this.paused || this.#isHovered || this.#isPressed || this.#isOffScreen || this.control.closest(":focus-visible") !== null;
198
216
  }
@@ -226,7 +244,7 @@ var SonicTicker = class extends SonicElement {
226
244
  this.#writeFades(this.#position);
227
245
  }
228
246
  #run(tape, departure = this.#where()) {
229
- const leg = !this.#isFocusInside && !this.#reducedMotion.matches && !this.hasState("dragging") ? tickerLeg(this.#travel, departure) : void 0;
247
+ const leg = !this.#isFocusInside && !this.#reducedMotion.matches && !this.hasState("dragging") && !this.isDisabled() ? tickerLeg(this.#travel, departure) : void 0;
230
248
  this.#stop();
231
249
  this.#heading = departure.heading;
232
250
  if (leg) this.#start(tape, leg);
@@ -38,4 +38,5 @@ export declare class SonicMeter extends SonicElement {
38
38
  protected connect(signal: AbortSignal): void;
39
39
  }
40
40
  //#endregion
41
+ export {};
41
42
  //# sourceMappingURL=meter.d.ts.map
@@ -41,4 +41,5 @@ export declare abstract class SonicPositionGroupElement extends SonicRadioGroupE
41
41
  protected springBack(): void;
42
42
  }
43
43
  //#endregion
44
+ export {};
44
45
  //# sourceMappingURL=position-group.d.ts.map
@@ -57,4 +57,5 @@ export declare class SonicSpectrum extends SonicAnalyserElement<Colour, Numeric>
57
57
  protected receive(frame: Float32Array): void;
58
58
  }
59
59
  //#endregion
60
+ export {};
60
61
  //# sourceMappingURL=spectrum.d.ts.map
@@ -17,7 +17,7 @@ export declare class SonicTicker extends SonicElement {
17
17
  protected readonly control: HTMLSpanElement;
18
18
  protected readonly sheet = "ticker.css";
19
19
  protected readonly sheetToken = "--_sonic-ticker-fade";
20
- attributeChangedCallback(): void;
20
+ attributeChangedCallback(name: string): void;
21
21
  protected connect(signal: AbortSignal): void;
22
22
  protected render(): void;
23
23
  }
@@ -17,7 +17,7 @@ const fadePx = 12;
17
17
  const holdMs = 2e3;
18
18
  const ratePxPerSecond = 40;
19
19
  var SonicTicker = class extends SonicElement {
20
- static observedAttributes = ["paused"];
20
+ static observedAttributes = ["disabled", "paused"];
21
21
  get cancellable() {
22
22
  return this.hasAttribute("cancellable");
23
23
  }
@@ -34,12 +34,15 @@ var SonicTicker = class extends SonicElement {
34
34
  return this.#originals().map((original) => original.textContent).join("");
35
35
  }
36
36
  set text(text) {
37
+ const next = text ?? "";
38
+ if (this.#holds(next)) return;
37
39
  for (const original of this.#originals()) original.remove();
38
- if (text !== void 0 && text !== "") this.prepend(text);
40
+ if (next !== "") this.prepend(next);
39
41
  }
40
42
  control = renderTicker();
41
43
  sheet = "ticker.css";
42
44
  sheetToken = "--_sonic-ticker-fade";
45
+ #drag;
43
46
  #fade = fadePx;
44
47
  #heading = 1;
45
48
  #isClickSwallowed = false;
@@ -52,22 +55,31 @@ var SonicTicker = class extends SonicElement {
52
55
  #position = 0;
53
56
  #reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
54
57
  #running;
58
+ #tape = requireChild(this.control, ".sonic-ticker-tape", HTMLSpanElement);
55
59
  #travel = {
56
60
  distance: 0,
57
61
  hold: holdMs,
58
62
  rate: ratePxPerSecond
59
63
  };
60
- attributeChangedCallback() {
64
+ attributeChangedCallback(name) {
65
+ if (name === "disabled" && this.isBound()) {
66
+ this.#drag?.end();
67
+ this.#run(this.#tape, {
68
+ from: 0,
69
+ heading: 1
70
+ });
71
+ }
61
72
  this.render();
62
73
  }
63
74
  connect(signal) {
64
75
  const control = this.control;
65
- const tape = requireChild(control, ".sonic-ticker-tape", HTMLSpanElement);
76
+ const tape = this.#tape;
66
77
  const measure = () => {
67
78
  this.#measure(tape);
68
79
  };
69
80
  const observer = new ResizeObserver(measure);
70
81
  const drag = this.#bindDrag(tape, signal);
82
+ this.#drag = drag;
71
83
  this.upgradeProperties("cancellable", "paused", "text");
72
84
  mirrorChildren(this, {
73
85
  canCopyLinks: true,
@@ -117,7 +129,7 @@ var SonicTicker = class extends SonicElement {
117
129
  }, { signal });
118
130
  return bindDrag(control, {
119
131
  grab: (event) => {
120
- if (this.#travel.distance <= 0) return;
132
+ if (this.#travel.distance <= 0 || this.isDisabled()) return;
121
133
  return {
122
134
  from: 0,
123
135
  isDragging: false,
@@ -193,6 +205,12 @@ var SonicTicker = class extends SonicElement {
193
205
  view.disconnect();
194
206
  }, { once: true });
195
207
  }
208
+ #holds(text) {
209
+ const [only, ...rest] = this.#originals();
210
+ if (only === void 0) return text === "";
211
+ if (rest.length > 0 || !(only instanceof Text)) return false;
212
+ return only.data === text;
213
+ }
196
214
  #isPaused() {
197
215
  return this.paused || this.#isHovered || this.#isPressed || this.#isOffScreen || this.control.closest(":focus-visible") !== null;
198
216
  }
@@ -226,7 +244,7 @@ var SonicTicker = class extends SonicElement {
226
244
  this.#writeFades(this.#position);
227
245
  }
228
246
  #run(tape, departure = this.#where()) {
229
- const leg = !this.#isFocusInside && !this.#reducedMotion.matches && !this.hasState("dragging") ? tickerLeg(this.#travel, departure) : void 0;
247
+ const leg = !this.#isFocusInside && !this.#reducedMotion.matches && !this.hasState("dragging") && !this.isDisabled() ? tickerLeg(this.#travel, departure) : void 0;
230
248
  this.#stop();
231
249
  this.#heading = departure.heading;
232
250
  if (leg) this.#start(tape, leg);
@@ -1 +1 @@
1
- {"version":3,"file":"ticker.js","names":[],"sources":["../../../src/elements/ticker.ts"],"sourcesContent":["import type { TickerDeparture, TickerHeading, TickerLeg, TickerTravel } from '#lib/ticker-leg.ts';\n\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { outsideCancelZone } from '#lib/cancel-zone.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { dragThresholdPx } from '#lib/drag-step.ts';\nimport { clamp } from '#lib/math.ts';\nimport { mirrorChildren } from '#lib/mirror-children.ts';\nimport { bindDrag, capturePointer } from '#lib/pointer-drag.ts';\nimport { readMsProperty } from '#lib/read-ms-property.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { placeChildren, requireChild, template } from '#lib/render.ts';\nimport {\n\tclippedEnds,\n\tfadeWidth,\n\tlegDeparture,\n\tlegFades,\n\tlegPosition,\n\ttickerLeg,\n} from '#lib/ticker-leg.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-ticker': SonicTicker;\n\t}\n}\n\nconst renderTicker = template(\n\t/* HTML */ `<span class=\"sonic-ticker\"><span class=\"sonic-ticker-tape\"></span></span>`,\n\tHTMLSpanElement,\n);\n\nconst fadePx = 12;\n\nconst holdMs = 2000;\n\nconst ratePxPerSecond = 40;\n\ninterface Running {\n\tanimation: Animation;\n\tfades: Array<Animation>;\n\tleg: TickerLeg;\n}\n\ninterface TapeDrag {\n\tfrom: number;\n\tisDragging: boolean;\n\toutside: ((event: PointerEvent) => boolean) | undefined;\n\tthresholdPx: number;\n\tx: number;\n}\n\nexport class SonicTicker extends SonicElement {\n\tstatic override readonly observedAttributes = ['paused'];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget cancellable(): boolean {\n\t\treturn this.hasAttribute('cancellable');\n\t}\n\n\tset cancellable(isCancellable: boolean) {\n\t\tthis.reflect('cancellable', isCancellable);\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.hasAttribute('paused');\n\t}\n\n\tset paused(isPaused: boolean) {\n\t\tthis.reflect('paused', isPaused);\n\t}\n\n\tget text(): string {\n\t\treturn this.#originals()\n\t\t\t.map((original) => original.textContent)\n\t\t\t.join('');\n\t}\n\n\tset text(text: string | undefined) {\n\t\tfor (const original of this.#originals()) original.remove();\n\t\tif (text !== undefined && text !== '') this.prepend(text);\n\t}\n\n\tprotected override readonly control = renderTicker();\n\n\tprotected override readonly sheet = 'ticker.css';\n\n\tprotected override readonly sheetToken = '--_sonic-ticker-fade';\n\n\t#fade = fadePx;\n\n\t#heading: TickerHeading = 1;\n\n\t#isClickSwallowed = false;\n\n\t#isFocusInside = false;\n\n\t#isHovered = false;\n\n\t#isOffScreen = false;\n\n\t#isPressed = false;\n\n\t#mark: ReturnType<typeof setTimeout> | undefined;\n\n\t#measured = '';\n\n\t#position = 0;\n\n\treadonly #reducedMotion = matchMedia('(prefers-reduced-motion: reduce)');\n\n\t#running: Running | undefined;\n\n\t#travel: TickerTravel = { distance: 0, hold: holdMs, rate: ratePxPerSecond };\n\n\tattributeChangedCallback(): void {\n\t\tthis.render();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\t\tconst tape = requireChild(control, '.sonic-ticker-tape', HTMLSpanElement);\n\t\tconst measure = (): void => {\n\t\t\tthis.#measure(tape);\n\t\t};\n\t\tconst observer = new ResizeObserver(measure);\n\t\tconst drag = this.#bindDrag(tape, signal);\n\n\t\tthis.upgradeProperties('cancellable', 'paused', 'text');\n\t\tmirrorChildren(\n\t\t\tthis,\n\t\t\t{\n\t\t\t\tcanCopyLinks: true,\n\t\t\t\tcontrol,\n\t\t\t\tcopy: copyNode,\n\t\t\t\tplace: (copies) => {\n\t\t\t\t\tplaceChildren(tape, copies);\n\t\t\t\t\tdrag.end();\n\t\t\t\t\tthis.#measure(tape, { from: 0, heading: 1 });\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\n\t\tobserver.observe(control);\n\t\tobserver.observe(tape);\n\t\tif ('fonts' in document) document.fonts.addEventListener('loadingdone', measure, { signal });\n\t\tthis.#bindFocus(tape, signal);\n\t\tthis.#bindPauses(signal);\n\t\tthis.#reducedMotion.addEventListener(\n\t\t\t'change',\n\t\t\t() => {\n\t\t\t\tthis.#run(tape);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tobserver.disconnect();\n\t\t\t\tthis.#stop();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tthis.toggleState('paused', this.paused);\n\t\tthis.#syncPause();\n\t}\n\n\t#bindDrag(tape: HTMLElement, signal: AbortSignal): { end: () => void } {\n\t\tconst control = this.control;\n\n\t\tcontrol.addEventListener(\n\t\t\t'click',\n\t\t\t(event) => {\n\t\t\t\tif (!this.#isClickSwallowed) return;\n\n\t\t\t\tthis.#isClickSwallowed = false;\n\t\t\t\tevent.preventDefault();\n\t\t\t\tevent.stopPropagation();\n\t\t\t},\n\t\t\t{ capture: true, signal },\n\t\t);\n\t\t// In WebKit, dragging a link that wraps the ticker fires `dragstart` on the link, outside the control\n\t\tthis.getRootNode().addEventListener(\n\t\t\t'dragstart',\n\t\t\t(event) => {\n\t\t\t\tif (this.#isPressed) event.preventDefault();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\treturn bindDrag<TapeDrag>(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (this.#travel.distance <= 0) return;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfrom: 0,\n\t\t\t\t\t\tisDragging: false,\n\t\t\t\t\t\toutside: undefined,\n\t\t\t\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\t\t\t\tx: event.clientX,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\tisCaptureLate: true,\n\t\t\t\tmove: (held, event) => {\n\t\t\t\t\tif (!held.isDragging) {\n\t\t\t\t\t\tif (Math.abs(event.clientX - held.x) < held.thresholdPx) return;\n\n\t\t\t\t\t\tconst { from, heading } = this.#where();\n\n\t\t\t\t\t\theld.isDragging = true;\n\t\t\t\t\t\theld.from = from;\n\t\t\t\t\t\theld.outside = this.cancellable\n\t\t\t\t\t\t\t? outsideCancelZone(control, '--_sonic-ticker-cancel-zone')\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\theld.x = event.clientX;\n\t\t\t\t\t\tcapturePointer(control, event.pointerId);\n\t\t\t\t\t\tthis.toggleState('dragging', true);\n\t\t\t\t\t\tthis.#stop();\n\t\t\t\t\t\tthis.#heading = heading;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst isOutside = held.outside?.(event) === true;\n\n\t\t\t\t\tthis.toggleState('cancelling', isOutside);\n\t\t\t\t\tthis.#place(tape, isOutside ? held.from : held.from - (event.clientX - held.x));\n\t\t\t\t},\n\t\t\t\trelease: (held) => {\n\t\t\t\t\tif (!held.isDragging) return;\n\n\t\t\t\t\t// Safari sends no click after a drag, so the flag is cleared on a timer or it would cancel the next real click\n\t\t\t\t\tthis.#isClickSwallowed = true;\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tthis.#isClickSwallowed = false;\n\t\t\t\t\t}, 0);\n\t\t\t\t\tthis.toggleState('dragging', false);\n\t\t\t\t\tthis.toggleState('cancelling', false);\n\t\t\t\t\tthis.#run(tape);\n\t\t\t\t},\n\t\t\t\ttoggle: (isPressed) => {\n\t\t\t\t\tthis.#isPressed = isPressed;\n\t\t\t\t\tthis.#syncPause();\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#bindFocus(tape: HTMLElement, signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tcontrol.addEventListener(\n\t\t\t'focusout',\n\t\t\t(event) => {\n\t\t\t\tif (event.relatedTarget instanceof Node && control.contains(event.relatedTarget)) return;\n\n\t\t\t\tcontrol.scrollLeft = 0;\n\t\t\t\tthis.#isFocusInside = false;\n\t\t\t\tthis.#run(tape);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'focusin',\n\t\t\t(event) => {\n\t\t\t\tthis.#isFocusInside = true;\n\t\t\t\tthis.#run(tape);\n\t\t\t\tif (event.target instanceof Element) event.target.scrollIntoView({ block: 'nearest' });\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#bindPauses(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tfor (const type of ['pointerenter', 'pointerleave'] as const) {\n\t\t\tcontrol.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (event.pointerType !== 'mouse') return;\n\n\t\t\t\t\tthis.#isHovered = type === 'pointerenter';\n\t\t\t\t\tthis.#syncPause();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t\tfor (const type of ['focusin', 'focusout'] as const) {\n\t\t\tthis.getRootNode().addEventListener(\n\t\t\t\ttype,\n\t\t\t\t() => {\n\t\t\t\t\tthis.#syncPause();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\n\t\tconst view = new IntersectionObserver((entries) => {\n\t\t\tthis.#isOffScreen = entries.at(-1)?.isIntersecting === false;\n\t\t\tthis.#syncPause();\n\t\t});\n\n\t\tview.observe(control);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tview.disconnect();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#isPaused(): boolean {\n\t\treturn (\n\t\t\tthis.paused ||\n\t\t\tthis.#isHovered ||\n\t\t\tthis.#isPressed ||\n\t\t\tthis.#isOffScreen ||\n\t\t\tthis.control.closest(':focus-visible') !== null\n\t\t);\n\t}\n\n\t#measure(tape: HTMLElement, departure?: TickerDeparture): void {\n\t\tconst control = this.control;\n\t\tconst styles = getComputedStyle(control);\n\t\tconst box =\n\t\t\tcontrol.clientWidth -\n\t\t\treadPxProperty(styles, 'padding-left', 0) -\n\t\t\treadPxProperty(styles, 'padding-right', 0);\n\t\tconst travel = {\n\t\t\tdistance: Math.max(0, Math.round(tape.offsetWidth - box)),\n\t\t\thold: readMsProperty(styles, '--_sonic-ticker-hold', holdMs),\n\t\t\trate: readPxProperty(styles, '--_sonic-ticker-rate', ratePxPerSecond),\n\t\t};\n\t\tconst fade = readPxProperty(styles, '--_sonic-ticker-fade', fadePx);\n\t\tconst measured = [...Object.values(travel), fade].join(' ');\n\t\tif (!departure && measured === this.#measured) return;\n\n\t\tconst from = departure ?? this.#where();\n\n\t\tthis.#fade = fade;\n\t\tthis.#measured = measured;\n\t\tthis.#travel = travel;\n\t\tthis.toggleState('overflowing', travel.distance > 0);\n\t\tthis.#run(tape, from);\n\t}\n\n\t#originals(): Array<ChildNode> {\n\t\treturn [...this.childNodes].filter((child) => child !== this.control);\n\t}\n\n\t#place(tape: HTMLElement, position: number): void {\n\t\tconst { distance } = this.#travel;\n\n\t\tthis.#position = clamp(position, 0, distance);\n\t\ttape.style.translate = this.#position > 0 ? `${String(-this.#position)}px` : '';\n\t\twriteAttribute(this.control, 'data-sonic-clipped', clippedEnds(this.#position, distance));\n\t\tthis.#writeFades(this.#position);\n\t}\n\n\t#run(tape: HTMLElement, departure: TickerDeparture = this.#where()): void {\n\t\tconst canTravel =\n\t\t\t!this.#isFocusInside && !this.#reducedMotion.matches && !this.hasState('dragging');\n\t\tconst leg = canTravel ? tickerLeg(this.#travel, departure) : undefined;\n\n\t\tthis.#stop();\n\t\tthis.#heading = departure.heading;\n\t\tif (leg) this.#start(tape, leg);\n\t\telse this.#place(tape, this.#isFocusInside ? 0 : departure.from);\n\t}\n\n\t#start(tape: HTMLElement, leg: TickerLeg): void {\n\t\tconst animation = tape.animate(\n\t\t\t{ translate: [`${String(-leg.from)}px`, `${String(-leg.to)}px`] },\n\t\t\t{ delay: leg.hold, duration: leg.duration, fill: 'both' },\n\t\t);\n\n\t\tanimation.addEventListener('finish', () => {\n\t\t\tthis.#run(tape, { from: leg.to, heading: leg.heading });\n\t\t});\n\t\tthis.#running = { animation, fades: [], leg };\n\t\ttape.style.translate = '';\n\t\twriteAttribute(\n\t\t\tthis.control,\n\t\t\t'data-sonic-clipped',\n\t\t\tclippedEnds(leg.from, this.#travel.distance),\n\t\t);\n\t\tthis.#syncPause();\n\t}\n\n\t#startFades(leg: TickerLeg, elapsed: number): Array<Animation> {\n\t\tconst fades = legFades(leg, this.#travel.distance, this.#fade);\n\n\t\treturn (['start', 'end'] as const).flatMap((end) => {\n\t\t\tconst { delay, duration, widths } = fades[end];\n\t\t\tif (duration <= 0 || delay + duration <= elapsed) return [];\n\n\t\t\treturn this.control.animate(\n\t\t\t\t{ [`--_sonic-ticker-fade-${end}`]: widths.map((width) => `${String(width)}px`) },\n\t\t\t\t{ delay: delay - elapsed, duration, fill: 'both' },\n\t\t\t);\n\t\t});\n\t}\n\n\t#stop(): void {\n\t\tconst running = this.#running;\n\n\t\tclearTimeout(this.#mark);\n\t\tif (!running) return;\n\n\t\tfor (const fade of running.fades) fade.cancel();\n\t\trunning.animation.cancel();\n\t\tthis.#running = undefined;\n\t}\n\n\t// Calling `play()` on a finished animation restarts it, so the fades are recreated on resume, not paused\n\t#syncPause(): void {\n\t\tconst running = this.#running;\n\t\tif (!running) return;\n\n\t\tconst { animation, leg } = running;\n\t\tconst elapsed = Number(animation.currentTime);\n\n\t\tclearTimeout(this.#mark);\n\t\tfor (const fade of running.fades) fade.cancel();\n\t\trunning.fades = [];\n\t\tthis.#writeFades(legPosition(leg, elapsed));\n\t\tif (elapsed >= leg.hold) writeAttribute(this.control, 'data-sonic-clipped', 'start end');\n\t\tif (this.#isPaused()) {\n\t\t\tanimation.pause();\n\t\t\treturn;\n\t\t}\n\t\tif (animation.playState === 'paused') animation.play();\n\t\trunning.fades = this.#startFades(leg, elapsed);\n\t\tif (elapsed >= leg.hold) return;\n\n\t\tthis.#mark = setTimeout(() => {\n\t\t\twriteAttribute(this.control, 'data-sonic-clipped', 'start end');\n\t\t}, leg.hold - elapsed);\n\t}\n\n\t#where(): TickerDeparture {\n\t\tconst running = this.#running;\n\t\tif (!running) return { from: this.#position, heading: this.#heading };\n\n\t\treturn legDeparture(running.leg, Number(running.animation.currentTime));\n\t}\n\n\t#writeFades(position: number): void {\n\t\tconst { style } = this.control;\n\n\t\tstyle.setProperty('--_sonic-ticker-fade-start', `${String(fadeWidth(position, this.#fade))}px`);\n\t\tstyle.setProperty(\n\t\t\t'--_sonic-ticker-fade-end',\n\t\t\t`${String(fadeWidth(this.#travel.distance - position, this.#fade))}px`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ticker.js","names":[],"sources":["../../../src/elements/ticker.ts"],"sourcesContent":["import type { TickerDeparture, TickerHeading, TickerLeg, TickerTravel } from '#lib/ticker-leg.ts';\n\nimport { SonicElement } from '#elements/sonic-element.ts';\nimport { outsideCancelZone } from '#lib/cancel-zone.ts';\nimport { copyNode } from '#lib/copy-node.ts';\nimport { dragThresholdPx } from '#lib/drag-step.ts';\nimport { clamp } from '#lib/math.ts';\nimport { mirrorChildren } from '#lib/mirror-children.ts';\nimport { bindDrag, capturePointer } from '#lib/pointer-drag.ts';\nimport { readMsProperty } from '#lib/read-ms-property.ts';\nimport { readPxProperty } from '#lib/read-px-property.ts';\nimport { placeChildren, requireChild, template } from '#lib/render.ts';\nimport {\n\tclippedEnds,\n\tfadeWidth,\n\tlegDeparture,\n\tlegFades,\n\tlegPosition,\n\ttickerLeg,\n} from '#lib/ticker-leg.ts';\nimport { writeAttribute } from '#lib/write-attribute.ts';\n\ndeclare global {\n\tinterface HTMLElementTagNameMap {\n\t\t'sonic-ticker': SonicTicker;\n\t}\n}\n\nconst renderTicker = template(\n\t/* HTML */ `<span class=\"sonic-ticker\"><span class=\"sonic-ticker-tape\"></span></span>`,\n\tHTMLSpanElement,\n);\n\nconst fadePx = 12;\n\nconst holdMs = 2000;\n\nconst ratePxPerSecond = 40;\n\ninterface Running {\n\tanimation: Animation;\n\tfades: Array<Animation>;\n\tleg: TickerLeg;\n}\n\ninterface TapeDrag {\n\tfrom: number;\n\tisDragging: boolean;\n\toutside: ((event: PointerEvent) => boolean) | undefined;\n\tthresholdPx: number;\n\tx: number;\n}\n\nexport class SonicTicker extends SonicElement {\n\tstatic override readonly observedAttributes = ['disabled', 'paused'];\n\n\t// fallow-ignore-next-line code-duplication -- one accessor pair per reflected attribute\n\tget cancellable(): boolean {\n\t\treturn this.hasAttribute('cancellable');\n\t}\n\n\tset cancellable(isCancellable: boolean) {\n\t\tthis.reflect('cancellable', isCancellable);\n\t}\n\n\tget paused(): boolean {\n\t\treturn this.hasAttribute('paused');\n\t}\n\n\tset paused(isPaused: boolean) {\n\t\tthis.reflect('paused', isPaused);\n\t}\n\n\tget text(): string {\n\t\treturn this.#originals()\n\t\t\t.map((original) => original.textContent)\n\t\t\t.join('');\n\t}\n\n\tset text(text: string | undefined) {\n\t\tconst next = text ?? '';\n\n\t\t// A host that re-renders the same title would otherwise restart the tape\n\t\tif (this.#holds(next)) return;\n\n\t\tfor (const original of this.#originals()) original.remove();\n\t\tif (next !== '') this.prepend(next);\n\t}\n\n\tprotected override readonly control = renderTicker();\n\n\tprotected override readonly sheet = 'ticker.css';\n\n\tprotected override readonly sheetToken = '--_sonic-ticker-fade';\n\n\t#drag: undefined | { end: () => void };\n\n\t#fade = fadePx;\n\n\t#heading: TickerHeading = 1;\n\n\t#isClickSwallowed = false;\n\n\t#isFocusInside = false;\n\n\t#isHovered = false;\n\n\t#isOffScreen = false;\n\n\t#isPressed = false;\n\n\t#mark: ReturnType<typeof setTimeout> | undefined;\n\n\t#measured = '';\n\n\t#position = 0;\n\n\treadonly #reducedMotion = matchMedia('(prefers-reduced-motion: reduce)');\n\n\t#running: Running | undefined;\n\n\treadonly #tape = requireChild(this.control, '.sonic-ticker-tape', HTMLSpanElement);\n\n\t#travel: TickerTravel = { distance: 0, hold: holdMs, rate: ratePxPerSecond };\n\n\tattributeChangedCallback(name: string): void {\n\t\tif (name === 'disabled' && this.isBound()) {\n\t\t\tthis.#drag?.end();\n\t\t\tthis.#run(this.#tape, { from: 0, heading: 1 });\n\t\t}\n\n\t\tthis.render();\n\t}\n\n\tprotected connect(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\t\tconst tape = this.#tape;\n\t\tconst measure = (): void => {\n\t\t\tthis.#measure(tape);\n\t\t};\n\t\tconst observer = new ResizeObserver(measure);\n\t\tconst drag = this.#bindDrag(tape, signal);\n\n\t\tthis.#drag = drag;\n\n\t\tthis.upgradeProperties('cancellable', 'paused', 'text');\n\t\tmirrorChildren(\n\t\t\tthis,\n\t\t\t{\n\t\t\t\tcanCopyLinks: true,\n\t\t\t\tcontrol,\n\t\t\t\tcopy: copyNode,\n\t\t\t\tplace: (copies) => {\n\t\t\t\t\tplaceChildren(tape, copies);\n\t\t\t\t\tdrag.end();\n\t\t\t\t\tthis.#measure(tape, { from: 0, heading: 1 });\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t\tthis.render();\n\n\t\tobserver.observe(control);\n\t\tobserver.observe(tape);\n\t\tif ('fonts' in document) document.fonts.addEventListener('loadingdone', measure, { signal });\n\t\tthis.#bindFocus(tape, signal);\n\t\tthis.#bindPauses(signal);\n\t\tthis.#reducedMotion.addEventListener(\n\t\t\t'change',\n\t\t\t() => {\n\t\t\t\tthis.#run(tape);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tobserver.disconnect();\n\t\t\t\tthis.#stop();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\tprotected render(): void {\n\t\tif (!this.isBound()) return;\n\n\t\tthis.toggleState('paused', this.paused);\n\t\tthis.#syncPause();\n\t}\n\n\t#bindDrag(tape: HTMLElement, signal: AbortSignal): { end: () => void } {\n\t\tconst control = this.control;\n\n\t\tcontrol.addEventListener(\n\t\t\t'click',\n\t\t\t(event) => {\n\t\t\t\tif (!this.#isClickSwallowed) return;\n\n\t\t\t\tthis.#isClickSwallowed = false;\n\t\t\t\tevent.preventDefault();\n\t\t\t\tevent.stopPropagation();\n\t\t\t},\n\t\t\t{ capture: true, signal },\n\t\t);\n\t\t// In WebKit, dragging a link that wraps the ticker fires `dragstart` on the link, outside the control\n\t\tthis.getRootNode().addEventListener(\n\t\t\t'dragstart',\n\t\t\t(event) => {\n\t\t\t\tif (this.#isPressed) event.preventDefault();\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\n\t\treturn bindDrag<TapeDrag>(\n\t\t\tcontrol,\n\t\t\t{\n\t\t\t\tgrab: (event) => {\n\t\t\t\t\tif (this.#travel.distance <= 0 || this.isDisabled()) return;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfrom: 0,\n\t\t\t\t\t\tisDragging: false,\n\t\t\t\t\t\toutside: undefined,\n\t\t\t\t\t\tthresholdPx: dragThresholdPx(event.pointerType),\n\t\t\t\t\t\tx: event.clientX,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\tisCaptureLate: true,\n\t\t\t\tmove: (held, event) => {\n\t\t\t\t\tif (!held.isDragging) {\n\t\t\t\t\t\tif (Math.abs(event.clientX - held.x) < held.thresholdPx) return;\n\n\t\t\t\t\t\tconst { from, heading } = this.#where();\n\n\t\t\t\t\t\theld.isDragging = true;\n\t\t\t\t\t\theld.from = from;\n\t\t\t\t\t\theld.outside = this.cancellable\n\t\t\t\t\t\t\t? outsideCancelZone(control, '--_sonic-ticker-cancel-zone')\n\t\t\t\t\t\t\t: undefined;\n\t\t\t\t\t\theld.x = event.clientX;\n\t\t\t\t\t\tcapturePointer(control, event.pointerId);\n\t\t\t\t\t\tthis.toggleState('dragging', true);\n\t\t\t\t\t\tthis.#stop();\n\t\t\t\t\t\tthis.#heading = heading;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst isOutside = held.outside?.(event) === true;\n\n\t\t\t\t\tthis.toggleState('cancelling', isOutside);\n\t\t\t\t\tthis.#place(tape, isOutside ? held.from : held.from - (event.clientX - held.x));\n\t\t\t\t},\n\t\t\t\trelease: (held) => {\n\t\t\t\t\tif (!held.isDragging) return;\n\n\t\t\t\t\t// Safari sends no click after a drag, so the flag is cleared on a timer or it would cancel the next real click\n\t\t\t\t\tthis.#isClickSwallowed = true;\n\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\tthis.#isClickSwallowed = false;\n\t\t\t\t\t}, 0);\n\t\t\t\t\tthis.toggleState('dragging', false);\n\t\t\t\t\tthis.toggleState('cancelling', false);\n\t\t\t\t\tthis.#run(tape);\n\t\t\t\t},\n\t\t\t\ttoggle: (isPressed) => {\n\t\t\t\t\tthis.#isPressed = isPressed;\n\t\t\t\t\tthis.#syncPause();\n\t\t\t\t},\n\t\t\t},\n\t\t\tsignal,\n\t\t);\n\t}\n\n\t#bindFocus(tape: HTMLElement, signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tcontrol.addEventListener(\n\t\t\t'focusout',\n\t\t\t(event) => {\n\t\t\t\tif (event.relatedTarget instanceof Node && control.contains(event.relatedTarget)) return;\n\n\t\t\t\tcontrol.scrollLeft = 0;\n\t\t\t\tthis.#isFocusInside = false;\n\t\t\t\tthis.#run(tape);\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t\tcontrol.addEventListener(\n\t\t\t'focusin',\n\t\t\t(event) => {\n\t\t\t\tthis.#isFocusInside = true;\n\t\t\t\tthis.#run(tape);\n\t\t\t\tif (event.target instanceof Element) event.target.scrollIntoView({ block: 'nearest' });\n\t\t\t},\n\t\t\t{ signal },\n\t\t);\n\t}\n\n\t#bindPauses(signal: AbortSignal): void {\n\t\tconst control = this.control;\n\n\t\tfor (const type of ['pointerenter', 'pointerleave'] as const) {\n\t\t\tcontrol.addEventListener(\n\t\t\t\ttype,\n\t\t\t\t(event) => {\n\t\t\t\t\tif (event.pointerType !== 'mouse') return;\n\n\t\t\t\t\tthis.#isHovered = type === 'pointerenter';\n\t\t\t\t\tthis.#syncPause();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\t\tfor (const type of ['focusin', 'focusout'] as const) {\n\t\t\tthis.getRootNode().addEventListener(\n\t\t\t\ttype,\n\t\t\t\t() => {\n\t\t\t\t\tthis.#syncPause();\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t}\n\n\t\tconst view = new IntersectionObserver((entries) => {\n\t\t\tthis.#isOffScreen = entries.at(-1)?.isIntersecting === false;\n\t\t\tthis.#syncPause();\n\t\t});\n\n\t\tview.observe(control);\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\tview.disconnect();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t#holds(text: string): boolean {\n\t\tconst [only, ...rest] = this.#originals();\n\t\tif (only === undefined) return text === '';\n\t\tif (rest.length > 0 || !(only instanceof Text)) return false;\n\n\t\treturn only.data === text;\n\t}\n\n\t#isPaused(): boolean {\n\t\treturn (\n\t\t\tthis.paused ||\n\t\t\tthis.#isHovered ||\n\t\t\tthis.#isPressed ||\n\t\t\tthis.#isOffScreen ||\n\t\t\tthis.control.closest(':focus-visible') !== null\n\t\t);\n\t}\n\n\t#measure(tape: HTMLElement, departure?: TickerDeparture): void {\n\t\tconst control = this.control;\n\t\tconst styles = getComputedStyle(control);\n\t\tconst box =\n\t\t\tcontrol.clientWidth -\n\t\t\treadPxProperty(styles, 'padding-left', 0) -\n\t\t\treadPxProperty(styles, 'padding-right', 0);\n\t\tconst travel = {\n\t\t\tdistance: Math.max(0, Math.round(tape.offsetWidth - box)),\n\t\t\thold: readMsProperty(styles, '--_sonic-ticker-hold', holdMs),\n\t\t\trate: readPxProperty(styles, '--_sonic-ticker-rate', ratePxPerSecond),\n\t\t};\n\t\tconst fade = readPxProperty(styles, '--_sonic-ticker-fade', fadePx);\n\t\tconst measured = [...Object.values(travel), fade].join(' ');\n\t\tif (!departure && measured === this.#measured) return;\n\n\t\tconst from = departure ?? this.#where();\n\n\t\tthis.#fade = fade;\n\t\tthis.#measured = measured;\n\t\tthis.#travel = travel;\n\t\tthis.toggleState('overflowing', travel.distance > 0);\n\t\tthis.#run(tape, from);\n\t}\n\n\t#originals(): Array<ChildNode> {\n\t\treturn [...this.childNodes].filter((child) => child !== this.control);\n\t}\n\n\t#place(tape: HTMLElement, position: number): void {\n\t\tconst { distance } = this.#travel;\n\n\t\tthis.#position = clamp(position, 0, distance);\n\t\ttape.style.translate = this.#position > 0 ? `${String(-this.#position)}px` : '';\n\t\twriteAttribute(this.control, 'data-sonic-clipped', clippedEnds(this.#position, distance));\n\t\tthis.#writeFades(this.#position);\n\t}\n\n\t#run(tape: HTMLElement, departure: TickerDeparture = this.#where()): void {\n\t\tconst canTravel =\n\t\t\t!this.#isFocusInside &&\n\t\t\t!this.#reducedMotion.matches &&\n\t\t\t!this.hasState('dragging') &&\n\t\t\t!this.isDisabled();\n\t\tconst leg = canTravel ? tickerLeg(this.#travel, departure) : undefined;\n\n\t\tthis.#stop();\n\t\tthis.#heading = departure.heading;\n\t\tif (leg) this.#start(tape, leg);\n\t\telse this.#place(tape, this.#isFocusInside ? 0 : departure.from);\n\t}\n\n\t#start(tape: HTMLElement, leg: TickerLeg): void {\n\t\tconst animation = tape.animate(\n\t\t\t{ translate: [`${String(-leg.from)}px`, `${String(-leg.to)}px`] },\n\t\t\t{ delay: leg.hold, duration: leg.duration, fill: 'both' },\n\t\t);\n\n\t\tanimation.addEventListener('finish', () => {\n\t\t\tthis.#run(tape, { from: leg.to, heading: leg.heading });\n\t\t});\n\t\tthis.#running = { animation, fades: [], leg };\n\t\ttape.style.translate = '';\n\t\twriteAttribute(\n\t\t\tthis.control,\n\t\t\t'data-sonic-clipped',\n\t\t\tclippedEnds(leg.from, this.#travel.distance),\n\t\t);\n\t\tthis.#syncPause();\n\t}\n\n\t#startFades(leg: TickerLeg, elapsed: number): Array<Animation> {\n\t\tconst fades = legFades(leg, this.#travel.distance, this.#fade);\n\n\t\treturn (['start', 'end'] as const).flatMap((end) => {\n\t\t\tconst { delay, duration, widths } = fades[end];\n\t\t\tif (duration <= 0 || delay + duration <= elapsed) return [];\n\n\t\t\treturn this.control.animate(\n\t\t\t\t{ [`--_sonic-ticker-fade-${end}`]: widths.map((width) => `${String(width)}px`) },\n\t\t\t\t{ delay: delay - elapsed, duration, fill: 'both' },\n\t\t\t);\n\t\t});\n\t}\n\n\t#stop(): void {\n\t\tconst running = this.#running;\n\n\t\tclearTimeout(this.#mark);\n\t\tif (!running) return;\n\n\t\tfor (const fade of running.fades) fade.cancel();\n\t\trunning.animation.cancel();\n\t\tthis.#running = undefined;\n\t}\n\n\t// Calling `play()` on a finished animation restarts it, so the fades are recreated on resume, not paused\n\t#syncPause(): void {\n\t\tconst running = this.#running;\n\t\tif (!running) return;\n\n\t\tconst { animation, leg } = running;\n\t\tconst elapsed = Number(animation.currentTime);\n\n\t\tclearTimeout(this.#mark);\n\t\tfor (const fade of running.fades) fade.cancel();\n\t\trunning.fades = [];\n\t\tthis.#writeFades(legPosition(leg, elapsed));\n\t\tif (elapsed >= leg.hold) writeAttribute(this.control, 'data-sonic-clipped', 'start end');\n\t\tif (this.#isPaused()) {\n\t\t\tanimation.pause();\n\t\t\treturn;\n\t\t}\n\t\tif (animation.playState === 'paused') animation.play();\n\t\trunning.fades = this.#startFades(leg, elapsed);\n\t\tif (elapsed >= leg.hold) return;\n\n\t\tthis.#mark = setTimeout(() => {\n\t\t\twriteAttribute(this.control, 'data-sonic-clipped', 'start end');\n\t\t}, leg.hold - elapsed);\n\t}\n\n\t#where(): TickerDeparture {\n\t\tconst running = this.#running;\n\t\tif (!running) return { from: this.#position, heading: this.#heading };\n\n\t\treturn legDeparture(running.leg, Number(running.animation.currentTime));\n\t}\n\n\t#writeFades(position: number): void {\n\t\tconst { style } = this.control;\n\n\t\tstyle.setProperty('--_sonic-ticker-fade-start', `${String(fadeWidth(position, this.#fade))}px`);\n\t\tstyle.setProperty(\n\t\t\t'--_sonic-ticker-fade-end',\n\t\t\t`${String(fadeWidth(this.#travel.distance - position, this.#fade))}px`,\n\t\t);\n\t}\n}\n"],"mappings":"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"}
@@ -102,4 +102,5 @@ export declare abstract class SonicValueElement extends SonicFormElement {
102
102
  protected upwardAxis(travelProperty: `--_sonic-${string}`): ValueAxis;
103
103
  }
104
104
  //#endregion
105
+ export {};
105
106
  //# sourceMappingURL=value-element.d.ts.map
@@ -78,4 +78,5 @@ export declare class SonicWavestrip extends SonicWaveElement<Colour, Length> {
78
78
  protected usesChild(child: Node): boolean;
79
79
  }
80
80
  //#endregion
81
+ export {};
81
82
  //# sourceMappingURL=wavestrip.d.ts.map
@@ -10,4 +10,5 @@ interface CrossfadeGains {
10
10
  /** `position` runs from 0, all a, to 1, all b; `sharpness` from 0 to 1 shortens the overlap */
11
11
  export declare function crossfadeGains(position: number, options?: CrossfadeOptions): CrossfadeGains;
12
12
  //#endregion
13
+ export {};
13
14
  //# sourceMappingURL=crossfade.d.ts.map
@@ -61,7 +61,7 @@
61
61
  user-select: none;
62
62
  }
63
63
 
64
- .sonic-ticker[data-sonic-overflowing] {
64
+ .sonic-ticker[data-sonic-overflowing]:not([disabled] > *) {
65
65
  text-overflow: clip;
66
66
  touch-action: var(--sonic-touch-action, pan-y);
67
67
  -webkit-mask: linear-gradient(
@@ -80,11 +80,11 @@
80
80
  );
81
81
  }
82
82
 
83
- .sonic-ticker[data-sonic-overflowing] > .sonic-ticker-tape {
83
+ .sonic-ticker[data-sonic-overflowing]:not([disabled] > *) > .sonic-ticker-tape {
84
84
  display: inline-block;
85
85
  }
86
86
 
87
- .sonic-ticker[data-sonic-overflowing]:not(:any-link *) {
87
+ .sonic-ticker[data-sonic-overflowing]:not(:any-link *, [disabled] > *) {
88
88
  cursor: grab;
89
89
  }
90
90
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xsynaptic/sonic-ui",
3
- "version": "0.28.0",
3
+ "version": "0.28.1",
4
4
  "description": "Native custom elements for audio interfaces, skinned with CSS custom properties",
5
5
  "keywords": [
6
6
  "custom-elements",
@@ -79,7 +79,7 @@
79
79
  ],
80
80
  "devDependencies": {
81
81
  "publint": "^0.3.25",
82
- "tsdown": "^0.23.0",
82
+ "tsdown": "^0.23.1",
83
83
  "typescript": "^6.0.3"
84
84
  },
85
85
  "scripts": {