@aznabee/freehand-ui 0.1.6 → 0.1.7
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 +5 -3
- package/dist/freehand-ui.cjs +43 -76
- package/dist/freehand-ui.js +43 -76
- package/dist/react.cjs +43 -76
- package/dist/react.js +43 -76
- 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
|
|
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 element's own styling and never blocks clicks (`pointer-events: none`).
|
|
10
10
|
|
|
11
11
|
**Good for:** call-to-action buttons, feature cards, onboarding hints, playful marketing UI.
|
|
12
12
|
|
|
@@ -19,6 +19,8 @@ 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
|
+
|
|
22
24
|
**Live on [Aznabee.com](https://aznabee.com)** - see it in production on the real product.
|
|
23
25
|
|
|
24
26
|
## 
|
|
@@ -255,7 +257,7 @@ A `dotted` arrow spends its dash pattern on the dots, and one pattern cannot bot
|
|
|
255
257
|
Notes:
|
|
256
258
|
|
|
257
259
|
- **`prefers-reduced-motion: reduce` turns both gestures off** and leaves the arrow fully drawn.
|
|
258
|
-
- The overlay is rebuilt
|
|
260
|
+
- The overlay moves with the element for free through ordinary layout - scrolling, a sibling loading in above it - and is only rebuilt when the element's own size actually changes. Animations are placed by how long the overlay has been on the page rather than restarted, so repositioning never replays a draw or jolts a lean mid-cycle.
|
|
259
261
|
|
|
260
262
|
### Decorations
|
|
261
263
|
|
|
@@ -454,7 +456,7 @@ Then open:
|
|
|
454
456
|
- **Framework agnostic** - plain JavaScript at the core; React is an optional entry point
|
|
455
457
|
- **SVG based** - hand-drawn paths, not CSS borders
|
|
456
458
|
- **Non-invasive** - `pointer-events: none`, no layout changes
|
|
457
|
-
- **Responsive** -
|
|
459
|
+
- **Responsive** - absolutely positioned beside the element, so it tracks scroll and layout shifts for free; `ResizeObserver` covers the rest
|
|
458
460
|
- **Lightweight** - zero runtime dependencies
|
|
459
461
|
|
|
460
462
|
---
|
package/dist/freehand-ui.cjs
CHANGED
|
@@ -399,20 +399,6 @@ function isValidPosition(position) {
|
|
|
399
399
|
"center"
|
|
400
400
|
].includes(position);
|
|
401
401
|
}
|
|
402
|
-
function getScrollableAncestors(element) {
|
|
403
|
-
const ancestors = [];
|
|
404
|
-
let node = element.parentElement;
|
|
405
|
-
while (node && node !== document.documentElement) {
|
|
406
|
-
const style = getComputedStyle(node);
|
|
407
|
-
const overflow = style.overflow + style.overflowY + style.overflowX;
|
|
408
|
-
if (/(auto|scroll|overlay)/.test(overflow)) {
|
|
409
|
-
ancestors.push(node);
|
|
410
|
-
}
|
|
411
|
-
node = node.parentElement;
|
|
412
|
-
}
|
|
413
|
-
ancestors.push(window);
|
|
414
|
-
return ancestors;
|
|
415
|
-
}
|
|
416
402
|
function resolveOverlayZIndex(element, override) {
|
|
417
403
|
if (override != null) return override;
|
|
418
404
|
const { zIndex, position } = getComputedStyle(element);
|
|
@@ -431,13 +417,45 @@ function resolveOverlayZIndex(element, override) {
|
|
|
431
417
|
}
|
|
432
418
|
return null;
|
|
433
419
|
}
|
|
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
434
|
function insertOverlaySvg(svg, element) {
|
|
435
435
|
const parent = element.parentNode;
|
|
436
436
|
if (!parent) {
|
|
437
437
|
document.body.appendChild(svg);
|
|
438
|
-
return;
|
|
438
|
+
return null;
|
|
439
439
|
}
|
|
440
|
-
|
|
440
|
+
const wrapper = document.createElement("div");
|
|
441
|
+
wrapper.style.position = "relative";
|
|
442
|
+
const computed = getComputedStyle(element);
|
|
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) {
|
|
454
|
+
if (svg.parentNode) {
|
|
455
|
+
svg.parentNode.removeChild(svg);
|
|
456
|
+
}
|
|
457
|
+
if (!wrapper || !wrapper.parentNode) return;
|
|
458
|
+
wrapper.parentNode.replaceChild(element, wrapper);
|
|
441
459
|
}
|
|
442
460
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
443
461
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -1325,14 +1343,13 @@ function createOverlaySvg(left, top, width, height) {
|
|
|
1325
1343
|
svg.setAttribute("width", String(width));
|
|
1326
1344
|
svg.setAttribute("height", String(height));
|
|
1327
1345
|
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1328
|
-
svg.style.position = "
|
|
1346
|
+
svg.style.position = "absolute";
|
|
1329
1347
|
svg.style.left = `${left}px`;
|
|
1330
1348
|
svg.style.top = `${top}px`;
|
|
1331
1349
|
svg.style.width = `${width}px`;
|
|
1332
1350
|
svg.style.height = `${height}px`;
|
|
1333
1351
|
svg.style.pointerEvents = "none";
|
|
1334
1352
|
svg.style.overflow = "visible";
|
|
1335
|
-
svg.style.willChange = "transform";
|
|
1336
1353
|
return svg;
|
|
1337
1354
|
}
|
|
1338
1355
|
|
|
@@ -1881,13 +1898,11 @@ var DoodleOverlay = class {
|
|
|
1881
1898
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1882
1899
|
this.startedAt = now();
|
|
1883
1900
|
this.svg = null;
|
|
1901
|
+
this.wrapper = null;
|
|
1884
1902
|
this.resizeObserver = null;
|
|
1885
1903
|
this.mutationObserver = null;
|
|
1886
1904
|
this.pendingFrame = null;
|
|
1887
|
-
this.pendingRepositionFrame = null;
|
|
1888
|
-
this.basePosition = { x: 0, y: 0 };
|
|
1889
1905
|
this.listeners = [];
|
|
1890
|
-
this.onScroll = this.scheduleReposition.bind(this);
|
|
1891
1906
|
this.onResize = this.scheduleUpdate.bind(this);
|
|
1892
1907
|
this.onLanguageChange = this.scheduleUpdate.bind(this);
|
|
1893
1908
|
this.childElements = [];
|
|
@@ -1895,7 +1910,7 @@ var DoodleOverlay = class {
|
|
|
1895
1910
|
}
|
|
1896
1911
|
mount() {
|
|
1897
1912
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1898
|
-
insertOverlaySvg(this.svg, this.element);
|
|
1913
|
+
this.wrapper = insertOverlaySvg(this.svg, this.element);
|
|
1899
1914
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1900
1915
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1901
1916
|
this.resizeObserver.observe(this.element);
|
|
@@ -1908,10 +1923,6 @@ var DoodleOverlay = class {
|
|
|
1908
1923
|
subtree: true,
|
|
1909
1924
|
attributes: true
|
|
1910
1925
|
});
|
|
1911
|
-
for (const target of getScrollableAncestors(this.element)) {
|
|
1912
|
-
target.addEventListener("scroll", this.onScroll, { passive: true });
|
|
1913
|
-
this.listeners.push({ target, type: "scroll", handler: this.onScroll });
|
|
1914
|
-
}
|
|
1915
1926
|
window.addEventListener("resize", this.onResize, { passive: true });
|
|
1916
1927
|
this.listeners.push({
|
|
1917
1928
|
target: window,
|
|
@@ -1947,46 +1958,6 @@ var DoodleOverlay = class {
|
|
|
1947
1958
|
this.update();
|
|
1948
1959
|
});
|
|
1949
1960
|
}
|
|
1950
|
-
scheduleReposition() {
|
|
1951
|
-
if (this.pendingFrame != null || this.pendingRepositionFrame != null) {
|
|
1952
|
-
return;
|
|
1953
|
-
}
|
|
1954
|
-
this.pendingRepositionFrame = scheduleFrame(() => {
|
|
1955
|
-
this.pendingRepositionFrame = null;
|
|
1956
|
-
this.reposition();
|
|
1957
|
-
});
|
|
1958
|
-
}
|
|
1959
|
-
/**
|
|
1960
|
-
* Cheap scroll-driven path: slide the overlay to the element's new
|
|
1961
|
-
* viewport position without touching its contents. Scrolling can't change
|
|
1962
|
-
* an element's size, so the rects, viewBox and every hand-drawn stroke
|
|
1963
|
-
* inside them are still valid - only the overlay's position needs to move.
|
|
1964
|
-
*
|
|
1965
|
-
* That move is a `transform`, not `left`/`top`. `left`/`top` sit in the
|
|
1966
|
-
* same box-geometry pass as layout, so the browser re-checks layout on
|
|
1967
|
-
* every write; `transform` is composited, so the scroll path never
|
|
1968
|
-
* touches layout at all.
|
|
1969
|
-
*/
|
|
1970
|
-
reposition() {
|
|
1971
|
-
if (!this.svg || !this.element.isConnected) return;
|
|
1972
|
-
const { padding, radius } = this.options;
|
|
1973
|
-
const targets = this.getTargets();
|
|
1974
|
-
const margin = this.overlayMargin();
|
|
1975
|
-
const absoluteRects = targets.map(
|
|
1976
|
-
(target) => getElementBounds(target, padding, radius)
|
|
1977
|
-
);
|
|
1978
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1979
|
-
const currentWidth = parseFloat(this.svg.style.width) || 0;
|
|
1980
|
-
const currentHeight = parseFloat(this.svg.style.height) || 0;
|
|
1981
|
-
const resized = Math.abs(overlayRect.width - currentWidth) > 0.5 || Math.abs(overlayRect.height - currentHeight) > 0.5;
|
|
1982
|
-
if (resized) {
|
|
1983
|
-
this.update();
|
|
1984
|
-
return;
|
|
1985
|
-
}
|
|
1986
|
-
const dx = overlayRect.x - this.basePosition.x;
|
|
1987
|
-
const dy = overlayRect.y - this.basePosition.y;
|
|
1988
|
-
this.svg.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
|
|
1989
|
-
}
|
|
1990
1961
|
getTargets() {
|
|
1991
1962
|
if (this.options.children) {
|
|
1992
1963
|
const nodes = this.element.querySelectorAll(this.options.children);
|
|
@@ -2006,15 +1977,14 @@ var DoodleOverlay = class {
|
|
|
2006
1977
|
const overlayRect = unionRects(absoluteRects, margin);
|
|
2007
1978
|
clearSvg(this.svg);
|
|
2008
1979
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1980
|
+
const origin = this.wrapper ? this.wrapper.getBoundingClientRect() : { left: 0, top: 0 };
|
|
2009
1981
|
this.svg.setAttribute("width", String(overlayRect.width));
|
|
2010
1982
|
this.svg.setAttribute("height", String(overlayRect.height));
|
|
2011
1983
|
this.svg.setAttribute("viewBox", `0 0 ${overlayRect.width} ${overlayRect.height}`);
|
|
2012
|
-
this.svg.style.left = `${overlayRect.x}px`;
|
|
2013
|
-
this.svg.style.top = `${overlayRect.y}px`;
|
|
1984
|
+
this.svg.style.left = `${overlayRect.x - origin.left}px`;
|
|
1985
|
+
this.svg.style.top = `${overlayRect.y - origin.top}px`;
|
|
2014
1986
|
this.svg.style.width = `${overlayRect.width}px`;
|
|
2015
1987
|
this.svg.style.height = `${overlayRect.height}px`;
|
|
2016
|
-
this.svg.style.transform = "";
|
|
2017
|
-
this.basePosition = { x: overlayRect.x, y: overlayRect.y };
|
|
2018
1988
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
2019
1989
|
const noteStyle = {
|
|
2020
1990
|
color,
|
|
@@ -2090,10 +2060,6 @@ var DoodleOverlay = class {
|
|
|
2090
2060
|
cancelFrame(this.pendingFrame);
|
|
2091
2061
|
this.pendingFrame = null;
|
|
2092
2062
|
}
|
|
2093
|
-
if (this.pendingRepositionFrame != null) {
|
|
2094
|
-
cancelFrame(this.pendingRepositionFrame);
|
|
2095
|
-
this.pendingRepositionFrame = null;
|
|
2096
|
-
}
|
|
2097
2063
|
if (this.resizeObserver) {
|
|
2098
2064
|
this.resizeObserver.disconnect();
|
|
2099
2065
|
this.resizeObserver = null;
|
|
@@ -2110,10 +2076,11 @@ var DoodleOverlay = class {
|
|
|
2110
2076
|
target.removeEventListener(type, handler);
|
|
2111
2077
|
}
|
|
2112
2078
|
this.listeners = [];
|
|
2113
|
-
if (this.svg
|
|
2114
|
-
this.svg.
|
|
2079
|
+
if (this.svg) {
|
|
2080
|
+
removeOverlaySvg(this.svg, this.wrapper, this.element);
|
|
2115
2081
|
}
|
|
2116
2082
|
this.svg = null;
|
|
2083
|
+
this.wrapper = null;
|
|
2117
2084
|
instances.delete(this.element);
|
|
2118
2085
|
}
|
|
2119
2086
|
};
|
package/dist/freehand-ui.js
CHANGED
|
@@ -374,20 +374,6 @@ function isValidPosition(position) {
|
|
|
374
374
|
"center"
|
|
375
375
|
].includes(position);
|
|
376
376
|
}
|
|
377
|
-
function getScrollableAncestors(element) {
|
|
378
|
-
const ancestors = [];
|
|
379
|
-
let node = element.parentElement;
|
|
380
|
-
while (node && node !== document.documentElement) {
|
|
381
|
-
const style = getComputedStyle(node);
|
|
382
|
-
const overflow = style.overflow + style.overflowY + style.overflowX;
|
|
383
|
-
if (/(auto|scroll|overlay)/.test(overflow)) {
|
|
384
|
-
ancestors.push(node);
|
|
385
|
-
}
|
|
386
|
-
node = node.parentElement;
|
|
387
|
-
}
|
|
388
|
-
ancestors.push(window);
|
|
389
|
-
return ancestors;
|
|
390
|
-
}
|
|
391
377
|
function resolveOverlayZIndex(element, override) {
|
|
392
378
|
if (override != null) return override;
|
|
393
379
|
const { zIndex, position } = getComputedStyle(element);
|
|
@@ -406,13 +392,45 @@ function resolveOverlayZIndex(element, override) {
|
|
|
406
392
|
}
|
|
407
393
|
return null;
|
|
408
394
|
}
|
|
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
409
|
function insertOverlaySvg(svg, element) {
|
|
410
410
|
const parent = element.parentNode;
|
|
411
411
|
if (!parent) {
|
|
412
412
|
document.body.appendChild(svg);
|
|
413
|
-
return;
|
|
413
|
+
return null;
|
|
414
414
|
}
|
|
415
|
-
|
|
415
|
+
const wrapper = document.createElement("div");
|
|
416
|
+
wrapper.style.position = "relative";
|
|
417
|
+
const computed = getComputedStyle(element);
|
|
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) {
|
|
429
|
+
if (svg.parentNode) {
|
|
430
|
+
svg.parentNode.removeChild(svg);
|
|
431
|
+
}
|
|
432
|
+
if (!wrapper || !wrapper.parentNode) return;
|
|
433
|
+
wrapper.parentNode.replaceChild(element, wrapper);
|
|
416
434
|
}
|
|
417
435
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
418
436
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -1300,14 +1318,13 @@ function createOverlaySvg(left, top, width, height) {
|
|
|
1300
1318
|
svg.setAttribute("width", String(width));
|
|
1301
1319
|
svg.setAttribute("height", String(height));
|
|
1302
1320
|
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1303
|
-
svg.style.position = "
|
|
1321
|
+
svg.style.position = "absolute";
|
|
1304
1322
|
svg.style.left = `${left}px`;
|
|
1305
1323
|
svg.style.top = `${top}px`;
|
|
1306
1324
|
svg.style.width = `${width}px`;
|
|
1307
1325
|
svg.style.height = `${height}px`;
|
|
1308
1326
|
svg.style.pointerEvents = "none";
|
|
1309
1327
|
svg.style.overflow = "visible";
|
|
1310
|
-
svg.style.willChange = "transform";
|
|
1311
1328
|
return svg;
|
|
1312
1329
|
}
|
|
1313
1330
|
|
|
@@ -1856,13 +1873,11 @@ var DoodleOverlay = class {
|
|
|
1856
1873
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1857
1874
|
this.startedAt = now();
|
|
1858
1875
|
this.svg = null;
|
|
1876
|
+
this.wrapper = null;
|
|
1859
1877
|
this.resizeObserver = null;
|
|
1860
1878
|
this.mutationObserver = null;
|
|
1861
1879
|
this.pendingFrame = null;
|
|
1862
|
-
this.pendingRepositionFrame = null;
|
|
1863
|
-
this.basePosition = { x: 0, y: 0 };
|
|
1864
1880
|
this.listeners = [];
|
|
1865
|
-
this.onScroll = this.scheduleReposition.bind(this);
|
|
1866
1881
|
this.onResize = this.scheduleUpdate.bind(this);
|
|
1867
1882
|
this.onLanguageChange = this.scheduleUpdate.bind(this);
|
|
1868
1883
|
this.childElements = [];
|
|
@@ -1870,7 +1885,7 @@ var DoodleOverlay = class {
|
|
|
1870
1885
|
}
|
|
1871
1886
|
mount() {
|
|
1872
1887
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1873
|
-
insertOverlaySvg(this.svg, this.element);
|
|
1888
|
+
this.wrapper = insertOverlaySvg(this.svg, this.element);
|
|
1874
1889
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1875
1890
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1876
1891
|
this.resizeObserver.observe(this.element);
|
|
@@ -1883,10 +1898,6 @@ var DoodleOverlay = class {
|
|
|
1883
1898
|
subtree: true,
|
|
1884
1899
|
attributes: true
|
|
1885
1900
|
});
|
|
1886
|
-
for (const target of getScrollableAncestors(this.element)) {
|
|
1887
|
-
target.addEventListener("scroll", this.onScroll, { passive: true });
|
|
1888
|
-
this.listeners.push({ target, type: "scroll", handler: this.onScroll });
|
|
1889
|
-
}
|
|
1890
1901
|
window.addEventListener("resize", this.onResize, { passive: true });
|
|
1891
1902
|
this.listeners.push({
|
|
1892
1903
|
target: window,
|
|
@@ -1922,46 +1933,6 @@ var DoodleOverlay = class {
|
|
|
1922
1933
|
this.update();
|
|
1923
1934
|
});
|
|
1924
1935
|
}
|
|
1925
|
-
scheduleReposition() {
|
|
1926
|
-
if (this.pendingFrame != null || this.pendingRepositionFrame != null) {
|
|
1927
|
-
return;
|
|
1928
|
-
}
|
|
1929
|
-
this.pendingRepositionFrame = scheduleFrame(() => {
|
|
1930
|
-
this.pendingRepositionFrame = null;
|
|
1931
|
-
this.reposition();
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Cheap scroll-driven path: slide the overlay to the element's new
|
|
1936
|
-
* viewport position without touching its contents. Scrolling can't change
|
|
1937
|
-
* an element's size, so the rects, viewBox and every hand-drawn stroke
|
|
1938
|
-
* inside them are still valid - only the overlay's position needs to move.
|
|
1939
|
-
*
|
|
1940
|
-
* That move is a `transform`, not `left`/`top`. `left`/`top` sit in the
|
|
1941
|
-
* same box-geometry pass as layout, so the browser re-checks layout on
|
|
1942
|
-
* every write; `transform` is composited, so the scroll path never
|
|
1943
|
-
* touches layout at all.
|
|
1944
|
-
*/
|
|
1945
|
-
reposition() {
|
|
1946
|
-
if (!this.svg || !this.element.isConnected) return;
|
|
1947
|
-
const { padding, radius } = this.options;
|
|
1948
|
-
const targets = this.getTargets();
|
|
1949
|
-
const margin = this.overlayMargin();
|
|
1950
|
-
const absoluteRects = targets.map(
|
|
1951
|
-
(target) => getElementBounds(target, padding, radius)
|
|
1952
|
-
);
|
|
1953
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1954
|
-
const currentWidth = parseFloat(this.svg.style.width) || 0;
|
|
1955
|
-
const currentHeight = parseFloat(this.svg.style.height) || 0;
|
|
1956
|
-
const resized = Math.abs(overlayRect.width - currentWidth) > 0.5 || Math.abs(overlayRect.height - currentHeight) > 0.5;
|
|
1957
|
-
if (resized) {
|
|
1958
|
-
this.update();
|
|
1959
|
-
return;
|
|
1960
|
-
}
|
|
1961
|
-
const dx = overlayRect.x - this.basePosition.x;
|
|
1962
|
-
const dy = overlayRect.y - this.basePosition.y;
|
|
1963
|
-
this.svg.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
|
|
1964
|
-
}
|
|
1965
1936
|
getTargets() {
|
|
1966
1937
|
if (this.options.children) {
|
|
1967
1938
|
const nodes = this.element.querySelectorAll(this.options.children);
|
|
@@ -1981,15 +1952,14 @@ var DoodleOverlay = class {
|
|
|
1981
1952
|
const overlayRect = unionRects(absoluteRects, margin);
|
|
1982
1953
|
clearSvg(this.svg);
|
|
1983
1954
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1955
|
+
const origin = this.wrapper ? this.wrapper.getBoundingClientRect() : { left: 0, top: 0 };
|
|
1984
1956
|
this.svg.setAttribute("width", String(overlayRect.width));
|
|
1985
1957
|
this.svg.setAttribute("height", String(overlayRect.height));
|
|
1986
1958
|
this.svg.setAttribute("viewBox", `0 0 ${overlayRect.width} ${overlayRect.height}`);
|
|
1987
|
-
this.svg.style.left = `${overlayRect.x}px`;
|
|
1988
|
-
this.svg.style.top = `${overlayRect.y}px`;
|
|
1959
|
+
this.svg.style.left = `${overlayRect.x - origin.left}px`;
|
|
1960
|
+
this.svg.style.top = `${overlayRect.y - origin.top}px`;
|
|
1989
1961
|
this.svg.style.width = `${overlayRect.width}px`;
|
|
1990
1962
|
this.svg.style.height = `${overlayRect.height}px`;
|
|
1991
|
-
this.svg.style.transform = "";
|
|
1992
|
-
this.basePosition = { x: overlayRect.x, y: overlayRect.y };
|
|
1993
1963
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1994
1964
|
const noteStyle = {
|
|
1995
1965
|
color,
|
|
@@ -2065,10 +2035,6 @@ var DoodleOverlay = class {
|
|
|
2065
2035
|
cancelFrame(this.pendingFrame);
|
|
2066
2036
|
this.pendingFrame = null;
|
|
2067
2037
|
}
|
|
2068
|
-
if (this.pendingRepositionFrame != null) {
|
|
2069
|
-
cancelFrame(this.pendingRepositionFrame);
|
|
2070
|
-
this.pendingRepositionFrame = null;
|
|
2071
|
-
}
|
|
2072
2038
|
if (this.resizeObserver) {
|
|
2073
2039
|
this.resizeObserver.disconnect();
|
|
2074
2040
|
this.resizeObserver = null;
|
|
@@ -2085,10 +2051,11 @@ var DoodleOverlay = class {
|
|
|
2085
2051
|
target.removeEventListener(type, handler);
|
|
2086
2052
|
}
|
|
2087
2053
|
this.listeners = [];
|
|
2088
|
-
if (this.svg
|
|
2089
|
-
this.svg.
|
|
2054
|
+
if (this.svg) {
|
|
2055
|
+
removeOverlaySvg(this.svg, this.wrapper, this.element);
|
|
2090
2056
|
}
|
|
2091
2057
|
this.svg = null;
|
|
2058
|
+
this.wrapper = null;
|
|
2092
2059
|
instances.delete(this.element);
|
|
2093
2060
|
}
|
|
2094
2061
|
};
|
package/dist/react.cjs
CHANGED
|
@@ -404,20 +404,6 @@ function isValidPosition(position) {
|
|
|
404
404
|
"center"
|
|
405
405
|
].includes(position);
|
|
406
406
|
}
|
|
407
|
-
function getScrollableAncestors(element) {
|
|
408
|
-
const ancestors = [];
|
|
409
|
-
let node = element.parentElement;
|
|
410
|
-
while (node && node !== document.documentElement) {
|
|
411
|
-
const style = getComputedStyle(node);
|
|
412
|
-
const overflow = style.overflow + style.overflowY + style.overflowX;
|
|
413
|
-
if (/(auto|scroll|overlay)/.test(overflow)) {
|
|
414
|
-
ancestors.push(node);
|
|
415
|
-
}
|
|
416
|
-
node = node.parentElement;
|
|
417
|
-
}
|
|
418
|
-
ancestors.push(window);
|
|
419
|
-
return ancestors;
|
|
420
|
-
}
|
|
421
407
|
function resolveOverlayZIndex(element, override) {
|
|
422
408
|
if (override != null) return override;
|
|
423
409
|
const { zIndex, position } = getComputedStyle(element);
|
|
@@ -436,13 +422,45 @@ function resolveOverlayZIndex(element, override) {
|
|
|
436
422
|
}
|
|
437
423
|
return null;
|
|
438
424
|
}
|
|
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
439
|
function insertOverlaySvg(svg, element) {
|
|
440
440
|
const parent = element.parentNode;
|
|
441
441
|
if (!parent) {
|
|
442
442
|
document.body.appendChild(svg);
|
|
443
|
-
return;
|
|
443
|
+
return null;
|
|
444
444
|
}
|
|
445
|
-
|
|
445
|
+
const wrapper = document.createElement("div");
|
|
446
|
+
wrapper.style.position = "relative";
|
|
447
|
+
const computed = getComputedStyle(element);
|
|
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) {
|
|
459
|
+
if (svg.parentNode) {
|
|
460
|
+
svg.parentNode.removeChild(svg);
|
|
461
|
+
}
|
|
462
|
+
if (!wrapper || !wrapper.parentNode) return;
|
|
463
|
+
wrapper.parentNode.replaceChild(element, wrapper);
|
|
446
464
|
}
|
|
447
465
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
448
466
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -1330,14 +1348,13 @@ function createOverlaySvg(left, top, width, height) {
|
|
|
1330
1348
|
svg.setAttribute("width", String(width));
|
|
1331
1349
|
svg.setAttribute("height", String(height));
|
|
1332
1350
|
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1333
|
-
svg.style.position = "
|
|
1351
|
+
svg.style.position = "absolute";
|
|
1334
1352
|
svg.style.left = `${left}px`;
|
|
1335
1353
|
svg.style.top = `${top}px`;
|
|
1336
1354
|
svg.style.width = `${width}px`;
|
|
1337
1355
|
svg.style.height = `${height}px`;
|
|
1338
1356
|
svg.style.pointerEvents = "none";
|
|
1339
1357
|
svg.style.overflow = "visible";
|
|
1340
|
-
svg.style.willChange = "transform";
|
|
1341
1358
|
return svg;
|
|
1342
1359
|
}
|
|
1343
1360
|
|
|
@@ -1886,13 +1903,11 @@ var DoodleOverlay = class {
|
|
|
1886
1903
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1887
1904
|
this.startedAt = now();
|
|
1888
1905
|
this.svg = null;
|
|
1906
|
+
this.wrapper = null;
|
|
1889
1907
|
this.resizeObserver = null;
|
|
1890
1908
|
this.mutationObserver = null;
|
|
1891
1909
|
this.pendingFrame = null;
|
|
1892
|
-
this.pendingRepositionFrame = null;
|
|
1893
|
-
this.basePosition = { x: 0, y: 0 };
|
|
1894
1910
|
this.listeners = [];
|
|
1895
|
-
this.onScroll = this.scheduleReposition.bind(this);
|
|
1896
1911
|
this.onResize = this.scheduleUpdate.bind(this);
|
|
1897
1912
|
this.onLanguageChange = this.scheduleUpdate.bind(this);
|
|
1898
1913
|
this.childElements = [];
|
|
@@ -1900,7 +1915,7 @@ var DoodleOverlay = class {
|
|
|
1900
1915
|
}
|
|
1901
1916
|
mount() {
|
|
1902
1917
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1903
|
-
insertOverlaySvg(this.svg, this.element);
|
|
1918
|
+
this.wrapper = insertOverlaySvg(this.svg, this.element);
|
|
1904
1919
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1905
1920
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1906
1921
|
this.resizeObserver.observe(this.element);
|
|
@@ -1913,10 +1928,6 @@ var DoodleOverlay = class {
|
|
|
1913
1928
|
subtree: true,
|
|
1914
1929
|
attributes: true
|
|
1915
1930
|
});
|
|
1916
|
-
for (const target of getScrollableAncestors(this.element)) {
|
|
1917
|
-
target.addEventListener("scroll", this.onScroll, { passive: true });
|
|
1918
|
-
this.listeners.push({ target, type: "scroll", handler: this.onScroll });
|
|
1919
|
-
}
|
|
1920
1931
|
window.addEventListener("resize", this.onResize, { passive: true });
|
|
1921
1932
|
this.listeners.push({
|
|
1922
1933
|
target: window,
|
|
@@ -1952,46 +1963,6 @@ var DoodleOverlay = class {
|
|
|
1952
1963
|
this.update();
|
|
1953
1964
|
});
|
|
1954
1965
|
}
|
|
1955
|
-
scheduleReposition() {
|
|
1956
|
-
if (this.pendingFrame != null || this.pendingRepositionFrame != null) {
|
|
1957
|
-
return;
|
|
1958
|
-
}
|
|
1959
|
-
this.pendingRepositionFrame = scheduleFrame(() => {
|
|
1960
|
-
this.pendingRepositionFrame = null;
|
|
1961
|
-
this.reposition();
|
|
1962
|
-
});
|
|
1963
|
-
}
|
|
1964
|
-
/**
|
|
1965
|
-
* Cheap scroll-driven path: slide the overlay to the element's new
|
|
1966
|
-
* viewport position without touching its contents. Scrolling can't change
|
|
1967
|
-
* an element's size, so the rects, viewBox and every hand-drawn stroke
|
|
1968
|
-
* inside them are still valid - only the overlay's position needs to move.
|
|
1969
|
-
*
|
|
1970
|
-
* That move is a `transform`, not `left`/`top`. `left`/`top` sit in the
|
|
1971
|
-
* same box-geometry pass as layout, so the browser re-checks layout on
|
|
1972
|
-
* every write; `transform` is composited, so the scroll path never
|
|
1973
|
-
* touches layout at all.
|
|
1974
|
-
*/
|
|
1975
|
-
reposition() {
|
|
1976
|
-
if (!this.svg || !this.element.isConnected) return;
|
|
1977
|
-
const { padding, radius } = this.options;
|
|
1978
|
-
const targets = this.getTargets();
|
|
1979
|
-
const margin = this.overlayMargin();
|
|
1980
|
-
const absoluteRects = targets.map(
|
|
1981
|
-
(target) => getElementBounds(target, padding, radius)
|
|
1982
|
-
);
|
|
1983
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1984
|
-
const currentWidth = parseFloat(this.svg.style.width) || 0;
|
|
1985
|
-
const currentHeight = parseFloat(this.svg.style.height) || 0;
|
|
1986
|
-
const resized = Math.abs(overlayRect.width - currentWidth) > 0.5 || Math.abs(overlayRect.height - currentHeight) > 0.5;
|
|
1987
|
-
if (resized) {
|
|
1988
|
-
this.update();
|
|
1989
|
-
return;
|
|
1990
|
-
}
|
|
1991
|
-
const dx = overlayRect.x - this.basePosition.x;
|
|
1992
|
-
const dy = overlayRect.y - this.basePosition.y;
|
|
1993
|
-
this.svg.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
|
|
1994
|
-
}
|
|
1995
1966
|
getTargets() {
|
|
1996
1967
|
if (this.options.children) {
|
|
1997
1968
|
const nodes = this.element.querySelectorAll(this.options.children);
|
|
@@ -2011,15 +1982,14 @@ var DoodleOverlay = class {
|
|
|
2011
1982
|
const overlayRect = unionRects(absoluteRects, margin);
|
|
2012
1983
|
clearSvg(this.svg);
|
|
2013
1984
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1985
|
+
const origin = this.wrapper ? this.wrapper.getBoundingClientRect() : { left: 0, top: 0 };
|
|
2014
1986
|
this.svg.setAttribute("width", String(overlayRect.width));
|
|
2015
1987
|
this.svg.setAttribute("height", String(overlayRect.height));
|
|
2016
1988
|
this.svg.setAttribute("viewBox", `0 0 ${overlayRect.width} ${overlayRect.height}`);
|
|
2017
|
-
this.svg.style.left = `${overlayRect.x}px`;
|
|
2018
|
-
this.svg.style.top = `${overlayRect.y}px`;
|
|
1989
|
+
this.svg.style.left = `${overlayRect.x - origin.left}px`;
|
|
1990
|
+
this.svg.style.top = `${overlayRect.y - origin.top}px`;
|
|
2019
1991
|
this.svg.style.width = `${overlayRect.width}px`;
|
|
2020
1992
|
this.svg.style.height = `${overlayRect.height}px`;
|
|
2021
|
-
this.svg.style.transform = "";
|
|
2022
|
-
this.basePosition = { x: overlayRect.x, y: overlayRect.y };
|
|
2023
1993
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
2024
1994
|
const noteStyle = {
|
|
2025
1995
|
color,
|
|
@@ -2095,10 +2065,6 @@ var DoodleOverlay = class {
|
|
|
2095
2065
|
cancelFrame(this.pendingFrame);
|
|
2096
2066
|
this.pendingFrame = null;
|
|
2097
2067
|
}
|
|
2098
|
-
if (this.pendingRepositionFrame != null) {
|
|
2099
|
-
cancelFrame(this.pendingRepositionFrame);
|
|
2100
|
-
this.pendingRepositionFrame = null;
|
|
2101
|
-
}
|
|
2102
2068
|
if (this.resizeObserver) {
|
|
2103
2069
|
this.resizeObserver.disconnect();
|
|
2104
2070
|
this.resizeObserver = null;
|
|
@@ -2115,10 +2081,11 @@ var DoodleOverlay = class {
|
|
|
2115
2081
|
target.removeEventListener(type, handler);
|
|
2116
2082
|
}
|
|
2117
2083
|
this.listeners = [];
|
|
2118
|
-
if (this.svg
|
|
2119
|
-
this.svg.
|
|
2084
|
+
if (this.svg) {
|
|
2085
|
+
removeOverlaySvg(this.svg, this.wrapper, this.element);
|
|
2120
2086
|
}
|
|
2121
2087
|
this.svg = null;
|
|
2088
|
+
this.wrapper = null;
|
|
2122
2089
|
instances.delete(this.element);
|
|
2123
2090
|
}
|
|
2124
2091
|
};
|
package/dist/react.js
CHANGED
|
@@ -387,20 +387,6 @@ function isValidPosition(position) {
|
|
|
387
387
|
"center"
|
|
388
388
|
].includes(position);
|
|
389
389
|
}
|
|
390
|
-
function getScrollableAncestors(element) {
|
|
391
|
-
const ancestors = [];
|
|
392
|
-
let node = element.parentElement;
|
|
393
|
-
while (node && node !== document.documentElement) {
|
|
394
|
-
const style = getComputedStyle(node);
|
|
395
|
-
const overflow = style.overflow + style.overflowY + style.overflowX;
|
|
396
|
-
if (/(auto|scroll|overlay)/.test(overflow)) {
|
|
397
|
-
ancestors.push(node);
|
|
398
|
-
}
|
|
399
|
-
node = node.parentElement;
|
|
400
|
-
}
|
|
401
|
-
ancestors.push(window);
|
|
402
|
-
return ancestors;
|
|
403
|
-
}
|
|
404
390
|
function resolveOverlayZIndex(element, override) {
|
|
405
391
|
if (override != null) return override;
|
|
406
392
|
const { zIndex, position } = getComputedStyle(element);
|
|
@@ -419,13 +405,45 @@ function resolveOverlayZIndex(element, override) {
|
|
|
419
405
|
}
|
|
420
406
|
return null;
|
|
421
407
|
}
|
|
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
422
|
function insertOverlaySvg(svg, element) {
|
|
423
423
|
const parent = element.parentNode;
|
|
424
424
|
if (!parent) {
|
|
425
425
|
document.body.appendChild(svg);
|
|
426
|
-
return;
|
|
426
|
+
return null;
|
|
427
427
|
}
|
|
428
|
-
|
|
428
|
+
const wrapper = document.createElement("div");
|
|
429
|
+
wrapper.style.position = "relative";
|
|
430
|
+
const computed = getComputedStyle(element);
|
|
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) {
|
|
442
|
+
if (svg.parentNode) {
|
|
443
|
+
svg.parentNode.removeChild(svg);
|
|
444
|
+
}
|
|
445
|
+
if (!wrapper || !wrapper.parentNode) return;
|
|
446
|
+
wrapper.parentNode.replaceChild(element, wrapper);
|
|
429
447
|
}
|
|
430
448
|
function syncOverlayStacking(svg, element, zIndexOverride) {
|
|
431
449
|
const zIndex = resolveOverlayZIndex(element, zIndexOverride);
|
|
@@ -1313,14 +1331,13 @@ function createOverlaySvg(left, top, width, height) {
|
|
|
1313
1331
|
svg.setAttribute("width", String(width));
|
|
1314
1332
|
svg.setAttribute("height", String(height));
|
|
1315
1333
|
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1316
|
-
svg.style.position = "
|
|
1334
|
+
svg.style.position = "absolute";
|
|
1317
1335
|
svg.style.left = `${left}px`;
|
|
1318
1336
|
svg.style.top = `${top}px`;
|
|
1319
1337
|
svg.style.width = `${width}px`;
|
|
1320
1338
|
svg.style.height = `${height}px`;
|
|
1321
1339
|
svg.style.pointerEvents = "none";
|
|
1322
1340
|
svg.style.overflow = "visible";
|
|
1323
|
-
svg.style.willChange = "transform";
|
|
1324
1341
|
return svg;
|
|
1325
1342
|
}
|
|
1326
1343
|
|
|
@@ -1869,13 +1886,11 @@ var DoodleOverlay = class {
|
|
|
1869
1886
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1870
1887
|
this.startedAt = now();
|
|
1871
1888
|
this.svg = null;
|
|
1889
|
+
this.wrapper = null;
|
|
1872
1890
|
this.resizeObserver = null;
|
|
1873
1891
|
this.mutationObserver = null;
|
|
1874
1892
|
this.pendingFrame = null;
|
|
1875
|
-
this.pendingRepositionFrame = null;
|
|
1876
|
-
this.basePosition = { x: 0, y: 0 };
|
|
1877
1893
|
this.listeners = [];
|
|
1878
|
-
this.onScroll = this.scheduleReposition.bind(this);
|
|
1879
1894
|
this.onResize = this.scheduleUpdate.bind(this);
|
|
1880
1895
|
this.onLanguageChange = this.scheduleUpdate.bind(this);
|
|
1881
1896
|
this.childElements = [];
|
|
@@ -1883,7 +1898,7 @@ var DoodleOverlay = class {
|
|
|
1883
1898
|
}
|
|
1884
1899
|
mount() {
|
|
1885
1900
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1886
|
-
insertOverlaySvg(this.svg, this.element);
|
|
1901
|
+
this.wrapper = insertOverlaySvg(this.svg, this.element);
|
|
1887
1902
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1888
1903
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1889
1904
|
this.resizeObserver.observe(this.element);
|
|
@@ -1896,10 +1911,6 @@ var DoodleOverlay = class {
|
|
|
1896
1911
|
subtree: true,
|
|
1897
1912
|
attributes: true
|
|
1898
1913
|
});
|
|
1899
|
-
for (const target of getScrollableAncestors(this.element)) {
|
|
1900
|
-
target.addEventListener("scroll", this.onScroll, { passive: true });
|
|
1901
|
-
this.listeners.push({ target, type: "scroll", handler: this.onScroll });
|
|
1902
|
-
}
|
|
1903
1914
|
window.addEventListener("resize", this.onResize, { passive: true });
|
|
1904
1915
|
this.listeners.push({
|
|
1905
1916
|
target: window,
|
|
@@ -1935,46 +1946,6 @@ var DoodleOverlay = class {
|
|
|
1935
1946
|
this.update();
|
|
1936
1947
|
});
|
|
1937
1948
|
}
|
|
1938
|
-
scheduleReposition() {
|
|
1939
|
-
if (this.pendingFrame != null || this.pendingRepositionFrame != null) {
|
|
1940
|
-
return;
|
|
1941
|
-
}
|
|
1942
|
-
this.pendingRepositionFrame = scheduleFrame(() => {
|
|
1943
|
-
this.pendingRepositionFrame = null;
|
|
1944
|
-
this.reposition();
|
|
1945
|
-
});
|
|
1946
|
-
}
|
|
1947
|
-
/**
|
|
1948
|
-
* Cheap scroll-driven path: slide the overlay to the element's new
|
|
1949
|
-
* viewport position without touching its contents. Scrolling can't change
|
|
1950
|
-
* an element's size, so the rects, viewBox and every hand-drawn stroke
|
|
1951
|
-
* inside them are still valid - only the overlay's position needs to move.
|
|
1952
|
-
*
|
|
1953
|
-
* That move is a `transform`, not `left`/`top`. `left`/`top` sit in the
|
|
1954
|
-
* same box-geometry pass as layout, so the browser re-checks layout on
|
|
1955
|
-
* every write; `transform` is composited, so the scroll path never
|
|
1956
|
-
* touches layout at all.
|
|
1957
|
-
*/
|
|
1958
|
-
reposition() {
|
|
1959
|
-
if (!this.svg || !this.element.isConnected) return;
|
|
1960
|
-
const { padding, radius } = this.options;
|
|
1961
|
-
const targets = this.getTargets();
|
|
1962
|
-
const margin = this.overlayMargin();
|
|
1963
|
-
const absoluteRects = targets.map(
|
|
1964
|
-
(target) => getElementBounds(target, padding, radius)
|
|
1965
|
-
);
|
|
1966
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1967
|
-
const currentWidth = parseFloat(this.svg.style.width) || 0;
|
|
1968
|
-
const currentHeight = parseFloat(this.svg.style.height) || 0;
|
|
1969
|
-
const resized = Math.abs(overlayRect.width - currentWidth) > 0.5 || Math.abs(overlayRect.height - currentHeight) > 0.5;
|
|
1970
|
-
if (resized) {
|
|
1971
|
-
this.update();
|
|
1972
|
-
return;
|
|
1973
|
-
}
|
|
1974
|
-
const dx = overlayRect.x - this.basePosition.x;
|
|
1975
|
-
const dy = overlayRect.y - this.basePosition.y;
|
|
1976
|
-
this.svg.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
|
|
1977
|
-
}
|
|
1978
1949
|
getTargets() {
|
|
1979
1950
|
if (this.options.children) {
|
|
1980
1951
|
const nodes = this.element.querySelectorAll(this.options.children);
|
|
@@ -1994,15 +1965,14 @@ var DoodleOverlay = class {
|
|
|
1994
1965
|
const overlayRect = unionRects(absoluteRects, margin);
|
|
1995
1966
|
clearSvg(this.svg);
|
|
1996
1967
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1968
|
+
const origin = this.wrapper ? this.wrapper.getBoundingClientRect() : { left: 0, top: 0 };
|
|
1997
1969
|
this.svg.setAttribute("width", String(overlayRect.width));
|
|
1998
1970
|
this.svg.setAttribute("height", String(overlayRect.height));
|
|
1999
1971
|
this.svg.setAttribute("viewBox", `0 0 ${overlayRect.width} ${overlayRect.height}`);
|
|
2000
|
-
this.svg.style.left = `${overlayRect.x}px`;
|
|
2001
|
-
this.svg.style.top = `${overlayRect.y}px`;
|
|
1972
|
+
this.svg.style.left = `${overlayRect.x - origin.left}px`;
|
|
1973
|
+
this.svg.style.top = `${overlayRect.y - origin.top}px`;
|
|
2002
1974
|
this.svg.style.width = `${overlayRect.width}px`;
|
|
2003
1975
|
this.svg.style.height = `${overlayRect.height}px`;
|
|
2004
|
-
this.svg.style.transform = "";
|
|
2005
|
-
this.basePosition = { x: overlayRect.x, y: overlayRect.y };
|
|
2006
1976
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
2007
1977
|
const noteStyle = {
|
|
2008
1978
|
color,
|
|
@@ -2078,10 +2048,6 @@ var DoodleOverlay = class {
|
|
|
2078
2048
|
cancelFrame(this.pendingFrame);
|
|
2079
2049
|
this.pendingFrame = null;
|
|
2080
2050
|
}
|
|
2081
|
-
if (this.pendingRepositionFrame != null) {
|
|
2082
|
-
cancelFrame(this.pendingRepositionFrame);
|
|
2083
|
-
this.pendingRepositionFrame = null;
|
|
2084
|
-
}
|
|
2085
2051
|
if (this.resizeObserver) {
|
|
2086
2052
|
this.resizeObserver.disconnect();
|
|
2087
2053
|
this.resizeObserver = null;
|
|
@@ -2098,10 +2064,11 @@ var DoodleOverlay = class {
|
|
|
2098
2064
|
target.removeEventListener(type, handler);
|
|
2099
2065
|
}
|
|
2100
2066
|
this.listeners = [];
|
|
2101
|
-
if (this.svg
|
|
2102
|
-
this.svg.
|
|
2067
|
+
if (this.svg) {
|
|
2068
|
+
removeOverlaySvg(this.svg, this.wrapper, this.element);
|
|
2103
2069
|
}
|
|
2104
2070
|
this.svg = null;
|
|
2071
|
+
this.wrapper = null;
|
|
2105
2072
|
instances.delete(this.element);
|
|
2106
2073
|
}
|
|
2107
2074
|
};
|
package/package.json
CHANGED