@aznabee/freehand-ui 0.1.7 → 0.1.8
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 +3 -5
- package/dist/freehand-ui.cjs +56 -81
- package/dist/freehand-ui.js +56 -81
- package/dist/react.cjs +56 -81
- package/dist/react.js +56 -81
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Wrap any existing web element with a beautiful, responsive, hand-drawn doodle la
|
|
|
6
6
|
|
|
7
7
|
## What it does
|
|
8
8
|
|
|
9
|
-
`freehand-ui` adds a subtle SVG overlay on top of your UI - thin pen strokes, handwritten notes, arrows, and small decorations. It does not change your
|
|
9
|
+
`freehand-ui` adds a subtle SVG overlay on top of your UI - thin pen strokes, handwritten notes, arrows, and small decorations. It does **not** change your HTML layout or block clicks (`pointer-events: none`).
|
|
10
10
|
|
|
11
11
|
**Good for:** call-to-action buttons, feature cards, onboarding hints, playful marketing UI.
|
|
12
12
|
|
|
@@ -19,8 +19,6 @@ Wrap any existing web element with a beautiful, responsive, hand-drawn doodle la
|
|
|
19
19
|
| **Localised** | Notes in the visitor's own language, right-to-left included |
|
|
20
20
|
| **Responsive** | Stays aligned on resize, scroll, and reflow |
|
|
21
21
|
|
|
22
|
-
**DOM note:** the element is wrapped in a plain positioning `<div>` so the overlay can be mounted beside it (last child, so it paints on top) - the wrapped element keeps its own display and, in a flex/grid parent, its own placement, but a selector depending on it being a *direct* child of its original parent (`.parent > .card`, `:nth-child`) will no longer match. The overlay is also clipped by an `overflow: hidden` ancestor, since it no longer escapes the page via `position: fixed`.
|
|
23
|
-
|
|
24
22
|
**Live on [Aznabee.com](https://aznabee.com)** - see it in production on the real product.
|
|
25
23
|
|
|
26
24
|
## 
|
|
@@ -257,7 +255,7 @@ A `dotted` arrow spends its dash pattern on the dots, and one pattern cannot bot
|
|
|
257
255
|
Notes:
|
|
258
256
|
|
|
259
257
|
- **`prefers-reduced-motion: reduce` turns both gestures off** and leaves the arrow fully drawn.
|
|
260
|
-
- The overlay
|
|
258
|
+
- The overlay is rebuilt on every scroll and resize. Animations are placed by how long the overlay has been on the page rather than restarted, so scrolling never replays a draw or jolts a lean mid-cycle.
|
|
261
259
|
|
|
262
260
|
### Decorations
|
|
263
261
|
|
|
@@ -456,7 +454,7 @@ Then open:
|
|
|
456
454
|
- **Framework agnostic** - plain JavaScript at the core; React is an optional entry point
|
|
457
455
|
- **SVG based** - hand-drawn paths, not CSS borders
|
|
458
456
|
- **Non-invasive** - `pointer-events: none`, no layout changes
|
|
459
|
-
- **Responsive** -
|
|
457
|
+
- **Responsive** - `ResizeObserver`, scroll listeners, and `requestAnimationFrame`
|
|
460
458
|
- **Lightweight** - zero runtime dependencies
|
|
461
459
|
|
|
462
460
|
---
|
package/dist/freehand-ui.cjs
CHANGED
|
@@ -312,6 +312,10 @@ var DEFAULT_OPTIONS = {
|
|
|
312
312
|
*/
|
|
313
313
|
zIndex: null
|
|
314
314
|
};
|
|
315
|
+
var OVERLAY_CLASS = "doodle-ui-overlay";
|
|
316
|
+
function isOverlayNode(node) {
|
|
317
|
+
return node?.classList?.contains(OVERLAY_CLASS) ?? false;
|
|
318
|
+
}
|
|
315
319
|
function resolveElement(target) {
|
|
316
320
|
if (!target) return null;
|
|
317
321
|
if (typeof target === "string") {
|
|
@@ -417,45 +421,21 @@ function resolveOverlayZIndex(element, override) {
|
|
|
417
421
|
}
|
|
418
422
|
return null;
|
|
419
423
|
}
|
|
420
|
-
var ITEM_PLACEMENT_PROPERTIES = [
|
|
421
|
-
"order",
|
|
422
|
-
"flexGrow",
|
|
423
|
-
"flexShrink",
|
|
424
|
-
"flexBasis",
|
|
425
|
-
"alignSelf",
|
|
426
|
-
"justifySelf",
|
|
427
|
-
"gridColumn",
|
|
428
|
-
"gridRow",
|
|
429
|
-
"gridArea"
|
|
430
|
-
];
|
|
431
|
-
function isInlineDisplay(computed) {
|
|
432
|
-
return computed.display.startsWith("inline");
|
|
433
|
-
}
|
|
434
424
|
function insertOverlaySvg(svg, element) {
|
|
435
|
-
const
|
|
436
|
-
if (
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
wrapper.style.display = isInlineDisplay(computed) ? "inline-block" : "block";
|
|
444
|
-
for (const property of ITEM_PLACEMENT_PROPERTIES) {
|
|
445
|
-
const value = computed[property];
|
|
446
|
-
if (value) wrapper.style[property] = value;
|
|
447
|
-
}
|
|
448
|
-
parent.replaceChild(wrapper, element);
|
|
449
|
-
wrapper.appendChild(element);
|
|
450
|
-
wrapper.appendChild(svg);
|
|
451
|
-
return wrapper;
|
|
452
|
-
}
|
|
453
|
-
function removeOverlaySvg(svg, wrapper, element) {
|
|
425
|
+
const setPosition = getComputedStyle(element).position === "static";
|
|
426
|
+
if (setPosition) {
|
|
427
|
+
element.style.position = "relative";
|
|
428
|
+
}
|
|
429
|
+
element.appendChild(svg);
|
|
430
|
+
return setPosition;
|
|
431
|
+
}
|
|
432
|
+
function removeOverlaySvg(svg, element, clearPosition) {
|
|
454
433
|
if (svg.parentNode) {
|
|
455
434
|
svg.parentNode.removeChild(svg);
|
|
456
435
|
}
|
|
457
|
-
if (
|
|
458
|
-
|
|
436
|
+
if (clearPosition) {
|
|
437
|
+
element.style.position = "";
|
|
438
|
+
}
|
|
459
439
|
}
|
|
460
440
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
461
441
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -482,7 +462,10 @@ function averageCornerRadius(element) {
|
|
|
482
462
|
function detectBorderRadius(element) {
|
|
483
463
|
const own = averageCornerRadius(element);
|
|
484
464
|
if (own > 0) return own;
|
|
485
|
-
const
|
|
465
|
+
const content = Array.from(element.children).filter(
|
|
466
|
+
(node) => !isOverlayNode(node)
|
|
467
|
+
);
|
|
468
|
+
const child = content.length === 1 ? content[0] : null;
|
|
486
469
|
if (!child) return 0;
|
|
487
470
|
const outer = element.getBoundingClientRect();
|
|
488
471
|
const inner = child.getBoundingClientRect();
|
|
@@ -513,22 +496,6 @@ function relativeRect(outer, inner) {
|
|
|
513
496
|
radius: inner.radius
|
|
514
497
|
};
|
|
515
498
|
}
|
|
516
|
-
function unionRects(rects, margin = 0) {
|
|
517
|
-
if (rects.length === 0) {
|
|
518
|
-
return { x: 0, y: 0, width: 0, height: 0, radius: 0 };
|
|
519
|
-
}
|
|
520
|
-
const minX = Math.min(...rects.map((r) => r.x)) - margin;
|
|
521
|
-
const minY = Math.min(...rects.map((r) => r.y)) - margin;
|
|
522
|
-
const maxX = Math.max(...rects.map((r) => r.x + r.width)) + margin;
|
|
523
|
-
const maxY = Math.max(...rects.map((r) => r.y + r.height)) + margin;
|
|
524
|
-
return {
|
|
525
|
-
x: minX,
|
|
526
|
-
y: minY,
|
|
527
|
-
width: maxX - minX,
|
|
528
|
-
height: maxY - minY,
|
|
529
|
-
radius: 0
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
499
|
function inflateRect(rect, amount) {
|
|
533
500
|
return {
|
|
534
501
|
x: rect.x - amount,
|
|
@@ -1338,7 +1305,7 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1338
1305
|
}
|
|
1339
1306
|
function createOverlaySvg(left, top, width, height) {
|
|
1340
1307
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
1341
|
-
svg.setAttribute("class",
|
|
1308
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1342
1309
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1343
1310
|
svg.setAttribute("width", String(width));
|
|
1344
1311
|
svg.setAttribute("height", String(height));
|
|
@@ -1880,13 +1847,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1880
1847
|
function now() {
|
|
1881
1848
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1882
1849
|
}
|
|
1883
|
-
function localBand(
|
|
1850
|
+
function localBand(originLeft, inset = 8) {
|
|
1884
1851
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1885
1852
|
return {
|
|
1886
|
-
x: -
|
|
1853
|
+
x: -originLeft + inset,
|
|
1887
1854
|
width: Math.max(0, width - inset * 2)
|
|
1888
1855
|
};
|
|
1889
1856
|
}
|
|
1857
|
+
function paddingBox(element) {
|
|
1858
|
+
const rect = element.getBoundingClientRect();
|
|
1859
|
+
const style = getComputedStyle(element);
|
|
1860
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1861
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1862
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1863
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1864
|
+
return {
|
|
1865
|
+
left: rect.left + left,
|
|
1866
|
+
top: rect.top + top,
|
|
1867
|
+
width: Math.max(0, rect.width - left - right),
|
|
1868
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1890
1871
|
var DoodleOverlay = class {
|
|
1891
1872
|
/**
|
|
1892
1873
|
* @param {Element} element
|
|
@@ -1898,7 +1879,7 @@ var DoodleOverlay = class {
|
|
|
1898
1879
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1899
1880
|
this.startedAt = now();
|
|
1900
1881
|
this.svg = null;
|
|
1901
|
-
this.
|
|
1882
|
+
this.setPosition = false;
|
|
1902
1883
|
this.resizeObserver = null;
|
|
1903
1884
|
this.mutationObserver = null;
|
|
1904
1885
|
this.pendingFrame = null;
|
|
@@ -1910,14 +1891,17 @@ var DoodleOverlay = class {
|
|
|
1910
1891
|
}
|
|
1911
1892
|
mount() {
|
|
1912
1893
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1913
|
-
this.
|
|
1894
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1914
1895
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1915
1896
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1916
1897
|
this.resizeObserver.observe(this.element);
|
|
1917
1898
|
if (this.options.children) {
|
|
1918
1899
|
this.observeChildren();
|
|
1919
1900
|
}
|
|
1920
|
-
this.mutationObserver = new MutationObserver(
|
|
1901
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1902
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1903
|
+
if (relevant) this.scheduleUpdate();
|
|
1904
|
+
});
|
|
1921
1905
|
this.mutationObserver.observe(this.element, {
|
|
1922
1906
|
childList: true,
|
|
1923
1907
|
subtree: true,
|
|
@@ -1969,22 +1953,23 @@ var DoodleOverlay = class {
|
|
|
1969
1953
|
if (!this.svg || !this.element.isConnected) return;
|
|
1970
1954
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1971
1955
|
const targets = this.getTargets();
|
|
1972
|
-
const margin = this.overlayMargin();
|
|
1973
1956
|
const elapsed = now() - this.startedAt;
|
|
1974
1957
|
const absoluteRects = targets.map(
|
|
1975
1958
|
(target) => getElementBounds(target, padding, radius)
|
|
1976
1959
|
);
|
|
1977
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1978
1960
|
clearSvg(this.svg);
|
|
1979
1961
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
this.svg.
|
|
1985
|
-
this.svg.
|
|
1986
|
-
this.svg.
|
|
1987
|
-
this.svg.style.
|
|
1962
|
+
const box = paddingBox(this.element);
|
|
1963
|
+
const width = Math.max(1, box.width);
|
|
1964
|
+
const height = Math.max(1, box.height);
|
|
1965
|
+
const origin = { x: box.left, y: box.top };
|
|
1966
|
+
this.svg.setAttribute("width", String(width));
|
|
1967
|
+
this.svg.setAttribute("height", String(height));
|
|
1968
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1969
|
+
this.svg.style.left = "0px";
|
|
1970
|
+
this.svg.style.top = "0px";
|
|
1971
|
+
this.svg.style.width = `${width}px`;
|
|
1972
|
+
this.svg.style.height = `${height}px`;
|
|
1988
1973
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1989
1974
|
const noteStyle = {
|
|
1990
1975
|
color,
|
|
@@ -1995,12 +1980,12 @@ var DoodleOverlay = class {
|
|
|
1995
1980
|
};
|
|
1996
1981
|
const noteGroups = [];
|
|
1997
1982
|
const noteLayout = {
|
|
1998
|
-
band: localBand(
|
|
1983
|
+
band: localBand(origin.x),
|
|
1999
1984
|
gap: this.options.arrow ? 40 : 14
|
|
2000
1985
|
};
|
|
2001
1986
|
targets.forEach((target, index) => {
|
|
2002
1987
|
const absolute = absoluteRects[index];
|
|
2003
|
-
const local = relativeRect(
|
|
1988
|
+
const local = relativeRect(origin, absolute);
|
|
2004
1989
|
const seed = this.seed + index * 31;
|
|
2005
1990
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
2006
1991
|
this.svg,
|
|
@@ -2046,15 +2031,6 @@ var DoodleOverlay = class {
|
|
|
2046
2031
|
this.svg.appendChild(group);
|
|
2047
2032
|
}
|
|
2048
2033
|
}
|
|
2049
|
-
/**
|
|
2050
|
-
* Room the overlay needs beyond the element for notes, arrows and decorations.
|
|
2051
|
-
*/
|
|
2052
|
-
overlayMargin() {
|
|
2053
|
-
const { note, arrow, decorations } = this.options;
|
|
2054
|
-
if (!note && !arrow && !decorations) return 48;
|
|
2055
|
-
const { text } = resolveLocalizedText(note?.text, note?.locale);
|
|
2056
|
-
return Math.max(96, Math.min(240, 80 + text.length * 8));
|
|
2057
|
-
}
|
|
2058
2034
|
destroy() {
|
|
2059
2035
|
if (this.pendingFrame != null) {
|
|
2060
2036
|
cancelFrame(this.pendingFrame);
|
|
@@ -2077,10 +2053,9 @@ var DoodleOverlay = class {
|
|
|
2077
2053
|
}
|
|
2078
2054
|
this.listeners = [];
|
|
2079
2055
|
if (this.svg) {
|
|
2080
|
-
removeOverlaySvg(this.svg, this.
|
|
2056
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2081
2057
|
}
|
|
2082
2058
|
this.svg = null;
|
|
2083
|
-
this.wrapper = null;
|
|
2084
2059
|
instances.delete(this.element);
|
|
2085
2060
|
}
|
|
2086
2061
|
};
|
package/dist/freehand-ui.js
CHANGED
|
@@ -287,6 +287,10 @@ var DEFAULT_OPTIONS = {
|
|
|
287
287
|
*/
|
|
288
288
|
zIndex: null
|
|
289
289
|
};
|
|
290
|
+
var OVERLAY_CLASS = "doodle-ui-overlay";
|
|
291
|
+
function isOverlayNode(node) {
|
|
292
|
+
return node?.classList?.contains(OVERLAY_CLASS) ?? false;
|
|
293
|
+
}
|
|
290
294
|
function resolveElement(target) {
|
|
291
295
|
if (!target) return null;
|
|
292
296
|
if (typeof target === "string") {
|
|
@@ -392,45 +396,21 @@ function resolveOverlayZIndex(element, override) {
|
|
|
392
396
|
}
|
|
393
397
|
return null;
|
|
394
398
|
}
|
|
395
|
-
var ITEM_PLACEMENT_PROPERTIES = [
|
|
396
|
-
"order",
|
|
397
|
-
"flexGrow",
|
|
398
|
-
"flexShrink",
|
|
399
|
-
"flexBasis",
|
|
400
|
-
"alignSelf",
|
|
401
|
-
"justifySelf",
|
|
402
|
-
"gridColumn",
|
|
403
|
-
"gridRow",
|
|
404
|
-
"gridArea"
|
|
405
|
-
];
|
|
406
|
-
function isInlineDisplay(computed) {
|
|
407
|
-
return computed.display.startsWith("inline");
|
|
408
|
-
}
|
|
409
399
|
function insertOverlaySvg(svg, element) {
|
|
410
|
-
const
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
wrapper.style.display = isInlineDisplay(computed) ? "inline-block" : "block";
|
|
419
|
-
for (const property of ITEM_PLACEMENT_PROPERTIES) {
|
|
420
|
-
const value = computed[property];
|
|
421
|
-
if (value) wrapper.style[property] = value;
|
|
422
|
-
}
|
|
423
|
-
parent.replaceChild(wrapper, element);
|
|
424
|
-
wrapper.appendChild(element);
|
|
425
|
-
wrapper.appendChild(svg);
|
|
426
|
-
return wrapper;
|
|
427
|
-
}
|
|
428
|
-
function removeOverlaySvg(svg, wrapper, element) {
|
|
400
|
+
const setPosition = getComputedStyle(element).position === "static";
|
|
401
|
+
if (setPosition) {
|
|
402
|
+
element.style.position = "relative";
|
|
403
|
+
}
|
|
404
|
+
element.appendChild(svg);
|
|
405
|
+
return setPosition;
|
|
406
|
+
}
|
|
407
|
+
function removeOverlaySvg(svg, element, clearPosition) {
|
|
429
408
|
if (svg.parentNode) {
|
|
430
409
|
svg.parentNode.removeChild(svg);
|
|
431
410
|
}
|
|
432
|
-
if (
|
|
433
|
-
|
|
411
|
+
if (clearPosition) {
|
|
412
|
+
element.style.position = "";
|
|
413
|
+
}
|
|
434
414
|
}
|
|
435
415
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
436
416
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -457,7 +437,10 @@ function averageCornerRadius(element) {
|
|
|
457
437
|
function detectBorderRadius(element) {
|
|
458
438
|
const own = averageCornerRadius(element);
|
|
459
439
|
if (own > 0) return own;
|
|
460
|
-
const
|
|
440
|
+
const content = Array.from(element.children).filter(
|
|
441
|
+
(node) => !isOverlayNode(node)
|
|
442
|
+
);
|
|
443
|
+
const child = content.length === 1 ? content[0] : null;
|
|
461
444
|
if (!child) return 0;
|
|
462
445
|
const outer = element.getBoundingClientRect();
|
|
463
446
|
const inner = child.getBoundingClientRect();
|
|
@@ -488,22 +471,6 @@ function relativeRect(outer, inner) {
|
|
|
488
471
|
radius: inner.radius
|
|
489
472
|
};
|
|
490
473
|
}
|
|
491
|
-
function unionRects(rects, margin = 0) {
|
|
492
|
-
if (rects.length === 0) {
|
|
493
|
-
return { x: 0, y: 0, width: 0, height: 0, radius: 0 };
|
|
494
|
-
}
|
|
495
|
-
const minX = Math.min(...rects.map((r) => r.x)) - margin;
|
|
496
|
-
const minY = Math.min(...rects.map((r) => r.y)) - margin;
|
|
497
|
-
const maxX = Math.max(...rects.map((r) => r.x + r.width)) + margin;
|
|
498
|
-
const maxY = Math.max(...rects.map((r) => r.y + r.height)) + margin;
|
|
499
|
-
return {
|
|
500
|
-
x: minX,
|
|
501
|
-
y: minY,
|
|
502
|
-
width: maxX - minX,
|
|
503
|
-
height: maxY - minY,
|
|
504
|
-
radius: 0
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
474
|
function inflateRect(rect, amount) {
|
|
508
475
|
return {
|
|
509
476
|
x: rect.x - amount,
|
|
@@ -1313,7 +1280,7 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1313
1280
|
}
|
|
1314
1281
|
function createOverlaySvg(left, top, width, height) {
|
|
1315
1282
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
1316
|
-
svg.setAttribute("class",
|
|
1283
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1317
1284
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1318
1285
|
svg.setAttribute("width", String(width));
|
|
1319
1286
|
svg.setAttribute("height", String(height));
|
|
@@ -1855,13 +1822,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1855
1822
|
function now() {
|
|
1856
1823
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1857
1824
|
}
|
|
1858
|
-
function localBand(
|
|
1825
|
+
function localBand(originLeft, inset = 8) {
|
|
1859
1826
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1860
1827
|
return {
|
|
1861
|
-
x: -
|
|
1828
|
+
x: -originLeft + inset,
|
|
1862
1829
|
width: Math.max(0, width - inset * 2)
|
|
1863
1830
|
};
|
|
1864
1831
|
}
|
|
1832
|
+
function paddingBox(element) {
|
|
1833
|
+
const rect = element.getBoundingClientRect();
|
|
1834
|
+
const style = getComputedStyle(element);
|
|
1835
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1836
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1837
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1838
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1839
|
+
return {
|
|
1840
|
+
left: rect.left + left,
|
|
1841
|
+
top: rect.top + top,
|
|
1842
|
+
width: Math.max(0, rect.width - left - right),
|
|
1843
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1865
1846
|
var DoodleOverlay = class {
|
|
1866
1847
|
/**
|
|
1867
1848
|
* @param {Element} element
|
|
@@ -1873,7 +1854,7 @@ var DoodleOverlay = class {
|
|
|
1873
1854
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1874
1855
|
this.startedAt = now();
|
|
1875
1856
|
this.svg = null;
|
|
1876
|
-
this.
|
|
1857
|
+
this.setPosition = false;
|
|
1877
1858
|
this.resizeObserver = null;
|
|
1878
1859
|
this.mutationObserver = null;
|
|
1879
1860
|
this.pendingFrame = null;
|
|
@@ -1885,14 +1866,17 @@ var DoodleOverlay = class {
|
|
|
1885
1866
|
}
|
|
1886
1867
|
mount() {
|
|
1887
1868
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1888
|
-
this.
|
|
1869
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1889
1870
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1890
1871
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1891
1872
|
this.resizeObserver.observe(this.element);
|
|
1892
1873
|
if (this.options.children) {
|
|
1893
1874
|
this.observeChildren();
|
|
1894
1875
|
}
|
|
1895
|
-
this.mutationObserver = new MutationObserver(
|
|
1876
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1877
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1878
|
+
if (relevant) this.scheduleUpdate();
|
|
1879
|
+
});
|
|
1896
1880
|
this.mutationObserver.observe(this.element, {
|
|
1897
1881
|
childList: true,
|
|
1898
1882
|
subtree: true,
|
|
@@ -1944,22 +1928,23 @@ var DoodleOverlay = class {
|
|
|
1944
1928
|
if (!this.svg || !this.element.isConnected) return;
|
|
1945
1929
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1946
1930
|
const targets = this.getTargets();
|
|
1947
|
-
const margin = this.overlayMargin();
|
|
1948
1931
|
const elapsed = now() - this.startedAt;
|
|
1949
1932
|
const absoluteRects = targets.map(
|
|
1950
1933
|
(target) => getElementBounds(target, padding, radius)
|
|
1951
1934
|
);
|
|
1952
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1953
1935
|
clearSvg(this.svg);
|
|
1954
1936
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1955
|
-
const
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
this.svg.
|
|
1960
|
-
this.svg.
|
|
1961
|
-
this.svg.
|
|
1962
|
-
this.svg.style.
|
|
1937
|
+
const box = paddingBox(this.element);
|
|
1938
|
+
const width = Math.max(1, box.width);
|
|
1939
|
+
const height = Math.max(1, box.height);
|
|
1940
|
+
const origin = { x: box.left, y: box.top };
|
|
1941
|
+
this.svg.setAttribute("width", String(width));
|
|
1942
|
+
this.svg.setAttribute("height", String(height));
|
|
1943
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1944
|
+
this.svg.style.left = "0px";
|
|
1945
|
+
this.svg.style.top = "0px";
|
|
1946
|
+
this.svg.style.width = `${width}px`;
|
|
1947
|
+
this.svg.style.height = `${height}px`;
|
|
1963
1948
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1964
1949
|
const noteStyle = {
|
|
1965
1950
|
color,
|
|
@@ -1970,12 +1955,12 @@ var DoodleOverlay = class {
|
|
|
1970
1955
|
};
|
|
1971
1956
|
const noteGroups = [];
|
|
1972
1957
|
const noteLayout = {
|
|
1973
|
-
band: localBand(
|
|
1958
|
+
band: localBand(origin.x),
|
|
1974
1959
|
gap: this.options.arrow ? 40 : 14
|
|
1975
1960
|
};
|
|
1976
1961
|
targets.forEach((target, index) => {
|
|
1977
1962
|
const absolute = absoluteRects[index];
|
|
1978
|
-
const local = relativeRect(
|
|
1963
|
+
const local = relativeRect(origin, absolute);
|
|
1979
1964
|
const seed = this.seed + index * 31;
|
|
1980
1965
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
1981
1966
|
this.svg,
|
|
@@ -2021,15 +2006,6 @@ var DoodleOverlay = class {
|
|
|
2021
2006
|
this.svg.appendChild(group);
|
|
2022
2007
|
}
|
|
2023
2008
|
}
|
|
2024
|
-
/**
|
|
2025
|
-
* Room the overlay needs beyond the element for notes, arrows and decorations.
|
|
2026
|
-
*/
|
|
2027
|
-
overlayMargin() {
|
|
2028
|
-
const { note, arrow, decorations } = this.options;
|
|
2029
|
-
if (!note && !arrow && !decorations) return 48;
|
|
2030
|
-
const { text } = resolveLocalizedText(note?.text, note?.locale);
|
|
2031
|
-
return Math.max(96, Math.min(240, 80 + text.length * 8));
|
|
2032
|
-
}
|
|
2033
2009
|
destroy() {
|
|
2034
2010
|
if (this.pendingFrame != null) {
|
|
2035
2011
|
cancelFrame(this.pendingFrame);
|
|
@@ -2052,10 +2028,9 @@ var DoodleOverlay = class {
|
|
|
2052
2028
|
}
|
|
2053
2029
|
this.listeners = [];
|
|
2054
2030
|
if (this.svg) {
|
|
2055
|
-
removeOverlaySvg(this.svg, this.
|
|
2031
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2056
2032
|
}
|
|
2057
2033
|
this.svg = null;
|
|
2058
|
-
this.wrapper = null;
|
|
2059
2034
|
instances.delete(this.element);
|
|
2060
2035
|
}
|
|
2061
2036
|
};
|
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,
|
|
@@ -1343,7 +1310,7 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1343
1310
|
}
|
|
1344
1311
|
function createOverlaySvg(left, top, width, height) {
|
|
1345
1312
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
1346
|
-
svg.setAttribute("class",
|
|
1313
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1347
1314
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1348
1315
|
svg.setAttribute("width", String(width));
|
|
1349
1316
|
svg.setAttribute("height", String(height));
|
|
@@ -1885,13 +1852,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1885
1852
|
function now() {
|
|
1886
1853
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1887
1854
|
}
|
|
1888
|
-
function localBand(
|
|
1855
|
+
function localBand(originLeft, inset = 8) {
|
|
1889
1856
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1890
1857
|
return {
|
|
1891
|
-
x: -
|
|
1858
|
+
x: -originLeft + inset,
|
|
1892
1859
|
width: Math.max(0, width - inset * 2)
|
|
1893
1860
|
};
|
|
1894
1861
|
}
|
|
1862
|
+
function paddingBox(element) {
|
|
1863
|
+
const rect = element.getBoundingClientRect();
|
|
1864
|
+
const style = getComputedStyle(element);
|
|
1865
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1866
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1867
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1868
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1869
|
+
return {
|
|
1870
|
+
left: rect.left + left,
|
|
1871
|
+
top: rect.top + top,
|
|
1872
|
+
width: Math.max(0, rect.width - left - right),
|
|
1873
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1895
1876
|
var DoodleOverlay = class {
|
|
1896
1877
|
/**
|
|
1897
1878
|
* @param {Element} element
|
|
@@ -1903,7 +1884,7 @@ var DoodleOverlay = class {
|
|
|
1903
1884
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1904
1885
|
this.startedAt = now();
|
|
1905
1886
|
this.svg = null;
|
|
1906
|
-
this.
|
|
1887
|
+
this.setPosition = false;
|
|
1907
1888
|
this.resizeObserver = null;
|
|
1908
1889
|
this.mutationObserver = null;
|
|
1909
1890
|
this.pendingFrame = null;
|
|
@@ -1915,14 +1896,17 @@ var DoodleOverlay = class {
|
|
|
1915
1896
|
}
|
|
1916
1897
|
mount() {
|
|
1917
1898
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1918
|
-
this.
|
|
1899
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1919
1900
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1920
1901
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1921
1902
|
this.resizeObserver.observe(this.element);
|
|
1922
1903
|
if (this.options.children) {
|
|
1923
1904
|
this.observeChildren();
|
|
1924
1905
|
}
|
|
1925
|
-
this.mutationObserver = new MutationObserver(
|
|
1906
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1907
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1908
|
+
if (relevant) this.scheduleUpdate();
|
|
1909
|
+
});
|
|
1926
1910
|
this.mutationObserver.observe(this.element, {
|
|
1927
1911
|
childList: true,
|
|
1928
1912
|
subtree: true,
|
|
@@ -1974,22 +1958,23 @@ var DoodleOverlay = class {
|
|
|
1974
1958
|
if (!this.svg || !this.element.isConnected) return;
|
|
1975
1959
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1976
1960
|
const targets = this.getTargets();
|
|
1977
|
-
const margin = this.overlayMargin();
|
|
1978
1961
|
const elapsed = now() - this.startedAt;
|
|
1979
1962
|
const absoluteRects = targets.map(
|
|
1980
1963
|
(target) => getElementBounds(target, padding, radius)
|
|
1981
1964
|
);
|
|
1982
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1983
1965
|
clearSvg(this.svg);
|
|
1984
1966
|
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.
|
|
1967
|
+
const box = paddingBox(this.element);
|
|
1968
|
+
const width = Math.max(1, box.width);
|
|
1969
|
+
const height = Math.max(1, box.height);
|
|
1970
|
+
const origin = { x: box.left, y: box.top };
|
|
1971
|
+
this.svg.setAttribute("width", String(width));
|
|
1972
|
+
this.svg.setAttribute("height", String(height));
|
|
1973
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1974
|
+
this.svg.style.left = "0px";
|
|
1975
|
+
this.svg.style.top = "0px";
|
|
1976
|
+
this.svg.style.width = `${width}px`;
|
|
1977
|
+
this.svg.style.height = `${height}px`;
|
|
1993
1978
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1994
1979
|
const noteStyle = {
|
|
1995
1980
|
color,
|
|
@@ -2000,12 +1985,12 @@ var DoodleOverlay = class {
|
|
|
2000
1985
|
};
|
|
2001
1986
|
const noteGroups = [];
|
|
2002
1987
|
const noteLayout = {
|
|
2003
|
-
band: localBand(
|
|
1988
|
+
band: localBand(origin.x),
|
|
2004
1989
|
gap: this.options.arrow ? 40 : 14
|
|
2005
1990
|
};
|
|
2006
1991
|
targets.forEach((target, index) => {
|
|
2007
1992
|
const absolute = absoluteRects[index];
|
|
2008
|
-
const local = relativeRect(
|
|
1993
|
+
const local = relativeRect(origin, absolute);
|
|
2009
1994
|
const seed = this.seed + index * 31;
|
|
2010
1995
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
2011
1996
|
this.svg,
|
|
@@ -2051,15 +2036,6 @@ var DoodleOverlay = class {
|
|
|
2051
2036
|
this.svg.appendChild(group);
|
|
2052
2037
|
}
|
|
2053
2038
|
}
|
|
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
2039
|
destroy() {
|
|
2064
2040
|
if (this.pendingFrame != null) {
|
|
2065
2041
|
cancelFrame(this.pendingFrame);
|
|
@@ -2082,10 +2058,9 @@ var DoodleOverlay = class {
|
|
|
2082
2058
|
}
|
|
2083
2059
|
this.listeners = [];
|
|
2084
2060
|
if (this.svg) {
|
|
2085
|
-
removeOverlaySvg(this.svg, this.
|
|
2061
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2086
2062
|
}
|
|
2087
2063
|
this.svg = null;
|
|
2088
|
-
this.wrapper = null;
|
|
2089
2064
|
instances.delete(this.element);
|
|
2090
2065
|
}
|
|
2091
2066
|
};
|
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,
|
|
@@ -1326,7 +1293,7 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1326
1293
|
}
|
|
1327
1294
|
function createOverlaySvg(left, top, width, height) {
|
|
1328
1295
|
const svg = document.createElementNS(SVG_NS, "svg");
|
|
1329
|
-
svg.setAttribute("class",
|
|
1296
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1330
1297
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1331
1298
|
svg.setAttribute("width", String(width));
|
|
1332
1299
|
svg.setAttribute("height", String(height));
|
|
@@ -1868,13 +1835,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1868
1835
|
function now() {
|
|
1869
1836
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1870
1837
|
}
|
|
1871
|
-
function localBand(
|
|
1838
|
+
function localBand(originLeft, inset = 8) {
|
|
1872
1839
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1873
1840
|
return {
|
|
1874
|
-
x: -
|
|
1841
|
+
x: -originLeft + inset,
|
|
1875
1842
|
width: Math.max(0, width - inset * 2)
|
|
1876
1843
|
};
|
|
1877
1844
|
}
|
|
1845
|
+
function paddingBox(element) {
|
|
1846
|
+
const rect = element.getBoundingClientRect();
|
|
1847
|
+
const style = getComputedStyle(element);
|
|
1848
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1849
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1850
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1851
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1852
|
+
return {
|
|
1853
|
+
left: rect.left + left,
|
|
1854
|
+
top: rect.top + top,
|
|
1855
|
+
width: Math.max(0, rect.width - left - right),
|
|
1856
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1878
1859
|
var DoodleOverlay = class {
|
|
1879
1860
|
/**
|
|
1880
1861
|
* @param {Element} element
|
|
@@ -1886,7 +1867,7 @@ var DoodleOverlay = class {
|
|
|
1886
1867
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1887
1868
|
this.startedAt = now();
|
|
1888
1869
|
this.svg = null;
|
|
1889
|
-
this.
|
|
1870
|
+
this.setPosition = false;
|
|
1890
1871
|
this.resizeObserver = null;
|
|
1891
1872
|
this.mutationObserver = null;
|
|
1892
1873
|
this.pendingFrame = null;
|
|
@@ -1898,14 +1879,17 @@ var DoodleOverlay = class {
|
|
|
1898
1879
|
}
|
|
1899
1880
|
mount() {
|
|
1900
1881
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1901
|
-
this.
|
|
1882
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1902
1883
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1903
1884
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1904
1885
|
this.resizeObserver.observe(this.element);
|
|
1905
1886
|
if (this.options.children) {
|
|
1906
1887
|
this.observeChildren();
|
|
1907
1888
|
}
|
|
1908
|
-
this.mutationObserver = new MutationObserver(
|
|
1889
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1890
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1891
|
+
if (relevant) this.scheduleUpdate();
|
|
1892
|
+
});
|
|
1909
1893
|
this.mutationObserver.observe(this.element, {
|
|
1910
1894
|
childList: true,
|
|
1911
1895
|
subtree: true,
|
|
@@ -1957,22 +1941,23 @@ var DoodleOverlay = class {
|
|
|
1957
1941
|
if (!this.svg || !this.element.isConnected) return;
|
|
1958
1942
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1959
1943
|
const targets = this.getTargets();
|
|
1960
|
-
const margin = this.overlayMargin();
|
|
1961
1944
|
const elapsed = now() - this.startedAt;
|
|
1962
1945
|
const absoluteRects = targets.map(
|
|
1963
1946
|
(target) => getElementBounds(target, padding, radius)
|
|
1964
1947
|
);
|
|
1965
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1966
1948
|
clearSvg(this.svg);
|
|
1967
1949
|
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.
|
|
1950
|
+
const box = paddingBox(this.element);
|
|
1951
|
+
const width = Math.max(1, box.width);
|
|
1952
|
+
const height = Math.max(1, box.height);
|
|
1953
|
+
const origin = { x: box.left, y: box.top };
|
|
1954
|
+
this.svg.setAttribute("width", String(width));
|
|
1955
|
+
this.svg.setAttribute("height", String(height));
|
|
1956
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1957
|
+
this.svg.style.left = "0px";
|
|
1958
|
+
this.svg.style.top = "0px";
|
|
1959
|
+
this.svg.style.width = `${width}px`;
|
|
1960
|
+
this.svg.style.height = `${height}px`;
|
|
1976
1961
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1977
1962
|
const noteStyle = {
|
|
1978
1963
|
color,
|
|
@@ -1983,12 +1968,12 @@ var DoodleOverlay = class {
|
|
|
1983
1968
|
};
|
|
1984
1969
|
const noteGroups = [];
|
|
1985
1970
|
const noteLayout = {
|
|
1986
|
-
band: localBand(
|
|
1971
|
+
band: localBand(origin.x),
|
|
1987
1972
|
gap: this.options.arrow ? 40 : 14
|
|
1988
1973
|
};
|
|
1989
1974
|
targets.forEach((target, index) => {
|
|
1990
1975
|
const absolute = absoluteRects[index];
|
|
1991
|
-
const local = relativeRect(
|
|
1976
|
+
const local = relativeRect(origin, absolute);
|
|
1992
1977
|
const seed = this.seed + index * 31;
|
|
1993
1978
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
1994
1979
|
this.svg,
|
|
@@ -2034,15 +2019,6 @@ var DoodleOverlay = class {
|
|
|
2034
2019
|
this.svg.appendChild(group);
|
|
2035
2020
|
}
|
|
2036
2021
|
}
|
|
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
2022
|
destroy() {
|
|
2047
2023
|
if (this.pendingFrame != null) {
|
|
2048
2024
|
cancelFrame(this.pendingFrame);
|
|
@@ -2065,10 +2041,9 @@ var DoodleOverlay = class {
|
|
|
2065
2041
|
}
|
|
2066
2042
|
this.listeners = [];
|
|
2067
2043
|
if (this.svg) {
|
|
2068
|
-
removeOverlaySvg(this.svg, this.
|
|
2044
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2069
2045
|
}
|
|
2070
2046
|
this.svg = null;
|
|
2071
|
-
this.wrapper = null;
|
|
2072
2047
|
instances.delete(this.element);
|
|
2073
2048
|
}
|
|
2074
2049
|
};
|
package/package.json
CHANGED