@formulaxjs/editor 0.2.0 → 0.3.1
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/README.md +66 -34
- package/dist/index.cjs +196 -445
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +1241 -804
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +188 -431
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,76 +1,164 @@
|
|
|
1
1
|
// src/formula-modal.ts
|
|
2
2
|
import { createEmptyState, parseLatex } from "@formulaxjs/core";
|
|
3
3
|
import { mountKityEditor } from "@formulaxjs/kity-runtime";
|
|
4
|
+
import { escapeHtml, ensureFormulaXBaseStyles } from "@formulaxjs/renderer";
|
|
5
|
+
import {
|
|
6
|
+
serializeKityFormulaFromRoot,
|
|
7
|
+
waitForKityFormulaSvgLayout
|
|
8
|
+
} from "@formulaxjs/renderer-kity";
|
|
4
9
|
|
|
5
|
-
// src/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
function createFormulaMarkup(latex, options = {}) {
|
|
16
|
-
const attributeName = options.attributeName ?? DEFAULT_FORMULA_ATTRIBUTE;
|
|
17
|
-
const className = options.className ?? DEFAULT_FORMULA_CLASS;
|
|
18
|
-
const displayClass = options.displayMode ? `${className} ${className}--block` : className;
|
|
19
|
-
const safeLatex = escapeAttribute(latex);
|
|
20
|
-
const cursorStyle = options.cursorStyle?.trim() || "pointer";
|
|
21
|
-
const extraAttributes = {
|
|
22
|
-
...options.extraAttributes ?? {},
|
|
23
|
-
style: mergeInlineStyles(
|
|
24
|
-
typeof options.extraAttributes?.style === "string" ? options.extraAttributes.style : "",
|
|
25
|
-
cursorStyle ? `cursor: ${cursorStyle}` : ""
|
|
26
|
-
)
|
|
10
|
+
// src/perf.ts
|
|
11
|
+
import { ensureKityRuntime } from "@formulaxjs/kity-runtime";
|
|
12
|
+
function getPerfHost() {
|
|
13
|
+
return globalThis;
|
|
14
|
+
}
|
|
15
|
+
function getPerfState() {
|
|
16
|
+
const host = getPerfHost();
|
|
17
|
+
host.__FORMULAX_PERF_STATE__ ??= {
|
|
18
|
+
reportedMeasureCount: 0,
|
|
19
|
+
reportScheduled: false
|
|
27
20
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
73
|
-
|
|
21
|
+
return host.__FORMULAX_PERF_STATE__;
|
|
22
|
+
}
|
|
23
|
+
function hasPerfSupport() {
|
|
24
|
+
return typeof performance !== "undefined" && typeof performance.mark === "function" && typeof performance.measure === "function" && typeof performance.getEntriesByType === "function";
|
|
25
|
+
}
|
|
26
|
+
function isPerfDebugEnabled() {
|
|
27
|
+
return getPerfHost().__FORMULAX_PERF__ === true;
|
|
28
|
+
}
|
|
29
|
+
function schedulePerfReport() {
|
|
30
|
+
if (!hasPerfSupport() || !isPerfDebugEnabled()) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const state = getPerfState();
|
|
34
|
+
if (state.reportScheduled) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
state.reportScheduled = true;
|
|
38
|
+
queueMicrotask(() => {
|
|
39
|
+
state.reportScheduled = false;
|
|
40
|
+
const entries = performance.getEntriesByType("measure").filter((entry) => entry.name.startsWith("fx:")).sort((left, right) => left.startTime - right.startTime);
|
|
41
|
+
const nextEntries = entries.slice(state.reportedMeasureCount);
|
|
42
|
+
state.reportedMeasureCount = entries.length;
|
|
43
|
+
if (!nextEntries.length) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.table(nextEntries.map((entry) => ({
|
|
47
|
+
name: entry.name,
|
|
48
|
+
duration: Number(entry.duration.toFixed(2)),
|
|
49
|
+
startTime: Number(entry.startTime.toFixed(2))
|
|
50
|
+
})));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function markFormulaXPerf(name) {
|
|
54
|
+
if (!hasPerfSupport()) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const markName = `${name}::${Date.now()}::${Math.random().toString(36).slice(2, 8)}`;
|
|
58
|
+
performance.mark(markName);
|
|
59
|
+
return markName;
|
|
60
|
+
}
|
|
61
|
+
function measureFormulaXPerf(name, startMark, endMark) {
|
|
62
|
+
if (!hasPerfSupport() || !startMark) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const resolvedEndMark = endMark ?? markFormulaXPerf(`${name}:end`);
|
|
66
|
+
if (!resolvedEndMark) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
performance.measure(name, startMark, resolvedEndMark);
|
|
70
|
+
schedulePerfReport();
|
|
71
|
+
return resolvedEndMark;
|
|
72
|
+
}
|
|
73
|
+
function recordFormulaXPerfPoint(name) {
|
|
74
|
+
const markName = markFormulaXPerf(name);
|
|
75
|
+
if (!markName) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
measureFormulaXPerf(name, markName, markName);
|
|
79
|
+
clearFormulaXPerfMarks(markName);
|
|
80
|
+
}
|
|
81
|
+
function clearFormulaXPerfMarks(...marks) {
|
|
82
|
+
if (!hasPerfSupport()) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
for (const mark of marks) {
|
|
86
|
+
if (!mark) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
performance.clearMarks(mark);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function preloadFormulaXEditor() {
|
|
93
|
+
await ensureKityRuntime();
|
|
94
|
+
}
|
|
95
|
+
function scheduleFormulaXEditorPreload(mode, target) {
|
|
96
|
+
if (mode === false || typeof window === "undefined") {
|
|
97
|
+
return () => void 0;
|
|
98
|
+
}
|
|
99
|
+
let disposed = false;
|
|
100
|
+
let triggered = false;
|
|
101
|
+
const cleanupCallbacks = [];
|
|
102
|
+
const trigger = () => {
|
|
103
|
+
if (disposed || triggered) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
triggered = true;
|
|
107
|
+
while (cleanupCallbacks.length) {
|
|
108
|
+
cleanupCallbacks.pop()?.();
|
|
109
|
+
}
|
|
110
|
+
void preloadFormulaXEditor();
|
|
111
|
+
};
|
|
112
|
+
if (mode === "idle") {
|
|
113
|
+
const host = getPerfHost();
|
|
114
|
+
if (typeof host.requestIdleCallback === "function") {
|
|
115
|
+
const handle = host.requestIdleCallback(() => {
|
|
116
|
+
trigger();
|
|
117
|
+
});
|
|
118
|
+
cleanupCallbacks.push(() => {
|
|
119
|
+
host.cancelIdleCallback?.(handle);
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
const handle = window.setTimeout(() => {
|
|
123
|
+
trigger();
|
|
124
|
+
}, 1);
|
|
125
|
+
cleanupCallbacks.push(() => {
|
|
126
|
+
window.clearTimeout(handle);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return () => {
|
|
130
|
+
disposed = true;
|
|
131
|
+
while (cleanupCallbacks.length) {
|
|
132
|
+
cleanupCallbacks.pop()?.();
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (target && "addEventListener" in target && "removeEventListener" in target) {
|
|
137
|
+
const eventTarget = target;
|
|
138
|
+
const onActivate = () => {
|
|
139
|
+
trigger();
|
|
140
|
+
};
|
|
141
|
+
eventTarget.addEventListener("pointerenter", onActivate, { once: true, passive: true });
|
|
142
|
+
eventTarget.addEventListener("focusin", onActivate, { once: true });
|
|
143
|
+
cleanupCallbacks.push(() => {
|
|
144
|
+
eventTarget.removeEventListener("pointerenter", onActivate);
|
|
145
|
+
eventTarget.removeEventListener("focusin", onActivate);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return () => {
|
|
149
|
+
disposed = true;
|
|
150
|
+
while (cleanupCallbacks.length) {
|
|
151
|
+
cleanupCallbacks.pop()?.();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function waitForFormulaXAnimationFrame() {
|
|
156
|
+
if (typeof window === "undefined" || typeof window.requestAnimationFrame !== "function") {
|
|
157
|
+
return Promise.resolve();
|
|
158
|
+
}
|
|
159
|
+
return new Promise((resolve) => {
|
|
160
|
+
window.requestAnimationFrame(() => resolve());
|
|
161
|
+
});
|
|
74
162
|
}
|
|
75
163
|
|
|
76
164
|
// src/formula-modal.ts
|
|
@@ -179,6 +267,8 @@ var formulaXModalStyles = `
|
|
|
179
267
|
}
|
|
180
268
|
|
|
181
269
|
.fx-formula-kity-host .kf-editor {
|
|
270
|
+
box-sizing: border-box;
|
|
271
|
+
width: 100%;
|
|
182
272
|
height: var(--fx-formula-editor-body-height) !important;
|
|
183
273
|
overflow: visible !important;
|
|
184
274
|
}
|
|
@@ -211,8 +301,7 @@ var formulaXModalStyles = `
|
|
|
211
301
|
.fx-formula-kity-host .kf-editor svg text,
|
|
212
302
|
.fx-formula-kity-host .kf-editor-ui-area-item-text,
|
|
213
303
|
.fx-formula-kity-host .kf-editor-ui-box-item-text,
|
|
214
|
-
.fx-formula-kity-host .kf-editor-ui-box-item-val
|
|
215
|
-
.formulax-math__render {
|
|
304
|
+
.fx-formula-kity-host .kf-editor-ui-box-item-val {
|
|
216
305
|
font-family: "KF AMS MAIN", "Cambria Math", "Latin Modern Math", "Times New Roman", serif !important;
|
|
217
306
|
}
|
|
218
307
|
|
|
@@ -287,59 +376,34 @@ var formulaXModalStyles = `
|
|
|
287
376
|
background: #2563eb;
|
|
288
377
|
color: #fff;
|
|
289
378
|
}
|
|
290
|
-
|
|
291
|
-
.formulax-math {
|
|
292
|
-
display: inline-flex;
|
|
293
|
-
align-items: center;
|
|
294
|
-
vertical-align: middle;
|
|
295
|
-
line-height: 1;
|
|
296
|
-
padding: 0 2px;
|
|
297
|
-
margin: 0 1px;
|
|
298
|
-
border-radius: 3px;
|
|
299
|
-
background: transparent;
|
|
300
|
-
cursor: pointer;
|
|
301
|
-
user-select: none;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
.formulax-math:hover {
|
|
305
|
-
outline: 1px solid rgba(37, 99, 235, 0.35);
|
|
306
|
-
background: rgba(37, 99, 235, 0.06);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.formulax-math__svg {
|
|
310
|
-
display: inline-block;
|
|
311
|
-
flex: 0 0 auto;
|
|
312
|
-
max-width: 100%;
|
|
313
|
-
vertical-align: -0.35em;
|
|
314
|
-
pointer-events: none;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
.formulax-math__image {
|
|
318
|
-
display: inline-block;
|
|
319
|
-
max-width: 100%;
|
|
320
|
-
height: auto;
|
|
321
|
-
vertical-align: middle;
|
|
322
|
-
pointer-events: none;
|
|
323
|
-
}
|
|
324
379
|
`;
|
|
325
380
|
function ensureFormulaXModalStyles(doc = document) {
|
|
381
|
+
ensureFormulaXBaseStyles(doc);
|
|
326
382
|
if (doc.getElementById(STYLE_ID)) return;
|
|
327
383
|
const style = doc.createElement("style");
|
|
328
384
|
style.id = STYLE_ID;
|
|
329
385
|
style.textContent = formulaXModalStyles;
|
|
330
386
|
doc.head.appendChild(style);
|
|
331
387
|
}
|
|
332
|
-
function
|
|
333
|
-
let destroyed = false;
|
|
334
|
-
let latestLatex = options.initialLatex ?? "";
|
|
335
|
-
let handle = null;
|
|
336
|
-
const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
|
|
388
|
+
function renderFormulaXEditorLoadingState(root) {
|
|
337
389
|
root.classList.add("fx-formula-kity-host");
|
|
338
390
|
root.innerHTML = `
|
|
339
391
|
<div class="fx-formula-editor-loading" role="status" aria-live="polite">
|
|
340
392
|
Loading FormulaX editor...
|
|
341
393
|
</div>
|
|
342
394
|
`;
|
|
395
|
+
}
|
|
396
|
+
function mountFormulaXEditor(root, options = {}) {
|
|
397
|
+
recordFormulaXPerfPoint("fx:formula-editor:mount:start");
|
|
398
|
+
const mountStart = markFormulaXPerf("fx:formula-editor:mount:start:scope");
|
|
399
|
+
let destroyed = false;
|
|
400
|
+
let latestLatex = options.initialLatex ?? "";
|
|
401
|
+
let handle = null;
|
|
402
|
+
const initialLatex = latestLatex.trim() ? latestLatex : EMPTY_FORMULA_PLACEHOLDER;
|
|
403
|
+
renderFormulaXEditorLoadingState(root);
|
|
404
|
+
const loadingVisibleMark = markFormulaXPerf("fx:formula-editor:loading-visible");
|
|
405
|
+
measureFormulaXPerf("fx:formula-editor:loading-visible", mountStart, loadingVisibleMark);
|
|
406
|
+
clearFormulaXPerfMarks(loadingVisibleMark);
|
|
343
407
|
const readyPromise = mountKityEditor(root, {
|
|
344
408
|
initialLatex,
|
|
345
409
|
height: options.height ?? "100%",
|
|
@@ -353,6 +417,9 @@ function mountFormulaXEditor(root, options = {}) {
|
|
|
353
417
|
nextHandle.destroy();
|
|
354
418
|
throw new Error("FormulaX editor mount cancelled");
|
|
355
419
|
}
|
|
420
|
+
const readyMark = markFormulaXPerf("fx:kity-editor:ready");
|
|
421
|
+
measureFormulaXPerf("fx:kity-editor:ready", mountStart, readyMark);
|
|
422
|
+
clearFormulaXPerfMarks(readyMark);
|
|
356
423
|
handle = nextHandle;
|
|
357
424
|
return nextHandle;
|
|
358
425
|
}).catch((error) => {
|
|
@@ -366,6 +433,8 @@ function mountFormulaXEditor(root, options = {}) {
|
|
|
366
433
|
`;
|
|
367
434
|
}
|
|
368
435
|
throw error;
|
|
436
|
+
}).finally(() => {
|
|
437
|
+
clearFormulaXPerfMarks(mountStart);
|
|
369
438
|
});
|
|
370
439
|
const getCurrentLatex = async () => {
|
|
371
440
|
const readyHandle = handle ?? await readyPromise;
|
|
@@ -391,8 +460,8 @@ function mountFormulaXEditor(root, options = {}) {
|
|
|
391
460
|
},
|
|
392
461
|
async getRenderHtml() {
|
|
393
462
|
await readyPromise;
|
|
394
|
-
await
|
|
395
|
-
return
|
|
463
|
+
await waitForKityFormulaSvgLayout(root);
|
|
464
|
+
return serializeKityFormulaFromRoot(root);
|
|
396
465
|
},
|
|
397
466
|
destroy() {
|
|
398
467
|
if (destroyed) return;
|
|
@@ -442,329 +511,17 @@ async function tryReadLatexFromKityHandle(handle) {
|
|
|
442
511
|
}
|
|
443
512
|
return null;
|
|
444
513
|
}
|
|
445
|
-
function renderCurrentFormulaAsSvgHtml(root) {
|
|
446
|
-
const svg = findFormulaSvg(root);
|
|
447
|
-
if (!svg) {
|
|
448
|
-
return "";
|
|
449
|
-
}
|
|
450
|
-
return serializeSvgForInsertion(svg);
|
|
451
|
-
}
|
|
452
|
-
async function waitForFormulaSvgLayout(root) {
|
|
453
|
-
const doc = root.ownerDocument ?? document;
|
|
454
|
-
const view = doc.defaultView ?? window;
|
|
455
|
-
await waitForDocumentFonts(doc);
|
|
456
|
-
let previous = readRenderedFormulaBox(root);
|
|
457
|
-
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
458
|
-
await waitForAnimationFrame(view);
|
|
459
|
-
const current = readRenderedFormulaBox(root);
|
|
460
|
-
if (previous && current && areSvgBoxesClose(previous, current)) {
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
previous = current;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
function findFormulaSvg(root) {
|
|
467
|
-
return root.querySelector(
|
|
468
|
-
".kf-editor-edit-area svg, .kf-editor-canvas-container svg, svg"
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
function readRenderedFormulaBox(root) {
|
|
472
|
-
const svg = findFormulaSvg(root);
|
|
473
|
-
if (!svg) {
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
return getInlineSvgContent(svg)?.box ?? readSvgBox(svg);
|
|
477
|
-
}
|
|
478
|
-
function areSvgBoxesClose(left, right) {
|
|
479
|
-
return Math.abs(left.x - right.x) < 0.01 && Math.abs(left.y - right.y) < 0.01 && Math.abs(left.width - right.width) < 0.01 && Math.abs(left.height - right.height) < 0.01;
|
|
480
|
-
}
|
|
481
|
-
async function waitForDocumentFonts(doc) {
|
|
482
|
-
if (!doc.fonts?.ready) {
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
try {
|
|
486
|
-
await doc.fonts.ready;
|
|
487
|
-
} catch {
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
function waitForAnimationFrame(view) {
|
|
491
|
-
return new Promise((resolve) => {
|
|
492
|
-
view.requestAnimationFrame(() => resolve());
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
function serializeSvgForInsertion(svg) {
|
|
496
|
-
const content = getInlineSvgContent(svg);
|
|
497
|
-
const inlineViewport = content ? createInlineSvgViewport(content.box) : null;
|
|
498
|
-
const clone = content && inlineViewport ? createInlineSvgClone(svg, content, inlineViewport) : svg.cloneNode(true);
|
|
499
|
-
uniquifySvgIds(clone);
|
|
500
|
-
sizeSvgForInlineDisplay(clone, svg, inlineViewport);
|
|
501
|
-
clone.removeAttribute("id");
|
|
502
|
-
clone.removeAttribute("xmlns");
|
|
503
|
-
clone.removeAttribute("xmlns:xlink");
|
|
504
|
-
clone.setAttribute("class", mergeClassNames(clone.getAttribute("class"), "formulax-math__svg"));
|
|
505
|
-
clone.setAttribute("focusable", "false");
|
|
506
|
-
clone.setAttribute("aria-hidden", "true");
|
|
507
|
-
clone.setAttribute("preserveAspectRatio", clone.getAttribute("preserveAspectRatio") || "xMinYMin meet");
|
|
508
|
-
return new XMLSerializer().serializeToString(clone);
|
|
509
|
-
}
|
|
510
|
-
function getInlineSvgContent(svg) {
|
|
511
|
-
const candidates = [
|
|
512
|
-
'[data-root="true"] > g[data-type="kf-editor-exp-content-box"]',
|
|
513
|
-
'g[data-type="kf-editor-exp-content-box"]',
|
|
514
|
-
'g[data-type="kf-container"]',
|
|
515
|
-
"svg > g, g"
|
|
516
|
-
];
|
|
517
|
-
for (const selector of candidates) {
|
|
518
|
-
const content = svg.querySelector(selector);
|
|
519
|
-
const rootSpace = content ? readSvgBoxInRootSpace(content) : null;
|
|
520
|
-
if (content && rootSpace) {
|
|
521
|
-
return {
|
|
522
|
-
root: content,
|
|
523
|
-
box: rootSpace.box,
|
|
524
|
-
matrix: rootSpace.matrix
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
return null;
|
|
529
|
-
}
|
|
530
|
-
function readSvgBoxInRootSpace(element) {
|
|
531
|
-
const box = readSvgBox(element);
|
|
532
|
-
const matrix = getSvgRootSpaceMatrix(element);
|
|
533
|
-
if (!box || !matrix) {
|
|
534
|
-
return null;
|
|
535
|
-
}
|
|
536
|
-
const points = [
|
|
537
|
-
{ x: box.x, y: box.y },
|
|
538
|
-
{ x: box.x + box.width, y: box.y },
|
|
539
|
-
{ x: box.x, y: box.y + box.height },
|
|
540
|
-
{ x: box.x + box.width, y: box.y + box.height }
|
|
541
|
-
].map((point) => ({
|
|
542
|
-
x: matrix.a * point.x + matrix.c * point.y + matrix.e,
|
|
543
|
-
y: matrix.b * point.x + matrix.d * point.y + matrix.f
|
|
544
|
-
}));
|
|
545
|
-
const xs = points.map((point) => point.x);
|
|
546
|
-
const ys = points.map((point) => point.y);
|
|
547
|
-
const x = Math.min(...xs);
|
|
548
|
-
const y = Math.min(...ys);
|
|
549
|
-
const width = Math.max(...xs) - x;
|
|
550
|
-
const height = Math.max(...ys) - y;
|
|
551
|
-
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
552
|
-
return null;
|
|
553
|
-
}
|
|
554
|
-
return {
|
|
555
|
-
box: { x, y, width, height },
|
|
556
|
-
matrix
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
function getSvgRootSpaceMatrix(element) {
|
|
560
|
-
const elementMatrix = typeof element.getCTM === "function" ? element.getCTM() : null;
|
|
561
|
-
const rootMatrix = typeof element.ownerSVGElement?.getCTM === "function" ? element.ownerSVGElement.getCTM() : null;
|
|
562
|
-
if (!elementMatrix) {
|
|
563
|
-
return null;
|
|
564
|
-
}
|
|
565
|
-
return rootMatrix ? multiplySvgMatrices(invertSvgMatrix(rootMatrix), elementMatrix) : toSvgMatrixLike(elementMatrix);
|
|
566
|
-
}
|
|
567
|
-
function invertSvgMatrix(matrix) {
|
|
568
|
-
const determinant = matrix.a * matrix.d - matrix.b * matrix.c;
|
|
569
|
-
if (!Number.isFinite(determinant) || determinant === 0) {
|
|
570
|
-
return {
|
|
571
|
-
a: 1,
|
|
572
|
-
b: 0,
|
|
573
|
-
c: 0,
|
|
574
|
-
d: 1,
|
|
575
|
-
e: 0,
|
|
576
|
-
f: 0
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
return {
|
|
580
|
-
a: matrix.d / determinant,
|
|
581
|
-
b: -matrix.b / determinant,
|
|
582
|
-
c: -matrix.c / determinant,
|
|
583
|
-
d: matrix.a / determinant,
|
|
584
|
-
e: (matrix.c * matrix.f - matrix.d * matrix.e) / determinant,
|
|
585
|
-
f: (matrix.b * matrix.e - matrix.a * matrix.f) / determinant
|
|
586
|
-
};
|
|
587
|
-
}
|
|
588
|
-
function multiplySvgMatrices(left, right) {
|
|
589
|
-
return {
|
|
590
|
-
a: left.a * right.a + left.c * right.b,
|
|
591
|
-
b: left.b * right.a + left.d * right.b,
|
|
592
|
-
c: left.a * right.c + left.c * right.d,
|
|
593
|
-
d: left.b * right.c + left.d * right.d,
|
|
594
|
-
e: left.a * right.e + left.c * right.f + left.e,
|
|
595
|
-
f: left.b * right.e + left.d * right.f + left.f
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
function toSvgMatrixLike(matrix) {
|
|
599
|
-
return {
|
|
600
|
-
a: matrix.a,
|
|
601
|
-
b: matrix.b,
|
|
602
|
-
c: matrix.c,
|
|
603
|
-
d: matrix.d,
|
|
604
|
-
e: matrix.e,
|
|
605
|
-
f: matrix.f
|
|
606
|
-
};
|
|
607
|
-
}
|
|
608
|
-
function readSvgBox(element) {
|
|
609
|
-
if (typeof element.getBBox !== "function") {
|
|
610
|
-
return null;
|
|
611
|
-
}
|
|
612
|
-
try {
|
|
613
|
-
const box = element.getBBox();
|
|
614
|
-
if (!Number.isFinite(box.width) || !Number.isFinite(box.height) || box.width <= 0 || box.height <= 0) {
|
|
615
|
-
return null;
|
|
616
|
-
}
|
|
617
|
-
return {
|
|
618
|
-
x: box.x,
|
|
619
|
-
y: box.y,
|
|
620
|
-
width: box.width,
|
|
621
|
-
height: box.height
|
|
622
|
-
};
|
|
623
|
-
} catch {
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
function createInlineSvgViewport(contentBox) {
|
|
628
|
-
const edgePadding = Math.max(0.5, Math.min(contentBox.width, contentBox.height) * 6e-3);
|
|
629
|
-
const inset = edgePadding / 2;
|
|
630
|
-
return {
|
|
631
|
-
x: contentBox.x - inset,
|
|
632
|
-
y: contentBox.y - inset,
|
|
633
|
-
width: contentBox.width + edgePadding,
|
|
634
|
-
height: contentBox.height + edgePadding
|
|
635
|
-
};
|
|
636
|
-
}
|
|
637
|
-
function createInlineSvgClone(source, content, viewport) {
|
|
638
|
-
const clone = source.cloneNode(false);
|
|
639
|
-
const ownerDocument = source.ownerDocument;
|
|
640
|
-
copySvgRootAttributes(source, clone);
|
|
641
|
-
clone.setAttribute(
|
|
642
|
-
"viewBox",
|
|
643
|
-
`0 0 ${roundLength(viewport.width)} ${roundLength(viewport.height)}`
|
|
644
|
-
);
|
|
645
|
-
Array.from(source.children).forEach((child) => {
|
|
646
|
-
if (child.tagName.toLowerCase() === "defs") {
|
|
647
|
-
clone.appendChild(child.cloneNode(true));
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
const wrapper = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
651
|
-
wrapper.setAttribute(
|
|
652
|
-
"transform",
|
|
653
|
-
`translate(${roundLength(-viewport.x)} ${roundLength(-viewport.y)})`
|
|
654
|
-
);
|
|
655
|
-
const flattened = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
656
|
-
flattened.setAttribute(
|
|
657
|
-
"transform",
|
|
658
|
-
`matrix(${roundLength(content.matrix.a)} ${roundLength(content.matrix.b)} ${roundLength(content.matrix.c)} ${roundLength(content.matrix.d)} ${roundLength(content.matrix.e)} ${roundLength(content.matrix.f)})`
|
|
659
|
-
);
|
|
660
|
-
flattened.appendChild(content.root.cloneNode(true));
|
|
661
|
-
wrapper.appendChild(flattened);
|
|
662
|
-
clone.appendChild(wrapper);
|
|
663
|
-
return clone;
|
|
664
|
-
}
|
|
665
|
-
function copySvgRootAttributes(source, target) {
|
|
666
|
-
const excluded = /* @__PURE__ */ new Set([
|
|
667
|
-
"id",
|
|
668
|
-
"width",
|
|
669
|
-
"height",
|
|
670
|
-
"viewBox",
|
|
671
|
-
"class",
|
|
672
|
-
"focusable",
|
|
673
|
-
"aria-hidden",
|
|
674
|
-
"xmlns",
|
|
675
|
-
"xmlns:xlink"
|
|
676
|
-
]);
|
|
677
|
-
Array.from(source.attributes).forEach((attribute) => {
|
|
678
|
-
if (excluded.has(attribute.name)) return;
|
|
679
|
-
target.setAttribute(attribute.name, attribute.value);
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
function sizeSvgForInlineDisplay(clone, source, viewport) {
|
|
683
|
-
const viewBox = clone.viewBox?.baseVal;
|
|
684
|
-
const rect = source.getBoundingClientRect();
|
|
685
|
-
const width = viewport?.width || viewBox?.width || rect.width || Number(clone.getAttribute("width")) || 1;
|
|
686
|
-
const height = viewport?.height || viewBox?.height || rect.height || Number(clone.getAttribute("height")) || 1;
|
|
687
|
-
const ratio = Math.max(0.1, width / Math.max(1, height));
|
|
688
|
-
const inlineHeightEm = 0.875;
|
|
689
|
-
const inlineWidthEm = Math.min(40, Math.max(0.75, ratio * inlineHeightEm));
|
|
690
|
-
clone.setAttribute("width", roundLength(width));
|
|
691
|
-
clone.setAttribute("height", roundLength(height));
|
|
692
|
-
clone.setAttribute(
|
|
693
|
-
"style",
|
|
694
|
-
mergeInlineStyles2(
|
|
695
|
-
clone.getAttribute("style"),
|
|
696
|
-
`width:${roundLength(inlineWidthEm)}em`,
|
|
697
|
-
`height:${inlineHeightEm}em`
|
|
698
|
-
)
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
function roundLength(value) {
|
|
702
|
-
return String(Math.round(value * 1e3) / 1e3);
|
|
703
|
-
}
|
|
704
|
-
function uniquifySvgIds(svg) {
|
|
705
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
706
|
-
const prefix = `fx-${randomIdPrefix()}-`;
|
|
707
|
-
const elementsWithId = svg.querySelectorAll("[id]");
|
|
708
|
-
elementsWithId.forEach((element) => {
|
|
709
|
-
const id = element.getAttribute("id");
|
|
710
|
-
if (!id) return;
|
|
711
|
-
const nextId = `${prefix}${id}`;
|
|
712
|
-
idMap.set(id, nextId);
|
|
713
|
-
element.setAttribute("id", nextId);
|
|
714
|
-
});
|
|
715
|
-
if (!idMap.size) return;
|
|
716
|
-
svg.querySelectorAll("*").forEach((element) => {
|
|
717
|
-
Array.from(element.attributes).forEach((attribute) => {
|
|
718
|
-
const nextValue = rewriteSvgReferences(attribute.value, idMap);
|
|
719
|
-
if (nextValue !== attribute.value) {
|
|
720
|
-
element.setAttribute(attribute.name, nextValue);
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
function randomIdPrefix() {
|
|
726
|
-
return Math.random().toString(36).slice(2, 5).padEnd(3, "0");
|
|
727
|
-
}
|
|
728
|
-
function rewriteSvgReferences(value, idMap) {
|
|
729
|
-
let nextValue = value;
|
|
730
|
-
idMap.forEach((nextId, id) => {
|
|
731
|
-
nextValue = nextValue.replaceAll(`#${id}`, `#${nextId}`).replaceAll(`url(${id})`, `url(${nextId})`).replaceAll(`url(#${id})`, `url(#${nextId})`);
|
|
732
|
-
});
|
|
733
|
-
return nextValue;
|
|
734
|
-
}
|
|
735
|
-
function mergeClassNames(...values) {
|
|
736
|
-
return values.flatMap((value) => value?.split(/\s+/) ?? []).filter(Boolean).filter((value, index, list) => list.indexOf(value) === index).join(" ");
|
|
737
|
-
}
|
|
738
|
-
function mergeInlineStyles2(...values) {
|
|
739
|
-
return values.flatMap((value) => value?.split(";") ?? []).map((value) => value.trim()).filter(Boolean).join("; ");
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// src/index.ts
|
|
743
|
-
import {
|
|
744
|
-
FormulaXEditor,
|
|
745
|
-
createKityEditor,
|
|
746
|
-
ensureKityRuntime,
|
|
747
|
-
mountKityEditor as mountKityEditor2
|
|
748
|
-
} from "@formulaxjs/kity-runtime";
|
|
749
514
|
export {
|
|
750
|
-
|
|
751
|
-
DEFAULT_FORMULA_CLASS,
|
|
752
|
-
FORMULA_FLAG_ATTRIBUTE,
|
|
753
|
-
FormulaXEditor,
|
|
754
|
-
createFormulaElement,
|
|
755
|
-
createFormulaMarkup,
|
|
756
|
-
createKityEditor,
|
|
515
|
+
clearFormulaXPerfMarks,
|
|
757
516
|
ensureFormulaXModalStyles,
|
|
758
|
-
ensureKityRuntime,
|
|
759
|
-
escapeAttribute,
|
|
760
|
-
escapeHtml,
|
|
761
|
-
findFormulaElement,
|
|
762
517
|
formulaXModalStyles,
|
|
763
|
-
|
|
764
|
-
|
|
518
|
+
markFormulaXPerf,
|
|
519
|
+
measureFormulaXPerf,
|
|
765
520
|
mountFormulaXEditor,
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
521
|
+
preloadFormulaXEditor,
|
|
522
|
+
recordFormulaXPerfPoint,
|
|
523
|
+
renderFormulaXEditorLoadingState,
|
|
524
|
+
scheduleFormulaXEditorPreload,
|
|
525
|
+
waitForFormulaXAnimationFrame
|
|
769
526
|
};
|
|
770
527
|
//# sourceMappingURL=index.js.map
|