@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
|
@@ -12,6 +12,10 @@ var ANIMATABLE_PROPS = /* @__PURE__ */ new Set([
|
|
|
12
12
|
"rotation",
|
|
13
13
|
"opacity"
|
|
14
14
|
]);
|
|
15
|
+
function contentLineInHint(hint, y, height) {
|
|
16
|
+
if (hint?.minY === void 0 || hint.maxY === void 0) return true;
|
|
17
|
+
return y + height >= hint.minY && y <= hint.maxY;
|
|
18
|
+
}
|
|
15
19
|
var VectoJSEvent = class {
|
|
16
20
|
/** The event name. */
|
|
17
21
|
type;
|
|
@@ -251,6 +255,42 @@ var Entity = class {
|
|
|
251
255
|
* nodes, so on-top components stay clickable.
|
|
252
256
|
*/
|
|
253
257
|
a11yFullViewport = false;
|
|
258
|
+
/**
|
|
259
|
+
* When this entity's a11y shadow node is materialized.
|
|
260
|
+
*
|
|
261
|
+
* `'eager'` (the default) keeps today's behaviour: a shadow node exists for as
|
|
262
|
+
* long as the entity is `interactive` with a box. That is right for a button or
|
|
263
|
+
* a link, and wrong for thousands of ephemeral, individually-meaningless
|
|
264
|
+
* entities — particles, danmaku, graph nodes — where it produces one DOM node
|
|
265
|
+
* per entity every frame.
|
|
266
|
+
*
|
|
267
|
+
* Measured on 5,000 moving interactive entities (`benchmarks/lazy-a11y/`):
|
|
268
|
+
* eager costs **72.2 ms/frame on Chrome and 114.3 ms on Firefox**, missing even
|
|
269
|
+
* 60 Hz, against **1.55/1.63 ms** for the same scene with one node projected —
|
|
270
|
+
* within noise of the 1.26/1.65 ms floor of projecting nothing at all.
|
|
271
|
+
*
|
|
272
|
+
* `'onDemand'` projects a node only while {@link Scene} considers the entity
|
|
273
|
+
* *engaged*: it is focused, it is the current pointer target, or it has been
|
|
274
|
+
* explicitly requested via {@link Scene.requestA11yProjection}. Crucially the
|
|
275
|
+
* trigger is not hover alone — a keyboard or assistive-technology user
|
|
276
|
+
* generates no hover, so a hover-only gate would remove exactly those users'
|
|
277
|
+
* access. Engagement therefore includes focus and an explicit request, and the
|
|
278
|
+
* entity stays hit-testable on canvas throughout, so a click still reaches it
|
|
279
|
+
* and promotes it.
|
|
280
|
+
*
|
|
281
|
+
* `'never'` suppresses the node entirely. Prefer `interactive = false` unless
|
|
282
|
+
* the entity genuinely needs pointer events without any semantic presence;
|
|
283
|
+
* this exists so a purely decorative interactive surface can opt out without
|
|
284
|
+
* losing canvas hit-testing.
|
|
285
|
+
*
|
|
286
|
+
* **This does not replace an aggregate description.** A thousand `'onDemand'`
|
|
287
|
+
* danmaku are individually reachable but say nothing collectively. The proven
|
|
288
|
+
* pattern is one aggregate live region (`role: 'status'`, `a11yFullViewport`)
|
|
289
|
+
* plus a small pool of persistent hotspots for the current selection — see
|
|
290
|
+
* `vectojs-native/danmaku`. Use `'onDemand'` to stop paying per entity, not as
|
|
291
|
+
* the whole accessibility story.
|
|
292
|
+
*/
|
|
293
|
+
a11yProjection = "eager";
|
|
254
294
|
/**
|
|
255
295
|
* Hide this entity AND its whole subtree from the accessibility/automation
|
|
256
296
|
* projection, regardless of each node's own `interactive` flag.
|
|
@@ -1045,9 +1085,42 @@ var Entity = class {
|
|
|
1045
1085
|
* `selectable` is set — natively selectable. Returns `null` by default.
|
|
1046
1086
|
* Read on the a11y sync cadence, so text changes propagate automatically.
|
|
1047
1087
|
*
|
|
1088
|
+
* @param hint - Optional advice about which part of the entity is worth
|
|
1089
|
+
* describing. Purely an optimization: ignoring it is always correct, which
|
|
1090
|
+
* is why it is a parameter rather than a required contract change. See
|
|
1091
|
+
* {@link ContentProjectionHint}.
|
|
1048
1092
|
* @returns The projection descriptor, or `null` to project nothing.
|
|
1049
1093
|
*/
|
|
1050
|
-
getContentProjection() {
|
|
1094
|
+
getContentProjection(hint) {
|
|
1095
|
+
void hint;
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* A cheap, monotonically-increasing stamp of this entity's projected content.
|
|
1100
|
+
*
|
|
1101
|
+
* Purely an optimization, and opt-in: returning `null` (the default) means
|
|
1102
|
+
* "I cannot cheaply tell whether my content changed", and the {@link Scene}
|
|
1103
|
+
* then rebuilds the projection every synced frame exactly as before. An
|
|
1104
|
+
* implementation must bump the value whenever anything
|
|
1105
|
+
* {@link getContentProjection} would report changes — text, fonts, line
|
|
1106
|
+
* geometry, `selectable`, grid revision.
|
|
1107
|
+
*
|
|
1108
|
+
* When two consecutive syncs report the same epoch AND the entity's geometry
|
|
1109
|
+
* is unchanged, `Scene` skips the block *before* calling
|
|
1110
|
+
* {@link getContentProjection}. That matters because the projection call is
|
|
1111
|
+
* O(glyphs-in-block) and the DOM diff around it costs about the same again:
|
|
1112
|
+
* measured on a 1500-resident-block document, a sync in which the projected
|
|
1113
|
+
* text was byte-identical before and after still cost 17.875 ms, and skipping
|
|
1114
|
+
* unchanged blocks took that to 0.475 ms (carryctx CTX-0199, vectojs#343).
|
|
1115
|
+
*
|
|
1116
|
+
* Correctness is entirely on the implementer: a stale epoch means stale DOM,
|
|
1117
|
+
* so bump it in the same place the content is invalidated rather than trying
|
|
1118
|
+
* to enumerate mutation sites afterwards. Any monotonic counter works; the
|
|
1119
|
+
* value is only ever compared for equality with the previous sync's.
|
|
1120
|
+
*
|
|
1121
|
+
* @returns The current content epoch, or `null` to disable skipping.
|
|
1122
|
+
*/
|
|
1123
|
+
getContentEpoch() {
|
|
1051
1124
|
return null;
|
|
1052
1125
|
}
|
|
1053
1126
|
/**
|
|
@@ -1093,6 +1166,8 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1093
1166
|
layoutText = "";
|
|
1094
1167
|
text = "";
|
|
1095
1168
|
lastRenderedSeqId = 0;
|
|
1169
|
+
/** Bumped by {@link queueLayout}; read by `Scene` to skip an unchanged sync. */
|
|
1170
|
+
contentEpoch = 0;
|
|
1096
1171
|
// Atlas-decode subscription (see watchAtlasDecode). Held so `destroy()` can
|
|
1097
1172
|
// release it: the handler closes over `this`, so leaving it attached to a
|
|
1098
1173
|
// long-lived shared atlas image would retain the whole entity.
|
|
@@ -1212,6 +1287,7 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1212
1287
|
});
|
|
1213
1288
|
}
|
|
1214
1289
|
queueLayout() {
|
|
1290
|
+
this.contentEpoch++;
|
|
1215
1291
|
LayoutWorkerManager.getInstance().queueLayout(this.id, this.layoutText, {
|
|
1216
1292
|
fontId: this.font.id,
|
|
1217
1293
|
fontSize: this.fontSize,
|
|
@@ -1241,6 +1317,9 @@ var MSDFTextEntity = class extends Entity {
|
|
|
1241
1317
|
lineHeight: this.lineHeight
|
|
1242
1318
|
};
|
|
1243
1319
|
}
|
|
1320
|
+
getContentEpoch() {
|
|
1321
|
+
return this.contentEpoch;
|
|
1322
|
+
}
|
|
1244
1323
|
isPointInside(globalX, globalY) {
|
|
1245
1324
|
if (!this.layoutResult) return false;
|
|
1246
1325
|
const local = this.worldToLocal(globalX, globalY);
|
|
@@ -1634,6 +1713,7 @@ var SVGEntity = class extends Entity {
|
|
|
1634
1713
|
};
|
|
1635
1714
|
|
|
1636
1715
|
export {
|
|
1716
|
+
contentLineInHint,
|
|
1637
1717
|
VectoJSEvent,
|
|
1638
1718
|
Entity,
|
|
1639
1719
|
MSDFTextEntity,
|