@xsynaptic/sonic-ui 0.4.0 → 0.5.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.
Files changed (240) hide show
  1. package/README.md +2 -21
  2. package/dist/default/dat.js +50 -0
  3. package/dist/default/define/button.js +7 -0
  4. package/dist/default/define/envelope.js +7 -0
  5. package/dist/default/define/split.js +7 -0
  6. package/dist/default/define/switch.js +7 -0
  7. package/dist/default/define/waveform.js +7 -0
  8. package/dist/default/define/wavestrip.js +7 -0
  9. package/dist/default/define/xy.js +7 -0
  10. package/dist/default/define.js +7 -1
  11. package/dist/default/elements/button.js +165 -0
  12. package/dist/default/elements/dial.js +16 -22
  13. package/dist/default/elements/envelope.js +367 -0
  14. package/dist/default/elements/meter.js +149 -61
  15. package/dist/default/elements/number.js +21 -8
  16. package/dist/default/elements/radio-group.js +185 -0
  17. package/dist/default/elements/readout-claim.js +81 -0
  18. package/dist/default/elements/readout.js +35 -0
  19. package/dist/default/elements/segmented.js +9 -159
  20. package/dist/default/elements/slider.js +42 -51
  21. package/dist/default/elements/sonic-element.js +24 -13
  22. package/dist/default/elements/split.js +249 -0
  23. package/dist/default/elements/switch.js +279 -0
  24. package/dist/default/elements/value-element.js +691 -0
  25. package/dist/default/elements/{range-entry.js → value-entry.js} +3 -3
  26. package/dist/default/elements/wave-element.js +77 -0
  27. package/dist/default/elements/waveform.js +305 -0
  28. package/dist/default/elements/wavestrip.js +182 -0
  29. package/dist/default/elements/xy.js +473 -0
  30. package/dist/default/index.js +10 -2
  31. package/dist/default/lib/adsr-shape.js +108 -0
  32. package/dist/default/lib/ballistics.js +55 -0
  33. package/dist/default/lib/canvas-surface.js +192 -0
  34. package/dist/default/lib/copy-node.js +32 -2
  35. package/dist/default/lib/crossfade.js +22 -0
  36. package/dist/default/lib/distribute.js +215 -0
  37. package/dist/default/lib/drag-step.js +99 -0
  38. package/dist/default/lib/envelope-curve.js +9 -0
  39. package/dist/default/lib/field.js +94 -0
  40. package/dist/default/lib/focus-by-pointer.js +28 -0
  41. package/dist/default/lib/frame-timeline.js +62 -0
  42. package/dist/default/lib/hold.js +38 -0
  43. package/dist/default/lib/marker-band.js +36 -0
  44. package/dist/default/lib/marker-rider.js +114 -0
  45. package/dist/default/lib/math.js +12 -3
  46. package/dist/default/lib/modifier-press.js +11 -0
  47. package/dist/default/lib/number-list.js +6 -6
  48. package/dist/default/lib/pointer-drag.js +43 -0
  49. package/dist/default/lib/render.js +20 -5
  50. package/dist/default/lib/resample-peaks.js +36 -0
  51. package/dist/default/lib/strip-scene.js +48 -0
  52. package/dist/default/lib/taper.js +23 -17
  53. package/dist/default/lib/time-regions.js +8 -0
  54. package/dist/default/lib/tracking-clock.js +35 -0
  55. package/dist/default/lib/value-mapping.js +115 -0
  56. package/dist/default/lib/waveform-buckets.js +41 -0
  57. package/dist/dev/dat.d.ts +19 -0
  58. package/dist/dev/dat.js +51 -0
  59. package/dist/dev/dat.js.map +1 -0
  60. package/dist/dev/define/button.d.ts +2 -0
  61. package/dist/dev/define/button.js +8 -0
  62. package/dist/dev/define/button.js.map +1 -0
  63. package/dist/dev/define/dial.js.map +1 -1
  64. package/dist/dev/define/envelope.d.ts +2 -0
  65. package/dist/dev/define/envelope.js +8 -0
  66. package/dist/dev/define/envelope.js.map +1 -0
  67. package/dist/dev/define/meter.js.map +1 -1
  68. package/dist/dev/define/number.js.map +1 -1
  69. package/dist/dev/define/segmented.js.map +1 -1
  70. package/dist/dev/define/slider.js.map +1 -1
  71. package/dist/dev/define/split.d.ts +2 -0
  72. package/dist/dev/define/split.js +8 -0
  73. package/dist/dev/define/split.js.map +1 -0
  74. package/dist/dev/define/switch.d.ts +2 -0
  75. package/dist/dev/define/switch.js +8 -0
  76. package/dist/dev/define/switch.js.map +1 -0
  77. package/dist/dev/define/waveform.d.ts +2 -0
  78. package/dist/dev/define/waveform.js +8 -0
  79. package/dist/dev/define/waveform.js.map +1 -0
  80. package/dist/dev/define/wavestrip.d.ts +2 -0
  81. package/dist/dev/define/wavestrip.js +8 -0
  82. package/dist/dev/define/wavestrip.js.map +1 -0
  83. package/dist/dev/define/xy.d.ts +2 -0
  84. package/dist/dev/define/xy.js +8 -0
  85. package/dist/dev/define/xy.js.map +1 -0
  86. package/dist/dev/define.d.ts +15 -3
  87. package/dist/dev/define.js +7 -1
  88. package/dist/dev/elements/{key.d.ts → button.d.ts} +8 -6
  89. package/dist/dev/elements/button.js +166 -0
  90. package/dist/dev/elements/button.js.map +1 -0
  91. package/dist/dev/elements/dial.d.ts +6 -4
  92. package/dist/dev/elements/dial.js +16 -22
  93. package/dist/dev/elements/dial.js.map +1 -1
  94. package/dist/dev/elements/envelope.d.ts +36 -0
  95. package/dist/dev/elements/envelope.js +372 -0
  96. package/dist/dev/elements/envelope.js.map +1 -0
  97. package/dist/dev/elements/form-element.d.ts +1 -1
  98. package/dist/dev/elements/form-element.js.map +1 -1
  99. package/dist/dev/elements/meter.d.ts +10 -5
  100. package/dist/dev/elements/meter.js +149 -61
  101. package/dist/dev/elements/meter.js.map +1 -1
  102. package/dist/dev/elements/number.d.ts +7 -3
  103. package/dist/dev/elements/number.js +21 -8
  104. package/dist/dev/elements/number.js.map +1 -1
  105. package/dist/dev/elements/radio-group.d.ts +25 -0
  106. package/dist/dev/elements/radio-group.js +186 -0
  107. package/dist/dev/elements/radio-group.js.map +1 -0
  108. package/dist/dev/elements/readout-claim.js +82 -0
  109. package/dist/dev/elements/readout-claim.js.map +1 -0
  110. package/dist/dev/elements/readout.js +36 -0
  111. package/dist/dev/elements/readout.js.map +1 -0
  112. package/dist/dev/elements/segmented.d.ts +4 -10
  113. package/dist/dev/elements/segmented.js +9 -159
  114. package/dist/dev/elements/segmented.js.map +1 -1
  115. package/dist/dev/elements/slider.d.ts +6 -6
  116. package/dist/dev/elements/slider.js +42 -51
  117. package/dist/dev/elements/slider.js.map +1 -1
  118. package/dist/dev/elements/sonic-element.d.ts +4 -3
  119. package/dist/dev/elements/sonic-element.js +51 -22
  120. package/dist/dev/elements/sonic-element.js.map +1 -1
  121. package/dist/dev/elements/split.d.ts +21 -0
  122. package/dist/dev/elements/split.js +258 -0
  123. package/dist/dev/elements/split.js.map +1 -0
  124. package/dist/dev/elements/switch.d.ts +35 -0
  125. package/dist/dev/elements/switch.js +280 -0
  126. package/dist/dev/elements/switch.js.map +1 -0
  127. package/dist/dev/elements/value-element.d.ts +84 -0
  128. package/dist/dev/elements/value-element.js +692 -0
  129. package/dist/dev/elements/value-element.js.map +1 -0
  130. package/dist/dev/elements/{range-entry.js → value-entry.js} +4 -4
  131. package/dist/dev/elements/value-entry.js.map +1 -0
  132. package/dist/dev/elements/wave-element.d.ts +36 -0
  133. package/dist/dev/elements/wave-element.js +78 -0
  134. package/dist/dev/elements/wave-element.js.map +1 -0
  135. package/dist/dev/elements/waveform.d.ts +55 -0
  136. package/dist/dev/elements/waveform.js +306 -0
  137. package/dist/dev/elements/waveform.js.map +1 -0
  138. package/dist/dev/elements/wavestrip.d.ts +51 -0
  139. package/dist/dev/elements/wavestrip.js +183 -0
  140. package/dist/dev/elements/wavestrip.js.map +1 -0
  141. package/dist/dev/elements/xy.d.ts +62 -0
  142. package/dist/dev/elements/xy.js +474 -0
  143. package/dist/dev/elements/xy.js.map +1 -0
  144. package/dist/dev/index.d.ts +11 -2
  145. package/dist/dev/index.js +10 -2
  146. package/dist/dev/lib/adsr-shape.js +109 -0
  147. package/dist/dev/lib/adsr-shape.js.map +1 -0
  148. package/dist/dev/lib/ballistics.js +56 -0
  149. package/dist/dev/lib/ballistics.js.map +1 -0
  150. package/dist/dev/lib/canvas-surface.d.ts +25 -0
  151. package/dist/dev/lib/canvas-surface.js +193 -0
  152. package/dist/dev/lib/canvas-surface.js.map +1 -0
  153. package/dist/dev/lib/copy-node.js +32 -2
  154. package/dist/dev/lib/copy-node.js.map +1 -1
  155. package/dist/dev/lib/crossfade.d.ts +12 -0
  156. package/dist/dev/lib/crossfade.js +23 -0
  157. package/dist/dev/lib/crossfade.js.map +1 -0
  158. package/dist/dev/lib/distribute.d.ts +4 -0
  159. package/dist/dev/lib/distribute.js +216 -0
  160. package/dist/dev/lib/distribute.js.map +1 -0
  161. package/dist/dev/lib/drag-step.js +100 -0
  162. package/dist/dev/lib/drag-step.js.map +1 -0
  163. package/dist/dev/lib/envelope-curve.d.ts +4 -0
  164. package/dist/dev/lib/envelope-curve.js +10 -0
  165. package/dist/dev/lib/envelope-curve.js.map +1 -0
  166. package/dist/dev/lib/field.d.ts +4 -0
  167. package/dist/dev/lib/field.js +95 -0
  168. package/dist/dev/lib/field.js.map +1 -0
  169. package/dist/dev/lib/focus-by-pointer.js +29 -0
  170. package/dist/dev/lib/focus-by-pointer.js.map +1 -0
  171. package/dist/dev/lib/frame-timeline.js +63 -0
  172. package/dist/dev/lib/frame-timeline.js.map +1 -0
  173. package/dist/dev/lib/hold.js +39 -0
  174. package/dist/dev/lib/hold.js.map +1 -0
  175. package/dist/dev/lib/marker-band.js +37 -0
  176. package/dist/dev/lib/marker-band.js.map +1 -0
  177. package/dist/dev/lib/marker-rider.js +115 -0
  178. package/dist/dev/lib/marker-rider.js.map +1 -0
  179. package/dist/dev/lib/math.js +12 -3
  180. package/dist/dev/lib/math.js.map +1 -1
  181. package/dist/dev/lib/modifier-press.js +12 -0
  182. package/dist/dev/lib/modifier-press.js.map +1 -0
  183. package/dist/dev/lib/number-list.js +6 -6
  184. package/dist/dev/lib/number-list.js.map +1 -1
  185. package/dist/dev/lib/pointer-drag.js +44 -0
  186. package/dist/dev/lib/pointer-drag.js.map +1 -0
  187. package/dist/dev/lib/read-px-property.js.map +1 -1
  188. package/dist/dev/lib/render.js +20 -5
  189. package/dist/dev/lib/render.js.map +1 -1
  190. package/dist/dev/lib/resample-peaks.js +37 -0
  191. package/dist/dev/lib/resample-peaks.js.map +1 -0
  192. package/dist/dev/lib/strip-scene.js +49 -0
  193. package/dist/dev/lib/strip-scene.js.map +1 -0
  194. package/dist/dev/lib/taper.js +23 -17
  195. package/dist/dev/lib/taper.js.map +1 -1
  196. package/dist/dev/lib/time-regions.d.ts +4 -0
  197. package/dist/dev/lib/time-regions.js +9 -0
  198. package/dist/dev/lib/time-regions.js.map +1 -0
  199. package/dist/dev/lib/tracking-clock.js +36 -0
  200. package/dist/dev/lib/tracking-clock.js.map +1 -0
  201. package/dist/dev/lib/value-mapping.d.ts +23 -0
  202. package/dist/dev/lib/value-mapping.js +116 -0
  203. package/dist/dev/lib/value-mapping.js.map +1 -0
  204. package/dist/dev/lib/waveform-buckets.d.ts +9 -0
  205. package/dist/dev/lib/waveform-buckets.js +42 -0
  206. package/dist/dev/lib/waveform-buckets.js.map +1 -0
  207. package/dist/skins/ivory.css +1 -2
  208. package/dist/styles/button.css +212 -0
  209. package/dist/styles/controls.css +8 -1
  210. package/dist/styles/dial.css +88 -42
  211. package/dist/styles/envelope.css +159 -0
  212. package/dist/styles/led.css +36 -5
  213. package/dist/styles/material.css +277 -76
  214. package/dist/styles/meter.css +33 -48
  215. package/dist/styles/number.css +5 -1
  216. package/dist/styles/panel.css +59 -0
  217. package/dist/styles/ring.css +74 -0
  218. package/dist/styles/segmented.css +21 -48
  219. package/dist/styles/slider.css +175 -39
  220. package/dist/styles/switch.css +347 -0
  221. package/dist/styles/waveform.css +173 -0
  222. package/dist/styles/wavestrip.css +215 -0
  223. package/dist/styles/xy.css +204 -0
  224. package/package.json +14 -2
  225. package/dist/default/define/key.js +0 -7
  226. package/dist/default/elements/key.js +0 -169
  227. package/dist/default/elements/range-element.js +0 -546
  228. package/dist/default/lib/detent.js +0 -17
  229. package/dist/dev/define/key.d.ts +0 -2
  230. package/dist/dev/define/key.js +0 -8
  231. package/dist/dev/define/key.js.map +0 -1
  232. package/dist/dev/elements/key.js +0 -170
  233. package/dist/dev/elements/key.js.map +0 -1
  234. package/dist/dev/elements/range-element.d.ts +0 -64
  235. package/dist/dev/elements/range-element.js +0 -547
  236. package/dist/dev/elements/range-element.js.map +0 -1
  237. package/dist/dev/elements/range-entry.js.map +0 -1
  238. package/dist/dev/lib/detent.js +0 -18
  239. package/dist/dev/lib/detent.js.map +0 -1
  240. package/dist/styles/key.css +0 -242
