@johnmorrisdotca/dougu-sports 0.7.2 → 0.9.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 (46) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/NOTICE.md +1 -1
  3. package/README.md +58 -28
  4. package/dist/emblems/ball/ball.baseball.js +2 -1
  5. package/dist/emblems/ball/ball.contrast.d.ts +4 -0
  6. package/dist/emblems/ball/ball.contrast.js +22 -0
  7. package/dist/emblems/ball/ball.core.d.ts +42 -0
  8. package/dist/emblems/ball/ball.core.js +34 -1
  9. package/dist/emblems/ball/ball.engine.d.ts +70 -4
  10. package/dist/emblems/ball/ball.engine.js +70 -20
  11. package/dist/emblems/ball/ball.family.seamed.d.ts +4 -0
  12. package/dist/emblems/ball/ball.family.seamed.js +42 -7
  13. package/dist/emblems/ball/ball.image.d.ts +8 -0
  14. package/dist/emblems/ball/ball.image.js +14 -5
  15. package/dist/emblems/ball/ball.index.js +2 -4
  16. package/dist/emblems/ball/ball.letters.d.ts +3 -0
  17. package/dist/emblems/ball/ball.letters.js +51 -0
  18. package/dist/emblems/ball/ball.print.d.ts +81 -0
  19. package/dist/emblems/ball/ball.print.js +268 -0
  20. package/dist/emblems/ball/ball.printable.d.ts +6 -0
  21. package/dist/emblems/ball/ball.printable.js +13 -0
  22. package/dist/emblems/ball/ball.region.d.ts +12 -0
  23. package/dist/emblems/ball/ball.region.js +94 -0
  24. package/dist/emblems/ball/ball.script.d.ts +15 -0
  25. package/dist/emblems/ball/ball.script.js +107 -0
  26. package/dist/emblems/ball/ball.soccer.js +7 -3
  27. package/dist/emblems/ball/ball.soccer.panels.d.ts +2 -0
  28. package/dist/emblems/ball/ball.soccer.panels.js +6 -3
  29. package/dist/emblems/ball/ball.soccer.size.d.ts +2 -0
  30. package/dist/emblems/ball/ball.soccer.size.js +8 -0
  31. package/dist/emblems/ball/ball.softball.js +2 -1
  32. package/dist/emblems/ball/ball.warp.d.ts +48 -0
  33. package/dist/emblems/ball/ball.warp.js +441 -0
  34. package/dist/emblems/emblem.core.d.ts +13 -0
  35. package/dist/emblems-entry.d.ts +1 -0
  36. package/dist/index.d.ts +5 -5
  37. package/dist/index.js +4 -5
  38. package/dist/turn-control.d.ts +24 -5
  39. package/dist/turn-control.js +67 -23
  40. package/dist/turn-full.d.ts +15 -0
  41. package/dist/turn-full.js +19 -0
  42. package/dist/turn-registry.d.ts +1 -0
  43. package/dist/turn-registry.js +3 -0
  44. package/dist/version.d.ts +1 -1
  45. package/dist/version.js +1 -1
  46. package/package.json +10 -10
@@ -1,5 +1,13 @@
1
- import { type EmblemRef } from "./emblems/emblem.api.ts";
2
- import type { FrameOptions } from "./emblems/emblem.core.ts";
1
+ import type { EmblemRef } from "./emblems/emblem.api.ts";
2
+ import type { Emblem, FrameOptions } from "./emblems/emblem.core.ts";
3
+ /** What reading an id needs of the registry: `emblemOf`, `renderEmblem` and `emblemFrame` of `emblem.api`. The main entry hands them to `bindTurn` itself; `./turn` loads them when an id is bound. */
4
+ export interface TurnRegistry {
5
+ emblemOf(ref: EmblemRef): {
6
+ emblem: Emblem;
7
+ };
8
+ renderEmblem(ref: EmblemRef, options?: FrameOptions): string;
9
+ emblemFrame(ref: EmblemRef, options?: FrameOptions): string;
10
+ }
3
11
  /** The three angles of a turn, in degrees. */
