@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/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
|
|
|
@@ -426,7 +424,41 @@ Server-rendered markup is unaffected: the overlay is drawn in an effect, so the
|
|
|
426
424
|
|
|
427
425
|
- Inline object props (`note={{ text: "hi" }}`) are compared by value, so a re-render does not tear down and redraw the overlay with a new random seed.
|
|
428
426
|
- `disabled` skips drawing entirely - useful behind a reduced-motion check or a feature flag.
|
|
429
|
-
- TypeScript definitions ship with the package for
|
|
427
|
+
- TypeScript definitions ship with the package for every entry point.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## React Native
|
|
432
|
+
|
|
433
|
+
The same doodles, drawn with [react-native-svg](https://github.com/software-mansion/react-native-svg). Import from the `native` entry point - it needs `react-native-svg` installed in the app.
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
npm install @aznabee/freehand-ui react-native-svg
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
import Doodle from "@aznabee/freehand-ui/native";
|
|
441
|
+
|
|
442
|
+
<Doodle
|
|
443
|
+
note={{ text: "start chat", position: "bottom", underline: false }}
|
|
444
|
+
arrow={{ from: "left", to: "edge", style: "curved" }}
|
|
445
|
+
decorations={{ types: ["heart"] }}
|
|
446
|
+
radius={16}
|
|
447
|
+
>
|
|
448
|
+
<TouchableOpacity onPress={start}>...</TouchableOpacity>
|
|
449
|
+
</Doodle>
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
`<Doodle>` is a plain `View` - every `View` prop passes through - and takes the same options as the web component. The doodle is drawn in an absolutely positioned layer that ignores touches and never affects layout, so, as on the web, an ancestor with `overflow: "hidden"` clips it.
|
|
453
|
+
|
|
454
|
+
Differences from the web:
|
|
455
|
+
|
|
456
|
+
- **Corner radius** is not detected from the children. Pass `radius`, or it is read from the wrapper's own `borderRadius` style.
|
|
457
|
+
- **Handwriting** uses the platform's built-in face - Noteworthy on iOS, `casual` on Android - because nothing is fetched at runtime. Link Caveat into the app and pass `fontFamily="Caveat"` to match the web exactly.
|
|
458
|
+
- **Note width** is estimated from the glyphs rather than measured, which is close enough to place the note and its underline.
|
|
459
|
+
- **Arrows** draw on and lean as they do on the web (the lean runs on the native driver). A dotted shaft stays still instead of crawling. Both are skipped when the OS asks for reduced motion.
|
|
460
|
+
- **Language**: React Native has no `navigator.languages`, so pass `locale` (your app's i18n language) to choose from a multilingual note.
|
|
461
|
+
- `childSelector` has no equivalent.
|
|
430
462
|
|
|
431
463
|
---
|
|
432
464
|
|
|
@@ -456,7 +488,7 @@ Then open:
|
|
|
456
488
|
- **Framework agnostic** - plain JavaScript at the core; React is an optional entry point
|
|
457
489
|
- **SVG based** - hand-drawn paths, not CSS borders
|
|
458
490
|
- **Non-invasive** - `pointer-events: none`, no layout changes
|
|
459
|
-
- **Responsive** -
|
|
491
|
+
- **Responsive** - `ResizeObserver`, scroll listeners, and `requestAnimationFrame`
|
|
460
492
|
- **Lightweight** - zero runtime dependencies
|
|
461
493
|
|
|
462
494
|
---
|
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,
|
|
@@ -610,8 +577,13 @@ function annotationAnchor(position, rect, offset = 12) {
|
|
|
610
577
|
return point;
|
|
611
578
|
}
|
|
612
579
|
|
|
613
|
-
// src/
|
|
580
|
+
// src/svg-dom.js
|
|
614
581
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
582
|
+
function createSvgElement(tag) {
|
|
583
|
+
return document.createElementNS(SVG_NS, tag);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// src/renderer.js
|
|
615
587
|
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
616
588
|
var HANDWRITTEN_FONT = '"Caveat", "Kalam", "Patrick Hand", "Bradley Hand", "Segoe Script", "Comic Sans MS", cursive';
|
|
617
589
|
var NOTE_FONT_SIZE = 19;
|
|
@@ -835,7 +807,7 @@ function createBorderBreaks(perimeter, config, random) {
|
|
|
835
807
|
return breaks;
|
|
836
808
|
}
|
|
837
809
|
function createGroup(parent, x, y, rotation = 0) {
|
|
838
|
-
const group =
|
|
810
|
+
const group = createSvgElement("g");
|
|
839
811
|
group.setAttribute(
|
|
840
812
|
"transform",
|
|
841
813
|
`translate(${fmt(x)} ${fmt(y)}) rotate(${rotation.toFixed(1)})`
|
|
@@ -844,7 +816,7 @@ function createGroup(parent, x, y, rotation = 0) {
|
|
|
844
816
|
return group;
|
|
845
817
|
}
|
|
846
818
|
function appendPath(svg, d, style) {
|
|
847
|
-
const path =
|
|
819
|
+
const path = createSvgElement("path");
|
|
848
820
|
path.setAttribute("d", d);
|
|
849
821
|
path.setAttribute("fill", style.fill ?? "none");
|
|
850
822
|
path.setAttribute("stroke", style.color);
|
|
@@ -859,7 +831,7 @@ function appendPath(svg, d, style) {
|
|
|
859
831
|
return path;
|
|
860
832
|
}
|
|
861
833
|
function appendGlow(svg, radius, style) {
|
|
862
|
-
const circle =
|
|
834
|
+
const circle = createSvgElement("circle");
|
|
863
835
|
circle.setAttribute("r", fmt(radius));
|
|
864
836
|
circle.setAttribute("fill", style.color);
|
|
865
837
|
circle.setAttribute("opacity", String(style.opacity));
|
|
@@ -1163,7 +1135,7 @@ function drawArrow(svg, rect, arrowOptions, style, seed, origin = null, context
|
|
|
1163
1135
|
tipAngle = Math.atan2(end.y - c2y, end.x - c2x);
|
|
1164
1136
|
}
|
|
1165
1137
|
}
|
|
1166
|
-
const group =
|
|
1138
|
+
const group = createSvgElement("g");
|
|
1167
1139
|
svg.appendChild(group);
|
|
1168
1140
|
const shaftPaths = [];
|
|
1169
1141
|
if (ghost) {
|
|
@@ -1253,8 +1225,8 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1253
1225
|
const lines = wrapWords(text, options.wordsPerLine ?? DEFAULT_WORDS_PER_LINE);
|
|
1254
1226
|
const lineHeight = fontSize * 1.05;
|
|
1255
1227
|
const rtl = options.rtl ?? isRtlText(text);
|
|
1256
|
-
const group =
|
|
1257
|
-
const textEl =
|
|
1228
|
+
const group = createSvgElement("g");
|
|
1229
|
+
const textEl = createSvgElement("text");
|
|
1258
1230
|
textEl.setAttribute("x", "0");
|
|
1259
1231
|
textEl.setAttribute("y", "0");
|
|
1260
1232
|
textEl.setAttribute("fill", style.color);
|
|
@@ -1273,7 +1245,7 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1273
1245
|
textEl.setAttributeNS(XML_NS, "xml:lang", options.locale);
|
|
1274
1246
|
}
|
|
1275
1247
|
const tspans = lines.map((line, index) => {
|
|
1276
|
-
const tspan =
|
|
1248
|
+
const tspan = createSvgElement("tspan");
|
|
1277
1249
|
tspan.textContent = line;
|
|
1278
1250
|
tspan.setAttribute("x", "0");
|
|
1279
1251
|
tspan.setAttribute("dy", index === 0 ? "0" : String(lineHeight));
|
|
@@ -1337,8 +1309,8 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1337
1309
|
return note.group;
|
|
1338
1310
|
}
|
|
1339
1311
|
function createOverlaySvg(left, top, width, height) {
|
|
1340
|
-
const svg =
|
|
1341
|
-
svg.setAttribute("class",
|
|
1312
|
+
const svg = createSvgElement("svg");
|
|
1313
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1342
1314
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1343
1315
|
svg.setAttribute("width", String(width));
|
|
1344
1316
|
svg.setAttribute("height", String(height));
|
|
@@ -1880,13 +1852,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1880
1852
|
function now() {
|
|
1881
1853
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1882
1854
|
}
|
|
1883
|
-
function localBand(
|
|
1855
|
+
function localBand(originLeft, inset = 8) {
|
|
1884
1856
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1885
1857
|
return {
|
|
1886
|
-
x: -
|
|
1858
|
+
x: -originLeft + inset,
|
|
1887
1859
|
width: Math.max(0, width - inset * 2)
|
|
1888
1860
|
};
|
|
1889
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
|
+
}
|
|
1890
1876
|
var DoodleOverlay = class {
|
|
1891
1877
|
/**
|
|
1892
1878
|
* @param {Element} element
|
|
@@ -1898,7 +1884,7 @@ var DoodleOverlay = class {
|
|
|
1898
1884
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1899
1885
|
this.startedAt = now();
|
|
1900
1886
|
this.svg = null;
|
|
1901
|
-
this.
|
|
1887
|
+
this.setPosition = false;
|
|
1902
1888
|
this.resizeObserver = null;
|
|
1903
1889
|
this.mutationObserver = null;
|
|
1904
1890
|
this.pendingFrame = null;
|
|
@@ -1910,14 +1896,17 @@ var DoodleOverlay = class {
|
|
|
1910
1896
|
}
|
|
1911
1897
|
mount() {
|
|
1912
1898
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1913
|
-
this.
|
|
1899
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1914
1900
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1915
1901
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1916
1902
|
this.resizeObserver.observe(this.element);
|
|
1917
1903
|
if (this.options.children) {
|
|
1918
1904
|
this.observeChildren();
|
|
1919
1905
|
}
|
|
1920
|
-
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
|
+
});
|
|
1921
1910
|
this.mutationObserver.observe(this.element, {
|
|
1922
1911
|
childList: true,
|
|
1923
1912
|
subtree: true,
|
|
@@ -1969,22 +1958,23 @@ var DoodleOverlay = class {
|
|
|
1969
1958
|
if (!this.svg || !this.element.isConnected) return;
|
|
1970
1959
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1971
1960
|
const targets = this.getTargets();
|
|
1972
|
-
const margin = this.overlayMargin();
|
|
1973
1961
|
const elapsed = now() - this.startedAt;
|
|
1974
1962
|
const absoluteRects = targets.map(
|
|
1975
1963
|
(target) => getElementBounds(target, padding, radius)
|
|
1976
1964
|
);
|
|
1977
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1978
1965
|
clearSvg(this.svg);
|
|
1979
1966
|
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.
|
|
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`;
|
|
1988
1978
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1989
1979
|
const noteStyle = {
|
|
1990
1980
|
color,
|
|
@@ -1995,12 +1985,12 @@ var DoodleOverlay = class {
|
|
|
1995
1985
|
};
|
|
1996
1986
|
const noteGroups = [];
|
|
1997
1987
|
const noteLayout = {
|
|
1998
|
-
band: localBand(
|
|
1988
|
+
band: localBand(origin.x),
|
|
1999
1989
|
gap: this.options.arrow ? 40 : 14
|
|
2000
1990
|
};
|
|
2001
1991
|
targets.forEach((target, index) => {
|
|
2002
1992
|
const absolute = absoluteRects[index];
|
|
2003
|
-
const local = relativeRect(
|
|
1993
|
+
const local = relativeRect(origin, absolute);
|
|
2004
1994
|
const seed = this.seed + index * 31;
|
|
2005
1995
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
2006
1996
|
this.svg,
|
|
@@ -2046,15 +2036,6 @@ var DoodleOverlay = class {
|
|
|
2046
2036
|
this.svg.appendChild(group);
|
|
2047
2037
|
}
|
|
2048
2038
|
}
|
|
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
2039
|
destroy() {
|
|
2059
2040
|
if (this.pendingFrame != null) {
|
|
2060
2041
|
cancelFrame(this.pendingFrame);
|
|
@@ -2077,10 +2058,9 @@ var DoodleOverlay = class {
|
|
|
2077
2058
|
}
|
|
2078
2059
|
this.listeners = [];
|
|
2079
2060
|
if (this.svg) {
|
|
2080
|
-
removeOverlaySvg(this.svg, this.
|
|
2061
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2081
2062
|
}
|
|
2082
2063
|
this.svg = null;
|
|
2083
|
-
this.wrapper = null;
|
|
2084
2064
|
instances.delete(this.element);
|
|
2085
2065
|
}
|
|
2086
2066
|
};
|