@@ -0,0 +1,192 @@
1
+ import { readPxProperty } from "./read-px-property.js";
2
+
3
+ //#region src/lib/canvas-surface.ts
4
+ function deviceRatio() {
5
+ return globalThis.devicePixelRatio || 1;
6
+ }
7
+ function deviceBox(entry, css) {
8
+ const device = "devicePixelContentBoxSize" in entry ? entry.devicePixelContentBoxSize[0] : void 0;
9
+ const dpr = deviceRatio();
10
+ if (!device || Math.abs(device.inlineSize - css.inlineSize * dpr) > 1) return void 0;
11
+ return Math.abs(device.blockSize - css.blockSize * dpr) > 1 ? void 0 : device;
12
+ }
13
+ function readEach(properties, read) {
14
+ return Object.fromEntries(Object.entries(properties).flatMap(([name, property]) => property === void 0 ? [] : [[name, read(property)]]));
15
+ }
16
+ var CanvasSurface = class {
17
+ get size() {
18
+ return this.#size;
19
+ }
20
+ #context;
21
+ #cssBox;
22
+ #frame;
23
+ #isDirty = true;
24
+ #isRebuildWanted = true;
25
+ #isVisible = true;
26
+ #look;
27
+ #options;
28
+ #reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
29
+ #size = {
30
+ dpr: deviceRatio(),
31
+ height: 0,
32
+ width: 0
33
+ };
34
+ constructor(options) {
35
+ const { canvas, signal } = options;
36
+ this.#options = options;
37
+ this.#context = canvas.getContext("2d") ?? void 0;
38
+ this.#armTransitions();
39
+ this.#observeSize();
40
+ this.#observeView();
41
+ this.#watchResolution();
42
+ this.#reducedMotion.addEventListener("change", () => {
43
+ this.rebuild();
44
+ }, { signal });
45
+ canvas.addEventListener("transitionrun", () => {
46
+ this.rebuild();
47
+ }, { signal });
48
+ signal.addEventListener("abort", () => {
49
+ if (this.#frame !== void 0) cancelAnimationFrame(this.#frame);
50
+ this.#frame = void 0;
51
+ }, { once: true });
52
+ this.#schedule();
53
+ }
54
+ invalidate() {
55
+ this.#isDirty = true;
56
+ this.#schedule();
57
+ }
58
+ rebuild() {
59
+ this.#isRebuildWanted = true;
60
+ this.invalidate();
61
+ }
62
+ requestFrame() {
63
+ this.#schedule();
64
+ }
65
+ #armTransitions() {
66
+ const { style } = this.#options.canvas;
67
+ style.setProperty("transition-property", Object.values(this.#options.colours).join(", "));
68
+ style.setProperty("transition-duration", "0.001ms");
69
+ style.setProperty("transition-timing-function", "step-start");
70
+ style.setProperty("transition-behavior", "allow-discrete");
71
+ }
72
+ #draw(frameMs) {
73
+ this.#frame = void 0;
74
+ const size = this.#size;
75
+ if (size.width === 0 || size.height === 0) return;
76
+ const { canvas, paint } = this.#options;
77
+ const look = this.#isRebuildWanted || !this.#look ? this.#read() : this.#look;
78
+ const isDirty = this.#isDirty;
79
+ this.#look = look;
80
+ this.#isDirty = false;
81
+ this.#isRebuildWanted = false;
82
+ if (canvas.width !== size.width) canvas.width = size.width;
83
+ if (canvas.height !== size.height) canvas.height = size.height;
84
+ if (this.#context) paint(this.#context, {
85
+ frameMs,
86
+ isDirty,
87
+ look,
88
+ size
89
+ });
90
+ }
91
+ #measure(entry) {
92
+ const [box] = entry.contentBoxSize;
93
+ if (!box) return;
94
+ const device = deviceBox(entry, box);
95
+ if (device) {
96
+ this.#cssBox = void 0;
97
+ this.#resize(device.inlineSize, device.blockSize);
98
+ return;
99
+ }
100
+ this.#cssBox = {
101
+ height: box.blockSize,
102
+ width: box.inlineSize
103
+ };
104
+ this.#rescale();
105
+ }
106
+ #observeSize() {
107
+ const { canvas, signal } = this.#options;
108
+ const observer = new ResizeObserver((entries) => {
109
+ const entry = entries.at(-1);
110
+ if (entry) this.#measure(entry);
111
+ });
112
+ try {
113
+ observer.observe(canvas, { box: "device-pixel-content-box" });
114
+ } catch {
115
+ observer.observe(canvas, { box: "content-box" });
116
+ }
117
+ signal.addEventListener("abort", () => {
118
+ observer.disconnect();
119
+ }, { once: true });
120
+ }
121
+ #observeView() {
122
+ const { canvas, signal } = this.#options;
123
+ const observer = new IntersectionObserver((entries) => {
124
+ const entry = entries.at(-1);
125
+ if (!entry) return;
126
+ const wasVisible = this.#isVisible;
127
+ this.#isVisible = entry.isIntersecting;
128
+ if (!entry.isIntersecting && this.#frame !== void 0) {
129
+ cancelAnimationFrame(this.#frame);
130
+ this.#frame = void 0;
131
+ }
132
+ if (!wasVisible && entry.isIntersecting) this.rebuild();
133
+ });
134
+ observer.observe(canvas);
135
+ signal.addEventListener("abort", () => {
136
+ observer.disconnect();
137
+ }, { once: true });
138
+ }
139
+ #read() {
140
+ const { canvas, colours, lengths = {} } = this.#options;
141
+ const styles = getComputedStyle(canvas);
142
+ return {
143
+ colours: readEach(colours, (property) => styles.getPropertyValue(property).trim()),
144
+ isReducedMotion: this.#reducedMotion.matches,
145
+ lengths: readEach(lengths, (property) => readPxProperty(styles, property, 0))
146
+ };
147
+ }
148
+ #rescale() {
149
+ const box = this.#cssBox;
150
+ if (!box) {
151
+ this.#resize(this.#size.width, this.#size.height);
152
+ return;
153
+ }
154
+ const dpr = deviceRatio();
155
+ this.#resize(Math.round(box.width * dpr), Math.round(box.height * dpr));
156
+ }
157
+ #resize(width, height) {
158
+ const previous = this.#size;
159
+ const dpr = deviceRatio();
160
+ if (previous.width === width && previous.height === height && previous.dpr === dpr) return;
161
+ this.#size = {
162
+ dpr,
163
+ height,
164
+ width
165
+ };
166
+ this.#options.resize?.(this.#size);
167
+ if (previous.width === 0 || previous.height === 0) this.rebuild();
168
+ else this.invalidate();
169
+ }
170
+ #schedule() {
171
+ if (this.#options.signal.aborted || this.#frame !== void 0 || !this.#isVisible) return;
172
+ this.#frame = requestAnimationFrame((frameMs) => {
173
+ this.#draw(frameMs);
174
+ });
175
+ }
176
+ #watchResolution() {
177
+ const { signal } = this.#options;
178
+ matchMedia(`(resolution: ${String(deviceRatio())}dppx)`).addEventListener("change", () => {
179
+ this.#watchResolution();
180
+ this.#rescale();
181
+ }, {
182
+ once: true,
183
+ signal
184
+ });
185
+ }
186
+ };
187
+ function bindSurface(options) {
188
+ return new CanvasSurface(options);
189
+ }
190
+
191
+ //#endregion
192
+ export { bindSurface };
@@ -2,14 +2,44 @@
2
2
  const copyPrefix = "sonic-copy-";