4
12
  export interface TurnAngles {
5
13
  /** About the vertical axis, from -180 (excluded) to 180. */
@@ -37,8 +45,13 @@ export interface TurnController {
37
45
  readonly angles: TurnAngles;
38
46
  /** Whether the auto-turn is running. */
39
47
  readonly playing: boolean;
40
- /** Whether the emblem now drawn can be turned at all (read from the registry, so an emblem that gains a turn is turnable here without a change). */
48
+ /** Whether the emblem now drawn can be turned at all (read from the emblem's own frames, so an emblem that gains a turn is turnable here without a change). `false` until an id has loaded; see `ready`. */
41
49
  readonly turnable: boolean;
50
+ /**
51
+ * Resolves when the emblem is drawn and bound. At once (already resolved) for an emblem object, and for an id from the main entry or once the registry has loaded; otherwise after the registry's chunk has loaded.
52
+ * Rejects for an id that names no emblem.
53
+ */
54
+ readonly ready: Promise<void>;
42
55
  /** Sets some of the angles (wrapped or clamped as a drag would) and draws. Pauses the auto-turn when `pauseOnInteract` is on. */
43
56
  setAngles(angles: Partial<TurnAngles>): void;
44
57
  /** Draws another emblem, or the same one with other options, and keeps the angles (unless the options carry some). */
@@ -86,16 +99,22 @@ export interface TurnContainer {
86
99
  /** An angle wrapped into -180 (excluded) to 180, as the demo's slider is. */
87
100
  export declare function wrapAngle(value: number): number;
88
101
  /**
89
- * Binds an emblem to an element so that it can be turned: it draws `emblemFrame` into `container`, and a drag turns it (left and right are yaw, up and down are pitch, and Shift on a ball is roll), the arrow keys turn
102
+ * Binds an emblem to an element so that it can be turned: it draws the emblem's `frame` into `container`, and a drag turns it (left and right are yaw, up and down are pitch, and Shift on a ball is roll), the arrow keys turn
90
103
  * it, Home or 0 puts it back, and Space starts and stops an auto-turn. An emblem that cannot turn is drawn as it is, and nothing is listened to. Nothing moves by itself: `autoPlay` is off by default and, under
91
104
  * `prefers-reduced-motion`, only starts when it is `"force"`. The element gets `touch-action: none`, `user-select: none`, a grab cursor, a tab stop, a role description and a hint; `destroy()` takes them away.
105
+ *
106
+ * Pass the emblem itself (`TROPHY_HOCKEY_CUP`, from its own module) and nothing but the turning and that one emblem is in the bundle. Pass an id (`"trophy:hockey-cup"`) and the registry is loaded on
107
+ * demand: the element is drawn when `ready` resolves, not before. The main entry's `bindTurn` has the registry already and draws an id at once.
92
108
  * @example
93
109
  * ```ts
94
110
  * import { bindTurn } from "@johnmorrisdotca/dougu-sports/turn";
95
- * const turner = bindTurn(document.getElementById("cup")!, "trophy:tennis-cup", { size: 160 });
111
+ * import { TROPHY_TENNIS_CUP } from "@johnmorrisdotca/dougu-sports/trophy/tennis-cup";
112
+ * const turner = bindTurn(document.getElementById("cup")!, TROPHY_TENNIS_CUP, { size: 160 });
96
113
  * turner.setAngles({ yaw: 40 });
97
114
  * console.log(turner.angles.yaw);
98
115
  * // 40
99
116
  * ```
100
117
  */
101
118
  export declare function bindTurn(container: TurnContainer, ref: EmblemRef, options?: TurnControlOptions): TurnController;
119
+ /** `bindTurn` with the registry in hand (or none, to load it when an id is bound): the main entry gives it, so that an id is drawn at once. An emblem object needs neither. */
120
+ export declare function bindTurnWith(registry: TurnRegistry | undefined, container: TurnContainer, ref: EmblemRef, options?: TurnControlOptions): TurnController;
@@ -1,6 +1,3 @@
1
- // `bindTurn`: an emblem that a pointer, the arrow keys, the Space bar or a clock can turn. It draws `emblemFrame` into an element and listens to that element, and it is a module of its own, so importing an
2
- // emblem never pulls it in. Nothing here moves by itself: an auto-turn starts only when the caller asks for it, and not at all under `prefers-reduced-motion` unless the caller forces it.
3
- import { emblemFrame, emblemOf, renderEmblem } from "./emblems/emblem.api.js";
4
1
  const WORDS = {
5
2
  en: { role: "turnable emblem", hint: "Drag, or use the arrow keys, to turn it. Space starts and stops the turning." },
6
3
  ja: { role: "回せるエンブレム", hint: "ドラッグするか矢印キーで回せます。スペースキーで自動回転を始めたり止めたりできます。" },
@@ -13,7 +10,7 @@ export function wrapAngle(value) {
13
10
  const finite = (value, fallback) => (typeof value === "number" && Number.isFinite(value) ? value : fallback);
14
11
  const clamp = (value, low, high) => Math.max(low, Math.min(high, value));
15
12
  const globals = globalThis;
16
- /** Whether an emblem turns at all, read from the registry's own frames: a turned view that differs from the resting one. An emblem may say so itself with a `turnable` flag. */
13
+ /** Whether an emblem turns at all, read from the emblem's own frames: a turned view that differs from the resting one. An emblem may say so itself with a `turnable` flag. */
17
14
  const turns = new WeakMap();
18
15
  function canTurn(emblem) {
19
16
  const flag = emblem.turnable;
@@ -26,22 +23,41 @@ function canTurn(emblem) {
26
23
  }
27
24
  return known;
28
25
  }
26
+ const fromEmblem = (emblem) => ({ emblem, still: (o) => emblem.svg(o), frame: (o) => emblem.frame(o) });
27
+ const fromId = (registry, ref) => ({ emblem: registry.emblemOf(ref).emblem, still: (o) => registry.renderEmblem(ref, o), frame: (o) => registry.emblemFrame(ref, o) });
28
+ /** The registry, once something has loaded it: later ids are bound at once. */
29
+ let loaded;
30
+ let loading;
31
+ const load = () => {
32
+ loading ??= import("./turn-registry.js").then((api) => (loaded = api));
33
+ return loading;
34
+ };
29
35
  /**
30
- * Binds an emblem to an element so that it can be turned: it draws `emblemFrame` into `container`, and a drag turns it (left and right are yaw, up and down are pitch, and Shift on a ball is roll), the arrow keys turn
36
+ * Binds an emblem to an element so that it can be turned: it draws the emblem's `frame` into `container`, and a drag turns it (left and right are yaw, up and down are pitch, and Shift on a ball is roll), the arrow keys turn
31
37
  * it, Home or 0 puts it back, and Space starts and stops an auto-turn. An emblem that cannot turn is drawn as it is, and nothing is listened to. Nothing moves by itself: `autoPlay` is off by default and, under
32
38
  * `prefers-reduced-motion`, only starts when it is `"force"`. The element gets `touch-action: none`, `user-select: none`, a grab cursor, a tab stop, a role description and a hint; `destroy()` takes them away.
39
+ *
40
+ * Pass the emblem itself (`TROPHY_HOCKEY_CUP`, from its own module) and nothing but the turning and that one emblem is in the bundle. Pass an id (`"trophy:hockey-cup"`) and the registry is loaded on
41
+ * demand: the element is drawn when `ready` resolves, not before. The main entry's `bindTurn` has the registry already and draws an id at once.
33
42
  * @example
34
43
  * ```ts
35
44
  * import { bindTurn } from "@johnmorrisdotca/dougu-sports/turn";
36
- * const turner = bindTurn(document.getElementById("cup")!, "trophy:tennis-cup", { size: 160 });
45
+ * import { TROPHY_TENNIS_CUP } from "@johnmorrisdotca/dougu-sports/trophy/tennis-cup";
46
+ * const turner = bindTurn(document.getElementById("cup")!, TROPHY_TENNIS_CUP, { size: 160 });
37
47
  * turner.setAngles({ yaw: 40 });
38
48
  * console.log(turner.angles.yaw);
39
49
  * // 40
40
50
  * ```
41
51
  */
42
52
  export function bindTurn(container, ref, options = {}) {
53
+ return bindTurnWith(undefined, container, ref, options);
54
+ }
55
+ /** `bindTurn` with the registry in hand (or none, to load it when an id is bound): the main entry gives it, so that an id is drawn at once. An emblem object needs neither. */
56
+ export function bindTurnWith(registry, container, ref, options = {}) {
43
57
  let current = ref;
44
- let emblem = emblemOf(ref).emblem;
58
+ let drawer;
59
+ let version = 0;
60
+ let pending;
45
61
  let turnable = false;
46
62
  let sensitivity = 1.5;
47
63
  let range = [-60, 60];
@@ -95,7 +111,9 @@ export function bindTurn(container, ref, options = {}) {
95
111
  }
96
112
  const at = angles.yaw === 0 && angles.pitch === 0 && angles.roll === 0;
97
113
  // The resting view of an emblem that turns is drawn in the box of the turn too, so that the element is the same size at every angle and a drag does not make the emblem jump or zoom at its first step.
98
- container.innerHTML = !turnable ? renderEmblem(current, frame) : at ? renderEmblem(current, { turnBox: true, ...frame }) : emblemFrame(current, { ...frame, ...angles });
114
+ if (drawer === undefined)
115
+ return;
116
+ container.innerHTML = !turnable ? drawer.still(frame) : at ? drawer.still({ turnBox: true, ...frame }) : drawer.frame({ ...frame, ...angles });
99
117
  };
100
118
  /** One drawing for any number of changes within a frame. */
101
119
  const schedule = () => {
@@ -178,7 +196,7 @@ export function bindTurn(container, ref, options = {}) {
178
196
  drag.y = y;
179
197
  if (dx === 0 && dy === 0)
180
198
  return;
181
- if (event.shiftKey === true && emblem.kind === "ball")
199
+ if (event.shiftKey === true && drawer?.emblem.kind === "ball")
182
200
  place({ roll: angles.roll + dx, pitch: angles.pitch - dy });
183
201
  else
184
202
  place({ yaw: angles.yaw + dx, pitch: angles.pitch - dy });
@@ -287,24 +305,46 @@ export function bindTurn(container, ref, options = {}) {
287
305
  drag = undefined;
288
306
  held = false;
289
307
  };
290
- const settle = () => {
291
- emblem = emblemOf(current).emblem;
292
- turnable = canTurn(emblem);
308
+ const adopt = (next) => {
309
+ drawer = next;
310
+ turnable = canTurn(next.emblem);
311
+ detach();
293
312
  if (turnable)
294
313
  attach();
295
- else {
296
- detach();
314
+ else
297
315
  setPlaying(false);
298
- }
299
316
  };
300
- configure(options);
301
- place({ yaw: options.yaw, pitch: options.pitch, roll: options.roll });
302
- settle();
303
- draw();
304
- if (turnable && options.autoPlay !== undefined && options.autoPlay !== false) {
317
+ /** Binds what `current` names: at once for an emblem, or for an id when the registry is in hand; otherwise when it has loaded. Gives the wait, if there is one. */
318
+ const settle = (first) => {
319
+ const mine = ++version;
320
+ const now = current;
321
+ if (typeof now !== "string")
322
+ return void adopt(fromEmblem(now));
323
+ const known = registry ?? loaded;
324
+ if (known !== undefined)
325
+ return void adopt(fromId(known, now));
326
+ return load().then((api) => {
327
+ if (mine !== version || destroyed)
328
+ return;
329
+ adopt(fromId(api, now));
330
+ draw();
331
+ if (first)
332
+ autoStart();
333
+ });
334
+ };
335
+ const autoStart = () => {
336
+ if (!turnable || options.autoPlay === undefined || options.autoPlay === false)
337
+ return;
305
338
  const reduced = globals.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
306
339
  if (options.autoPlay === "force" || !reduced)
307
340
  setPlaying(true);
341
+ };
342
+ configure(options);
343
+ place({ yaw: options.yaw, pitch: options.pitch, roll: options.roll });
344
+ pending = settle(true);
345
+ if (pending === undefined) {
346
+ draw();
347
+ autoStart();
308
348
  }
309
349
  return {
310
350
  get angles() {
@@ -316,6 +356,9 @@ export function bindTurn(container, ref, options = {}) {
316
356
  get turnable() {
317
357
  return turnable;
318
358
  },
359
+ get ready() {
360
+ return pending ?? Promise.resolve();
361
+ },
319
362
  setAngles(partial) {
320
363
  if (destroyed)
321
364
  return;
@@ -331,9 +374,9 @@ export function bindTurn(container, ref, options = {}) {
331
374
  place({ yaw: o.yaw, pitch: o.pitch, roll: o.roll });
332
375
  // The pitch the new range allows.
333
376
  place({ pitch: angles.pitch });
334
- detach();
335
- settle();
336
- draw();
377
+ pending = settle(false);
378
+ if (pending === undefined)
379
+ draw();
337
380
  },
338
381
  play() {
339
382
  if (!turnable || destroyed)
@@ -355,6 +398,7 @@ export function bindTurn(container, ref, options = {}) {
355
398
  return;
356
399
  setPlaying(false);
357
400
  destroyed = true;
401
+ version++;
358
402
  if (renderId !== undefined)
359
403
  globals.cancelAnimationFrame?.(renderId);
360
404
  renderId = undefined;
@@ -0,0 +1,15 @@
1
+ import { type EmblemRef } from "./emblems/emblem.api.ts";
2
+ import { type TurnContainer, type TurnControlOptions, type TurnController } from "./turn-control.ts";
3
+ /**
4
+ * Binds an emblem to an element so that a drag, the arrow keys, Space or a clock turn it; see `bindTurn` of `@johnmorrisdotca/dougu-sports/turn`, which takes an emblem object and loads nothing else. This one is
5
+ * from the main entry: an id is read from the registry and drawn at once, with no `await`.
6
+ * @example
7
+ * ```ts
8
+ * import { bindTurn } from "@johnmorrisdotca/dougu-sports";
9
+ * const turner = bindTurn(document.getElementById("cup")!, "trophy:tennis-cup", { size: 160 });
10
+ * turner.setAngles({ yaw: 40 });
11
+ * console.log(turner.angles.yaw);
12
+ * // 40
13
+ * ```
14
+ */
15
+ export declare function bindTurn(container: TurnContainer, ref: EmblemRef, options?: TurnControlOptions): TurnController;
@@ -0,0 +1,19 @@
1
+ // `bindTurn` of the main entry: the same as the light one of `./turn`, with the registry already in hand, so an id is drawn at once (as it always was) and an emblem object works too. It is a function of its own, so
2
+ // a page that imports only emblems from the main entry does not pull it in; one that imports `bindTurn` from here has the registry in the bundle anyway.
3
+ import { emblemFrame, emblemOf, renderEmblem } from "./emblems/emblem.api.js";
4
+ import { bindTurnWith } from "./turn-control.js";
5
+ /**
6
+ * Binds an emblem to an element so that a drag, the arrow keys, Space or a clock turn it; see `bindTurn` of `@johnmorrisdotca/dougu-sports/turn`, which takes an emblem object and loads nothing else. This one is
7
+ * from the main entry: an id is read from the registry and drawn at once, with no `await`.
8
+ * @example
9
+ * ```ts
10
+ * import { bindTurn } from "@johnmorrisdotca/dougu-sports";
11
+ * const turner = bindTurn(document.getElementById("cup")!, "trophy:tennis-cup", { size: 160 });
12
+ * turner.setAngles({ yaw: 40 });
13
+ * console.log(turner.angles.yaw);
14
+ * // 40
15
+ * ```
16
+ */
17
+ export function bindTurn(container, ref, options = {}) {
18
+ return bindTurnWith({ emblemOf, renderEmblem, emblemFrame }, container, ref, options);
19
+ }
@@ -0,0 +1 @@
1
+ export { emblemFrame, emblemOf, renderEmblem } from "./emblems/emblem.api.ts";
@@ -0,0 +1,3 @@
1
+ // What `bindTurn` loads when it is given an id: the three functions that read the registry. A module of its own, so that the dynamic import has a target no static import of `./turn` shares, and a bundler
2
+ // makes it a chunk of its own.
3
+ export { emblemFrame, emblemOf, renderEmblem } from "./emblems/emblem.api.js";
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.7.2";
1
+ export declare const VERSION = "0.9.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.7.2";
1
+ export const VERSION = "0.9.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnmorrisdotca/dougu-sports",
3
- "version": "0.7.2",
3
+ "version": "0.9.0",
4
4
  "description": "Dougu Sports 道具, sports emblems as SVG strings: trophies, medals, crests, sport icons, and the ball and gear of every sport, in metal, line or flat colour, with optional 3D turning. Light and dark, accessible, tree-shakeable. Zero dependencies.",
5
5
  "keywords": [
6
6
  "sports",
@@ -113,18 +113,18 @@
113
113
  "scripts": {
114
114
  "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc -p tsconfig.json",
115
115
  "lint": "eslint .",
116
- "typecheck": "tsc -p tsconfig.check.json",
117
- "test": "node --test test/*.test.ts test/*.test.js",
118
- "test:package": "pnpm build && node scripts/check-package.mjs",
119
- "test:readme": "pnpm build && node scripts/make-gallery.mjs --check && node scripts/check-readme-examples.mjs",
120
- "check": "pnpm lint && pnpm typecheck && pnpm test && pnpm build && node scripts/check-package.mjs",
116
+ "typecheck": "tsc -p tsconfig.check.json && tsc -p tsconfig.tools.json && tsc -p tsconfig.demo.json",
117
+ "test": "node --test test/*.test.ts",
118
+ "test:package": "pnpm build && node scripts/check-package.ts",
119
+ "test:readme": "pnpm build && node scripts/make-gallery.ts --check && node scripts/check-readme-examples.ts",
120
+ "check": "pnpm lint && pnpm typecheck && pnpm test && pnpm build && node scripts/check-package.ts",
121
121
  "prepublishOnly": "pnpm check",
122
122
  "family:readme": "node scripts/family-readme.mjs",
123
- "dev": "pnpm build && node scripts/dev.mjs",
124
- "gallery": "pnpm build && node scripts/make-gallery.mjs",
125
- "site": "pnpm build && node scripts/site.mjs",
123
+ "dev": "pnpm build && node scripts/dev.ts",
124
+ "gallery": "pnpm build && node scripts/make-gallery.ts",
125
+ "site": "pnpm build && node scripts/site.ts",
126
126
  "test:demo": "pnpm site && playwright test",
127
- "screenshots": "pnpm site && node scripts/screenshots.mjs"
127
+ "screenshots": "pnpm site && node scripts/screenshots.ts"
128
128
  },
129
129
  "devDependencies": {
130
130
  "@eslint/js": "^9.0.0",