@aznabee/freehand-ui 0.1.7 → 0.2.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/README.md +38 -6
- package/dist/freehand-ui.cjs +70 -90
- package/dist/freehand-ui.js +70 -90
- package/dist/native.cjs +2432 -0
- package/dist/native.d.ts +51 -0
- package/dist/native.js +2427 -0
- package/dist/react.cjs +70 -90
- package/dist/react.js +70 -90
- package/native/package.json +7 -0
- package/package.json +18 -2
- package/react/package.json +7 -0
package/dist/react.cjs
CHANGED
|
@@ -317,6 +317,10 @@ var DEFAULT_OPTIONS = {
|
|
|
317
317
|
*/
|
|
318
318
|
zIndex: null
|
|
319
319
|
};
|
|
320
|
+
var OVERLAY_CLASS = "doodle-ui-overlay";
|
|
321
|
+
function isOverlayNode(node) {
|
|
322
|
+
return node?.classList?.contains(OVERLAY_CLASS) ?? false;
|
|
323
|
+
}
|
|
320
324
|
function resolveElement(target) {
|
|
321
325
|
if (!target) return null;
|
|
322
326
|
if (typeof target === "string") {
|
|
@@ -422,45 +426,21 @@ function resolveOverlayZIndex(element, override) {
|
|
|
422
426
|
}
|
|
423
427
|
return null;
|
|
424
428
|
}
|
|
425
|
-
var ITEM_PLACEMENT_PROPERTIES = [
|
|
426
|
-
"order",
|
|
427
|
-
"flexGrow",
|
|
428
|
-
"flexShrink",
|
|
429
|
-
"flexBasis",
|
|
430
|
-
"alignSelf",
|
|
431
|
-
"justifySelf",
|
|
432
|
-
"gridColumn",
|
|
433
|
-
"gridRow",
|
|
434
|
-
"gridArea"
|
|
435
|
-
];
|
|
436
|
-
function isInlineDisplay(computed) {
|
|
437
|
-
return computed.display.startsWith("inline");
|
|
438
|
-
}
|
|
439
429
|
function insertOverlaySvg(svg, element) {
|
|
440
|
-
const
|
|
441
|
-
if (
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
wrapper.style.display = isInlineDisplay(computed) ? "inline-block" : "block";
|
|
449
|
-
for (const property of ITEM_PLACEMENT_PROPERTIES) {
|
|
450
|
-
const value = computed[property];
|
|
451
|
-
if (value) wrapper.style[property] = value;
|
|
452
|
-
}
|
|
453
|
-
parent.replaceChild(wrapper, element);
|
|
454
|
-
wrapper.appendChild(element);
|
|
455
|
-
wrapper.appendChild(svg);
|
|
456
|
-
return wrapper;
|
|
457
|
-
}
|
|
458
|
-
function removeOverlaySvg(svg, wrapper, element) {
|
|
430
|
+
const setPosition = getComputedStyle(element).position === "static";
|
|
431
|
+
if (setPosition) {
|
|
432
|
+
element.style.position = "relative";
|
|
433
|
+
}
|
|
434
|
+
element.appendChild(svg);
|
|
435
|
+
return setPosition;
|
|
436
|
+
}
|
|
437
|
+
function removeOverlaySvg(svg, element, clearPosition) {
|
|
459
438
|
if (svg.parentNode) {
|
|
460
439
|
svg.parentNode.removeChild(svg);
|
|
461
440
|
}
|
|
462
|
-
if (
|
|
463
|
-
|
|
441
|
+
if (clearPosition) {
|
|
442
|
+
element.style.position = "";
|
|
443
|
+
}
|
|
464
444
|
}
|
|
465
445
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
466
446
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -487,7 +467,10 @@ function averageCornerRadius(element) {
|
|
|
487
467
|
function detectBorderRadius(element) {
|
|
488
468
|
const own = averageCornerRadius(element);
|
|
489
469
|
if (own > 0) return own;
|
|
490
|
-
const
|
|
470
|
+
const content = Array.from(element.children).filter(
|
|
471
|
+
(node) => !isOverlayNode(node)
|
|
472
|
+
);
|
|
473
|
+
const child = content.length === 1 ? content[0] : null;
|
|
491
474
|
if (!child) return 0;
|
|
492
475
|
const outer = element.getBoundingClientRect();
|
|
493
476
|
const inner = child.getBoundingClientRect();
|
|
@@ -518,22 +501,6 @@ function relativeRect(outer, inner) {
|
|
|
518
501
|
radius: inner.radius
|
|
519
502
|
};
|
|
520
503
|
}
|
|
521
|
-
function unionRects(rects, margin = 0) {
|
|
522
|
-
if (rects.length === 0) {
|
|
523
|
-
return { x: 0, y: 0, width: 0, height: 0, radius: 0 };
|
|
524
|
-
}
|
|
525
|
-
const minX = Math.min(...rects.map((r) => r.x)) - margin;
|
|
526
|
-
const minY = Math.min(...rects.map((r) => r.y)) - margin;
|
|
527
|
-
const maxX = Math.max(...rects.map((r) => r.x + r.width)) + margin;
|
|
528
|
-
const maxY = Math.max(...rects.map((r) => r.y + r.height)) + margin;
|
|
529
|
-
return {
|
|
530
|
-
x: minX,
|
|
531
|
-
y: minY,
|
|
532
|
-
width: maxX - minX,
|
|
533
|
-
height: maxY - minY,
|
|
534
|
-
radius: 0
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
504
|
function inflateRect(rect, amount) {
|
|
538
505
|
return {
|
|
539
506
|
x: rect.x - amount,
|
|
@@ -615,8 +582,13 @@ function annotationAnchor(position, rect, offset = 12) {
|
|
|
615
582
|
return point;
|
|
616
583
|
}
|
|
617
584
|
|
|
618
|
-
// src/
|
|
585
|
+
// src/svg-dom.js
|
|
619
586
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
587
|
+
function createSvgElement(tag) {
|
|
588
|
+
return document.createElementNS(SVG_NS, tag);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/renderer.js
|
|
620
592
|
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
621
593
|
var HANDWRITTEN_FONT = '"Caveat", "Kalam", "Patrick Hand", "Bradley Hand", "Segoe Script", "Comic Sans MS", cursive';
|
|
622
594
|
var NOTE_FONT_SIZE = 19;
|
|
@@ -840,7 +812,7 @@ function createBorderBreaks(perimeter, config, random) {
|
|
|
840
812
|
return breaks;
|
|
841
813
|
}
|
|
842
814
|
function createGroup(parent, x, y, rotation = 0) {
|
|
843
|
-
const group =
|
|
815
|
+
const group = createSvgElement("g");
|
|
844
816
|
group.setAttribute(
|
|
845
817
|
"transform",
|
|
846
818
|
`translate(${fmt(x)} ${fmt(y)}) rotate(${rotation.toFixed(1)})`
|
|
@@ -849,7 +821,7 @@ function createGroup(parent, x, y, rotation = 0) {
|
|
|
849
821
|
return group;
|
|
850
822
|
}
|
|
851
823
|
function appendPath(svg, d, style) {
|
|
852
|
-
const path =
|
|
824
|
+
const path = createSvgElement("path");
|
|
853
825
|
path.setAttribute("d", d);
|
|
854
826
|
path.setAttribute("fill", style.fill ?? "none");
|
|
855
827
|
path.setAttribute("stroke", style.color);
|
|
@@ -864,7 +836,7 @@ function appendPath(svg, d, style) {
|
|
|
864
836
|
return path;
|
|
865
837
|
}
|
|
866
838
|
function appendGlow(svg, radius, style) {
|
|
867
|
-
const circle =
|
|
839
|
+
const circle = createSvgElement("circle");
|
|
868
840
|
circle.setAttribute("r", fmt(radius));
|
|
869
841
|
circle.setAttribute("fill", style.color);
|
|
870
842
|
circle.setAttribute("opacity", String(style.opacity));
|
|
@@ -1168,7 +1140,7 @@ function drawArrow(svg, rect, arrowOptions, style, seed, origin = null, context
|
|
|
1168
1140
|
tipAngle = Math.atan2(end.y - c2y, end.x - c2x);
|
|
1169
1141
|
}
|
|
1170
1142
|
}
|
|
1171
|
-
const group =
|
|
1143
|
+
const group = createSvgElement("g");
|
|
1172
1144
|
svg.appendChild(group);
|
|
1173
1145
|
const shaftPaths = [];
|
|
1174
1146
|
if (ghost) {
|
|
@@ -1258,8 +1230,8 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1258
1230
|
const lines = wrapWords(text, options.wordsPerLine ?? DEFAULT_WORDS_PER_LINE);
|
|
1259
1231
|
const lineHeight = fontSize * 1.05;
|
|
1260
1232
|
const rtl = options.rtl ?? isRtlText(text);
|
|
1261
|
-
const group =
|
|
1262
|
-
const textEl =
|
|
1233
|
+
const group = createSvgElement("g");
|
|
1234
|
+
const textEl = createSvgElement("text");
|
|
1263
1235
|
textEl.setAttribute("x", "0");
|
|
1264
1236
|
textEl.setAttribute("y", "0");
|
|
1265
1237
|
textEl.setAttribute("fill", style.color);
|
|
@@ -1278,7 +1250,7 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1278
1250
|
textEl.setAttributeNS(XML_NS, "xml:lang", options.locale);
|
|
1279
1251
|
}
|
|
1280
1252
|
const tspans = lines.map((line, index) => {
|
|
1281
|
-
const tspan =
|
|
1253
|
+
const tspan = createSvgElement("tspan");
|
|
1282
1254
|
tspan.textContent = line;
|
|
1283
1255
|
tspan.setAttribute("x", "0");
|
|
1284
1256
|
tspan.setAttribute("dy", index === 0 ? "0" : String(lineHeight));
|
|
@@ -1342,8 +1314,8 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1342
1314
|
return note.group;
|
|
1343
1315
|
}
|
|
1344
1316
|
function createOverlaySvg(left, top, width, height) {
|
|
1345
|
-
const svg =
|
|
1346
|
-
svg.setAttribute("class",
|
|
1317
|
+
const svg = createSvgElement("svg");
|
|
1318
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1347
1319
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1348
1320
|
svg.setAttribute("width", String(width));
|
|
1349
1321
|
svg.setAttribute("height", String(height));
|
|
@@ -1885,13 +1857,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1885
1857
|
function now() {
|
|
1886
1858
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1887
1859
|
}
|
|
1888
|
-
function localBand(
|
|
1860
|
+
function localBand(originLeft, inset = 8) {
|
|
1889
1861
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1890
1862
|
return {
|
|
1891
|
-
x: -
|
|
1863
|
+
x: -originLeft + inset,
|
|
1892
1864
|
width: Math.max(0, width - inset * 2)
|
|
1893
1865
|
};
|
|
1894
1866
|
}
|
|
1867
|
+
function paddingBox(element) {
|
|
1868
|
+
const rect = element.getBoundingClientRect();
|
|
1869
|
+
const style = getComputedStyle(element);
|
|
1870
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1871
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1872
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1873
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1874
|
+
return {
|
|
1875
|
+
left: rect.left + left,
|
|
1876
|
+
top: rect.top + top,
|
|
1877
|
+
width: Math.max(0, rect.width - left - right),
|
|
1878
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1895
1881
|
var DoodleOverlay = class {
|
|
1896
1882
|
/**
|
|
1897
1883
|
* @param {Element} element
|
|
@@ -1903,7 +1889,7 @@ var DoodleOverlay = class {
|
|
|
1903
1889
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1904
1890
|
this.startedAt = now();
|
|
1905
1891
|
this.svg = null;
|
|
1906
|
-
this.
|
|
1892
|
+
this.setPosition = false;
|
|
1907
1893
|
this.resizeObserver = null;
|
|
1908
1894
|
this.mutationObserver = null;
|
|
1909
1895
|
this.pendingFrame = null;
|
|
@@ -1915,14 +1901,17 @@ var DoodleOverlay = class {
|
|
|
1915
1901
|
}
|
|
1916
1902
|
mount() {
|
|
1917
1903
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1918
|
-
this.
|
|
1904
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1919
1905
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1920
1906
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1921
1907
|
this.resizeObserver.observe(this.element);
|
|
1922
1908
|
if (this.options.children) {
|
|
1923
1909
|
this.observeChildren();
|
|
1924
1910
|
}
|
|
1925
|
-
this.mutationObserver = new MutationObserver(
|
|
1911
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1912
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1913
|
+
if (relevant) this.scheduleUpdate();
|
|
1914
|
+
});
|
|
1926
1915
|
this.mutationObserver.observe(this.element, {
|
|
1927
1916
|
childList: true,
|
|
1928
1917
|
subtree: true,
|
|
@@ -1974,22 +1963,23 @@ var DoodleOverlay = class {
|
|
|
1974
1963
|
if (!this.svg || !this.element.isConnected) return;
|
|
1975
1964
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1976
1965
|
const targets = this.getTargets();
|
|
1977
|
-
const margin = this.overlayMargin();
|
|
1978
1966
|
const elapsed = now() - this.startedAt;
|
|
1979
1967
|
const absoluteRects = targets.map(
|
|
1980
1968
|
(target) => getElementBounds(target, padding, radius)
|
|
1981
1969
|
);
|
|
1982
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1983
1970
|
clearSvg(this.svg);
|
|
1984
1971
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1985
|
-
const
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
this.svg.
|
|
1990
|
-
this.svg.
|
|
1991
|
-
this.svg.
|
|
1992
|
-
this.svg.style.
|
|
1972
|
+
const box = paddingBox(this.element);
|
|
1973
|
+
const width = Math.max(1, box.width);
|
|
1974
|
+
const height = Math.max(1, box.height);
|
|
1975
|
+
const origin = { x: box.left, y: box.top };
|
|
1976
|
+
this.svg.setAttribute("width", String(width));
|
|
1977
|
+
this.svg.setAttribute("height", String(height));
|
|
1978
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1979
|
+
this.svg.style.left = "0px";
|
|
1980
|
+
this.svg.style.top = "0px";
|
|
1981
|
+
this.svg.style.width = `${width}px`;
|
|
1982
|
+
this.svg.style.height = `${height}px`;
|
|
1993
1983
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1994
1984
|
const noteStyle = {
|
|
1995
1985
|
color,
|
|
@@ -2000,12 +1990,12 @@ var DoodleOverlay = class {
|
|
|
2000
1990
|
};
|
|
2001
1991
|
const noteGroups = [];
|
|
2002
1992
|
const noteLayout = {
|
|
2003
|
-
band: localBand(
|
|
1993
|
+
band: localBand(origin.x),
|
|
2004
1994
|
gap: this.options.arrow ? 40 : 14
|
|
2005
1995
|
};
|
|
2006
1996
|
targets.forEach((target, index) => {
|
|
2007
1997
|
const absolute = absoluteRects[index];
|
|
2008
|
-
const local = relativeRect(
|
|
1998
|
+
const local = relativeRect(origin, absolute);
|
|
2009
1999
|
const seed = this.seed + index * 31;
|
|
2010
2000
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
2011
2001
|
this.svg,
|
|
@@ -2051,15 +2041,6 @@ var DoodleOverlay = class {
|
|
|
2051
2041
|
this.svg.appendChild(group);
|
|
2052
2042
|
}
|
|
2053
2043
|
}
|
|
2054
|
-
/**
|
|
2055
|
-
* Room the overlay needs beyond the element for notes, arrows and decorations.
|
|
2056
|
-
*/
|
|
2057
|
-
overlayMargin() {
|
|
2058
|
-
const { note, arrow, decorations } = this.options;
|
|
2059
|
-
if (!note && !arrow && !decorations) return 48;
|
|
2060
|
-
const { text } = resolveLocalizedText(note?.text, note?.locale);
|
|
2061
|
-
return Math.max(96, Math.min(240, 80 + text.length * 8));
|
|
2062
|
-
}
|
|
2063
2044
|
destroy() {
|
|
2064
2045
|
if (this.pendingFrame != null) {
|
|
2065
2046
|
cancelFrame(this.pendingFrame);
|
|
@@ -2082,10 +2063,9 @@ var DoodleOverlay = class {
|
|
|
2082
2063
|
}
|
|
2083
2064
|
this.listeners = [];
|
|
2084
2065
|
if (this.svg) {
|
|
2085
|
-
removeOverlaySvg(this.svg, this.
|
|
2066
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2086
2067
|
}
|
|
2087
2068
|
this.svg = null;
|
|
2088
|
-
this.wrapper = null;
|
|
2089
2069
|
instances.delete(this.element);
|
|
2090
2070
|
}
|
|
2091
2071
|
};
|
package/dist/react.js
CHANGED
|
@@ -300,6 +300,10 @@ var DEFAULT_OPTIONS = {
|
|
|
300
300
|
*/
|
|
301
301
|
zIndex: null
|
|
302
302
|
};
|
|
303
|
+
var OVERLAY_CLASS = "doodle-ui-overlay";
|
|
304
|
+
function isOverlayNode(node) {
|
|
305
|
+
return node?.classList?.contains(OVERLAY_CLASS) ?? false;
|
|
306
|
+
}
|
|
303
307
|
function resolveElement(target) {
|
|
304
308
|
if (!target) return null;
|
|
305
309
|
if (typeof target === "string") {
|
|
@@ -405,45 +409,21 @@ function resolveOverlayZIndex(element, override) {
|
|
|
405
409
|
}
|
|
406
410
|
return null;
|
|
407
411
|
}
|
|
408
|
-
var ITEM_PLACEMENT_PROPERTIES = [
|
|
409
|
-
"order",
|
|
410
|
-
"flexGrow",
|
|
411
|
-
"flexShrink",
|
|
412
|
-
"flexBasis",
|
|
413
|
-
"alignSelf",
|
|
414
|
-
"justifySelf",
|
|
415
|
-
"gridColumn",
|
|
416
|
-
"gridRow",
|
|
417
|
-
"gridArea"
|
|
418
|
-
];
|
|
419
|
-
function isInlineDisplay(computed) {
|
|
420
|
-
return computed.display.startsWith("inline");
|
|
421
|
-
}
|
|
422
412
|
function insertOverlaySvg(svg, element) {
|
|
423
|
-
const
|
|
424
|
-
if (
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
wrapper.style.display = isInlineDisplay(computed) ? "inline-block" : "block";
|
|
432
|
-
for (const property of ITEM_PLACEMENT_PROPERTIES) {
|
|
433
|
-
const value = computed[property];
|
|
434
|
-
if (value) wrapper.style[property] = value;
|
|
435
|
-
}
|
|
436
|
-
parent.replaceChild(wrapper, element);
|
|
437
|
-
wrapper.appendChild(element);
|
|
438
|
-
wrapper.appendChild(svg);
|
|
439
|
-
return wrapper;
|
|
440
|
-
}
|
|
441
|
-
function removeOverlaySvg(svg, wrapper, element) {
|
|
413
|
+
const setPosition = getComputedStyle(element).position === "static";
|
|
414
|
+
if (setPosition) {
|
|
415
|
+
element.style.position = "relative";
|
|
416
|
+
}
|
|
417
|
+
element.appendChild(svg);
|
|
418
|
+
return setPosition;
|
|
419
|
+
}
|
|
420
|
+
function removeOverlaySvg(svg, element, clearPosition) {
|
|
442
421
|
if (svg.parentNode) {
|
|
443
422
|
svg.parentNode.removeChild(svg);
|
|
444
423
|
}
|
|
445
|
-
if (
|
|
446
|
-
|
|
424
|
+
if (clearPosition) {
|
|
425
|
+
element.style.position = "";
|
|
426
|
+
}
|
|
447
427
|
}
|
|
448
428
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
449
429
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -470,7 +450,10 @@ function averageCornerRadius(element) {
|
|
|
470
450
|
function detectBorderRadius(element) {
|
|
471
451
|
const own = averageCornerRadius(element);
|
|
472
452
|
if (own > 0) return own;
|
|
473
|
-
const
|
|
453
|
+
const content = Array.from(element.children).filter(
|
|
454
|
+
(node) => !isOverlayNode(node)
|
|
455
|
+
);
|
|
456
|
+
const child = content.length === 1 ? content[0] : null;
|
|
474
457
|
if (!child) return 0;
|
|
475
458
|
const outer = element.getBoundingClientRect();
|
|
476
459
|
const inner = child.getBoundingClientRect();
|
|
@@ -501,22 +484,6 @@ function relativeRect(outer, inner) {
|
|
|
501
484
|
radius: inner.radius
|
|
502
485
|
};
|
|
503
486
|
}
|
|
504
|
-
function unionRects(rects, margin = 0) {
|
|
505
|
-
if (rects.length === 0) {
|
|
506
|
-
return { x: 0, y: 0, width: 0, height: 0, radius: 0 };
|
|
507
|
-
}
|
|
508
|
-
const minX = Math.min(...rects.map((r) => r.x)) - margin;
|
|
509
|
-
const minY = Math.min(...rects.map((r) => r.y)) - margin;
|
|
510
|
-
const maxX = Math.max(...rects.map((r) => r.x + r.width)) + margin;
|
|
511
|
-
const maxY = Math.max(...rects.map((r) => r.y + r.height)) + margin;
|
|
512
|
-
return {
|
|
513
|
-
x: minX,
|
|
514
|
-
y: minY,
|
|
515
|
-
width: maxX - minX,
|
|
516
|
-
height: maxY - minY,
|
|
517
|
-
radius: 0
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
487
|
function inflateRect(rect, amount) {
|
|
521
488
|
return {
|
|
522
489
|
x: rect.x - amount,
|
|
@@ -598,8 +565,13 @@ function annotationAnchor(position, rect, offset = 12) {
|
|
|
598
565
|
return point;
|
|
599
566
|
}
|
|
600
567
|
|
|
601
|
-
// src/
|
|
568
|
+
// src/svg-dom.js
|
|
602
569
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
570
|
+
function createSvgElement(tag) {
|
|
571
|
+
return document.createElementNS(SVG_NS, tag);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/renderer.js
|
|
603
575
|
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
604
576
|
var HANDWRITTEN_FONT = '"Caveat", "Kalam", "Patrick Hand", "Bradley Hand", "Segoe Script", "Comic Sans MS", cursive';
|
|
605
577
|
var NOTE_FONT_SIZE = 19;
|
|
@@ -823,7 +795,7 @@ function createBorderBreaks(perimeter, config, random) {
|
|
|
823
795
|
return breaks;
|
|
824
796
|
}
|
|
825
797
|
function createGroup(parent, x, y, rotation = 0) {
|
|
826
|
-
const group =
|
|
798
|
+
const group = createSvgElement("g");
|
|
827
799
|
group.setAttribute(
|
|
828
800
|
"transform",
|
|
829
801
|
`translate(${fmt(x)} ${fmt(y)}) rotate(${rotation.toFixed(1)})`
|
|
@@ -832,7 +804,7 @@ function createGroup(parent, x, y, rotation = 0) {
|
|
|
832
804
|
return group;
|
|
833
805
|
}
|
|
834
806
|
function appendPath(svg, d, style) {
|
|
835
|
-
const path =
|
|
807
|
+
const path = createSvgElement("path");
|
|
836
808
|
path.setAttribute("d", d);
|
|
837
809
|
path.setAttribute("fill", style.fill ?? "none");
|
|
838
810
|
path.setAttribute("stroke", style.color);
|
|
@@ -847,7 +819,7 @@ function appendPath(svg, d, style) {
|
|
|
847
819
|
return path;
|
|
848
820
|
}
|
|
849
821
|
function appendGlow(svg, radius, style) {
|
|
850
|
-
const circle =
|
|
822
|
+
const circle = createSvgElement("circle");
|
|
851
823
|
circle.setAttribute("r", fmt(radius));
|
|
852
824
|
circle.setAttribute("fill", style.color);
|
|
853
825
|
circle.setAttribute("opacity", String(style.opacity));
|
|
@@ -1151,7 +1123,7 @@ function drawArrow(svg, rect, arrowOptions, style, seed, origin = null, context
|
|
|
1151
1123
|
tipAngle = Math.atan2(end.y - c2y, end.x - c2x);
|
|
1152
1124
|
}
|
|
1153
1125
|
}
|
|
1154
|
-
const group =
|
|
1126
|
+
const group = createSvgElement("g");
|
|
1155
1127
|
svg.appendChild(group);
|
|
1156
1128
|
const shaftPaths = [];
|
|
1157
1129
|
if (ghost) {
|
|
@@ -1241,8 +1213,8 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1241
1213
|
const lines = wrapWords(text, options.wordsPerLine ?? DEFAULT_WORDS_PER_LINE);
|
|
1242
1214
|
const lineHeight = fontSize * 1.05;
|
|
1243
1215
|
const rtl = options.rtl ?? isRtlText(text);
|
|
1244
|
-
const group =
|
|
1245
|
-
const textEl =
|
|
1216
|
+
const group = createSvgElement("g");
|
|
1217
|
+
const textEl = createSvgElement("text");
|
|
1246
1218
|
textEl.setAttribute("x", "0");
|
|
1247
1219
|
textEl.setAttribute("y", "0");
|
|
1248
1220
|
textEl.setAttribute("fill", style.color);
|
|
@@ -1261,7 +1233,7 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1261
1233
|
textEl.setAttributeNS(XML_NS, "xml:lang", options.locale);
|
|
1262
1234
|
}
|
|
1263
1235
|
const tspans = lines.map((line, index) => {
|
|
1264
|
-
const tspan =
|
|
1236
|
+
const tspan = createSvgElement("tspan");
|
|
1265
1237
|
tspan.textContent = line;
|
|
1266
1238
|
tspan.setAttribute("x", "0");
|
|
1267
1239
|
tspan.setAttribute("dy", index === 0 ? "0" : String(lineHeight));
|
|
@@ -1325,8 +1297,8 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1325
1297
|
return note.group;
|
|
1326
1298
|
}
|
|
1327
1299
|
function createOverlaySvg(left, top, width, height) {
|
|
1328
|
-
const svg =
|
|
1329
|
-
svg.setAttribute("class",
|
|
1300
|
+
const svg = createSvgElement("svg");
|
|
1301
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1330
1302
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1331
1303
|
svg.setAttribute("width", String(width));
|
|
1332
1304
|
svg.setAttribute("height", String(height));
|
|
@@ -1868,13 +1840,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1868
1840
|
function now() {
|
|
1869
1841
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1870
1842
|
}
|
|
1871
|
-
function localBand(
|
|
1843
|
+
function localBand(originLeft, inset = 8) {
|
|
1872
1844
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1873
1845
|
return {
|
|
1874
|
-
x: -
|
|
1846
|
+
x: -originLeft + inset,
|
|
1875
1847
|
width: Math.max(0, width - inset * 2)
|
|
1876
1848
|
};
|
|
1877
1849
|
}
|
|
1850
|
+
function paddingBox(element) {
|
|
1851
|
+
const rect = element.getBoundingClientRect();
|
|
1852
|
+
const style = getComputedStyle(element);
|
|
1853
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1854
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1855
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1856
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1857
|
+
return {
|
|
1858
|
+
left: rect.left + left,
|
|
1859
|
+
top: rect.top + top,
|
|
1860
|
+
width: Math.max(0, rect.width - left - right),
|
|
1861
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1878
1864
|
var DoodleOverlay = class {
|
|
1879
1865
|
/**
|
|
1880
1866
|
* @param {Element} element
|
|
@@ -1886,7 +1872,7 @@ var DoodleOverlay = class {
|
|
|
1886
1872
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1887
1873
|
this.startedAt = now();
|
|
1888
1874
|
this.svg = null;
|
|
1889
|
-
this.
|
|
1875
|
+
this.setPosition = false;
|
|
1890
1876
|
this.resizeObserver = null;
|
|
1891
1877
|
this.mutationObserver = null;
|
|
1892
1878
|
this.pendingFrame = null;
|
|
@@ -1898,14 +1884,17 @@ var DoodleOverlay = class {
|
|
|
1898
1884
|
}
|
|
1899
1885
|
mount() {
|
|
1900
1886
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1901
|
-
this.
|
|
1887
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1902
1888
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1903
1889
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1904
1890
|
this.resizeObserver.observe(this.element);
|
|
1905
1891
|
if (this.options.children) {
|
|
1906
1892
|
this.observeChildren();
|
|
1907
1893
|
}
|
|
1908
|
-
this.mutationObserver = new MutationObserver(
|
|
1894
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1895
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1896
|
+
if (relevant) this.scheduleUpdate();
|
|
1897
|
+
});
|
|
1909
1898
|
this.mutationObserver.observe(this.element, {
|
|
1910
1899
|
childList: true,
|
|
1911
1900
|
subtree: true,
|
|
@@ -1957,22 +1946,23 @@ var DoodleOverlay = class {
|
|
|
1957
1946
|
if (!this.svg || !this.element.isConnected) return;
|
|
1958
1947
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1959
1948
|
const targets = this.getTargets();
|
|
1960
|
-
const margin = this.overlayMargin();
|
|
1961
1949
|
const elapsed = now() - this.startedAt;
|
|
1962
1950
|
const absoluteRects = targets.map(
|
|
1963
1951
|
(target) => getElementBounds(target, padding, radius)
|
|
1964
1952
|
);
|
|
1965
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1966
1953
|
clearSvg(this.svg);
|
|
1967
1954
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1968
|
-
const
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
this.svg.
|
|
1973
|
-
this.svg.
|
|
1974
|
-
this.svg.
|
|
1975
|
-
this.svg.style.
|
|
1955
|
+
const box = paddingBox(this.element);
|
|
1956
|
+
const width = Math.max(1, box.width);
|
|
1957
|
+
const height = Math.max(1, box.height);
|
|
1958
|
+
const origin = { x: box.left, y: box.top };
|
|
1959
|
+
this.svg.setAttribute("width", String(width));
|
|
1960
|
+
this.svg.setAttribute("height", String(height));
|
|
1961
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1962
|
+
this.svg.style.left = "0px";
|
|
1963
|
+
this.svg.style.top = "0px";
|
|
1964
|
+
this.svg.style.width = `${width}px`;
|
|
1965
|
+
this.svg.style.height = `${height}px`;
|
|
1976
1966
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1977
1967
|
const noteStyle = {
|
|
1978
1968
|
color,
|
|
@@ -1983,12 +1973,12 @@ var DoodleOverlay = class {
|
|
|
1983
1973
|
};
|
|
1984
1974
|
const noteGroups = [];
|
|
1985
1975
|
const noteLayout = {
|
|
1986
|
-
band: localBand(
|
|
1976
|
+
band: localBand(origin.x),
|
|
1987
1977
|
gap: this.options.arrow ? 40 : 14
|
|
1988
1978
|
};
|
|
1989
1979
|
targets.forEach((target, index) => {
|
|
1990
1980
|
const absolute = absoluteRects[index];
|
|
1991
|
-
const local = relativeRect(
|
|
1981
|
+
const local = relativeRect(origin, absolute);
|
|
1992
1982
|
const seed = this.seed + index * 31;
|
|
1993
1983
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
1994
1984
|
this.svg,
|
|
@@ -2034,15 +2024,6 @@ var DoodleOverlay = class {
|
|
|
2034
2024
|
this.svg.appendChild(group);
|
|
2035
2025
|
}
|
|
2036
2026
|
}
|
|
2037
|
-
/**
|
|
2038
|
-
* Room the overlay needs beyond the element for notes, arrows and decorations.
|
|
2039
|
-
*/
|
|
2040
|
-
overlayMargin() {
|
|
2041
|
-
const { note, arrow, decorations } = this.options;
|
|
2042
|
-
if (!note && !arrow && !decorations) return 48;
|
|
2043
|
-
const { text } = resolveLocalizedText(note?.text, note?.locale);
|
|
2044
|
-
return Math.max(96, Math.min(240, 80 + text.length * 8));
|
|
2045
|
-
}
|
|
2046
2027
|
destroy() {
|
|
2047
2028
|
if (this.pendingFrame != null) {
|
|
2048
2029
|
cancelFrame(this.pendingFrame);
|
|
@@ -2065,10 +2046,9 @@ var DoodleOverlay = class {
|
|
|
2065
2046
|
}
|
|
2066
2047
|
this.listeners = [];
|
|
2067
2048
|
if (this.svg) {
|
|
2068
|
-
removeOverlaySvg(this.svg, this.
|
|
2049
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2069
2050
|
}
|
|
2070
2051
|
this.svg = null;
|
|
2071
|
-
this.wrapper = null;
|
|
2072
2052
|
instances.delete(this.element);
|
|
2073
2053
|
}
|
|
2074
2054
|
};
|