@aznabee/freehand-ui 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -6
- package/dist/freehand-ui.cjs +70 -90
- package/dist/freehand-ui.js +70 -90
- package/dist/native.cjs +2432 -0
- package/dist/native.d.ts +51 -0
- package/dist/native.js +2427 -0
- package/dist/react.cjs +70 -90
- package/dist/react.js +70 -90
- package/native/package.json +7 -0
- package/package.json +18 -2
- package/react/package.json +7 -0
package/dist/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,
|
|
@@ -585,8 +552,13 @@ function annotationAnchor(position, rect, offset = 12) {
|
|
|
585
552
|
return point;
|
|
586
553
|
}
|
|
587
554
|
|
|
588
|
-
// src/
|
|
555
|
+
// src/svg-dom.js
|
|
589
556
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
557
|
+
function createSvgElement(tag) {
|
|
558
|
+
return document.createElementNS(SVG_NS, tag);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// src/renderer.js
|
|
590
562
|
var XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
591
563
|
var HANDWRITTEN_FONT = '"Caveat", "Kalam", "Patrick Hand", "Bradley Hand", "Segoe Script", "Comic Sans MS", cursive';
|
|
592
564
|
var NOTE_FONT_SIZE = 19;
|
|
@@ -810,7 +782,7 @@ function createBorderBreaks(perimeter, config, random) {
|
|
|
810
782
|
return breaks;
|
|
811
783
|
}
|
|
812
784
|
function createGroup(parent, x, y, rotation = 0) {
|
|
813
|
-
const group =
|
|
785
|
+
const group = createSvgElement("g");
|
|
814
786
|
group.setAttribute(
|
|
815
787
|
"transform",
|
|
816
788
|
`translate(${fmt(x)} ${fmt(y)}) rotate(${rotation.toFixed(1)})`
|
|
@@ -819,7 +791,7 @@ function createGroup(parent, x, y, rotation = 0) {
|
|
|
819
791
|
return group;
|
|
820
792
|
}
|
|
821
793
|
function appendPath(svg, d, style) {
|
|
822
|
-
const path =
|
|
794
|
+
const path = createSvgElement("path");
|
|
823
795
|
path.setAttribute("d", d);
|
|
824
796
|
path.setAttribute("fill", style.fill ?? "none");
|
|
825
797
|
path.setAttribute("stroke", style.color);
|
|
@@ -834,7 +806,7 @@ function appendPath(svg, d, style) {
|
|
|
834
806
|
return path;
|
|
835
807
|
}
|
|
836
808
|
function appendGlow(svg, radius, style) {
|
|
837
|
-
const circle =
|
|
809
|
+
const circle = createSvgElement("circle");
|
|
838
810
|
circle.setAttribute("r", fmt(radius));
|
|
839
811
|
circle.setAttribute("fill", style.color);
|
|
840
812
|
circle.setAttribute("opacity", String(style.opacity));
|
|
@@ -1138,7 +1110,7 @@ function drawArrow(svg, rect, arrowOptions, style, seed, origin = null, context
|
|
|
1138
1110
|
tipAngle = Math.atan2(end.y - c2y, end.x - c2x);
|
|
1139
1111
|
}
|
|
1140
1112
|
}
|
|
1141
|
-
const group =
|
|
1113
|
+
const group = createSvgElement("g");
|
|
1142
1114
|
svg.appendChild(group);
|
|
1143
1115
|
const shaftPaths = [];
|
|
1144
1116
|
if (ghost) {
|
|
@@ -1228,8 +1200,8 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1228
1200
|
const lines = wrapWords(text, options.wordsPerLine ?? DEFAULT_WORDS_PER_LINE);
|
|
1229
1201
|
const lineHeight = fontSize * 1.05;
|
|
1230
1202
|
const rtl = options.rtl ?? isRtlText(text);
|
|
1231
|
-
const group =
|
|
1232
|
-
const textEl =
|
|
1203
|
+
const group = createSvgElement("g");
|
|
1204
|
+
const textEl = createSvgElement("text");
|
|
1233
1205
|
textEl.setAttribute("x", "0");
|
|
1234
1206
|
textEl.setAttribute("y", "0");
|
|
1235
1207
|
textEl.setAttribute("fill", style.color);
|
|
@@ -1248,7 +1220,7 @@ function createNoteText(svg, text, style, options = {}) {
|
|
|
1248
1220
|
textEl.setAttributeNS(XML_NS, "xml:lang", options.locale);
|
|
1249
1221
|
}
|
|
1250
1222
|
const tspans = lines.map((line, index) => {
|
|
1251
|
-
const tspan =
|
|
1223
|
+
const tspan = createSvgElement("tspan");
|
|
1252
1224
|
tspan.textContent = line;
|
|
1253
1225
|
tspan.setAttribute("x", "0");
|
|
1254
1226
|
tspan.setAttribute("dy", index === 0 ? "0" : String(lineHeight));
|
|
@@ -1312,8 +1284,8 @@ function placeNoteText(note, box, style, seed, options = {}) {
|
|
|
1312
1284
|
return note.group;
|
|
1313
1285
|
}
|
|
1314
1286
|
function createOverlaySvg(left, top, width, height) {
|
|
1315
|
-
const svg =
|
|
1316
|
-
svg.setAttribute("class",
|
|
1287
|
+
const svg = createSvgElement("svg");
|
|
1288
|
+
svg.setAttribute("class", OVERLAY_CLASS);
|
|
1317
1289
|
svg.setAttribute("xmlns", SVG_NS);
|
|
1318
1290
|
svg.setAttribute("width", String(width));
|
|
1319
1291
|
svg.setAttribute("height", String(height));
|
|
@@ -1855,13 +1827,27 @@ var instances = /* @__PURE__ */ new WeakMap();
|
|
|
1855
1827
|
function now() {
|
|
1856
1828
|
return typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1857
1829
|
}
|
|
1858
|
-
function localBand(
|
|
1830
|
+
function localBand(originLeft, inset = 8) {
|
|
1859
1831
|
const width = window.innerWidth || document.documentElement.clientWidth || 0;
|
|
1860
1832
|
return {
|
|
1861
|
-
x: -
|
|
1833
|
+
x: -originLeft + inset,
|
|
1862
1834
|
width: Math.max(0, width - inset * 2)
|
|
1863
1835
|
};
|
|
1864
1836
|
}
|
|
1837
|
+
function paddingBox(element) {
|
|
1838
|
+
const rect = element.getBoundingClientRect();
|
|
1839
|
+
const style = getComputedStyle(element);
|
|
1840
|
+
const left = parseFloat(style.borderLeftWidth) || 0;
|
|
1841
|
+
const top = parseFloat(style.borderTopWidth) || 0;
|
|
1842
|
+
const right = parseFloat(style.borderRightWidth) || 0;
|
|
1843
|
+
const bottom = parseFloat(style.borderBottomWidth) || 0;
|
|
1844
|
+
return {
|
|
1845
|
+
left: rect.left + left,
|
|
1846
|
+
top: rect.top + top,
|
|
1847
|
+
width: Math.max(0, rect.width - left - right),
|
|
1848
|
+
height: Math.max(0, rect.height - top - bottom)
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1865
1851
|
var DoodleOverlay = class {
|
|
1866
1852
|
/**
|
|
1867
1853
|
* @param {Element} element
|
|
@@ -1873,7 +1859,7 @@ var DoodleOverlay = class {
|
|
|
1873
1859
|
this.seed = Math.floor(Math.random() * 1e9);
|
|
1874
1860
|
this.startedAt = now();
|
|
1875
1861
|
this.svg = null;
|
|
1876
|
-
this.
|
|
1862
|
+
this.setPosition = false;
|
|
1877
1863
|
this.resizeObserver = null;
|
|
1878
1864
|
this.mutationObserver = null;
|
|
1879
1865
|
this.pendingFrame = null;
|
|
@@ -1885,14 +1871,17 @@ var DoodleOverlay = class {
|
|
|
1885
1871
|
}
|
|
1886
1872
|
mount() {
|
|
1887
1873
|
this.svg = createOverlaySvg(0, 0, 0, 0);
|
|
1888
|
-
this.
|
|
1874
|
+
this.setPosition = insertOverlaySvg(this.svg, this.element);
|
|
1889
1875
|
syncOverlayStacking(this.svg, this.element, this.options.zIndex);
|
|
1890
1876
|
this.resizeObserver = new ResizeObserver(this.onResize);
|
|
1891
1877
|
this.resizeObserver.observe(this.element);
|
|
1892
1878
|
if (this.options.children) {
|
|
1893
1879
|
this.observeChildren();
|
|
1894
1880
|
}
|
|
1895
|
-
this.mutationObserver = new MutationObserver(
|
|
1881
|
+
this.mutationObserver = new MutationObserver((records) => {
|
|
1882
|
+
const relevant = records.some((record) => !this.svg.contains(record.target));
|
|
1883
|
+
if (relevant) this.scheduleUpdate();
|
|
1884
|
+
});
|
|
1896
1885
|
this.mutationObserver.observe(this.element, {
|
|
1897
1886
|
childList: true,
|
|
1898
1887
|
subtree: true,
|
|
@@ -1944,22 +1933,23 @@ var DoodleOverlay = class {
|
|
|
1944
1933
|
if (!this.svg || !this.element.isConnected) return;
|
|
1945
1934
|
const { padding, radius, color, strokeWidth, roughness, opacity } = this.options;
|
|
1946
1935
|
const targets = this.getTargets();
|
|
1947
|
-
const margin = this.overlayMargin();
|
|
1948
1936
|
const elapsed = now() - this.startedAt;
|
|
1949
1937
|
const absoluteRects = targets.map(
|
|
1950
1938
|
(target) => getElementBounds(target, padding, radius)
|
|
1951
1939
|
);
|
|
1952
|
-
const overlayRect = unionRects(absoluteRects, margin);
|
|
1953
1940
|
clearSvg(this.svg);
|
|
1954
1941
|
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.
|
|
1942
|
+
const box = paddingBox(this.element);
|
|
1943
|
+
const width = Math.max(1, box.width);
|
|
1944
|
+
const height = Math.max(1, box.height);
|
|
1945
|
+
const origin = { x: box.left, y: box.top };
|
|
1946
|
+
this.svg.setAttribute("width", String(width));
|
|
1947
|
+
this.svg.setAttribute("height", String(height));
|
|
1948
|
+
this.svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1949
|
+
this.svg.style.left = "0px";
|
|
1950
|
+
this.svg.style.top = "0px";
|
|
1951
|
+
this.svg.style.width = `${width}px`;
|
|
1952
|
+
this.svg.style.height = `${height}px`;
|
|
1963
1953
|
const strokeStyle = { color, strokeWidth, roughness, opacity };
|
|
1964
1954
|
const noteStyle = {
|
|
1965
1955
|
color,
|
|
@@ -1970,12 +1960,12 @@ var DoodleOverlay = class {
|
|
|
1970
1960
|
};
|
|
1971
1961
|
const noteGroups = [];
|
|
1972
1962
|
const noteLayout = {
|
|
1973
|
-
band: localBand(
|
|
1963
|
+
band: localBand(origin.x),
|
|
1974
1964
|
gap: this.options.arrow ? 40 : 14
|
|
1975
1965
|
};
|
|
1976
1966
|
targets.forEach((target, index) => {
|
|
1977
1967
|
const absolute = absoluteRects[index];
|
|
1978
|
-
const local = relativeRect(
|
|
1968
|
+
const local = relativeRect(origin, absolute);
|
|
1979
1969
|
const seed = this.seed + index * 31;
|
|
1980
1970
|
const note = this.options.note && index === 0 ? renderAnnotation(
|
|
1981
1971
|
this.svg,
|
|
@@ -2021,15 +2011,6 @@ var DoodleOverlay = class {
|
|
|
2021
2011
|
this.svg.appendChild(group);
|
|
2022
2012
|
}
|
|
2023
2013
|
}
|
|
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
2014
|
destroy() {
|
|
2034
2015
|
if (this.pendingFrame != null) {
|
|
2035
2016
|
cancelFrame(this.pendingFrame);
|
|
@@ -2052,10 +2033,9 @@ var DoodleOverlay = class {
|
|
|
2052
2033
|
}
|
|
2053
2034
|
this.listeners = [];
|
|
2054
2035
|
if (this.svg) {
|
|
2055
|
-
removeOverlaySvg(this.svg, this.
|
|
2036
|
+
removeOverlaySvg(this.svg, this.element, this.setPosition);
|
|
2056
2037
|
}
|
|
2057
2038
|
this.svg = null;
|
|
2058
|
-
this.wrapper = null;
|
|
2059
2039
|
instances.delete(this.element);
|
|
2060
2040
|
}
|
|
2061
2041
|
};
|