@phin-tech/dottie 0.5.0 → 0.5.2
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/PARITY.md +7 -2
- package/README.md +10 -3
- package/dist/compile.js +103 -5
- package/dist/dottie-compile.mjs +104 -6
- package/dist/dottie-player.js +1188 -655
- package/dist/dottie-player.min.js +61 -41
- package/dist/dottie.js +1187 -655
- package/dist/types/clicks.d.ts +30 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/model.d.ts +39 -1
- package/dist/types/player.d.ts +35 -0
- package/dist/types/symbols.d.ts +23 -0
- package/dist/types/timeline.d.ts +4 -1
- package/package.json +1 -1
- package/dist/types/cli.d.ts +0 -1
package/PARITY.md
CHANGED
|
@@ -38,6 +38,11 @@ fixture, so the gap is visible rather than silent.
|
|
|
38
38
|
| Damaged timelines (clamped, filtered, sorted) | Supported | [edge/timeline](../fixtures/edge/timeline.dottie) | Read the way DottieKit's decoders read them |
|
|
39
39
|
| Unknown kinds, fields and newer versions | Partial | [js-only/future](../fixtures/js-only/future.dottie) | Drawn as labelled placeholders, reported in `warnings`; Dottie refuses such files |
|
|
40
40
|
| Text fonts | Partial | | Positions use Helvetica Neue's metrics; browsers without Helvetica Neue draw Helvetica or Arial in the same places, so runs of mixed styles may sit slightly apart |
|
|
41
|
-
|
|
|
42
|
-
|
|
|
41
|
+
| Symbols: instances on their own clocks (loop, once, still, offset), nested, scaled and turned | Supported | [edge/symbols](../fixtures/edge/symbols.dottie) | Expanded as DottieKit expands them, with the same ids, seeds and limits ([js-only/hostile-symbols](../fixtures/js-only/hostile-symbols.dottie)) |
|
|
42
|
+
| Button symbols (up, over, down, hit) and clickable copies | Supported | [edge/buttons](../fixtures/edge/buttons.dottie) | As the HTML export draws them |
|
|
43
|
+
| Symbol parameters (`{{name}}` in a symbol's text, filled per copy) | Supported | [edge/params](../fixtures/edge/params.dottie) | Defaults, undeclared names left as written, values passed into nested copies, filled before text types out |
|
|
44
|
+
| Copies that play when clicked | Supported | [edge/buttons](../fixtures/edge/buttons.dottie) | `<dottie-player>` only: the HTML export is drawn ahead of time and shows them at rest |
|
|
45
|
+
| Clicks: play a scene, open a link, hover looks, keyboard | Supported | [edge/clicks](../fixtures/edge/clicks.dottie) | As the HTML export does; only http, https and mailto links are followed |
|
|
46
|
+
| Links between shapes and pages | Partial | [edge/clicks](../fixtures/edge/clicks.dottie) | A link to a shape on another page shows that page; one on the same page does nothing. The HTML export holds one page, so it has neither |
|
|
47
|
+
| Editing | Missing | | |
|
|
43
48
|
| Ticking task boxes in notes | Missing | | Drawn, not clickable |
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dottie.js
|
|
2
2
|
|
|
3
|
-
`npm install @phin-tech/dottie` · [Live demos](https://phin
|
|
3
|
+
`npm install @phin-tech/dottie` · [Live demos](https://dottie.phin.tech/dottie-js/)
|
|
4
4
|
|
|
5
5
|
Render and play [Dottie](https://github.com/phin-tech/dottie) drawings in the browser, straight from the
|
|
6
6
|
`.dottie` file: stills or animations, sketchy or clean, with no pre-rendered frames. It draws what Dottie draws:
|
|
@@ -29,8 +29,15 @@ prefer reduced motion. A still page is just the drawing.
|
|
|
29
29
|
| `no-controls`, `no-autoplay`, `no-loop` | |
|
|
30
30
|
| `label` | The accessible name; the page name by default |
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
Elements the drawing makes clickable work with the mouse and the keyboard (Tab, then Enter or Space). A click plays
|
|
33
|
+
a named scene on to the next one, opens a link in a new tab (only `http`, `https` and `mailto`), or shows the page a
|
|
34
|
+
shape's link points at. Each shows its hover look under the pointer; button symbols show their over and down looks
|
|
35
|
+
and take clicks on their hit area, and a symbol's copy set to play when clicked plays its animation through. Every
|
|
36
|
+
click first fires a cancelable `action` event whose `detail` is `{element, scene | url | page, replay?}`; call
|
|
37
|
+
`preventDefault()` to handle it yourself.
|
|
38
|
+
|
|
39
|
+
The element has `play()`, `pause()`, `nextScene()`, `prevScene()`, `activate(elementId)`, `currentTime`
|
|
40
|
+
(settable), `duration`, `paused`, and fires `load`, `frame`, `action` and `error` events. Its page color comes from the drawing's theme; set
|
|
34
41
|
`--dottie-radius` to round it, and style its parts with `::part(stage)` and `::part(controls)`.
|
|
35
42
|
|
|
36
43
|
Without modules, `dist/dottie-player.min.js` is a classic script that registers the element and sets
|
package/dist/compile.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
/*! dottie.js 0.5.
|
|
1
|
+
/*! dottie.js 0.5.2 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
|
|
6
6
|
// src/model.ts
|
|
7
7
|
var SUPPORTED_VERSION = 2;
|
|
8
|
-
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image"];
|
|
8
|
+
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
9
9
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
10
10
|
var KNOWN_FIELDS = {
|
|
11
11
|
file: ["version", "pages", "images"],
|
|
12
|
-
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline"],
|
|
12
|
+
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
|
|
13
|
+
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
13
14
|
layer: ["id", "name", "visible", "locked"],
|
|
14
15
|
rgba: ["r", "g", "b", "a"],
|
|
15
16
|
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "gridSize", "opacity", "dashOffset"],
|
|
@@ -33,6 +34,12 @@ var KNOWN_FIELDS = {
|
|
|
33
34
|
"rotation",
|
|
34
35
|
"link",
|
|
35
36
|
"anchor",
|
|
37
|
+
"click",
|
|
38
|
+
"hover",
|
|
39
|
+
"symbol",
|
|
40
|
+
"playback",
|
|
41
|
+
"offset",
|
|
42
|
+
"args",
|
|
36
43
|
"image",
|
|
37
44
|
"reveal"
|
|
38
45
|
],
|
|
@@ -47,7 +54,9 @@ var KNOWN_FIELDS = {
|
|
|
47
54
|
follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
|
|
48
55
|
repeat: ["start", "end", "count"],
|
|
49
56
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
50
|
-
marker: ["t", "name"]
|
|
57
|
+
marker: ["t", "name"],
|
|
58
|
+
click: ["scene", "url"],
|
|
59
|
+
hover: ["stroke", "fill", "opacity"]
|
|
51
60
|
};
|
|
52
61
|
var MAX_DURATION = 3600;
|
|
53
62
|
var MAX_MARCH = 1e4;
|
|
@@ -59,6 +68,7 @@ var DottieFormatError = class extends Error {
|
|
|
59
68
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
60
69
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
61
70
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
71
|
+
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
62
72
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
63
73
|
var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
|
|
64
74
|
function clampTime(t) {
|
|
@@ -68,6 +78,11 @@ function clampTime(t) {
|
|
|
68
78
|
function clampFPS(f) {
|
|
69
79
|
return Number.isFinite(f) ? Math.min(Math.max(f, 1), 60) : 12;
|
|
70
80
|
}
|
|
81
|
+
function allowedURL(url) {
|
|
82
|
+
const u = url.trim();
|
|
83
|
+
if (!/^(https?:\/\/|mailto:)/i.test(u) || [...u].length <= 7 || /[\u0000-\u001f\u007f-\u009f]/.test(u)) return null;
|
|
84
|
+
return u;
|
|
85
|
+
}
|
|
71
86
|
function rgba(v) {
|
|
72
87
|
if (!isObj(v)) return void 0;
|
|
73
88
|
const r = num(v.r), g = num(v.g), b = num(v.b);
|
|
@@ -182,10 +197,63 @@ var Reader = class {
|
|
|
182
197
|
rotation: num(v.rotation) ?? null,
|
|
183
198
|
link: uuid(v.link) ?? null,
|
|
184
199
|
anchor: str(v.anchor) ?? null,
|
|
200
|
+
click: this.click(v.click),
|
|
201
|
+
hover: this.hover(v.hover),
|
|
202
|
+
symbol: uuid(v.symbol) ?? null,
|
|
203
|
+
playback: this.playback(v.playback),
|
|
204
|
+
offset: num(v.offset) ?? null,
|
|
205
|
+
args: strings(v.args),
|
|
185
206
|
image: this.image(v.image, images),
|
|
186
207
|
reveal: num(v.reveal) ?? null
|
|
187
208
|
};
|
|
188
209
|
}
|
|
210
|
+
click(v) {
|
|
211
|
+
if (!isObj(v)) return null;
|
|
212
|
+
this.fields(v, "click");
|
|
213
|
+
const url = str(v.url);
|
|
214
|
+
const safe = url === void 0 ? null : allowedURL(url);
|
|
215
|
+
if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
|
|
216
|
+
return { scene: str(v.scene) ?? null, url: safe };
|
|
217
|
+
}
|
|
218
|
+
playback(v) {
|
|
219
|
+
const s = str(v);
|
|
220
|
+
if (s === void 0) return null;
|
|
221
|
+
return ["loop", "once", "still", "click"].includes(s) ? s : "loop";
|
|
222
|
+
}
|
|
223
|
+
symbol(v, images) {
|
|
224
|
+
if (!isObj(v)) return null;
|
|
225
|
+
this.fields(v, "symbol");
|
|
226
|
+
const id = uuid(v.id);
|
|
227
|
+
if (!id) {
|
|
228
|
+
this.warn("Skipped a symbol without an id");
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
const size = (x) => {
|
|
232
|
+
const n = num(x);
|
|
233
|
+
return Math.max(1, n ?? 100);
|
|
234
|
+
};
|
|
235
|
+
const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
|
|
236
|
+
const lid = uuid(l.id);
|
|
237
|
+
return lid ? [{ id: lid, name: str(l.name) ?? "", visible: bool(l.visible) ?? true, locked: bool(l.locked) ?? false }] : [];
|
|
238
|
+
});
|
|
239
|
+
if (layers.length === 0) layers.push({ id: `${id}-LAYER`, name: "Layer 1", visible: true, locked: false });
|
|
240
|
+
return {
|
|
241
|
+
id,
|
|
242
|
+
name: str(v.name) ?? "Symbol",
|
|
243
|
+
width: size(v.width),
|
|
244
|
+
height: size(v.height),
|
|
245
|
+
layers,
|
|
246
|
+
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
247
|
+
timeline: this.timeline(v.timeline),
|
|
248
|
+
button: bool(v.button) ?? null,
|
|
249
|
+
params: strings(v.params)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
hover(v) {
|
|
253
|
+
if (!isObj(v)) return null;
|
|
254
|
+
this.fields(v, "hover");
|
|
255
|
+
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num(v.opacity) ?? null };
|
|
256
|
+
}
|
|
189
257
|
ease(v) {
|
|
190
258
|
const s = str(v);
|
|
191
259
|
if (s === void 0) return null;
|
|
@@ -278,7 +346,8 @@ var Reader = class {
|
|
|
278
346
|
layers,
|
|
279
347
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
280
348
|
theme: str(v.theme) ?? null,
|
|
281
|
-
timeline: this.timeline(v.timeline)
|
|
349
|
+
timeline: this.timeline(v.timeline),
|
|
350
|
+
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
|
|
282
351
|
};
|
|
283
352
|
}
|
|
284
353
|
};
|
|
@@ -318,6 +387,19 @@ function parseDottie(source) {
|
|
|
318
387
|
return { version, pages, warnings: reader.warnings };
|
|
319
388
|
}
|
|
320
389
|
|
|
390
|
+
// src/clicks.ts
|
|
391
|
+
var CLICK_STYLE = `
|
|
392
|
+
.dottie-a { cursor: pointer; outline: none; }
|
|
393
|
+
.dottie-a .dottie-h { display: none; }
|
|
394
|
+
.dottie-a:hover .dottie-h, .dottie-a.dottie-hover .dottie-h, .dottie-a:focus-visible .dottie-h { display: inline; }
|
|
395
|
+
.dottie-a:hover .dottie-n, .dottie-a.dottie-hover .dottie-n, .dottie-a:focus-visible .dottie-n { display: none; }
|
|
396
|
+
.dottie-a:focus-visible { filter: drop-shadow(0 0 3px var(--dottie-focus, #3b82f6)); }
|
|
397
|
+
.dottie-a:not(.dottie-b):active, .dottie-a.dottie-press:not(.dottie-b) { transform: translate(1px, 1px); }
|
|
398
|
+
.dottie-a .dottie-d { display: none; }
|
|
399
|
+
.dottie-b:active .dottie-n, .dottie-b:active .dottie-h, .dottie-b.dottie-press .dottie-n, .dottie-b.dottie-press .dottie-h { display: none; }
|
|
400
|
+
.dottie-b:active .dottie-d, .dottie-b.dottie-press .dottie-d { display: inline; }
|
|
401
|
+
`;
|
|
402
|
+
|
|
321
403
|
// src/player.ts
|
|
322
404
|
function pageIndex(file, spec) {
|
|
323
405
|
const s = (spec ?? "").trim();
|
|
@@ -327,6 +409,22 @@ function pageIndex(file, spec) {
|
|
|
327
409
|
const n = Number(s);
|
|
328
410
|
return Number.isInteger(n) && n >= 1 && n <= file.pages.length ? n - 1 : 0;
|
|
329
411
|
}
|
|
412
|
+
var STYLE = `
|
|
413
|
+
:host { display: block; position: relative; color: var(--dottie-ink); background: var(--dottie-page);
|
|
414
|
+
font: 14px -apple-system, system-ui, sans-serif; border-radius: var(--dottie-radius, 0); }
|
|
415
|
+
:host([hidden]) { display: none; }
|
|
416
|
+
.stage { display: block; width: 100%; height: auto; }
|
|
417
|
+
.stage svg { display: block; width: 100%; height: auto; margin: 0 auto; }
|
|
418
|
+
.bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px 12px; max-width: 560px; margin: 0 auto; box-sizing: border-box; }
|
|
419
|
+
.bar[hidden] { display: none; }
|
|
420
|
+
button { font: inherit; color: inherit; background: none; border: 1.5px solid currentColor; border-radius: 6px;
|
|
421
|
+
width: 34px; height: 30px; cursor: pointer; flex: none; padding: 0; }
|
|
422
|
+
button:focus-visible, input:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
423
|
+
input[type=range] { flex: 1; min-width: 60px; accent-color: currentColor; }
|
|
424
|
+
output { font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: right; }
|
|
425
|
+
.scene { min-width: 0; flex: 0 1 auto; text-align: center; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
426
|
+
.error { padding: 16px; font-family: ui-monospace, Menlo, monospace; white-space: pre-wrap; }
|
|
427
|
+
${CLICK_STYLE}`;
|
|
330
428
|
|
|
331
429
|
// src/compile.ts
|
|
332
430
|
var LOADER = `;(function () {
|
package/dist/dottie-compile.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
/*! dottie.js 0.5.
|
|
2
|
+
/*! dottie.js 0.5.2 | Apache-2.0 | https://github.com/phin-tech/dottie */
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5
5
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -10,11 +10,12 @@ import { basename } from "node:path";
|
|
|
10
10
|
|
|
11
11
|
// src/model.ts
|
|
12
12
|
var SUPPORTED_VERSION = 2;
|
|
13
|
-
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image"];
|
|
13
|
+
var KINDS = ["rectangle", "ellipse", "diamond", "text", "connector", "freehand", "path", "note", "image", "symbol"];
|
|
14
14
|
var EASES = ["linear", "easeIn", "easeOut", "easeInOut", "hold", "spring", "bounce", "elastic"];
|
|
15
15
|
var KNOWN_FIELDS = {
|
|
16
16
|
file: ["version", "pages", "images"],
|
|
17
|
-
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline"],
|
|
17
|
+
page: ["id", "name", "layers", "elements", "theme", "guidesX", "guidesY", "guidesLocked", "timeline", "symbols"],
|
|
18
|
+
symbol: ["id", "name", "width", "height", "layers", "elements", "timeline", "button", "params"],
|
|
18
19
|
layer: ["id", "name", "visible", "locked"],
|
|
19
20
|
rgba: ["r", "g", "b", "a"],
|
|
20
21
|
style: ["stroke", "fill", "fillStyle", "strokeWidth", "dash", "roughness", "bowing", "fontSize", "gridSize", "opacity", "dashOffset"],
|
|
@@ -38,6 +39,12 @@ var KNOWN_FIELDS = {
|
|
|
38
39
|
"rotation",
|
|
39
40
|
"link",
|
|
40
41
|
"anchor",
|
|
42
|
+
"click",
|
|
43
|
+
"hover",
|
|
44
|
+
"symbol",
|
|
45
|
+
"playback",
|
|
46
|
+
"offset",
|
|
47
|
+
"args",
|
|
41
48
|
"image",
|
|
42
49
|
"reveal"
|
|
43
50
|
],
|
|
@@ -52,7 +59,9 @@ var KNOWN_FIELDS = {
|
|
|
52
59
|
follow: ["path", "start", "end", "ease", "reverse", "orient", "laps"],
|
|
53
60
|
repeat: ["start", "end", "count"],
|
|
54
61
|
cameraKey: ["t", "x", "y", "width", "height", "ease"],
|
|
55
|
-
marker: ["t", "name"]
|
|
62
|
+
marker: ["t", "name"],
|
|
63
|
+
click: ["scene", "url"],
|
|
64
|
+
hover: ["stroke", "fill", "opacity"]
|
|
56
65
|
};
|
|
57
66
|
var MAX_DURATION = 3600;
|
|
58
67
|
var MAX_MARCH = 1e4;
|
|
@@ -64,6 +73,7 @@ var DottieFormatError = class extends Error {
|
|
|
64
73
|
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
65
74
|
var num = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
66
75
|
var str = (v) => typeof v === "string" ? v : void 0;
|
|
76
|
+
var strings = (v) => isObj(v) && Object.values(v).every((x) => typeof x === "string") ? { ...v } : null;
|
|
67
77
|
var bool = (v) => typeof v === "boolean" ? v : void 0;
|
|
68
78
|
var uuid = (v) => typeof v === "string" ? v.toUpperCase() : void 0;
|
|
69
79
|
function clampTime(t) {
|
|
@@ -73,6 +83,11 @@ function clampTime(t) {
|
|
|
73
83
|
function clampFPS(f) {
|
|
74
84
|
return Number.isFinite(f) ? Math.min(Math.max(f, 1), 60) : 12;
|
|
75
85
|
}
|
|
86
|
+
function allowedURL(url) {
|
|
87
|
+
const u = url.trim();
|
|
88
|
+
if (!/^(https?:\/\/|mailto:)/i.test(u) || [...u].length <= 7 || /[\u0000-\u001f\u007f-\u009f]/.test(u)) return null;
|
|
89
|
+
return u;
|
|
90
|
+
}
|
|
76
91
|
function rgba(v) {
|
|
77
92
|
if (!isObj(v)) return void 0;
|
|
78
93
|
const r = num(v.r), g = num(v.g), b = num(v.b);
|
|
@@ -187,10 +202,63 @@ var Reader = class {
|
|
|
187
202
|
rotation: num(v.rotation) ?? null,
|
|
188
203
|
link: uuid(v.link) ?? null,
|
|
189
204
|
anchor: str(v.anchor) ?? null,
|
|
205
|
+
click: this.click(v.click),
|
|
206
|
+
hover: this.hover(v.hover),
|
|
207
|
+
symbol: uuid(v.symbol) ?? null,
|
|
208
|
+
playback: this.playback(v.playback),
|
|
209
|
+
offset: num(v.offset) ?? null,
|
|
210
|
+
args: strings(v.args),
|
|
190
211
|
image: this.image(v.image, images),
|
|
191
212
|
reveal: num(v.reveal) ?? null
|
|
192
213
|
};
|
|
193
214
|
}
|
|
215
|
+
click(v) {
|
|
216
|
+
if (!isObj(v)) return null;
|
|
217
|
+
this.fields(v, "click");
|
|
218
|
+
const url = str(v.url);
|
|
219
|
+
const safe = url === void 0 ? null : allowedURL(url);
|
|
220
|
+
if (url !== void 0 && safe === null) this.warn("Skipped a click link that isn't http, https or mailto");
|
|
221
|
+
return { scene: str(v.scene) ?? null, url: safe };
|
|
222
|
+
}
|
|
223
|
+
playback(v) {
|
|
224
|
+
const s = str(v);
|
|
225
|
+
if (s === void 0) return null;
|
|
226
|
+
return ["loop", "once", "still", "click"].includes(s) ? s : "loop";
|
|
227
|
+
}
|
|
228
|
+
symbol(v, images) {
|
|
229
|
+
if (!isObj(v)) return null;
|
|
230
|
+
this.fields(v, "symbol");
|
|
231
|
+
const id = uuid(v.id);
|
|
232
|
+
if (!id) {
|
|
233
|
+
this.warn("Skipped a symbol without an id");
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const size = (x) => {
|
|
237
|
+
const n = num(x);
|
|
238
|
+
return Math.max(1, n ?? 100);
|
|
239
|
+
};
|
|
240
|
+
const layers = (Array.isArray(v.layers) ? v.layers : []).filter(isObj).flatMap((l) => {
|
|
241
|
+
const lid = uuid(l.id);
|
|
242
|
+
return lid ? [{ id: lid, name: str(l.name) ?? "", visible: bool(l.visible) ?? true, locked: bool(l.locked) ?? false }] : [];
|
|
243
|
+
});
|
|
244
|
+
if (layers.length === 0) layers.push({ id: `${id}-LAYER`, name: "Layer 1", visible: true, locked: false });
|
|
245
|
+
return {
|
|
246
|
+
id,
|
|
247
|
+
name: str(v.name) ?? "Symbol",
|
|
248
|
+
width: size(v.width),
|
|
249
|
+
height: size(v.height),
|
|
250
|
+
layers,
|
|
251
|
+
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
252
|
+
timeline: this.timeline(v.timeline),
|
|
253
|
+
button: bool(v.button) ?? null,
|
|
254
|
+
params: strings(v.params)
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
hover(v) {
|
|
258
|
+
if (!isObj(v)) return null;
|
|
259
|
+
this.fields(v, "hover");
|
|
260
|
+
return { stroke: rgba(v.stroke) ?? null, fill: rgba(v.fill) ?? null, opacity: num(v.opacity) ?? null };
|
|
261
|
+
}
|
|
194
262
|
ease(v) {
|
|
195
263
|
const s = str(v);
|
|
196
264
|
if (s === void 0) return null;
|
|
@@ -283,7 +351,8 @@ var Reader = class {
|
|
|
283
351
|
layers,
|
|
284
352
|
elements: (Array.isArray(v.elements) ? v.elements : []).map((e) => this.element(e, images)).filter((e) => e !== null),
|
|
285
353
|
theme: str(v.theme) ?? null,
|
|
286
|
-
timeline: this.timeline(v.timeline)
|
|
354
|
+
timeline: this.timeline(v.timeline),
|
|
355
|
+
symbols: Array.isArray(v.symbols) ? v.symbols.map((x) => this.symbol(x, images)).filter((x) => x !== null) : null
|
|
287
356
|
};
|
|
288
357
|
}
|
|
289
358
|
};
|
|
@@ -323,6 +392,19 @@ function parseDottie(source2) {
|
|
|
323
392
|
return { version, pages, warnings: reader.warnings };
|
|
324
393
|
}
|
|
325
394
|
|
|
395
|
+
// src/clicks.ts
|
|
396
|
+
var CLICK_STYLE = `
|
|
397
|
+
.dottie-a { cursor: pointer; outline: none; }
|
|
398
|
+
.dottie-a .dottie-h { display: none; }
|
|
399
|
+
.dottie-a:hover .dottie-h, .dottie-a.dottie-hover .dottie-h, .dottie-a:focus-visible .dottie-h { display: inline; }
|
|
400
|
+
.dottie-a:hover .dottie-n, .dottie-a.dottie-hover .dottie-n, .dottie-a:focus-visible .dottie-n { display: none; }
|
|
401
|
+
.dottie-a:focus-visible { filter: drop-shadow(0 0 3px var(--dottie-focus, #3b82f6)); }
|
|
402
|
+
.dottie-a:not(.dottie-b):active, .dottie-a.dottie-press:not(.dottie-b) { transform: translate(1px, 1px); }
|
|
403
|
+
.dottie-a .dottie-d { display: none; }
|
|
404
|
+
.dottie-b:active .dottie-n, .dottie-b:active .dottie-h, .dottie-b.dottie-press .dottie-n, .dottie-b.dottie-press .dottie-h { display: none; }
|
|
405
|
+
.dottie-b:active .dottie-d, .dottie-b.dottie-press .dottie-d { display: inline; }
|
|
406
|
+
`;
|
|
407
|
+
|
|
326
408
|
// src/player.ts
|
|
327
409
|
function pageIndex(file, spec) {
|
|
328
410
|
const s = (spec ?? "").trim();
|
|
@@ -332,6 +414,22 @@ function pageIndex(file, spec) {
|
|
|
332
414
|
const n = Number(s);
|
|
333
415
|
return Number.isInteger(n) && n >= 1 && n <= file.pages.length ? n - 1 : 0;
|
|
334
416
|
}
|
|
417
|
+
var STYLE = `
|
|
418
|
+
:host { display: block; position: relative; color: var(--dottie-ink); background: var(--dottie-page);
|
|
419
|
+
font: 14px -apple-system, system-ui, sans-serif; border-radius: var(--dottie-radius, 0); }
|
|
420
|
+
:host([hidden]) { display: none; }
|
|
421
|
+
.stage { display: block; width: 100%; height: auto; }
|
|
422
|
+
.stage svg { display: block; width: 100%; height: auto; margin: 0 auto; }
|
|
423
|
+
.bar { display: flex; align-items: center; gap: 12px; padding: 8px 12px 12px; max-width: 560px; margin: 0 auto; box-sizing: border-box; }
|
|
424
|
+
.bar[hidden] { display: none; }
|
|
425
|
+
button { font: inherit; color: inherit; background: none; border: 1.5px solid currentColor; border-radius: 6px;
|
|
426
|
+
width: 34px; height: 30px; cursor: pointer; flex: none; padding: 0; }
|
|
427
|
+
button:focus-visible, input:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }
|
|
428
|
+
input[type=range] { flex: 1; min-width: 60px; accent-color: currentColor; }
|
|
429
|
+
output { font-variant-numeric: tabular-nums; min-width: 4.5em; text-align: right; }
|
|
430
|
+
.scene { min-width: 0; flex: 0 1 auto; text-align: center; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
431
|
+
.error { padding: 16px; font-family: ui-monospace, Menlo, monospace; white-space: pre-wrap; }
|
|
432
|
+
${CLICK_STYLE}`;
|
|
335
433
|
|
|
336
434
|
// src/compile.ts
|
|
337
435
|
var LOADER = `;(function () {
|
|
@@ -393,7 +491,7 @@ function compile(source2, runtime, options2 = {}) {
|
|
|
393
491
|
}
|
|
394
492
|
|
|
395
493
|
// src/index.ts
|
|
396
|
-
var VERSION = "0.5.
|
|
494
|
+
var VERSION = "0.5.2";
|
|
397
495
|
|
|
398
496
|
// src/cli.ts
|
|
399
497
|
var usage = `dottie-compile ${VERSION}: a Dottie drawing as one script that plays it on any page.
|