3
3
  const idListAttributes = /* @__PURE__ */ new Set(["aria-describedby", "aria-labelledby"]);
4
4
  const hrefAttributes = /* @__PURE__ */ new Set(["href", "xlink:href"]);
5
- const urlReference = /url\(\s*(['"]?)#([^'")\s]+)\1\s*\)/g;
5
+ const timingAttributes = /* @__PURE__ */ new Set(["begin", "end"]);
6
+ const urlAttributes = /* @__PURE__ */ new Set([
7
+ "clip-path",
8
+ "color-profile",
9
+ "fill",
10
+ "filter",
11
+ "marker-end",
12
+ "marker-mid",
13
+ "marker-start",
14
+ "mask",
15
+ "stroke",
16
+ "style"
17
+ ]);
18
+ const urlReference = /url\(\s*(['"]?)#([^'")\s]+)\1\s*\)/gi;
19
+ const idSelector = /#([\w-]+)(?=[^;{}]*\{)/g;
20
+ const timingReference = /(^|;\s*)([^\s;.]+)(?=\.[a-z])/gi;
21
+ function decoded(id) {
22
+ try {
23
+ return decodeURIComponent(id);
24
+ } catch {
25
+ return id;
26
+ }
27
+ }
28
+ function rewriteUrls(value, ids) {
29
+ return value.replaceAll(urlReference, (reference, quote, id) => ids.has(decoded(id)) ? `url(${quote}#${copyPrefix}${id}${quote})` : reference);
30
+ }
6
31
  function rewriteReferences(name, value, ids) {
7
32
  if (hrefAttributes.has(name)) return value.startsWith("#") && ids.has(value.slice(1)) ? `#${copyPrefix}${value.slice(1)}` : value;
8
33
  if (idListAttributes.has(name)) return value.split(/\s+/).map((id) => ids.has(id) ? copyPrefix + id : id).join(" ");
9
- return value.replaceAll(urlReference, (reference, quote, id) => ids.has(id) ? `url(${quote}#${copyPrefix}${id}${quote})` : reference);
34
+ if (timingAttributes.has(name)) return value.replaceAll(timingReference, (reference, lead, id) => ids.has(id) ? lead + copyPrefix + id : reference);
35
+ return urlAttributes.has(name) ? rewriteUrls(value, ids) : value;
36
+ }
37
+ function rewriteStyle(text, ids) {
38
+ return rewriteUrls(text, ids).replaceAll(idSelector, (selector, id) => ids.has(id) ? `#${copyPrefix}${id}` : selector);
10
39
  }
11
40
  function prefixIds(element, ids) {
12
41
  if (element.id !== "") element.id = copyPrefix + element.id;
42
+ if (element.localName === "style") element.textContent = rewriteStyle(element.textContent, ids);
13
43
  for (const attribute of element.attributes) {
14
44
  if (attribute.name === "id") continue;
15
45
  const value = rewriteReferences(attribute.name, attribute.value, ids);
@@ -0,0 +1,22 @@
1
+ import { clampProportion } from "./math.js";
2
+
3
+ //#region src/lib/crossfade.ts
4
+ function progress(side, span) {
5
+ if (span === 0) return side > 0 ? 1 : 0;
6
+ return Math.min(1, side / span);
7
+ }
8
+ function crossfadeGains(position, options = {}) {
9
+ const at = clampProportion(position);
10
+ const span = 1 - clampProportion(options.sharpness ?? 0);
11
+ const gain = (side) => {
12
+ const reached = progress(side, span);
13
+ return options.law === "linear" ? reached : Math.sin(reached * Math.PI / 2);
14
+ };
15
+ return {
16
+ a: gain(1 - at),
17
+ b: gain(at)
18
+ };
19
+ }
20
+
21
+ //#endregion
22
+ export { crossfadeGains };
@@ -0,0 +1,215 @@
1
+ import { clamp, trimFloat } from "./math.js";
2
+
3
+ //#region src/lib/distribute.ts
4
+ const epsilon = 1e-9;
5
+ function sumOf(values) {
6
+ return values.reduce((total, value) => total + value, 0);
7
+ }
8
+ function others(members, index) {
9
+ return members.filter((_member, at) => at !== index);
10
+ }
11
+ function splitLimits(members, index, total) {
12
+ const member = members[index];
13
+ if (!member) return [NaN, NaN];
14
+ const rest = others(members, index);
15
+ const free = rest.filter((other) => other.isFree);
16
+ if (free.length === 0) return [member.value, member.value];
17
+ const room = total - sumOf(rest.filter((other) => !other.isFree).map((other) => other.value));
18
+ return [Math.max(member.min, room - sumOf(free.map((other) => other.max))), Math.min(member.max, room - sumOf(free.map((other) => other.min)))];
19
+ }
20
+ function spreadWeighted(owed, pool) {
21
+ const shares = /* @__PURE__ */ new Map();
22
+ let remaining = owed;
23
+ let active = [...pool];
24
+ while (active.length > 0 && Math.abs(remaining) > epsilon) {
25
+ const weights = sumOf(active.map(({ weight }) => weight));
26
+ const count = active.length;
27
+ const shareOf = (entry) => remaining * (weights > 0 ? entry.weight / weights : 1 / count);
28
+ const full = active.filter((entry) => Math.abs(shareOf(entry)) >= entry.room);
29
+ if (full.length === 0) {
30
+ for (const entry of active) shares.set(entry.index, shareOf(entry));
31
+ break;
32
+ }
33
+ const sign = Math.sign(remaining);
34
+ for (const entry of full) {
35
+ shares.set(entry.index, sign * entry.room);
36
+ remaining -= sign * entry.room;
37
+ }
38
+ active = active.filter((entry) => !full.includes(entry));
39
+ }
40
+ return shares;
41
+ }
42
+ function spreadInOrder(owed, pool) {
43
+ const shares = /* @__PURE__ */ new Map();
44
+ let remaining = owed;
45
+ for (const entry of pool) {
46
+ if (Math.abs(remaining) <= epsilon) break;
47
+ const share = Math.sign(remaining) * Math.min(entry.room, Math.abs(remaining));
48
+ shares.set(entry.index, share);
49
+ remaining -= share;
50
+ }
51
+ return shares;
52
+ }
53
+ function poolOrder(members, index) {
54
+ const count = members.length;
55
+ const start = index + 1;
56
+ return Array.from({ length: count }, (_entry, offset) => (start + offset) % count).filter((at) => at !== index && members[at]?.isFree === true);
57
+ }
58
+ function stepWithin(member, from, owed) {
59
+ const next = member.stepFrom(from, owed > 0 ? 1 : -1);
60
+ const change = trimFloat(next - from);
61
+ const isInside = next >= member.min && next <= member.max;
62
+ return change !== 0 && isInside && Math.abs(change) <= Math.abs(owed) + epsilon ? next : void 0;
63
+ }
64
+ function stepToward(member, from, owed) {
65
+ let value = from;
66
+ let remainder = owed;
67
+ while (Math.abs(remainder) > epsilon) {
68
+ const next = stepWithin(member, value, remainder);
69
+ if (next === void 0) break;
70
+ remainder = trimFloat(remainder - (next - value));
71
+ value = next;
72
+ }
73
+ return value;
74
+ }
75
+ function byShortfall(values, { exact, order }, direction) {
76
+ const shortfall = (index) => {
77
+ const value = values[index] ?? NaN;
78
+ return trimFloat(direction * ((exact.get(index) ?? value) - value));
79
+ };
80
+ return order.toReversed().sort((first, second) => shortfall(second) - shortfall(first));
81
+ }
82
+ function stepEach(members, values, pass) {
83
+ let remainder = pass.owed;
84
+ for (const index of pass.neediest) {
85
+ const member = members[index];
86
+ const value = values[index];
87
+ const next = member && value !== void 0 ? stepWithin(member, value, remainder) : void 0;
88
+ if (value === void 0 || next === void 0) continue;
89
+ values[index] = next;
90
+ remainder = trimFloat(remainder - (next - value));
91
+ }
92
+ return remainder;
93
+ }
94
+ function settle(members, values, settling) {
95
+ let remainder = trimFloat(settling.total - sumOf(values));
96
+ let before = NaN;
97
+ while (remainder !== before && Math.abs(remainder) > epsilon) {
98
+ before = remainder;
99
+ remainder = stepEach(members, values, {
100
+ neediest: byShortfall(values, settling, Math.sign(remainder)),
101
+ owed: remainder
102
+ });
103
+ }
104
+ return remainder;
105
+ }
106
+ function pooled(members, order, owed) {
107
+ return order.flatMap((at) => {
108
+ const member = members[at];
109
+ if (!member) return [];
110
+ const room = owed > 0 ? member.max - member.value : member.value - member.min;
111
+ return [{
112
+ index: at,
113
+ room: Math.max(0, room),
114
+ weight: member.value - member.min
115
+ }];
116
+ });
117
+ }
118
+ function shares(pool, owed, mode) {
119
+ if (mode === "cascade") return spreadInOrder(owed, pool);
120
+ if (mode === "equal") return spreadWeighted(owed, pool.map((entry) => ({
121
+ ...entry,
122
+ weight: 1
123
+ })));
124
+ return spreadWeighted(owed, pool);
125
+ }
126
+ function landMover(members, values, move) {
127
+ const { index, target, total } = move;
128
+ const mover = members[index];
129
+ if (!mover?.isFree || !Number.isFinite(target)) return void 0;
130
+ const next = mover.snap(clamp(target, ...splitLimits(members, index, total)));
131
+ values[index] = next;
132
+ return mover.value - next;
133
+ }
134
+ function landShares(members, values, spread) {
135
+ const exact = /* @__PURE__ */ new Map();
136
+ for (const [at, share] of spread) {
137
+ const member = members[at];
138
+ if (!member) continue;
139
+ exact.set(at, member.value + share);
140
+ values[at] = clamp(member.snap(member.value + share), member.min, member.max);
141
+ }
142
+ return exact;
143
+ }
144
+ function spread(members, rule, move) {
145
+ const values = members.map(({ value }) => value);
146
+ const owed = move ? landMover(members, values, {
147
+ ...move,
148
+ total: rule.total
149
+ }) : rule.total - sumOf(values);
150
+ if (owed === void 0) return void 0;
151
+ const order = poolOrder(members, move?.index ?? -1);
152
+ return {
153
+ left: settle(members, values, {
154
+ exact: landShares(members, values, shares(pooled(members, order, owed), owed, rule.mode)),
155
+ order,
156
+ total: rule.total
157
+ }),
158
+ order,
159
+ values
160
+ };
161
+ }
162
+ function smallestStep(members, { left, order, values }) {
163
+ return order.flatMap((at) => {
164
+ const member = members[at];
165
+ const value = values[at];
166
+ if (!member || value === void 0) return [];
167
+ const next = member.stepFrom(value, left > 0 ? 1 : -1);
168
+ return next >= member.min && next <= member.max && next !== value ? [trimFloat(next - value)] : [];
169
+ }).toSorted((one, other) => Math.abs(one) - Math.abs(other))[0];
170
+ }
171
+ function moverTargets(members, landing, index) {
172
+ const mover = members[index];
173
+ const landed = landing.values[index];
174
+ if (!mover || landed === void 0) return [];
175
+ const step = smallestStep(members, landing);
176
+ const held = stepToward(mover, landed, landing.left);
177
+ return step === void 0 ? [held] : [held, stepToward(mover, landed, trimFloat(landing.left - step))];
178
+ }
179
+ function nearest(candidates, move, from) {
180
+ const { direction = 0, index } = move;
181
+ const moverOf = (candidate) => candidate[index] ?? NaN;
182
+ const [first, ...rest] = candidates.toSorted((one, other) => Math.abs(moverOf(one) - from.landed) - Math.abs(moverOf(other) - from.landed));
183
+ if (!first || direction === 0 || moverOf(first) !== from.start) return first;
184
+ return rest.find((candidate) => Math.sign(moverOf(candidate) - from.start) === direction) ?? first;
185
+ }
186
+ function close(members, rule, held) {
187
+ const { landing, move, mover } = held;
188
+ const { index } = move;
189
+ const landed = landing.values[index] ?? mover.value;
190
+ const targets = moverTargets(members, landing, index);
191
+ return nearest(targets.flatMap((target) => {
192
+ const attempt = spread(members, rule, {
193
+ index,
194
+ target
195
+ });
196
+ return attempt && Math.abs(attempt.left) <= epsilon ? [attempt.values] : [];
197
+ }), move, {
198
+ landed,
199
+ start: mover.value
200
+ }) ?? landing.values.with(index, targets[0] ?? landed);
201
+ }
202
+ function distribute(members, rule, move) {
203
+ const landing = spread(members, rule, move);
204
+ if (!landing) return members.map(({ value }) => value);
205
+ const mover = members[move?.index ?? -1];
206
+ if (!move || !mover || Math.abs(landing.left) <= epsilon) return landing.values;
207
+ return close(members, rule, {
208
+ landing,
209
+ move,
210
+ mover
211
+ });
212
+ }
213
+
214
+ //#endregion
215
+ export { distribute, splitLimits };
@@ -0,0 +1,99 @@
1
+ import { clampProportion, wrapProportion } from "./math.js";
2
+
3
+ //#region src/lib/drag-step.ts
4
+ const fineFactor = .1;
5
+ const mouseDragThresholdPx = 3;
6
+ const touchDragThresholdPx = 7;
7
+ function dragThresholdPx(pointerType) {
8
+ return pointerType === "touch" ? touchDragThresholdPx : mouseDragThresholdPx;
9
+ }
10
+ function isAcross(from, to, proportion) {
11
+ return from !== proportion && (from - proportion) * (to - proportion) <= 0;
12
+ }
13
+ function passDetent(hold, from, to) {
14
+ if (hold.slack === void 0 && !isAcross(from, to, hold.proportion)) return {
15
+ proportion: to,
16
+ slack: void 0
17
+ };
18
+ const slack = (hold.slack ?? 0) + to - hold.proportion;
19
+ if (Math.abs(slack) <= hold.zone) return {
20
+ proportion: void 0,
21
+ slack
22
+ };
23
+ return {
24
+ proportion: hold.proportion + slack - Math.sign(slack) * hold.zone,
25
+ slack: void 0
26
+ };
27
+ }
28
+ function nearestTurn(hold, from) {
29
+ return {
30
+ ...hold,
31
+ proportion: hold.proportion + Math.round(from - hold.proportion)
32
+ };
33
+ }
34
+ function land(mapping, state, proportion) {
35
+ const rawProportion = mapping.isWrapping ? wrapProportion(proportion) : clampProportion(proportion);
36
+ return {
37
+ state: {
38
+ ...state,
39
+ rawProportion
40
+ },
41
+ value: mapping.snap(mapping.valueAt(rawProportion))
42
+ };
43
+ }
44
+ function startDrag(mapping, start) {
45
+ const { detent: wanted, from, position, proportion, ...fixed } = start;
46
+ const state = {
47
+ ...fixed,
48
+ isEngaged: false,
49
+ lastPosition: position,
50
+ rawProportion: proportion,
51
+ startPosition: position
52
+ };
53
+ if (!wanted) return state;
54
+ const value = mapping.snap(wanted.value);
55
+ const detent = {
56
+ proportion: mapping.proportionOf(value),
57
+ slack: value === from ? 0 : void 0,
58
+ value,
59
+ zone: wanted.zone
60
+ };
61
+ return {
62
+ ...state,
63
+ detent
64
+ };
65
+ }
66
+ function stepDrag(mapping, state, move) {
67
+ if (!(state.isEngaged || move.isOutside || Math.abs(move.position - state.startPosition) >= state.thresholdPx)) return {
68
+ state,
69
+ value: void 0
70
+ };
71
+ const pace = move.isFine ? fineFactor : 1;
72
+ const to = state.rawProportion + (move.position - state.lastPosition) / state.travelPx * pace;
73
+ const moved = {
74
+ ...state,
75
+ isEngaged: true,
76
+ lastPosition: move.position
77
+ };
78
+ const { detent } = state;
79
+ if (!detent) return land(mapping, moved, to);
80
+ const passed = passDetent(mapping.isWrapping ? nearestTurn(detent, state.rawProportion) : detent, state.rawProportion, to);
81
+ const held = {
82
+ ...moved,
83
+ detent: {
84
+ ...detent,
85
+ slack: passed.slack
86
+ }
87
+ };
88
+ if (passed.proportion === void 0) return {
89
+ state: {
90
+ ...held,
91
+ rawProportion: detent.proportion
92
+ },
93
+ value: detent.value
94
+ };
95
+ return land(mapping, held, passed.proportion);
96
+ }
97
+
98
+ //#endregion
99
+ export { dragThresholdPx, startDrag, stepDrag };
@@ -0,0 +1,9 @@
1
+ //#region src/lib/envelope-curve.ts
2
+ const flatCurve = .001;
3
+ function envelopeCurve(position, curve) {
4
+ if (Math.abs(curve) < flatCurve) return position;
5
+ return Math.expm1(curve * position) / Math.expm1(curve);
6
+ }
7
+
8
+ //#endregion
9
+ export { envelopeCurve };
@@ -0,0 +1,94 @@
1
+ import { startDrag, stepDrag } from "./drag-step.js";
2
+
3
+ //#region src/lib/field.ts
4
+ function startAxis(start, position) {
5
+ if (!start) return void 0;
6
+ const { mapping, ...rest } = start;
7
+ return startDrag(mapping, {
8
+ ...rest,
9
+ position,
10
+ thresholdPx: 0
11
+ });
12
+ }
13
+ function startFieldDrag(start) {
14
+ const { position, thresholdPx } = start;
15
+ return {
16
+ isEngaged: false,
17
+ lock: void 0,
18
+ startPosition: position,
19
+ thresholdPx,
20
+ x: startAxis(start.x, position.x),
21
+ y: startAxis(start.y, position.y)
22
+ };
23
+ }
24
+ function dominantAxis(state, move) {
25
+ if (!state.x) return "y";
26
+ if (!state.y) return "x";
27
+ const { startPosition } = state;
28
+ return Math.abs(move.x - startPosition.x) >= Math.abs(move.y - startPosition.y) ? "x" : "y";
29
+ }
30
+ function stepAxis(mapping, drag, move) {
31
+ if (!mapping || !drag) return {
32
+ state: drag,
33
+ value: void 0
34
+ };
35
+ if (move.isHeld) return {
36
+ state: {
37
+ ...drag,
38
+ lastPosition: move.position
39
+ },
40
+ value: void 0
41
+ };
42
+ return stepDrag(mapping, drag, {
43
+ isFine: move.isFine,
44
+ isOutside: false,
45
+ position: move.position
46
+ });
47
+ }
48
+ function stepFieldDrag(mappings, state, move) {
49
+ const { startPosition } = state;
50
+ if (!(state.isEngaged || Math.hypot(move.x - startPosition.x, move.y - startPosition.y) >= state.thresholdPx)) return {
51
+ state,
52
+ x: void 0,
53
+ y: void 0
54
+ };
55
+ const lock = move.isLocked ? state.lock ?? dominantAxis(state, move) : void 0;
56
+ const { isFine } = move;
57
+ const x = stepAxis(mappings.x, state.x, {
58
+ isFine,
59
+ isHeld: lock === "y",
60
+ position: move.x
61
+ });
62
+ const y = stepAxis(mappings.y, state.y, {
63
+ isFine,
64
+ isHeld: lock === "x",
65
+ position: move.y
66
+ });
67
+ return {
68
+ state: {
69
+ ...state,
70
+ isEngaged: true,
71
+ lock,
72
+ x: x.state,
73
+ y: y.state
74
+ },
75
+ x: x.value,
76
+ y: y.value
77
+ };
78
+ }
79
+ function pointerPosition(event) {
80
+ return {
81
+ x: event.clientX,
82
+ y: -event.clientY
83
+ };
84
+ }
85
+ function pointerMove(event) {
86
+ return {
87
+ ...pointerPosition(event),
88
+ isFine: event.shiftKey,
89
+ isLocked: event.altKey
90
+ };
91
+ }
92
+
93
+ //#endregion
94
+ export { pointerMove, pointerPosition, startFieldDrag, stepFieldDrag };