@vectojs/core 1.29.0 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MVFPN4Y5.mjs → chunk-RWN3ITBD.mjs} +81 -1
- package/dist/{chunk-Z3HFY75R.js → chunk-TRT2TRCW.js} +158 -78
- package/dist/components/TextEntity.d.ts +3 -0
- package/dist/index.js +513 -244
- package/dist/index.mjs +293 -24
- package/dist/text/MSDFTextEntity.d.ts +3 -0
- package/dist/text.js +2 -2
- package/dist/text.mjs +1 -1
- package/dist/tree/Entity.d.ts +104 -1
- package/dist/tree/Scene.d.ts +127 -1
- package/package.json +1 -1
|
@@ -14,6 +14,10 @@ var ANIMATABLE_PROPS = /* @__PURE__ */ new Set([
|
|
|
14
14
|
"rotation",
|
|
15
15
|
"opacity"
|
|
16
16
|
]);
|
|
17
|
+
function contentLineInHint(hint, y, height) {
|
|
18
|
+
if (_optionalChain([hint, 'optionalAccess', _ => _.minY]) === void 0 || hint.maxY === void 0) return true;
|
|
19
|
+
return y + height >= hint.minY && y <= hint.maxY;
|
|
20
|
+
}
|
|
17
21
|
var VectoJSEvent = (_class = class {
|
|
18
22
|
/** The event name. */
|
|
19
23
|
|
|
@@ -47,7 +51,7 @@ var VectoJSEvent = (_class = class {
|
|
|
47
51
|
}
|
|
48
52
|
/** Forward to the native event's `preventDefault` (e.g. stop page scroll). */
|
|
49
53
|
preventDefault() {
|
|
50
|
-
_optionalChain([this, 'access',
|
|
54
|
+
_optionalChain([this, 'access', _2 => _2.nativeEvent, 'optionalAccess', _3 => _3.preventDefault, 'optionalCall', _4 => _4()]);
|
|
51
55
|
}
|
|
52
56
|
/** Whether {@link stopPropagation} has been called. */
|
|
53
57
|
get propagationStopped() {
|
|
@@ -59,72 +63,72 @@ var VectoJSEvent = (_class = class {
|
|
|
59
63
|
}
|
|
60
64
|
/** Whether the native event's default action was prevented. */
|
|
61
65
|
get defaultPrevented() {
|
|
62
|
-
return !!_optionalChain([this, 'access',
|
|
66
|
+
return !!_optionalChain([this, 'access', _5 => _5.nativeEvent, 'optionalAccess', _6 => _6.defaultPrevented]);
|
|
63
67
|
}
|
|
64
68
|
/** Native horizontal wheel delta, if this wraps a `WheelEvent`. */
|
|
65
69
|
get deltaX() {
|
|
66
|
-
return _optionalChain([this, 'access',
|
|
70
|
+
return _optionalChain([this, 'access', _7 => _7.nativeEvent, 'optionalAccess', _8 => _8.deltaX]);
|
|
67
71
|
}
|
|
68
72
|
/** Native vertical wheel delta, if this wraps a `WheelEvent`. */
|
|
69
73
|
get deltaY() {
|
|
70
|
-
return _optionalChain([this, 'access',
|
|
74
|
+
return _optionalChain([this, 'access', _9 => _9.nativeEvent, 'optionalAccess', _10 => _10.deltaY]);
|
|
71
75
|
}
|
|
72
76
|
/** Native pointer X, if this wraps a pointer/mouse event. */
|
|
73
77
|
get clientX() {
|
|
74
|
-
return _optionalChain([this, 'access',
|
|
78
|
+
return _optionalChain([this, 'access', _11 => _11.nativeEvent, 'optionalAccess', _12 => _12.clientX]);
|
|
75
79
|
}
|
|
76
80
|
/** Native pointer Y, if this wraps a pointer/mouse event. */
|
|
77
81
|
get clientY() {
|
|
78
|
-
return _optionalChain([this, 'access',
|
|
82
|
+
return _optionalChain([this, 'access', _13 => _13.nativeEvent, 'optionalAccess', _14 => _14.clientY]);
|
|
79
83
|
}
|
|
80
84
|
get resolvedScenePoint() {
|
|
81
85
|
if (this.explicitScenePoint) return this.explicitScenePoint;
|
|
82
86
|
const native = this.nativeEvent;
|
|
83
|
-
if (_optionalChain([native, 'optionalAccess',
|
|
87
|
+
if (_optionalChain([native, 'optionalAccess', _15 => _15.vectoSceneX]) !== void 0 && native.vectoSceneY !== void 0) {
|
|
84
88
|
return { x: native.vectoSceneX, y: native.vectoSceneY };
|
|
85
89
|
}
|
|
86
|
-
if (_optionalChain([native, 'optionalAccess',
|
|
90
|
+
if (_optionalChain([native, 'optionalAccess', _16 => _16.clientX]) === void 0 || native.clientY === void 0) return void 0;
|
|
87
91
|
const scene = this.target.scene;
|
|
88
|
-
return _nullishCoalesce(_optionalChain([scene, 'optionalAccess',
|
|
92
|
+
return _nullishCoalesce(_optionalChain([scene, 'optionalAccess', _17 => _17.clientToScene, 'optionalCall', _18 => _18(native.clientX, native.clientY)]), () => ( {
|
|
89
93
|
x: native.clientX,
|
|
90
94
|
y: native.clientY
|
|
91
95
|
}));
|
|
92
96
|
}
|
|
93
97
|
/** Pointer X in the Scene's logical coordinate space. */
|
|
94
98
|
get sceneX() {
|
|
95
|
-
return _optionalChain([this, 'access',
|
|
99
|
+
return _optionalChain([this, 'access', _19 => _19.resolvedScenePoint, 'optionalAccess', _20 => _20.x]);
|
|
96
100
|
}
|
|
97
101
|
/** Pointer Y in the Scene's logical coordinate space. */
|
|
98
102
|
get sceneY() {
|
|
99
|
-
return _optionalChain([this, 'access',
|
|
103
|
+
return _optionalChain([this, 'access', _21 => _21.resolvedScenePoint, 'optionalAccess', _22 => _22.y]);
|
|
100
104
|
}
|
|
101
105
|
/** Pointer X local to the entity whose listener is currently running. */
|
|
102
106
|
get localX() {
|
|
103
107
|
const point = this.resolvedScenePoint;
|
|
104
108
|
if (!point) return void 0;
|
|
105
|
-
return _optionalChain([this, 'access',
|
|
109
|
+
return _optionalChain([this, 'access', _23 => _23.currentTarget, 'access', _24 => _24.worldToLocal, 'call', _25 => _25(point.x, point.y), 'optionalAccess', _26 => _26.x]);
|
|
106
110
|
}
|
|
107
111
|
/** Pointer Y local to the entity whose listener is currently running. */
|
|
108
112
|
get localY() {
|
|
109
113
|
const point = this.resolvedScenePoint;
|
|
110
114
|
if (!point) return void 0;
|
|
111
|
-
return _optionalChain([this, 'access',
|
|
115
|
+
return _optionalChain([this, 'access', _27 => _27.currentTarget, 'access', _28 => _28.worldToLocal, 'call', _29 => _29(point.x, point.y), 'optionalAccess', _30 => _30.y]);
|
|
112
116
|
}
|
|
113
117
|
get shiftKey() {
|
|
114
|
-
return !!_optionalChain([this, 'access',
|
|
118
|
+
return !!_optionalChain([this, 'access', _31 => _31.nativeEvent, 'optionalAccess', _32 => _32.shiftKey]);
|
|
115
119
|
}
|
|
116
120
|
get ctrlKey() {
|
|
117
|
-
return !!_optionalChain([this, 'access',
|
|
121
|
+
return !!_optionalChain([this, 'access', _33 => _33.nativeEvent, 'optionalAccess', _34 => _34.ctrlKey]);
|
|
118
122
|
}
|
|
119
123
|
get altKey() {
|
|
120
|
-
return !!_optionalChain([this, 'access',
|
|
124
|
+
return !!_optionalChain([this, 'access', _35 => _35.nativeEvent, 'optionalAccess', _36 => _36.altKey]);
|
|
121
125
|
}
|
|
122
126
|
get metaKey() {
|
|
123
|
-
return !!_optionalChain([this, 'access',
|
|
127
|
+
return !!_optionalChain([this, 'access', _37 => _37.nativeEvent, 'optionalAccess', _38 => _38.metaKey]);
|
|
124
128
|
}
|
|
125
129
|
/** Native key, if this wraps a keyboard event. */
|
|
126
130
|
get key() {
|
|
127
|
-
return _optionalChain([this, 'access',
|
|
131
|
+
return _optionalChain([this, 'access', _39 => _39.nativeEvent, 'optionalAccess', _40 => _40.key]);
|
|
128
132
|
}
|
|
129
133
|
}, _class);
|
|
130
134
|
var Entity = (_class2 = class {
|
|
@@ -253,6 +257,42 @@ var Entity = (_class2 = class {
|
|
|
253
257
|
* nodes, so on-top components stay clickable.
|
|
254
258
|
*/
|
|
255
259
|
__init33() {this.a11yFullViewport = false}
|
|
260
|
+
/**
|
|
261
|
+
* When this entity's a11y shadow node is materialized.
|
|
262
|
+
*
|
|
263
|
+
* `'eager'` (the default) keeps today's behaviour: a shadow node exists for as
|
|
264
|
+
* long as the entity is `interactive` with a box. That is right for a button or
|
|
265
|
+
* a link, and wrong for thousands of ephemeral, individually-meaningless
|
|
266
|
+
* entities — particles, danmaku, graph nodes — where it produces one DOM node
|
|
267
|
+
* per entity every frame.
|
|
268
|
+
*
|
|
269
|
+
* Measured on 5,000 moving interactive entities (`benchmarks/lazy-a11y/`):
|
|
270
|
+
* eager costs **72.2 ms/frame on Chrome and 114.3 ms on Firefox**, missing even
|
|
271
|
+
* 60 Hz, against **1.55/1.63 ms** for the same scene with one node projected —
|
|
272
|
+
* within noise of the 1.26/1.65 ms floor of projecting nothing at all.
|
|
273
|
+
*
|
|
274
|
+
* `'onDemand'` projects a node only while {@link Scene} considers the entity
|
|
275
|
+
* *engaged*: it is focused, it is the current pointer target, or it has been
|
|
276
|
+
* explicitly requested via {@link Scene.requestA11yProjection}. Crucially the
|
|
277
|
+
* trigger is not hover alone — a keyboard or assistive-technology user
|
|
278
|
+
* generates no hover, so a hover-only gate would remove exactly those users'
|
|
279
|
+
* access. Engagement therefore includes focus and an explicit request, and the
|
|
280
|
+
* entity stays hit-testable on canvas throughout, so a click still reaches it
|
|
281
|
+
* and promotes it.
|
|
282
|
+
*
|
|
283
|
+
* `'never'` suppresses the node entirely. Prefer `interactive = false` unless
|
|
284
|
+
* the entity genuinely needs pointer events without any semantic presence;
|
|
285
|
+
* this exists so a purely decorative interactive surface can opt out without
|
|
286
|
+
* losing canvas hit-testing.
|
|
287
|
+
*
|
|
288
|
+
* **This does not replace an aggregate description.** A thousand `'onDemand'`
|
|
289
|
+
* danmaku are individually reachable but say nothing collectively. The proven
|
|
290
|
+
* pattern is one aggregate live region (`role: 'status'`, `a11yFullViewport`)
|
|
291
|
+
* plus a small pool of persistent hotspots for the current selection — see
|
|
292
|
+
* `vectojs-native/danmaku`. Use `'onDemand'` to stop paying per entity, not as
|
|
293
|
+
* the whole accessibility story.
|
|
294
|
+
*/
|
|
295
|
+
__init34() {this.a11yProjection = "eager"}
|
|
256
296
|
/**
|
|
257
297
|
* Hide this entity AND its whole subtree from the accessibility/automation
|
|
258
298
|
* projection, regardless of each node's own `interactive` flag.
|
|
@@ -270,21 +310,21 @@ var Entity = (_class2 = class {
|
|
|
270
310
|
* `=== 0` test never fires; a threshold would instead silently un-project a
|
|
271
311
|
* faint-but-live control.
|
|
272
312
|
*/
|
|
273
|
-
|
|
313
|
+
__init35() {this.a11yHidden = false}
|
|
274
314
|
/**
|
|
275
315
|
* Clip this node's children to its local box (`[0,0]–[width,height]`) while
|
|
276
316
|
* rendering. Combined with translating a content child, this is how
|
|
277
317
|
* scroll/overflow containers (e.g. `ScrollView`) keep their content inside a
|
|
278
318
|
* fixed viewport. Off by default (children render unclipped). Canvas2D only.
|
|
279
319
|
*/
|
|
280
|
-
|
|
320
|
+
__init36() {this.clipChildren = false}
|
|
281
321
|
// Lazily allocated (see _drivers above). Most entities never register a
|
|
282
322
|
// listener or an imperative animate() tween.
|
|
283
|
-
|
|
323
|
+
__init37() {this.listeners = null}
|
|
284
324
|
/** Capture-phase listeners (fired root→target before bubble). */
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);
|
|
325
|
+
__init38() {this.captureListeners = null}
|
|
326
|
+
__init39() {this.animations = null}
|
|
327
|
+
constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);
|
|
288
328
|
this.id = id || `entity_${Math.random().toString(36).substring(2, 9)}`;
|
|
289
329
|
}
|
|
290
330
|
/**
|
|
@@ -318,7 +358,7 @@ var Entity = (_class2 = class {
|
|
|
318
358
|
const s = this.scene;
|
|
319
359
|
if (s) {
|
|
320
360
|
s.a11yNeedsReorder = true;
|
|
321
|
-
_optionalChain([s, 'access',
|
|
361
|
+
_optionalChain([s, 'access', _41 => _41.markStructureChanged, 'optionalCall', _42 => _42()]);
|
|
322
362
|
s.markDirty({ entity: this.id, reason: "child-added" });
|
|
323
363
|
child._notifyMounted();
|
|
324
364
|
}
|
|
@@ -348,7 +388,7 @@ var Entity = (_class2 = class {
|
|
|
348
388
|
if (s) {
|
|
349
389
|
s.detachA11y(child);
|
|
350
390
|
s.a11yNeedsReorder = true;
|
|
351
|
-
_optionalChain([s, 'access',
|
|
391
|
+
_optionalChain([s, 'access', _43 => _43.markStructureChanged, 'optionalCall', _44 => _44()]);
|
|
352
392
|
s.markDirty({ entity: this.id, reason: "child-removed" });
|
|
353
393
|
}
|
|
354
394
|
}
|
|
@@ -403,7 +443,7 @@ var Entity = (_class2 = class {
|
|
|
403
443
|
startTime: -1,
|
|
404
444
|
startProps: {}
|
|
405
445
|
});
|
|
406
|
-
_optionalChain([this, 'access',
|
|
446
|
+
_optionalChain([this, 'access', _45 => _45.scene, 'optionalAccess', _46 => _46.markDirty, 'call', _47 => _47({ entity: this.id, reason: "animation-start" })]);
|
|
407
447
|
return this;
|
|
408
448
|
}
|
|
409
449
|
/** Write a driver-computed value to a backing field without re-triggering the setter. */
|
|
@@ -450,26 +490,26 @@ var Entity = (_class2 = class {
|
|
|
450
490
|
* that need to seed a starting state (e.g. the presence helper's enter `from`).
|
|
451
491
|
*/
|
|
452
492
|
setImmediate(prop, v) {
|
|
453
|
-
const existing = _optionalChain([this, 'access',
|
|
493
|
+
const existing = _optionalChain([this, 'access', _48 => _48._drivers, 'optionalAccess', _49 => _49.get, 'call', _50 => _50(prop)]);
|
|
454
494
|
if (existing) this._settleDriver(existing);
|
|
455
|
-
_optionalChain([this, 'access',
|
|
495
|
+
_optionalChain([this, 'access', _51 => _51._drivers, 'optionalAccess', _52 => _52.delete, 'call', _53 => _53(prop)]);
|
|
456
496
|
this._applyAnimated(prop, v);
|
|
457
497
|
}
|
|
458
498
|
_settleDriver(driver) {
|
|
459
499
|
const active = driver;
|
|
460
500
|
const onDone = active.onDone;
|
|
461
501
|
active.onDone = void 0;
|
|
462
|
-
_optionalChain([onDone, 'optionalCall',
|
|
502
|
+
_optionalChain([onDone, 'optionalCall', _54 => _54()]);
|
|
463
503
|
}
|
|
464
504
|
_spawnDriver(prop, to, cfg) {
|
|
465
|
-
if (prop !== "opacity" && _optionalChain([this, 'access',
|
|
466
|
-
const existing2 = _optionalChain([this, 'access',
|
|
505
|
+
if (prop !== "opacity" && _optionalChain([this, 'access', _55 => _55.scene, 'optionalAccess', _56 => _56.prefersReducedMotion])) {
|
|
506
|
+
const existing2 = _optionalChain([this, 'access', _57 => _57._drivers, 'optionalAccess', _58 => _58.get, 'call', _59 => _59(prop)]);
|
|
467
507
|
if (existing2) this._settleDriver(existing2);
|
|
468
|
-
_optionalChain([this, 'access',
|
|
508
|
+
_optionalChain([this, 'access', _60 => _60._drivers, 'optionalAccess', _61 => _61.delete, 'call', _62 => _62(prop)]);
|
|
469
509
|
this._applyAnimated(prop, to);
|
|
470
510
|
return;
|
|
471
511
|
}
|
|
472
|
-
const existing = _optionalChain([this, 'access',
|
|
512
|
+
const existing = _optionalChain([this, 'access', _63 => _63._drivers, 'optionalAccess', _64 => _64.get, 'call', _65 => _65(prop)]);
|
|
473
513
|
if (existing) {
|
|
474
514
|
this._settleDriver(existing);
|
|
475
515
|
existing.retarget(to);
|
|
@@ -478,12 +518,12 @@ var Entity = (_class2 = class {
|
|
|
478
518
|
const from = this._currentOf(prop);
|
|
479
519
|
const driver = _animation.isTweenConfig.call(void 0, cfg) ? new (0, _animation.TweenDriver)(from, to, cfg) : new (0, _animation.SpringDriver)(from, to, cfg === "spring" ? {} : cfg);
|
|
480
520
|
(this._drivers ??= /* @__PURE__ */ new Map()).set(prop, driver);
|
|
481
|
-
_optionalChain([this, 'access',
|
|
482
|
-
_optionalChain([this, 'access',
|
|
521
|
+
_optionalChain([this, 'access', _66 => _66.scene, 'optionalAccess', _67 => _67.markDirty, 'call', _68 => _68({ entity: this.id, reason: "driver-added" })]);
|
|
522
|
+
_optionalChain([this, 'access', _69 => _69.scene, 'optionalAccess', _70 => _70._registerActiveDriverEntity, 'call', _71 => _71(this)]);
|
|
483
523
|
}
|
|
484
524
|
/** Assignment path when a declarative transition is configured for `prop`. */
|
|
485
525
|
_animateProp(prop, to) {
|
|
486
|
-
const cfg = _optionalChain([this, 'access',
|
|
526
|
+
const cfg = _optionalChain([this, 'access', _72 => _72._transitions, 'optionalAccess', _73 => _73.get, 'call', _74 => _74(prop)]);
|
|
487
527
|
if (!cfg) {
|
|
488
528
|
this._applyAnimated(prop, to);
|
|
489
529
|
return;
|
|
@@ -512,7 +552,7 @@ var Entity = (_class2 = class {
|
|
|
512
552
|
entries.map(
|
|
513
553
|
(e) => new Promise((resolve) => {
|
|
514
554
|
this._spawnDriver(e[0], e[1], cfg);
|
|
515
|
-
const d = _optionalChain([this, 'access',
|
|
555
|
+
const d = _optionalChain([this, 'access', _75 => _75._drivers, 'optionalAccess', _76 => _76.get, 'call', _77 => _77(e[0])]);
|
|
516
556
|
if (!d)
|
|
517
557
|
resolve();
|
|
518
558
|
else d.onDone = resolve;
|
|
@@ -534,7 +574,7 @@ var Entity = (_class2 = class {
|
|
|
534
574
|
this._applyAnimated(prop, driver.value);
|
|
535
575
|
}
|
|
536
576
|
}
|
|
537
|
-
_optionalChain([this, 'access',
|
|
577
|
+
_optionalChain([this, 'access', _78 => _78.scene, 'optionalAccess', _79 => _79.markDirty, 'call', _80 => _80({ entity: this.id, reason: "driver-tick" })]);
|
|
538
578
|
}
|
|
539
579
|
/**
|
|
540
580
|
* Internal: this entity's active-driver map (read-only view), or `null` if
|
|
@@ -559,7 +599,7 @@ var Entity = (_class2 = class {
|
|
|
559
599
|
if (driver.isDone()) {
|
|
560
600
|
this._applyAnimated(prop, driver.target);
|
|
561
601
|
this._settleDriver(driver);
|
|
562
|
-
_optionalChain([this, 'access',
|
|
602
|
+
_optionalChain([this, 'access', _81 => _81._drivers, 'optionalAccess', _82 => _82.delete, 'call', _83 => _83(prop)]);
|
|
563
603
|
} else {
|
|
564
604
|
this._applyAnimated(prop, driver.value);
|
|
565
605
|
}
|
|
@@ -620,7 +660,7 @@ var Entity = (_class2 = class {
|
|
|
620
660
|
* @example entity.on('click', (e) => console.log('clicked', e));
|
|
621
661
|
*/
|
|
622
662
|
on(event, callback, options) {
|
|
623
|
-
const map = _optionalChain([options, 'optionalAccess',
|
|
663
|
+
const map = _optionalChain([options, 'optionalAccess', _84 => _84.capture]) ? this.captureListeners ??= /* @__PURE__ */ new Map() : this.listeners ??= /* @__PURE__ */ new Map();
|
|
624
664
|
if (!map.has(event)) {
|
|
625
665
|
map.set(event, []);
|
|
626
666
|
}
|
|
@@ -636,7 +676,7 @@ var Entity = (_class2 = class {
|
|
|
636
676
|
* @returns `this` for method chaining.
|
|
637
677
|
*/
|
|
638
678
|
off(event, callback, options) {
|
|
639
|
-
const handlers = _optionalChain([(_optionalChain([options, 'optionalAccess',
|
|
679
|
+
const handlers = _optionalChain([(_optionalChain([options, 'optionalAccess', _85 => _85.capture]) ? this.captureListeners : this.listeners), 'optionalAccess', _86 => _86.get, 'call', _87 => _87(event)]);
|
|
640
680
|
if (handlers) {
|
|
641
681
|
const idx = handlers.indexOf(callback);
|
|
642
682
|
if (idx !== -1) handlers.splice(idx, 1);
|
|
@@ -673,8 +713,8 @@ var Entity = (_class2 = class {
|
|
|
673
713
|
}
|
|
674
714
|
this._drivers.clear();
|
|
675
715
|
}
|
|
676
|
-
_optionalChain([this, 'access',
|
|
677
|
-
_optionalChain([this, 'access',
|
|
716
|
+
_optionalChain([this, 'access', _88 => _88.listeners, 'optionalAccess', _89 => _89.clear, 'call', _90 => _90()]);
|
|
717
|
+
_optionalChain([this, 'access', _91 => _91.captureListeners, 'optionalAccess', _92 => _92.clear, 'call', _93 => _93()]);
|
|
678
718
|
if (this.parent) {
|
|
679
719
|
this.parent.remove(this);
|
|
680
720
|
}
|
|
@@ -689,7 +729,7 @@ var Entity = (_class2 = class {
|
|
|
689
729
|
* @param payload - Arbitrary data forwarded to each listener.
|
|
690
730
|
*/
|
|
691
731
|
emit(event, payload) {
|
|
692
|
-
const handlers = _optionalChain([this, 'access',
|
|
732
|
+
const handlers = _optionalChain([this, 'access', _94 => _94.listeners, 'optionalAccess', _95 => _95.get, 'call', _96 => _96(event)]);
|
|
693
733
|
if (handlers) {
|
|
694
734
|
handlers.forEach((h) => h(payload));
|
|
695
735
|
}
|
|
@@ -703,19 +743,19 @@ var Entity = (_class2 = class {
|
|
|
703
743
|
* with `requestAnimationFrame(() => document.getElementById(id)?.focus())`.
|
|
704
744
|
*/
|
|
705
745
|
focus() {
|
|
706
|
-
const el = _optionalChain([this, 'access',
|
|
746
|
+
const el = _optionalChain([this, 'access', _97 => _97.scene, 'optionalAccess', _98 => _98.getA11yElement, 'call', _99 => _99(this.id)]);
|
|
707
747
|
if (el) {
|
|
708
748
|
el.focus();
|
|
709
749
|
return;
|
|
710
750
|
}
|
|
711
751
|
requestAnimationFrame(() => {
|
|
712
|
-
const retry = _optionalChain([this, 'access',
|
|
752
|
+
const retry = _optionalChain([this, 'access', _100 => _100.scene, 'optionalAccess', _101 => _101.getA11yElement, 'call', _102 => _102(this.id)]);
|
|
713
753
|
if (retry) retry.focus();
|
|
714
754
|
});
|
|
715
755
|
}
|
|
716
756
|
/** Run one node's listeners for the event, honoring stopImmediatePropagation. */
|
|
717
757
|
fireListeners(node, map, event) {
|
|
718
|
-
const handlers = _optionalChain([map, 'optionalAccess',
|
|
758
|
+
const handlers = _optionalChain([map, 'optionalAccess', _103 => _103.get, 'call', _104 => _104(event.type)]);
|
|
719
759
|
if (!handlers) return;
|
|
720
760
|
event.currentTarget = node;
|
|
721
761
|
for (const h of handlers.slice()) {
|
|
@@ -1047,9 +1087,42 @@ var Entity = (_class2 = class {
|
|
|
1047
1087
|
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
1048
1088
|
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
1049
1089
|
*
|
|
1090
|
+
* @param hint - Optional advice about which part of the entity is worth
|
|
1091
|
+
* describing. Purely an optimization: ignoring it is always correct, which
|
|
1092
|
+
* is why it is a parameter rather than a required contract change. See
|
|
1093
|
+
* {@link ContentProjectionHint}.
|
|
1050
1094
|
* @returns The projection descriptor, or `null` to project nothing.
|
|
1051
1095
|
*/
|
|
1052
|
-
getContentProjection() {
|
|
1096
|
+
getContentProjection(hint) {
|
|
1097
|
+
void hint;
|
|
1098
|
+
return null;
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* A cheap, monotonically-increasing stamp of this entity's projected content.
|
|
1102
|
+
*
|
|
1103
|
+
* Purely an optimization, and opt-in: returning `null` (the default) means
|
|
1104
|
+
* "I cannot cheaply tell whether my content changed", and the {@link Scene}
|
|
1105
|
+
* then rebuilds the projection every synced frame exactly as before. An
|
|
1106
|
+
* implementation must bump the value whenever anything
|
|
1107
|
+
* {@link getContentProjection} would report changes — text, fonts, line
|
|
1108
|
+
* geometry, `selectable`, grid revision.
|
|
1109
|
+
*
|
|
1110
|
+
* When two consecutive syncs report the same epoch AND the entity's geometry
|
|
1111
|
+
* is unchanged, `Scene` skips the block *before* calling
|
|
1112
|
+
* {@link getContentProjection}. That matters because the projection call is
|
|
1113
|
+
* O(glyphs-in-block) and the DOM diff around it costs about the same again:
|
|
1114
|
+
* measured on a 1500-resident-block document, a sync in which the projected
|
|
1115
|
+
* text was byte-identical before and after still cost 17.875 ms, and skipping
|
|
1116
|
+
* unchanged blocks took that to 0.475 ms (carryctx CTX-0199, vectojs#343).
|
|
1117
|
+
*
|
|
1118
|
+
* Correctness is entirely on the implementer: a stale epoch means stale DOM,
|
|
1119
|
+
* so bump it in the same place the content is invalidated rather than trying
|
|
1120
|
+
* to enumerate mutation sites afterwards. Any monotonic counter works; the
|
|
1121
|
+
* value is only ever compared for equality with the previous sync's.
|
|
1122
|
+
*
|
|
1123
|
+
* @returns The current content epoch, or `null` to disable skipping.
|
|
1124
|
+
*/
|
|
1125
|
+
getContentEpoch() {
|
|
1053
1126
|
return null;
|
|
1054
1127
|
}
|
|
1055
1128
|
/**
|
|
@@ -1069,7 +1142,7 @@ var Entity = (_class2 = class {
|
|
|
1069
1142
|
* @returns `true` if at least one animation or property driver remains.
|
|
1070
1143
|
*/
|
|
1071
1144
|
hasPendingAnimations() {
|
|
1072
|
-
return (_nullishCoalesce(_optionalChain([this, 'access',
|
|
1145
|
+
return (_nullishCoalesce(_optionalChain([this, 'access', _105 => _105.animations, 'optionalAccess', _106 => _106.length]), () => ( 0))) > 0 || (_nullishCoalesce(_optionalChain([this, 'access', _107 => _107._drivers, 'optionalAccess', _108 => _108.size]), () => ( 0))) > 0;
|
|
1073
1146
|
}
|
|
1074
1147
|
}, _class2);
|
|
1075
1148
|
|
|
@@ -1091,20 +1164,22 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1091
1164
|
// joined by soft hyphens (U+00AD); the worker then treats those as break
|
|
1092
1165
|
// opportunities. `text` keeps the original string for a11y/content
|
|
1093
1166
|
// projection; `layoutText` is the soft-hyphen-annotated string sent to layout.
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1167
|
+
__init40() {this.hyphenator = null}
|
|
1168
|
+
__init41() {this.layoutText = ""}
|
|
1169
|
+
__init42() {this.text = ""}
|
|
1170
|
+
__init43() {this.lastRenderedSeqId = 0}
|
|
1171
|
+
/** Bumped by {@link queueLayout}; read by `Scene` to skip an unchanged sync. */
|
|
1172
|
+
__init44() {this.contentEpoch = 0}
|
|
1098
1173
|
// Atlas-decode subscription (see watchAtlasDecode). Held so `destroy()` can
|
|
1099
1174
|
// release it: the handler closes over `this`, so leaving it attached to a
|
|
1100
1175
|
// long-lived shared atlas image would retain the whole entity.
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1176
|
+
__init45() {this.atlasDecodeTarget = null}
|
|
1177
|
+
__init46() {this.atlasDecodeHandler = null}
|
|
1178
|
+
__init47() {this.rgbColorCache = /* @__PURE__ */ new Map()}
|
|
1179
|
+
__init48() {this.fontStringCache = []}
|
|
1180
|
+
__init49() {this.layoutResult = null}
|
|
1106
1181
|
constructor(text, options) {
|
|
1107
|
-
super();_class3.prototype.
|
|
1182
|
+
super();_class3.prototype.__init40.call(this);_class3.prototype.__init41.call(this);_class3.prototype.__init42.call(this);_class3.prototype.__init43.call(this);_class3.prototype.__init44.call(this);_class3.prototype.__init45.call(this);_class3.prototype.__init46.call(this);_class3.prototype.__init47.call(this);_class3.prototype.__init48.call(this);_class3.prototype.__init49.call(this);;
|
|
1108
1183
|
this.font = options.font;
|
|
1109
1184
|
this.texture = options.texture;
|
|
1110
1185
|
this.fallbackFont = _nullishCoalesce(options.fallbackFont, () => ( "sans-serif"));
|
|
@@ -1149,7 +1224,7 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1149
1224
|
const target = this.texture;
|
|
1150
1225
|
this.atlasDecodeHandler = () => {
|
|
1151
1226
|
this.detachAtlasDecodeListener();
|
|
1152
|
-
_optionalChain([this, 'access',
|
|
1227
|
+
_optionalChain([this, 'access', _109 => _109.scene, 'optionalAccess', _110 => _110.markDirty, 'call', _111 => _111()]);
|
|
1153
1228
|
};
|
|
1154
1229
|
target.addEventListener("load", this.atlasDecodeHandler);
|
|
1155
1230
|
target.addEventListener("error", this.atlasDecodeHandler);
|
|
@@ -1214,6 +1289,7 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1214
1289
|
});
|
|
1215
1290
|
}
|
|
1216
1291
|
queueLayout() {
|
|
1292
|
+
this.contentEpoch++;
|
|
1217
1293
|
_layout.LayoutWorkerManager.getInstance().queueLayout(this.id, this.layoutText, {
|
|
1218
1294
|
fontId: this.font.id,
|
|
1219
1295
|
fontSize: this.fontSize,
|
|
@@ -1227,7 +1303,7 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1227
1303
|
if (res.seqId < this.lastRenderedSeqId) return;
|
|
1228
1304
|
this.lastRenderedSeqId = res.seqId;
|
|
1229
1305
|
this.layoutResult = res;
|
|
1230
|
-
_optionalChain([this, 'access',
|
|
1306
|
+
_optionalChain([this, 'access', _112 => _112.scene, 'optionalAccess', _113 => _113.markDirty, 'call', _114 => _114()]);
|
|
1231
1307
|
}
|
|
1232
1308
|
});
|
|
1233
1309
|
}
|
|
@@ -1243,6 +1319,9 @@ var MSDFTextEntity = (_class3 = class extends Entity {
|
|
|
1243
1319
|
lineHeight: this.lineHeight
|
|
1244
1320
|
};
|
|
1245
1321
|
}
|
|
1322
|
+
getContentEpoch() {
|
|
1323
|
+
return this.contentEpoch;
|
|
1324
|
+
}
|
|
1246
1325
|
isPointInside(globalX, globalY) {
|
|
1247
1326
|
if (!this.layoutResult) return false;
|
|
1248
1327
|
const local = this.worldToLocal(globalX, globalY);
|
|
@@ -1376,23 +1455,23 @@ var SVGEntity = (_class4 = class extends Entity {
|
|
|
1376
1455
|
* rasterized. Set to `'transparent'` to opt out and keep the box empty.
|
|
1377
1456
|
* Default `'rgba(248,113,113,0.9)'`.
|
|
1378
1457
|
*/
|
|
1379
|
-
|
|
1458
|
+
__init50() {this.fallbackStroke = "rgba(248,113,113,0.9)"}
|
|
1380
1459
|
/** Fill behind the fallback marker. Default `'rgba(248,113,113,0.12)'`. */
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1460
|
+
__init51() {this.fallbackFill = "rgba(248,113,113,0.12)"}
|
|
1461
|
+
__init52() {this.svgSource = ""}
|
|
1462
|
+
__init53() {this.imageBitmap = null}
|
|
1463
|
+
__init54() {this.imageElement = null}
|
|
1464
|
+
__init55() {this.blobURL = null}
|
|
1465
|
+
__init56() {this.currentImg = null}
|
|
1466
|
+
__init57() {this.lodTimeout = null}
|
|
1467
|
+
__init58() {this.rasterFailed = false}
|
|
1468
|
+
__init59() {this.cachedDoc = null}
|
|
1469
|
+
__init60() {this.baseWidth = 100}
|
|
1470
|
+
__init61() {this.baseHeight = 100}
|
|
1471
|
+
__init62() {this.lastRasterizedScale = 1}
|
|
1472
|
+
__init63() {this.targetScale = 1}
|
|
1394
1473
|
constructor(svgSource, id) {
|
|
1395
|
-
super(id);_class4.prototype.
|
|
1474
|
+
super(id);_class4.prototype.__init50.call(this);_class4.prototype.__init51.call(this);_class4.prototype.__init52.call(this);_class4.prototype.__init53.call(this);_class4.prototype.__init54.call(this);_class4.prototype.__init55.call(this);_class4.prototype.__init56.call(this);_class4.prototype.__init57.call(this);_class4.prototype.__init58.call(this);_class4.prototype.__init59.call(this);_class4.prototype.__init60.call(this);_class4.prototype.__init61.call(this);_class4.prototype.__init62.call(this);_class4.prototype.__init63.call(this);;
|
|
1396
1475
|
this.setSVGSource(svgSource);
|
|
1397
1476
|
}
|
|
1398
1477
|
setSVGSource(svgSource) {
|
|
@@ -1640,4 +1719,5 @@ var SVGEntity = (_class4 = class extends Entity {
|
|
|
1640
1719
|
|
|
1641
1720
|
|
|
1642
1721
|
|
|
1643
|
-
|
|
1722
|
+
|
|
1723
|
+
exports.contentLineInHint = contentLineInHint; exports.VectoJSEvent = VectoJSEvent; exports.Entity = Entity; exports.MSDFTextEntity = MSDFTextEntity; exports.SVGEntity = SVGEntity;
|
|
@@ -12,12 +12,15 @@ export declare class TextEntity extends Entity {
|
|
|
12
12
|
hoveredFillStyle: string | any;
|
|
13
13
|
lineWidth: number;
|
|
14
14
|
private isHovered;
|
|
15
|
+
/** Bumped by {@link applyLayout}; read by `Scene` to skip an unchanged sync. */
|
|
16
|
+
private contentEpoch;
|
|
15
17
|
constructor(text: string, atlas: any, maxWidth: number, fontSize?: number);
|
|
16
18
|
/**
|
|
17
19
|
* Mirror the rendered text into the DOM content layer: find-in-page, screen
|
|
18
20
|
* readers, crawlers, and translation see the same string the canvas draws.
|
|
19
21
|
*/
|
|
20
22
|
getContentProjection(): ContentProjection | null;
|
|
23
|
+
getContentEpoch(): number;
|
|
21
24
|
/**
|
|
22
25
|
* Replace the text content. Runs the **cold** measurement pass (re-segment +
|
|
23
26
|
* re-measure) since the glyphs changed, then re-lays out.
|