canvu-react 0.4.14 → 0.4.15
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/dist/native.cjs +830 -149
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +34 -13
- package/dist/native.d.ts +34 -13
- package/dist/native.js +829 -152
- package/dist/native.js.map +1 -1
- package/package.json +1 -1
package/dist/native.cjs
CHANGED
|
@@ -1822,13 +1822,52 @@ function NativeShapeRenderer({ item }) {
|
|
|
1822
1822
|
}
|
|
1823
1823
|
return null;
|
|
1824
1824
|
}
|
|
1825
|
+
|
|
1826
|
+
// src/native/native-overlay-style.ts
|
|
1827
|
+
var ERASER_TINT = "#cbd5e1";
|
|
1828
|
+
var ERASER_TINT_OPACITY = 0.95;
|
|
1829
|
+
var LASER_TINT = "#f43f5e";
|
|
1830
|
+
var LASER_TINT_OPACITY = 0.9;
|
|
1831
|
+
var ERASER_TRAIL_MAX_AGE_MS = 150;
|
|
1832
|
+
var LASER_TRAIL_MAX_AGE_MS = 650;
|
|
1833
|
+
function colorWithOpacity(hex, alpha) {
|
|
1834
|
+
if (alpha == null || alpha >= 1) return hex;
|
|
1835
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
1836
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
1837
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
1838
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return hex;
|
|
1839
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
1840
|
+
}
|
|
1841
|
+
function freshTimedTrailPoints(points, now, maxAgeMs) {
|
|
1842
|
+
return points.filter((point) => now - point.t <= maxAgeMs);
|
|
1843
|
+
}
|
|
1844
|
+
function timedTrailHeadOpacity(point, now, maxAgeMs) {
|
|
1845
|
+
if (!point) return 0;
|
|
1846
|
+
return Math.max(0, 1 - (now - point.t) / maxAgeMs);
|
|
1847
|
+
}
|
|
1848
|
+
function resolveNativeStrokePreviewStyle(tool, previewStrokeStyle) {
|
|
1849
|
+
const isLaser = tool === "laser";
|
|
1850
|
+
return {
|
|
1851
|
+
stroke: isLaser ? LASER_TINT : previewStrokeStyle?.stroke ?? "#64748b",
|
|
1852
|
+
strokeWidth: isLaser ? 4 : previewStrokeStyle?.strokeWidth ?? (tool === "marker" ? 16 : 3),
|
|
1853
|
+
...previewStrokeStyle?.strokeOpacity != null && !isLaser ? { strokeOpacity: previewStrokeStyle.strokeOpacity } : {},
|
|
1854
|
+
...previewStrokeStyle?.strokeDash != null && !isLaser ? { strokeDash: previewStrokeStyle.strokeDash } : {}
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1825
1857
|
var HANDLE_ORDER = ["nw", "n", "ne", "e", "se", "s", "sw", "w"];
|
|
1826
1858
|
var ERASER_PREVIEW_OPACITY = 0.3;
|
|
1859
|
+
var OVERLAY_STROKE_PX = 1.25;
|
|
1860
|
+
var MARQUEE_DASH_PX = 4;
|
|
1827
1861
|
function pointsToSmoothPathD(points) {
|
|
1828
1862
|
if (points.length < 2) return null;
|
|
1829
1863
|
const d = smoothFreehandPointsToPathD(points);
|
|
1830
1864
|
return d || null;
|
|
1831
1865
|
}
|
|
1866
|
+
function dashIntervalsFromStrokeDasharray(strokeDasharray) {
|
|
1867
|
+
if (!strokeDasharray) return null;
|
|
1868
|
+
const intervals = strokeDasharray.split(/\s+/).map((part) => Number(part)).filter((part) => Number.isFinite(part) && part > 0);
|
|
1869
|
+
return intervals.length > 0 ? intervals : null;
|
|
1870
|
+
}
|
|
1832
1871
|
function NativeInteractionOverlay({
|
|
1833
1872
|
camera,
|
|
1834
1873
|
width,
|
|
@@ -1838,13 +1877,18 @@ function NativeInteractionOverlay({
|
|
|
1838
1877
|
placementPreview,
|
|
1839
1878
|
eraserTrail,
|
|
1840
1879
|
laserTrail,
|
|
1841
|
-
eraserPreviewItems = []
|
|
1880
|
+
eraserPreviewItems = [],
|
|
1881
|
+
previewStrokeStyle
|
|
1842
1882
|
}) {
|
|
1843
1883
|
const z = camera.zoom;
|
|
1844
1884
|
const camTransform = skiaCameraTransform(z, camera.x, camera.y);
|
|
1845
1885
|
const handleR = 5 / z;
|
|
1846
|
-
const
|
|
1886
|
+
const overlayStrokeWorld = OVERLAY_STROKE_PX / z;
|
|
1887
|
+
const marqueeDashWorld = MARQUEE_DASH_PX / z;
|
|
1847
1888
|
const rotateOffsetWorld = 24 / z;
|
|
1889
|
+
const rotateIconWorld = 16 / z;
|
|
1890
|
+
const rotateIconScale = rotateIconWorld / 24;
|
|
1891
|
+
const rotateIconStroke = OVERLAY_STROKE_PX / (z * rotateIconScale);
|
|
1848
1892
|
const selectionElements = react.useMemo(() => {
|
|
1849
1893
|
if (selectedItems.length === 0) return null;
|
|
1850
1894
|
const single = selectedItems.length === 1 ? selectedItems[0] : void 0;
|
|
@@ -1866,7 +1910,7 @@ function NativeInteractionOverlay({
|
|
|
1866
1910
|
height: b.height,
|
|
1867
1911
|
color: "#3b82f6",
|
|
1868
1912
|
style: "stroke",
|
|
1869
|
-
strokeWidth:
|
|
1913
|
+
strokeWidth: overlayStrokeWorld,
|
|
1870
1914
|
antiAlias: true
|
|
1871
1915
|
}
|
|
1872
1916
|
) }, it.id);
|
|
@@ -1874,83 +1918,167 @@ function NativeInteractionOverlay({
|
|
|
1874
1918
|
showResizeHandles && bSingle && single && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1875
1919
|
HANDLE_ORDER.map((hid) => {
|
|
1876
1920
|
const p = getHandleWorldPositionRotated(bSingle, hid, rotSingle);
|
|
1877
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1921
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNativeSkia.Group, { children: [
|
|
1922
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1923
|
+
reactNativeSkia.Circle,
|
|
1924
|
+
{
|
|
1925
|
+
cx: p.x,
|
|
1926
|
+
cy: p.y,
|
|
1927
|
+
r: handleR,
|
|
1928
|
+
color: "#ffffff",
|
|
1929
|
+
style: "fill",
|
|
1930
|
+
antiAlias: true
|
|
1931
|
+
}
|
|
1932
|
+
),
|
|
1933
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1934
|
+
reactNativeSkia.Circle,
|
|
1935
|
+
{
|
|
1936
|
+
cx: p.x,
|
|
1937
|
+
cy: p.y,
|
|
1938
|
+
r: handleR,
|
|
1939
|
+
color: "#3b82f6",
|
|
1940
|
+
style: "stroke",
|
|
1941
|
+
strokeWidth: overlayStrokeWorld,
|
|
1942
|
+
antiAlias: true
|
|
1943
|
+
}
|
|
1944
|
+
)
|
|
1945
|
+
] }, hid);
|
|
1889
1946
|
}),
|
|
1890
|
-
rotHandlePos && /* @__PURE__ */ jsxRuntime.
|
|
1891
|
-
reactNativeSkia.
|
|
1947
|
+
rotHandlePos && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1948
|
+
reactNativeSkia.Group,
|
|
1892
1949
|
{
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1950
|
+
transform: [
|
|
1951
|
+
{ translateX: rotHandlePos.x },
|
|
1952
|
+
{ translateY: rotHandlePos.y },
|
|
1953
|
+
{ rotate: rotSingle },
|
|
1954
|
+
{ translateX: -rotateIconWorld / 2 },
|
|
1955
|
+
{ translateY: -rotateIconWorld / 2 },
|
|
1956
|
+
{ scale: rotateIconScale }
|
|
1957
|
+
],
|
|
1958
|
+
children: [
|
|
1959
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1960
|
+
reactNativeSkia.Path,
|
|
1961
|
+
{
|
|
1962
|
+
path: "M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8",
|
|
1963
|
+
color: "#3b82f6",
|
|
1964
|
+
style: "stroke",
|
|
1965
|
+
strokeWidth: rotateIconStroke,
|
|
1966
|
+
strokeCap: "round",
|
|
1967
|
+
strokeJoin: "round",
|
|
1968
|
+
antiAlias: true
|
|
1969
|
+
}
|
|
1970
|
+
),
|
|
1971
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1972
|
+
reactNativeSkia.Path,
|
|
1973
|
+
{
|
|
1974
|
+
path: "M21 3v5h-5",
|
|
1975
|
+
color: "#3b82f6",
|
|
1976
|
+
style: "stroke",
|
|
1977
|
+
strokeWidth: rotateIconStroke,
|
|
1978
|
+
strokeCap: "round",
|
|
1979
|
+
strokeJoin: "round",
|
|
1980
|
+
antiAlias: true
|
|
1981
|
+
}
|
|
1982
|
+
)
|
|
1983
|
+
]
|
|
1900
1984
|
}
|
|
1901
1985
|
)
|
|
1902
1986
|
] })
|
|
1903
1987
|
] });
|
|
1904
|
-
}, [
|
|
1988
|
+
}, [
|
|
1989
|
+
selectedItems,
|
|
1990
|
+
showResizeHandles,
|
|
1991
|
+
rotateOffsetWorld,
|
|
1992
|
+
handleR,
|
|
1993
|
+
overlayStrokeWorld,
|
|
1994
|
+
rotateIconWorld,
|
|
1995
|
+
rotateIconScale,
|
|
1996
|
+
rotateIconStroke
|
|
1997
|
+
]);
|
|
1905
1998
|
const previewElements = react.useMemo(() => {
|
|
1906
1999
|
if (!placementPreview) return null;
|
|
1907
2000
|
const p = placementPreview;
|
|
2001
|
+
const shapeStroke = previewStrokeStyle?.stroke ?? "#1d1d1d";
|
|
2002
|
+
const shapeWidth = previewStrokeStyle?.strokeWidth ?? 2;
|
|
2003
|
+
const shapeColor = colorWithOpacity(
|
|
2004
|
+
shapeStroke,
|
|
2005
|
+
previewStrokeStyle?.strokeOpacity
|
|
2006
|
+
);
|
|
1908
2007
|
if (p.kind === "rect" || p.kind === "ellipse" || p.kind === "architectural-cloud") {
|
|
1909
2008
|
const r = normalizeRect(p.rect);
|
|
1910
|
-
return p.kind === "rect"
|
|
1911
|
-
reactNativeSkia.
|
|
2009
|
+
return p.kind === "rect" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2010
|
+
reactNativeSkia.RoundedRect,
|
|
1912
2011
|
{
|
|
1913
2012
|
x: r.x,
|
|
1914
2013
|
y: r.y,
|
|
1915
2014
|
width: r.width,
|
|
1916
2015
|
height: r.height,
|
|
1917
|
-
|
|
2016
|
+
r: 4,
|
|
2017
|
+
color: shapeColor,
|
|
1918
2018
|
style: "stroke",
|
|
1919
|
-
strokeWidth:
|
|
2019
|
+
strokeWidth: shapeWidth,
|
|
1920
2020
|
antiAlias: true
|
|
1921
2021
|
}
|
|
1922
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1923
|
-
reactNativeSkia.
|
|
1924
|
-
{
|
|
1925
|
-
cx: r.x + r.width / 2,
|
|
1926
|
-
cy: r.y + r.height / 2,
|
|
1927
|
-
r: Math.max(0, r.width / 2),
|
|
1928
|
-
color: "#64748b",
|
|
1929
|
-
style: "stroke",
|
|
1930
|
-
strokeWidth: overlayStrokePx,
|
|
1931
|
-
antiAlias: true
|
|
1932
|
-
}
|
|
1933
|
-
);
|
|
1934
|
-
}
|
|
1935
|
-
if (p.kind === "marquee") {
|
|
1936
|
-
const r = normalizeRect(p.rect);
|
|
1937
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1938
|
-
reactNativeSkia.Rect,
|
|
2022
|
+
) : p.kind === "ellipse" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2023
|
+
reactNativeSkia.Oval,
|
|
1939
2024
|
{
|
|
1940
2025
|
x: r.x,
|
|
1941
2026
|
y: r.y,
|
|
1942
2027
|
width: r.width,
|
|
1943
2028
|
height: r.height,
|
|
1944
|
-
color:
|
|
1945
|
-
style: "
|
|
2029
|
+
color: shapeColor,
|
|
2030
|
+
style: "stroke",
|
|
2031
|
+
strokeWidth: shapeWidth,
|
|
1946
2032
|
antiAlias: true
|
|
1947
2033
|
}
|
|
1948
|
-
)
|
|
2034
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { transform: [{ translateX: r.x }, { translateY: r.y }], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2035
|
+
reactNativeSkia.Path,
|
|
2036
|
+
{
|
|
2037
|
+
path: buildArchitecturalCloudPathD(r.width, r.height, shapeWidth),
|
|
2038
|
+
color: shapeColor,
|
|
2039
|
+
style: "stroke",
|
|
2040
|
+
strokeWidth: shapeWidth,
|
|
2041
|
+
strokeCap: "round",
|
|
2042
|
+
strokeJoin: "round",
|
|
2043
|
+
antiAlias: true
|
|
2044
|
+
}
|
|
2045
|
+
) });
|
|
2046
|
+
}
|
|
2047
|
+
if (p.kind === "marquee") {
|
|
2048
|
+
const r = normalizeRect(p.rect);
|
|
2049
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2050
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2051
|
+
reactNativeSkia.Rect,
|
|
2052
|
+
{
|
|
2053
|
+
x: r.x,
|
|
2054
|
+
y: r.y,
|
|
2055
|
+
width: r.width,
|
|
2056
|
+
height: r.height,
|
|
2057
|
+
color: "rgba(59, 130, 246, 0.12)",
|
|
2058
|
+
style: "fill",
|
|
2059
|
+
antiAlias: true
|
|
2060
|
+
}
|
|
2061
|
+
),
|
|
2062
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2063
|
+
reactNativeSkia.Rect,
|
|
2064
|
+
{
|
|
2065
|
+
x: r.x,
|
|
2066
|
+
y: r.y,
|
|
2067
|
+
width: r.width,
|
|
2068
|
+
height: r.height,
|
|
2069
|
+
color: "#3b82f6",
|
|
2070
|
+
style: "stroke",
|
|
2071
|
+
strokeWidth: overlayStrokeWorld,
|
|
2072
|
+
antiAlias: true,
|
|
2073
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.DashPathEffect, { intervals: [marqueeDashWorld, marqueeDashWorld] })
|
|
2074
|
+
}
|
|
2075
|
+
)
|
|
2076
|
+
] });
|
|
1949
2077
|
}
|
|
1950
2078
|
if (p.kind === "line" || p.kind === "arrow") {
|
|
1951
2079
|
const geometry = p.kind === "arrow" ? computeStraightArrowGeometry(
|
|
1952
2080
|
{ x1: p.start.x, y1: p.start.y, x2: p.end.x, y2: p.end.y },
|
|
1953
|
-
|
|
2081
|
+
shapeWidth
|
|
1954
2082
|
) : null;
|
|
1955
2083
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1956
2084
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1958,9 +2086,9 @@ function NativeInteractionOverlay({
|
|
|
1958
2086
|
{
|
|
1959
2087
|
p1: reactNativeSkia.vec(p.start.x, p.start.y),
|
|
1960
2088
|
p2: reactNativeSkia.vec(geometry?.shaftEndX ?? p.end.x, geometry?.shaftEndY ?? p.end.y),
|
|
1961
|
-
color:
|
|
2089
|
+
color: shapeColor,
|
|
1962
2090
|
style: "stroke",
|
|
1963
|
-
strokeWidth:
|
|
2091
|
+
strokeWidth: shapeWidth,
|
|
1964
2092
|
strokeCap: "round",
|
|
1965
2093
|
antiAlias: true
|
|
1966
2094
|
}
|
|
@@ -1969,9 +2097,9 @@ function NativeInteractionOverlay({
|
|
|
1969
2097
|
reactNativeSkia.Path,
|
|
1970
2098
|
{
|
|
1971
2099
|
path: `M ${geometry.headLeftX} ${geometry.headLeftY} L ${geometry.headTipX} ${geometry.headTipY} L ${geometry.headRightX} ${geometry.headRightY}`,
|
|
1972
|
-
color:
|
|
2100
|
+
color: shapeColor,
|
|
1973
2101
|
style: "stroke",
|
|
1974
|
-
strokeWidth:
|
|
2102
|
+
strokeWidth: shapeWidth,
|
|
1975
2103
|
strokeCap: "round",
|
|
1976
2104
|
strokeJoin: "round",
|
|
1977
2105
|
antiAlias: true
|
|
@@ -1980,10 +2108,15 @@ function NativeInteractionOverlay({
|
|
|
1980
2108
|
] });
|
|
1981
2109
|
}
|
|
1982
2110
|
if (p.kind === "stroke" && p.points.length >= 1) {
|
|
2111
|
+
const isLaser = p.tool === "laser";
|
|
2112
|
+
const style = resolveNativeStrokePreviewStyle(
|
|
2113
|
+
p.tool,
|
|
2114
|
+
p.style ?? previewStrokeStyle
|
|
2115
|
+
);
|
|
1983
2116
|
const payload = computeFreehandSvgPayload(
|
|
1984
2117
|
p.points,
|
|
1985
|
-
|
|
1986
|
-
"draw",
|
|
2118
|
+
style,
|
|
2119
|
+
isLaser ? "draw" : p.tool,
|
|
1987
2120
|
p.points.length === 2
|
|
1988
2121
|
);
|
|
1989
2122
|
if (!payload) return null;
|
|
@@ -1994,33 +2127,47 @@ function NativeInteractionOverlay({
|
|
|
1994
2127
|
cx: payload.cx,
|
|
1995
2128
|
cy: payload.cy,
|
|
1996
2129
|
r: payload.r,
|
|
1997
|
-
color: payload.fill,
|
|
2130
|
+
color: colorWithOpacity(payload.fill, payload.fillOpacity),
|
|
1998
2131
|
style: "fill",
|
|
1999
2132
|
antiAlias: true
|
|
2000
2133
|
}
|
|
2001
2134
|
);
|
|
2002
2135
|
}
|
|
2003
2136
|
if (payload.kind === "fillPath") {
|
|
2004
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2137
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2138
|
+
reactNativeSkia.Path,
|
|
2139
|
+
{
|
|
2140
|
+
path: payload.d,
|
|
2141
|
+
color: colorWithOpacity(payload.fill, payload.fillOpacity),
|
|
2142
|
+
style: "fill",
|
|
2143
|
+
fillType: "winding",
|
|
2144
|
+
antiAlias: true
|
|
2145
|
+
}
|
|
2146
|
+
);
|
|
2005
2147
|
}
|
|
2006
2148
|
if (payload.kind === "strokePath") {
|
|
2149
|
+
const intervals = dashIntervalsFromStrokeDasharray(payload.strokeDasharray);
|
|
2007
2150
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2008
2151
|
reactNativeSkia.Path,
|
|
2009
2152
|
{
|
|
2010
2153
|
path: payload.d,
|
|
2011
|
-
color:
|
|
2154
|
+
color: colorWithOpacity(
|
|
2155
|
+
payload.stroke,
|
|
2156
|
+
isLaser ? 0.85 : payload.strokeOpacity
|
|
2157
|
+
),
|
|
2012
2158
|
style: "stroke",
|
|
2013
|
-
strokeWidth:
|
|
2159
|
+
strokeWidth: payload.strokeWidth,
|
|
2014
2160
|
strokeCap: "round",
|
|
2015
2161
|
strokeJoin: "round",
|
|
2016
|
-
antiAlias: true
|
|
2162
|
+
antiAlias: true,
|
|
2163
|
+
children: intervals ? /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.DashPathEffect, { intervals }) : null
|
|
2017
2164
|
}
|
|
2018
2165
|
);
|
|
2019
2166
|
}
|
|
2020
2167
|
return null;
|
|
2021
2168
|
}
|
|
2022
2169
|
return null;
|
|
2023
|
-
}, [placementPreview]);
|
|
2170
|
+
}, [placementPreview, previewStrokeStyle, overlayStrokeWorld, marqueeDashWorld]);
|
|
2024
2171
|
const eraserPreviewElements = react.useMemo(() => {
|
|
2025
2172
|
if (eraserPreviewItems.length === 0) return null;
|
|
2026
2173
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: eraserPreviewItems.map((it) => {
|
|
@@ -2041,59 +2188,71 @@ function NativeInteractionOverlay({
|
|
|
2041
2188
|
}, [eraserPreviewItems]);
|
|
2042
2189
|
const eraserTrailElements = react.useMemo(() => {
|
|
2043
2190
|
if (!eraserTrail || eraserTrail.length < 1) return null;
|
|
2044
|
-
const
|
|
2045
|
-
|
|
2046
|
-
|
|
2191
|
+
const now = Date.now();
|
|
2192
|
+
const alive = freshTimedTrailPoints(eraserTrail, now, ERASER_TRAIL_MAX_AGE_MS);
|
|
2193
|
+
if (alive.length === 0) return null;
|
|
2194
|
+
const d = pointsToSmoothPathD(alive);
|
|
2195
|
+
const newest = alive[alive.length - 1];
|
|
2196
|
+
const headOpacity = timedTrailHeadOpacity(newest, now, ERASER_TRAIL_MAX_AGE_MS);
|
|
2197
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2198
|
+
alive.length >= 2 && d ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2199
|
+
reactNativeSkia.Path,
|
|
2200
|
+
{
|
|
2201
|
+
path: d,
|
|
2202
|
+
color: colorWithOpacity(ERASER_TINT, ERASER_TINT_OPACITY),
|
|
2203
|
+
style: "stroke",
|
|
2204
|
+
strokeWidth: Math.max(3.5, OVERLAY_STROKE_PX) / z,
|
|
2205
|
+
strokeCap: "round",
|
|
2206
|
+
strokeJoin: "round",
|
|
2207
|
+
antiAlias: true
|
|
2208
|
+
}
|
|
2209
|
+
) : null,
|
|
2210
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2047
2211
|
reactNativeSkia.Circle,
|
|
2048
2212
|
{
|
|
2049
|
-
cx:
|
|
2050
|
-
cy:
|
|
2213
|
+
cx: newest?.x ?? alive[0]?.x ?? 0,
|
|
2214
|
+
cy: newest?.y ?? alive[0]?.y ?? 0,
|
|
2051
2215
|
r: Math.max(5 / z, 3),
|
|
2052
|
-
color:
|
|
2216
|
+
color: colorWithOpacity(ERASER_TINT, headOpacity),
|
|
2053
2217
|
style: "fill",
|
|
2054
2218
|
antiAlias: true
|
|
2055
2219
|
}
|
|
2056
|
-
)
|
|
2057
|
-
|
|
2058
|
-
reactNativeSkia.Path,
|
|
2059
|
-
{
|
|
2060
|
-
path: d,
|
|
2061
|
-
color: "#cbd5e1",
|
|
2062
|
-
style: "stroke",
|
|
2063
|
-
strokeWidth: Math.max(3.5 / z, overlayStrokePx),
|
|
2064
|
-
strokeCap: "round",
|
|
2065
|
-
strokeJoin: "round",
|
|
2066
|
-
antiAlias: true
|
|
2067
|
-
}
|
|
2068
|
-
);
|
|
2220
|
+
)
|
|
2221
|
+
] });
|
|
2069
2222
|
}, [eraserTrail, z]);
|
|
2070
2223
|
const laserTrailElements = react.useMemo(() => {
|
|
2071
2224
|
if (!laserTrail || laserTrail.length < 1) return null;
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
|
|
2225
|
+
const now = Date.now();
|
|
2226
|
+
const alive = freshTimedTrailPoints(laserTrail, now, LASER_TRAIL_MAX_AGE_MS);
|
|
2227
|
+
if (alive.length === 0) return null;
|
|
2228
|
+
const d = pointsToSmoothPathD(alive);
|
|
2229
|
+
const newest = alive[alive.length - 1];
|
|
2230
|
+
const headOpacity = timedTrailHeadOpacity(newest, now, LASER_TRAIL_MAX_AGE_MS);
|
|
2231
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2232
|
+
alive.length >= 2 && d ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2233
|
+
reactNativeSkia.Path,
|
|
2234
|
+
{
|
|
2235
|
+
path: d,
|
|
2236
|
+
color: colorWithOpacity(LASER_TINT, LASER_TINT_OPACITY),
|
|
2237
|
+
style: "stroke",
|
|
2238
|
+
strokeWidth: Math.max(4, OVERLAY_STROKE_PX) / z,
|
|
2239
|
+
strokeCap: "round",
|
|
2240
|
+
strokeJoin: "round",
|
|
2241
|
+
antiAlias: true
|
|
2242
|
+
}
|
|
2243
|
+
) : null,
|
|
2244
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2075
2245
|
reactNativeSkia.Circle,
|
|
2076
2246
|
{
|
|
2077
|
-
cx:
|
|
2078
|
-
cy:
|
|
2247
|
+
cx: newest?.x ?? alive[0]?.x ?? 0,
|
|
2248
|
+
cy: newest?.y ?? alive[0]?.y ?? 0,
|
|
2079
2249
|
r: Math.max(5 / z, 3),
|
|
2080
|
-
color:
|
|
2250
|
+
color: colorWithOpacity(LASER_TINT, headOpacity),
|
|
2081
2251
|
style: "fill",
|
|
2082
2252
|
antiAlias: true
|
|
2083
2253
|
}
|
|
2084
|
-
)
|
|
2085
|
-
|
|
2086
|
-
reactNativeSkia.Path,
|
|
2087
|
-
{
|
|
2088
|
-
path: d,
|
|
2089
|
-
color: "#f43f5e",
|
|
2090
|
-
style: "stroke",
|
|
2091
|
-
strokeWidth: Math.max(4 / z, overlayStrokePx),
|
|
2092
|
-
strokeCap: "round",
|
|
2093
|
-
strokeJoin: "round",
|
|
2094
|
-
antiAlias: true
|
|
2095
|
-
}
|
|
2096
|
-
);
|
|
2254
|
+
)
|
|
2255
|
+
] });
|
|
2097
2256
|
}, [laserTrail, z]);
|
|
2098
2257
|
if (width <= 0 || height <= 0) return null;
|
|
2099
2258
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2239,6 +2398,383 @@ function NativeSceneRenderer({
|
|
|
2239
2398
|
if (width <= 0 || height <= 0) return null;
|
|
2240
2399
|
return /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Canvas, { style: { width, height }, children: /* @__PURE__ */ jsxRuntime.jsx(reactNativeSkia.Group, { transform: cameraTransform, children: visible.map((item) => /* @__PURE__ */ jsxRuntime.jsx(MemoShape, { item }, item.id)) }) });
|
|
2241
2400
|
}
|
|
2401
|
+
|
|
2402
|
+
// src/native/native-style-inspector-values.ts
|
|
2403
|
+
var NATIVE_STYLE_PALETTE = [
|
|
2404
|
+
{ name: "black", hex: "#1d1d1d" },
|
|
2405
|
+
{ name: "grey", hex: "#9fa8b2" },
|
|
2406
|
+
{ name: "light-violet", hex: "#e085f4" },
|
|
2407
|
+
{ name: "violet", hex: "#ae3ec9" },
|
|
2408
|
+
{ name: "blue", hex: "#4263eb" },
|
|
2409
|
+
{ name: "light-blue", hex: "#4dabf7" },
|
|
2410
|
+
{ name: "yellow", hex: "#ffc078" },
|
|
2411
|
+
{ name: "orange", hex: "#f76707" },
|
|
2412
|
+
{ name: "green", hex: "#099268" },
|
|
2413
|
+
{ name: "light-green", hex: "#40c057" },
|
|
2414
|
+
{ name: "light-red", hex: "#ff8787" },
|
|
2415
|
+
{ name: "red", hex: "#e03131" }
|
|
2416
|
+
];
|
|
2417
|
+
function normalizeNativeStyleHex(stroke) {
|
|
2418
|
+
if (stroke && /^#[0-9A-Fa-f]{6}$/.test(stroke)) return stroke;
|
|
2419
|
+
return "#1d1d1d";
|
|
2420
|
+
}
|
|
2421
|
+
function nativeStyleColorWithOpacity(hex, opacity) {
|
|
2422
|
+
const alpha = opacity ?? 1;
|
|
2423
|
+
if (alpha >= 1) return hex;
|
|
2424
|
+
const r = Number.parseInt(hex.slice(1, 3), 16);
|
|
2425
|
+
const g = Number.parseInt(hex.slice(3, 5), 16);
|
|
2426
|
+
const b = Number.parseInt(hex.slice(5, 7), 16);
|
|
2427
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
|
2428
|
+
}
|
|
2429
|
+
function hexesEqual(a, b) {
|
|
2430
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
2431
|
+
}
|
|
2432
|
+
function DashPreview({
|
|
2433
|
+
color,
|
|
2434
|
+
width,
|
|
2435
|
+
dashed = false
|
|
2436
|
+
}) {
|
|
2437
|
+
const strokeHeight = Math.max(3, Math.min(6, width));
|
|
2438
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.dashPreviewTrack, children: [
|
|
2439
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2440
|
+
reactNative.View,
|
|
2441
|
+
{
|
|
2442
|
+
style: [
|
|
2443
|
+
styles.dashPreviewStroke,
|
|
2444
|
+
{
|
|
2445
|
+
width: dashed ? 11 : 26,
|
|
2446
|
+
height: strokeHeight,
|
|
2447
|
+
borderRadius: strokeHeight / 2,
|
|
2448
|
+
backgroundColor: color
|
|
2449
|
+
}
|
|
2450
|
+
]
|
|
2451
|
+
}
|
|
2452
|
+
),
|
|
2453
|
+
dashed ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2454
|
+
reactNative.View,
|
|
2455
|
+
{
|
|
2456
|
+
style: [
|
|
2457
|
+
styles.dashPreviewStroke,
|
|
2458
|
+
{
|
|
2459
|
+
width: 11,
|
|
2460
|
+
height: strokeHeight,
|
|
2461
|
+
borderRadius: strokeHeight / 2,
|
|
2462
|
+
backgroundColor: color
|
|
2463
|
+
}
|
|
2464
|
+
]
|
|
2465
|
+
}
|
|
2466
|
+
) : null
|
|
2467
|
+
] });
|
|
2468
|
+
}
|
|
2469
|
+
function clampValue(value, min, max) {
|
|
2470
|
+
return Math.min(max, Math.max(min, value));
|
|
2471
|
+
}
|
|
2472
|
+
function RangeControl({
|
|
2473
|
+
value,
|
|
2474
|
+
min,
|
|
2475
|
+
max,
|
|
2476
|
+
onChange,
|
|
2477
|
+
valueLabel
|
|
2478
|
+
}) {
|
|
2479
|
+
const [trackWidth, setTrackWidth] = react.useState(1);
|
|
2480
|
+
const normalized = (clampValue(value, min, max) - min) / (max - min);
|
|
2481
|
+
const updateFromX = react.useCallback(
|
|
2482
|
+
(x) => {
|
|
2483
|
+
const next = min + clampValue(x / trackWidth, 0, 1) * (max - min);
|
|
2484
|
+
onChange(Math.round(next));
|
|
2485
|
+
},
|
|
2486
|
+
[max, min, onChange, trackWidth]
|
|
2487
|
+
);
|
|
2488
|
+
const panResponder = react.useMemo(
|
|
2489
|
+
() => reactNative.PanResponder.create({
|
|
2490
|
+
onMoveShouldSetPanResponder: () => true,
|
|
2491
|
+
onStartShouldSetPanResponder: () => true,
|
|
2492
|
+
onPanResponderGrant: (event) => {
|
|
2493
|
+
updateFromX(event.nativeEvent.locationX);
|
|
2494
|
+
},
|
|
2495
|
+
onPanResponderMove: (event) => {
|
|
2496
|
+
updateFromX(event.nativeEvent.locationX);
|
|
2497
|
+
}
|
|
2498
|
+
}),
|
|
2499
|
+
[updateFromX]
|
|
2500
|
+
);
|
|
2501
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.rangeRow, children: [
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2503
|
+
reactNative.View,
|
|
2504
|
+
{
|
|
2505
|
+
style: styles.rangeTrack,
|
|
2506
|
+
onLayout: (event) => setTrackWidth(event.nativeEvent.layout.width),
|
|
2507
|
+
...panResponder.panHandlers,
|
|
2508
|
+
children: [
|
|
2509
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.rangeBase }),
|
|
2510
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.rangeFill, { width: `${normalized * 100}%` }] }),
|
|
2511
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles.rangeThumb, { left: `${normalized * 100}%` }] })
|
|
2512
|
+
]
|
|
2513
|
+
}
|
|
2514
|
+
),
|
|
2515
|
+
valueLabel ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.rangeValue, children: valueLabel }) : null
|
|
2516
|
+
] });
|
|
2517
|
+
}
|
|
2518
|
+
function InspectorSection({
|
|
2519
|
+
label,
|
|
2520
|
+
children
|
|
2521
|
+
}) {
|
|
2522
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles.section, children: [
|
|
2523
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles.sectionLabel, children: label }),
|
|
2524
|
+
children
|
|
2525
|
+
] });
|
|
2526
|
+
}
|
|
2527
|
+
function SegmentControl({
|
|
2528
|
+
segments,
|
|
2529
|
+
value,
|
|
2530
|
+
onChange
|
|
2531
|
+
}) {
|
|
2532
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.segmentGroup, children: segments.map((segment) => {
|
|
2533
|
+
const selected = segment.value === value;
|
|
2534
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2535
|
+
reactNative.Pressable,
|
|
2536
|
+
{
|
|
2537
|
+
accessibilityRole: "button",
|
|
2538
|
+
accessibilityState: { selected },
|
|
2539
|
+
accessibilityLabel: segment.label,
|
|
2540
|
+
onPress: () => onChange(segment.value),
|
|
2541
|
+
style: [styles.segment, selected ? styles.segmentSelected : null],
|
|
2542
|
+
children: [
|
|
2543
|
+
segment.preview,
|
|
2544
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2545
|
+
reactNative.Text,
|
|
2546
|
+
{
|
|
2547
|
+
style: [
|
|
2548
|
+
styles.segmentLabel,
|
|
2549
|
+
selected ? styles.segmentLabelSelected : null
|
|
2550
|
+
],
|
|
2551
|
+
children: segment.label
|
|
2552
|
+
}
|
|
2553
|
+
)
|
|
2554
|
+
]
|
|
2555
|
+
},
|
|
2556
|
+
String(segment.value)
|
|
2557
|
+
);
|
|
2558
|
+
}) });
|
|
2559
|
+
}
|
|
2560
|
+
function NativeVectorStyleInspector({
|
|
2561
|
+
toolId,
|
|
2562
|
+
value,
|
|
2563
|
+
onChange,
|
|
2564
|
+
style
|
|
2565
|
+
}) {
|
|
2566
|
+
const hex = normalizeNativeStyleHex(value.stroke);
|
|
2567
|
+
const opacity = toolId === "marker" ? value.strokeOpacity ?? 0.5 : void 0;
|
|
2568
|
+
const dashSegments = [
|
|
2569
|
+
{
|
|
2570
|
+
value: "solid",
|
|
2571
|
+
label: "Cont\xEDnuo",
|
|
2572
|
+
preview: /* @__PURE__ */ jsxRuntime.jsx(DashPreview, { color: "#18181b", width: 4 })
|
|
2573
|
+
},
|
|
2574
|
+
{
|
|
2575
|
+
value: "dashed",
|
|
2576
|
+
label: "Tracejado",
|
|
2577
|
+
preview: /* @__PURE__ */ jsxRuntime.jsx(DashPreview, { color: "#18181b", width: 4, dashed: true })
|
|
2578
|
+
}
|
|
2579
|
+
];
|
|
2580
|
+
const opacityPercent = Math.round((opacity ?? 1) * 100);
|
|
2581
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2582
|
+
reactNative.View,
|
|
2583
|
+
{
|
|
2584
|
+
pointerEvents: "auto",
|
|
2585
|
+
style: [styles.shell, style],
|
|
2586
|
+
accessibilityRole: "summary",
|
|
2587
|
+
accessibilityLabel: toolId === "marker" ? "Configura\xE7\xF5es do marcador" : "Configura\xE7\xF5es da caneta",
|
|
2588
|
+
children: [
|
|
2589
|
+
/* @__PURE__ */ jsxRuntime.jsx(InspectorSection, { label: "Cor", children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles.palette, children: NATIVE_STYLE_PALETTE.map((color) => {
|
|
2590
|
+
const selected = hexesEqual(color.hex, hex);
|
|
2591
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2592
|
+
reactNative.Pressable,
|
|
2593
|
+
{
|
|
2594
|
+
accessibilityRole: "button",
|
|
2595
|
+
accessibilityState: { selected },
|
|
2596
|
+
accessibilityLabel: color.name,
|
|
2597
|
+
onPress: () => onChange({ stroke: color.hex }),
|
|
2598
|
+
style: [
|
|
2599
|
+
styles.swatch,
|
|
2600
|
+
{
|
|
2601
|
+
backgroundColor: color.hex,
|
|
2602
|
+
borderWidth: selected ? 2 : 1,
|
|
2603
|
+
transform: [{ scale: selected ? 1.08 : 1 }],
|
|
2604
|
+
shadowOpacity: selected ? 0.08 : 0
|
|
2605
|
+
}
|
|
2606
|
+
]
|
|
2607
|
+
},
|
|
2608
|
+
color.name
|
|
2609
|
+
);
|
|
2610
|
+
}) }) }),
|
|
2611
|
+
/* @__PURE__ */ jsxRuntime.jsx(InspectorSection, { label: "Grossura", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2612
|
+
RangeControl,
|
|
2613
|
+
{
|
|
2614
|
+
value: value.strokeWidth,
|
|
2615
|
+
min: 1,
|
|
2616
|
+
max: 48,
|
|
2617
|
+
onChange: (strokeWidth) => onChange({ strokeWidth })
|
|
2618
|
+
}
|
|
2619
|
+
) }),
|
|
2620
|
+
toolId === "draw" ? /* @__PURE__ */ jsxRuntime.jsx(InspectorSection, { label: "Tra\xE7o", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2621
|
+
SegmentControl,
|
|
2622
|
+
{
|
|
2623
|
+
segments: dashSegments,
|
|
2624
|
+
value: value.strokeDash === "dashed" ? "dashed" : "solid",
|
|
2625
|
+
onChange: (strokeDash) => onChange({ strokeDash })
|
|
2626
|
+
}
|
|
2627
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx(InspectorSection, { label: "Opacidade", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2628
|
+
RangeControl,
|
|
2629
|
+
{
|
|
2630
|
+
value: opacityPercent,
|
|
2631
|
+
min: 10,
|
|
2632
|
+
max: 100,
|
|
2633
|
+
valueLabel: `${opacityPercent}%`,
|
|
2634
|
+
onChange: (nextOpacity) => onChange({ strokeOpacity: nextOpacity / 100 })
|
|
2635
|
+
}
|
|
2636
|
+
) })
|
|
2637
|
+
]
|
|
2638
|
+
}
|
|
2639
|
+
);
|
|
2640
|
+
}
|
|
2641
|
+
var styles = reactNative.StyleSheet.create({
|
|
2642
|
+
shell: {
|
|
2643
|
+
minWidth: 240,
|
|
2644
|
+
paddingHorizontal: 14,
|
|
2645
|
+
paddingVertical: 12,
|
|
2646
|
+
borderRadius: 10,
|
|
2647
|
+
borderWidth: 1,
|
|
2648
|
+
borderColor: "rgba(0,0,0,0.1)",
|
|
2649
|
+
backgroundColor: "rgba(255,255,255,0.97)",
|
|
2650
|
+
shadowColor: "#000000",
|
|
2651
|
+
shadowOpacity: 0.06,
|
|
2652
|
+
shadowRadius: 16,
|
|
2653
|
+
shadowOffset: { width: 0, height: 4 },
|
|
2654
|
+
gap: 10
|
|
2655
|
+
},
|
|
2656
|
+
section: {
|
|
2657
|
+
gap: 6
|
|
2658
|
+
},
|
|
2659
|
+
sectionLabel: {
|
|
2660
|
+
fontSize: 11,
|
|
2661
|
+
fontWeight: "600",
|
|
2662
|
+
color: "#52525b",
|
|
2663
|
+
textTransform: "uppercase",
|
|
2664
|
+
letterSpacing: 0.22
|
|
2665
|
+
},
|
|
2666
|
+
palette: {
|
|
2667
|
+
flexDirection: "row",
|
|
2668
|
+
flexWrap: "wrap",
|
|
2669
|
+
columnGap: 6,
|
|
2670
|
+
rowGap: 6,
|
|
2671
|
+
maxWidth: 174
|
|
2672
|
+
},
|
|
2673
|
+
swatch: {
|
|
2674
|
+
width: 24,
|
|
2675
|
+
height: 24,
|
|
2676
|
+
borderRadius: 12,
|
|
2677
|
+
borderColor: "rgba(0,0,0,0.12)",
|
|
2678
|
+
shadowColor: "#000000",
|
|
2679
|
+
shadowRadius: 2,
|
|
2680
|
+
shadowOffset: { width: 0, height: 1 }
|
|
2681
|
+
},
|
|
2682
|
+
rangeRow: {
|
|
2683
|
+
minHeight: 28,
|
|
2684
|
+
flexDirection: "row",
|
|
2685
|
+
alignItems: "center",
|
|
2686
|
+
gap: 8
|
|
2687
|
+
},
|
|
2688
|
+
rangeTrack: {
|
|
2689
|
+
position: "relative",
|
|
2690
|
+
flex: 1,
|
|
2691
|
+
height: 28,
|
|
2692
|
+
justifyContent: "center"
|
|
2693
|
+
},
|
|
2694
|
+
rangeBase: {
|
|
2695
|
+
position: "absolute",
|
|
2696
|
+
left: 0,
|
|
2697
|
+
right: 0,
|
|
2698
|
+
height: 3,
|
|
2699
|
+
borderRadius: 2,
|
|
2700
|
+
backgroundColor: "rgba(24,24,27,0.12)"
|
|
2701
|
+
},
|
|
2702
|
+
rangeFill: {
|
|
2703
|
+
position: "absolute",
|
|
2704
|
+
left: 0,
|
|
2705
|
+
height: 3,
|
|
2706
|
+
borderRadius: 2,
|
|
2707
|
+
backgroundColor: "#18181b"
|
|
2708
|
+
},
|
|
2709
|
+
rangeThumb: {
|
|
2710
|
+
position: "absolute",
|
|
2711
|
+
width: 18,
|
|
2712
|
+
height: 18,
|
|
2713
|
+
marginLeft: -9,
|
|
2714
|
+
borderRadius: 9,
|
|
2715
|
+
borderWidth: 2,
|
|
2716
|
+
borderColor: "#18181b",
|
|
2717
|
+
backgroundColor: "#ffffff",
|
|
2718
|
+
shadowColor: "#000000",
|
|
2719
|
+
shadowOpacity: 0.12,
|
|
2720
|
+
shadowRadius: 3,
|
|
2721
|
+
shadowOffset: { width: 0, height: 1 }
|
|
2722
|
+
},
|
|
2723
|
+
rangeValue: {
|
|
2724
|
+
minWidth: 34,
|
|
2725
|
+
textAlign: "right",
|
|
2726
|
+
fontSize: 11,
|
|
2727
|
+
fontWeight: "500",
|
|
2728
|
+
color: "#71717a"
|
|
2729
|
+
},
|
|
2730
|
+
segmentGroup: {
|
|
2731
|
+
flexDirection: "row",
|
|
2732
|
+
alignItems: "center",
|
|
2733
|
+
gap: 3,
|
|
2734
|
+
padding: 3,
|
|
2735
|
+
borderRadius: 8,
|
|
2736
|
+
backgroundColor: "rgba(24,24,27,0.06)"
|
|
2737
|
+
},
|
|
2738
|
+
segment: {
|
|
2739
|
+
flex: 1,
|
|
2740
|
+
minWidth: 96,
|
|
2741
|
+
minHeight: 32,
|
|
2742
|
+
flexDirection: "row",
|
|
2743
|
+
alignItems: "center",
|
|
2744
|
+
justifyContent: "center",
|
|
2745
|
+
gap: 6,
|
|
2746
|
+
paddingHorizontal: 8,
|
|
2747
|
+
paddingVertical: 5,
|
|
2748
|
+
borderRadius: 6
|
|
2749
|
+
},
|
|
2750
|
+
segmentSelected: {
|
|
2751
|
+
backgroundColor: "#ffffff",
|
|
2752
|
+
shadowColor: "#000000",
|
|
2753
|
+
shadowOpacity: 0.1,
|
|
2754
|
+
shadowRadius: 2,
|
|
2755
|
+
shadowOffset: { width: 0, height: 1 }
|
|
2756
|
+
},
|
|
2757
|
+
segmentLabel: {
|
|
2758
|
+
fontSize: 11,
|
|
2759
|
+
fontWeight: "600",
|
|
2760
|
+
color: "#71717a",
|
|
2761
|
+
flexShrink: 0
|
|
2762
|
+
},
|
|
2763
|
+
segmentLabelSelected: {
|
|
2764
|
+
color: "#18181b"
|
|
2765
|
+
},
|
|
2766
|
+
dashPreviewTrack: {
|
|
2767
|
+
width: 26,
|
|
2768
|
+
height: 6,
|
|
2769
|
+
flexDirection: "row",
|
|
2770
|
+
alignItems: "center",
|
|
2771
|
+
justifyContent: "center",
|
|
2772
|
+
gap: 3
|
|
2773
|
+
},
|
|
2774
|
+
dashPreviewStroke: {
|
|
2775
|
+
flexShrink: 0
|
|
2776
|
+
}
|
|
2777
|
+
});
|
|
2242
2778
|
var DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = [
|
|
2243
2779
|
"rect",
|
|
2244
2780
|
"ellipse",
|
|
@@ -2370,7 +2906,7 @@ function NativeVectorToolbar({
|
|
|
2370
2906
|
reactNative.View,
|
|
2371
2907
|
{
|
|
2372
2908
|
accessibilityLabel,
|
|
2373
|
-
style: [
|
|
2909
|
+
style: [styles2.shell, style],
|
|
2374
2910
|
pointerEvents: "box-none",
|
|
2375
2911
|
children: [
|
|
2376
2912
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -2379,8 +2915,8 @@ function NativeVectorToolbar({
|
|
|
2379
2915
|
horizontal: true,
|
|
2380
2916
|
showsHorizontalScrollIndicator: false,
|
|
2381
2917
|
contentContainerStyle: [
|
|
2382
|
-
|
|
2383
|
-
density === "comfortable" ?
|
|
2918
|
+
styles2.content,
|
|
2919
|
+
density === "comfortable" ? styles2.comfortableContent : void 0,
|
|
2384
2920
|
contentContainerStyle
|
|
2385
2921
|
],
|
|
2386
2922
|
children: [
|
|
@@ -2397,21 +2933,21 @@ function NativeVectorToolbar({
|
|
|
2397
2933
|
disabled: toolLockDisabled,
|
|
2398
2934
|
onPress: toggleToolLock,
|
|
2399
2935
|
style: ({ pressed }) => [
|
|
2400
|
-
|
|
2401
|
-
density === "comfortable" ?
|
|
2402
|
-
toolLocked ?
|
|
2403
|
-
pressed && !toolLockDisabled ?
|
|
2404
|
-
toolLockDisabled ?
|
|
2936
|
+
styles2.toolButton,
|
|
2937
|
+
density === "comfortable" ? styles2.comfortableToolButton : void 0,
|
|
2938
|
+
toolLocked ? styles2.activeToolButton : void 0,
|
|
2939
|
+
pressed && !toolLockDisabled ? styles2.pressedToolButton : void 0,
|
|
2940
|
+
toolLockDisabled ? styles2.disabledToolButton : void 0
|
|
2405
2941
|
],
|
|
2406
2942
|
children: renderToolLockIcon?.({
|
|
2407
2943
|
locked: toolLocked,
|
|
2408
2944
|
disabled: toolLockDisabled,
|
|
2409
2945
|
foregroundColor: "#18181b",
|
|
2410
2946
|
onToggle: toggleToolLock
|
|
2411
|
-
}) ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style:
|
|
2947
|
+
}) ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.lockGlyph, children: toolLocked ? "L" : "U" })
|
|
2412
2948
|
}
|
|
2413
2949
|
),
|
|
2414
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
2950
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.toolLockDivider })
|
|
2415
2951
|
] }) : null,
|
|
2416
2952
|
toolbarTools.map(
|
|
2417
2953
|
(tool) => renderNativeToolButton({
|
|
@@ -2429,7 +2965,7 @@ function NativeVectorToolbar({
|
|
|
2429
2965
|
renderToolButton
|
|
2430
2966
|
})
|
|
2431
2967
|
),
|
|
2432
|
-
showOverflowMenu ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
2968
|
+
showOverflowMenu ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.overflowSpacer }) : null,
|
|
2433
2969
|
showOverflowMenu ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2434
2970
|
reactNative.Pressable,
|
|
2435
2971
|
{
|
|
@@ -2443,27 +2979,27 @@ function NativeVectorToolbar({
|
|
|
2443
2979
|
disabled,
|
|
2444
2980
|
onPress: toggleOverflow,
|
|
2445
2981
|
style: ({ pressed }) => [
|
|
2446
|
-
|
|
2447
|
-
overflowOpen || activeOverflowTool ?
|
|
2448
|
-
pressed && !disabled ?
|
|
2449
|
-
disabled ?
|
|
2982
|
+
styles2.overflowTrigger,
|
|
2983
|
+
overflowOpen || activeOverflowTool ? styles2.activeToolButton : void 0,
|
|
2984
|
+
pressed && !disabled ? styles2.pressedToolButton : void 0,
|
|
2985
|
+
disabled ? styles2.disabledToolButton : void 0
|
|
2450
2986
|
],
|
|
2451
2987
|
children: [
|
|
2452
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
2988
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.iconSlot, children: activeOverflowTool ? renderToolIcon?.({
|
|
2453
2989
|
tool: activeOverflowTool,
|
|
2454
2990
|
selected: true,
|
|
2455
2991
|
disabled,
|
|
2456
2992
|
foregroundColor: "#18181b",
|
|
2457
2993
|
onSelect: () => onChange(activeOverflowTool.id)
|
|
2458
|
-
}) ?? renderNativeToolFallback(activeOverflowTool, "#18181b") : renderOverflowIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style:
|
|
2459
|
-
renderOverflowChevronIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style:
|
|
2994
|
+
}) ?? renderNativeToolFallback(activeOverflowTool, "#18181b") : renderOverflowIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.shapesGlyph, children: "S" }) }),
|
|
2995
|
+
renderOverflowChevronIcon?.(overflowRenderInput) ?? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles2.chevronGlyph, children: overflowOpen ? "^" : "v" })
|
|
2460
2996
|
]
|
|
2461
2997
|
}
|
|
2462
2998
|
) : null
|
|
2463
2999
|
]
|
|
2464
3000
|
}
|
|
2465
3001
|
),
|
|
2466
|
-
overflowOpen && showOverflowMenu ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [
|
|
3002
|
+
overflowOpen && showOverflowMenu ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: [styles2.overflowPanel, overflowPanelStyle], children: remainingOverflowTools.map(
|
|
2467
3003
|
(tool) => renderNativeToolButton({
|
|
2468
3004
|
tool,
|
|
2469
3005
|
value,
|
|
@@ -2474,7 +3010,7 @@ function NativeVectorToolbar({
|
|
|
2474
3010
|
disabled,
|
|
2475
3011
|
disabledIds,
|
|
2476
3012
|
density: "compact",
|
|
2477
|
-
toolButtonStyle:
|
|
3013
|
+
toolButtonStyle: styles2.overflowToolButton,
|
|
2478
3014
|
activeToolButtonStyle,
|
|
2479
3015
|
toolLabelStyle,
|
|
2480
3016
|
activeToolLabelStyle,
|
|
@@ -2515,22 +3051,22 @@ function renderNativeToolButton(input) {
|
|
|
2515
3051
|
disabled: toolDisabled,
|
|
2516
3052
|
onPress: onSelect,
|
|
2517
3053
|
style: ({ pressed }) => [
|
|
2518
|
-
|
|
2519
|
-
input.density === "comfortable" ?
|
|
3054
|
+
styles2.toolButton,
|
|
3055
|
+
input.density === "comfortable" ? styles2.comfortableToolButton : void 0,
|
|
2520
3056
|
input.toolButtonStyle,
|
|
2521
|
-
selected ?
|
|
3057
|
+
selected ? styles2.activeToolButton : void 0,
|
|
2522
3058
|
selected ? input.activeToolButtonStyle : void 0,
|
|
2523
|
-
pressed && !toolDisabled ?
|
|
2524
|
-
toolDisabled ?
|
|
3059
|
+
pressed && !toolDisabled ? styles2.pressedToolButton : void 0,
|
|
3060
|
+
toolDisabled ? styles2.disabledToolButton : void 0
|
|
2525
3061
|
],
|
|
2526
3062
|
children: [
|
|
2527
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style:
|
|
3063
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles2.iconSlot, children: icon }),
|
|
2528
3064
|
input.density === "comfortable" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2529
3065
|
reactNative.Text,
|
|
2530
3066
|
{
|
|
2531
3067
|
numberOfLines: 1,
|
|
2532
3068
|
style: [
|
|
2533
|
-
|
|
3069
|
+
styles2.toolLabel,
|
|
2534
3070
|
{ color: foregroundColor },
|
|
2535
3071
|
input.toolLabelStyle,
|
|
2536
3072
|
selected ? input.activeToolLabelStyle : void 0
|
|
@@ -2544,9 +3080,9 @@ function renderNativeToolButton(input) {
|
|
|
2544
3080
|
);
|
|
2545
3081
|
}
|
|
2546
3082
|
function renderNativeToolFallback(tool, foregroundColor, toolLabelStyle) {
|
|
2547
|
-
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [
|
|
3083
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [styles2.shortLabel, { color: foregroundColor }, toolLabelStyle], children: tool.shortLabel ?? tool.label.slice(0, 1).toUpperCase() });
|
|
2548
3084
|
}
|
|
2549
|
-
var
|
|
3085
|
+
var styles2 = reactNative.StyleSheet.create({
|
|
2550
3086
|
shell: {
|
|
2551
3087
|
borderRadius: 8,
|
|
2552
3088
|
borderWidth: reactNative.StyleSheet.hairlineWidth,
|
|
@@ -2871,9 +3407,27 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
|
|
|
2871
3407
|
var MIN_PLACE_SIZE = 8;
|
|
2872
3408
|
var MIN_ARROW_DRAG_PX = 8;
|
|
2873
3409
|
var TAP_PX = 20;
|
|
3410
|
+
var MARKER_TOOL_STYLE = {
|
|
3411
|
+
stroke: "#fde047",
|
|
3412
|
+
strokeWidth: 16,
|
|
3413
|
+
strokeOpacity: 0.5
|
|
3414
|
+
};
|
|
2874
3415
|
function isPlacementTool(toolId) {
|
|
2875
3416
|
return toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud" || toolId === "line" || toolId === "arrow";
|
|
2876
3417
|
}
|
|
3418
|
+
function isDefaultMarkerToolStyle(style) {
|
|
3419
|
+
return style.stroke === MARKER_TOOL_STYLE.stroke && style.strokeWidth === MARKER_TOOL_STYLE.strokeWidth && style.strokeOpacity === MARKER_TOOL_STYLE.strokeOpacity;
|
|
3420
|
+
}
|
|
3421
|
+
function supportsNativeResizeHandles(item) {
|
|
3422
|
+
const k = item?.toolKind;
|
|
3423
|
+
if (k === "rect" || k === "ellipse" || k === "architectural-cloud" || k === "line" || k === "arrow" || k === "image" || k === "text") {
|
|
3424
|
+
return true;
|
|
3425
|
+
}
|
|
3426
|
+
if ((k === "draw" || k === "pencil" || k === "brush" || k === "marker") && item?.pathPointsLocal && item.pathPointsLocal.length > 0) {
|
|
3427
|
+
return true;
|
|
3428
|
+
}
|
|
3429
|
+
return k === "custom" && !!item?.customIntrinsicSize && !!item?.customInnerSvg;
|
|
3430
|
+
}
|
|
2877
3431
|
function placementPreviewForTool(toolId, start, end) {
|
|
2878
3432
|
if (toolId === "rect" || toolId === "ellipse" || toolId === "architectural-cloud") {
|
|
2879
3433
|
return { kind: toolId, rect: rectFromCorners(start, end) };
|
|
@@ -2936,7 +3490,9 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2936
3490
|
onItemsChange,
|
|
2937
3491
|
onToolChangeRequest,
|
|
2938
3492
|
onCameraChange,
|
|
2939
|
-
toolbar
|
|
3493
|
+
toolbar,
|
|
3494
|
+
showStyleInspector = false,
|
|
3495
|
+
styleInspectorPlacement = "bottom"
|
|
2940
3496
|
}, ref) {
|
|
2941
3497
|
const [size, setSize] = react.useState({ width: 0, height: 0 });
|
|
2942
3498
|
const cameraRef = react.useRef(null);
|
|
@@ -2963,6 +3519,13 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2963
3519
|
const [eraserTrail, setEraserTrail] = react.useState([]);
|
|
2964
3520
|
const [laserTrail, setLaserTrail] = react.useState([]);
|
|
2965
3521
|
const laserClearTimerRef = react.useRef(null);
|
|
3522
|
+
const strokeStyleRef = react.useRef({ ...DEFAULT_STROKE_STYLE });
|
|
3523
|
+
const markerStrokeStyleRef = react.useRef({ ...MARKER_TOOL_STYLE });
|
|
3524
|
+
const styleBeforeMarkerRef = react.useRef({ ...DEFAULT_STROKE_STYLE });
|
|
3525
|
+
const lastToolIdRef = react.useRef(toolId);
|
|
3526
|
+
const [strokeStyleState, setStrokeStyleState] = react.useState({
|
|
3527
|
+
...DEFAULT_STROKE_STYLE
|
|
3528
|
+
});
|
|
2966
3529
|
react.useEffect(
|
|
2967
3530
|
() => () => {
|
|
2968
3531
|
if (laserClearTimerRef.current) {
|
|
@@ -2971,6 +3534,60 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
2971
3534
|
},
|
|
2972
3535
|
[]
|
|
2973
3536
|
);
|
|
3537
|
+
react.useEffect(() => {
|
|
3538
|
+
if (eraserTrail.length === 0 && laserTrail.length === 0) return;
|
|
3539
|
+
const timer = setInterval(() => {
|
|
3540
|
+
const now = Date.now();
|
|
3541
|
+
setEraserTrail(
|
|
3542
|
+
(prev) => freshTimedTrailPoints(prev, now, ERASER_TRAIL_MAX_AGE_MS)
|
|
3543
|
+
);
|
|
3544
|
+
setLaserTrail(
|
|
3545
|
+
(prev) => freshTimedTrailPoints(prev, now, LASER_TRAIL_MAX_AGE_MS)
|
|
3546
|
+
);
|
|
3547
|
+
}, 50);
|
|
3548
|
+
return () => clearInterval(timer);
|
|
3549
|
+
}, [eraserTrail.length, laserTrail.length]);
|
|
3550
|
+
react.useEffect(() => {
|
|
3551
|
+
const previousToolId = lastToolIdRef.current;
|
|
3552
|
+
const current = strokeStyleRef.current;
|
|
3553
|
+
let next = current;
|
|
3554
|
+
if (toolId === "marker") {
|
|
3555
|
+
if (previousToolId !== "marker") {
|
|
3556
|
+
styleBeforeMarkerRef.current = current;
|
|
3557
|
+
}
|
|
3558
|
+
next = markerStrokeStyleRef.current;
|
|
3559
|
+
} else if (previousToolId === "marker") {
|
|
3560
|
+
markerStrokeStyleRef.current = current;
|
|
3561
|
+
const restored = styleBeforeMarkerRef.current;
|
|
3562
|
+
next = {
|
|
3563
|
+
stroke: restored.stroke,
|
|
3564
|
+
strokeWidth: toolId === "draw" && restored.strokeWidth === DEFAULT_STROKE_STYLE.strokeWidth ? 10 : restored.strokeWidth,
|
|
3565
|
+
...restored.strokeDash != null ? { strokeDash: restored.strokeDash } : {}
|
|
3566
|
+
};
|
|
3567
|
+
} else if (toolId === "draw") {
|
|
3568
|
+
next = {
|
|
3569
|
+
...current,
|
|
3570
|
+
strokeWidth: current.strokeWidth === DEFAULT_STROKE_STYLE.strokeWidth ? 10 : current.strokeWidth
|
|
3571
|
+
};
|
|
3572
|
+
} else if (isDefaultMarkerToolStyle(current)) {
|
|
3573
|
+
next = {
|
|
3574
|
+
stroke: DEFAULT_STROKE_STYLE.stroke,
|
|
3575
|
+
strokeWidth: DEFAULT_STROKE_STYLE.strokeWidth,
|
|
3576
|
+
...current.strokeDash != null ? { strokeDash: current.strokeDash } : {}
|
|
3577
|
+
};
|
|
3578
|
+
}
|
|
3579
|
+
strokeStyleRef.current = next;
|
|
3580
|
+
setStrokeStyleState(next);
|
|
3581
|
+
lastToolIdRef.current = toolId;
|
|
3582
|
+
}, [toolId]);
|
|
3583
|
+
const patchCurrentStrokeStyle = react.useCallback((patch) => {
|
|
3584
|
+
const next = { ...strokeStyleRef.current, ...patch };
|
|
3585
|
+
strokeStyleRef.current = next;
|
|
3586
|
+
if (toolIdRef.current === "marker") {
|
|
3587
|
+
markerStrokeStyleRef.current = next;
|
|
3588
|
+
}
|
|
3589
|
+
setStrokeStyleState(next);
|
|
3590
|
+
}, []);
|
|
2974
3591
|
const [eraserPreviewIds, setEraserPreviewIds] = react.useState([]);
|
|
2975
3592
|
const eraserPreviewIdSetRef = react.useRef(/* @__PURE__ */ new Set());
|
|
2976
3593
|
const requestSelectToolAfterUse = react.useCallback(() => {
|
|
@@ -3008,7 +3625,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3008
3625
|
const hidden = new Set(eraserPreviewIds);
|
|
3009
3626
|
return items.filter((it) => !hidden.has(it.id));
|
|
3010
3627
|
}, [items, eraserPreviewIds]);
|
|
3011
|
-
const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked;
|
|
3628
|
+
const showResizeHandles = interactive && selectedItems.length === 1 && !selectedItems[0]?.locked && supportsNativeResizeHandles(selectedItems[0]);
|
|
3012
3629
|
const lastPinchDist = react.useRef(null);
|
|
3013
3630
|
const lastPanPoint = react.useRef(null);
|
|
3014
3631
|
const panResponder = react.useMemo(
|
|
@@ -3083,7 +3700,14 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3083
3700
|
clearTimeout(laserClearTimerRef.current);
|
|
3084
3701
|
laserClearTimerRef.current = null;
|
|
3085
3702
|
}
|
|
3086
|
-
setLaserTrail([{ x: worldX, y: worldY }]);
|
|
3703
|
+
setLaserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
|
|
3704
|
+
} else {
|
|
3705
|
+
setPlacementPreview({
|
|
3706
|
+
kind: "stroke",
|
|
3707
|
+
tool,
|
|
3708
|
+
points: [{ x: worldX, y: worldY }],
|
|
3709
|
+
style: { ...strokeStyleRef.current }
|
|
3710
|
+
});
|
|
3087
3711
|
}
|
|
3088
3712
|
return;
|
|
3089
3713
|
}
|
|
@@ -3091,7 +3715,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3091
3715
|
dragStateRef.current = { kind: "erase" };
|
|
3092
3716
|
eraserPreviewIdSetRef.current = /* @__PURE__ */ new Set();
|
|
3093
3717
|
setEraserPreviewIds([]);
|
|
3094
|
-
setEraserTrail([{ x: worldX, y: worldY }]);
|
|
3718
|
+
setEraserTrail([{ x: worldX, y: worldY, t: Date.now() }]);
|
|
3095
3719
|
const toErase = collectEraserTargetsAtWorldPoint(
|
|
3096
3720
|
itemsRef.current,
|
|
3097
3721
|
worldX,
|
|
@@ -3182,18 +3806,25 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3182
3806
|
const last = pts[pts.length - 1];
|
|
3183
3807
|
const dx = worldX - (last?.x ?? worldX);
|
|
3184
3808
|
const dy = worldY - (last?.y ?? worldY);
|
|
3185
|
-
|
|
3809
|
+
const shouldAppendPoint = Math.hypot(dx, dy) > 0.5 / cam.zoom;
|
|
3810
|
+
if (shouldAppendPoint) {
|
|
3186
3811
|
pts.push({ x: worldX, y: worldY });
|
|
3187
3812
|
}
|
|
3188
3813
|
if (st.tool === "laser") {
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3814
|
+
if (shouldAppendPoint) {
|
|
3815
|
+
setLaserTrail((prev) => [
|
|
3816
|
+
...prev,
|
|
3817
|
+
{ x: worldX, y: worldY, t: Date.now() }
|
|
3818
|
+
]);
|
|
3819
|
+
}
|
|
3820
|
+
return;
|
|
3196
3821
|
}
|
|
3822
|
+
setPlacementPreview({
|
|
3823
|
+
kind: "stroke",
|
|
3824
|
+
tool: st.tool,
|
|
3825
|
+
points: [...pts],
|
|
3826
|
+
style: { ...strokeStyleRef.current }
|
|
3827
|
+
});
|
|
3197
3828
|
return;
|
|
3198
3829
|
}
|
|
3199
3830
|
if (st.kind === "move") {
|
|
@@ -3231,7 +3862,10 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3231
3862
|
return;
|
|
3232
3863
|
}
|
|
3233
3864
|
if (st.kind === "erase") {
|
|
3234
|
-
setEraserTrail((prev) => [
|
|
3865
|
+
setEraserTrail((prev) => [
|
|
3866
|
+
...prev,
|
|
3867
|
+
{ x: worldX, y: worldY, t: Date.now() }
|
|
3868
|
+
]);
|
|
3235
3869
|
const toErase = collectEraserTargetsAtWorldPoint(
|
|
3236
3870
|
itemsRef.current,
|
|
3237
3871
|
worldX,
|
|
@@ -3276,7 +3910,12 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3276
3910
|
const change = onItemsChangeRef.current;
|
|
3277
3911
|
if (!change) return;
|
|
3278
3912
|
const id = createShapeId();
|
|
3279
|
-
const item = createFreehandStrokeItem(
|
|
3913
|
+
const item = createFreehandStrokeItem(
|
|
3914
|
+
id,
|
|
3915
|
+
st.points,
|
|
3916
|
+
st.tool,
|
|
3917
|
+
strokeStyleRef.current
|
|
3918
|
+
);
|
|
3280
3919
|
if (item) {
|
|
3281
3920
|
change([...itemsRef.current, item]);
|
|
3282
3921
|
}
|
|
@@ -3353,26 +3992,33 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3353
3992
|
}
|
|
3354
3993
|
}
|
|
3355
3994
|
const id = createShapeId();
|
|
3995
|
+
const style = strokeStyleRef.current;
|
|
3356
3996
|
if (st.tool === "rect") {
|
|
3357
|
-
change([...itemsRef.current, createRectangleItem(id, raw)]);
|
|
3997
|
+
change([...itemsRef.current, createRectangleItem(id, raw, style)]);
|
|
3358
3998
|
onSelectionChangeRef.current?.([id]);
|
|
3359
3999
|
requestSelectToolAfterUse();
|
|
3360
4000
|
return;
|
|
3361
4001
|
}
|
|
3362
4002
|
if (st.tool === "ellipse") {
|
|
3363
|
-
change([...itemsRef.current, createEllipseItem(id, raw)]);
|
|
4003
|
+
change([...itemsRef.current, createEllipseItem(id, raw, style)]);
|
|
3364
4004
|
onSelectionChangeRef.current?.([id]);
|
|
3365
4005
|
requestSelectToolAfterUse();
|
|
3366
4006
|
return;
|
|
3367
4007
|
}
|
|
3368
4008
|
if (st.tool === "architectural-cloud") {
|
|
3369
|
-
change([
|
|
4009
|
+
change([
|
|
4010
|
+
...itemsRef.current,
|
|
4011
|
+
createArchitecturalCloudItem(id, raw, style)
|
|
4012
|
+
]);
|
|
3370
4013
|
onSelectionChangeRef.current?.([id]);
|
|
3371
4014
|
requestSelectToolAfterUse();
|
|
3372
4015
|
return;
|
|
3373
4016
|
}
|
|
3374
4017
|
const line = lineEndpointsToLocal(br, lineStart, lineEnd);
|
|
3375
|
-
change([
|
|
4018
|
+
change([
|
|
4019
|
+
...itemsRef.current,
|
|
4020
|
+
createLineItem(id, br, line, st.tool, style)
|
|
4021
|
+
]);
|
|
3376
4022
|
onSelectionChangeRef.current?.([id]);
|
|
3377
4023
|
requestSelectToolAfterUse();
|
|
3378
4024
|
return;
|
|
@@ -3395,7 +4041,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3395
4041
|
height: 26
|
|
3396
4042
|
},
|
|
3397
4043
|
"Text",
|
|
3398
|
-
|
|
4044
|
+
strokeStyleRef.current,
|
|
3399
4045
|
18
|
|
3400
4046
|
);
|
|
3401
4047
|
change([...itemsRef.current, item]);
|
|
@@ -3431,6 +4077,9 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3431
4077
|
dragStateRef.current = { kind: "idle" };
|
|
3432
4078
|
setPlacementPreview(null);
|
|
3433
4079
|
setLaserTrail([]);
|
|
4080
|
+
setEraserTrail([]);
|
|
4081
|
+
setEraserPreviewIds([]);
|
|
4082
|
+
eraserPreviewIdSetRef.current.clear();
|
|
3434
4083
|
}
|
|
3435
4084
|
}),
|
|
3436
4085
|
[screenToWorld, requestRender, requestSelectToolAfterUse, interactive]
|
|
@@ -3456,6 +4105,7 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3456
4105
|
}),
|
|
3457
4106
|
[requestRender, size]
|
|
3458
4107
|
);
|
|
4108
|
+
const activeStyleToolId = toolId === "draw" || toolId === "marker" ? toolId : null;
|
|
3459
4109
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3460
4110
|
reactNative.View,
|
|
3461
4111
|
{
|
|
@@ -3485,9 +4135,36 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3485
4135
|
eraserTrail,
|
|
3486
4136
|
eraserPreviewItems: items.filter(
|
|
3487
4137
|
(it) => eraserPreviewIds.includes(it.id)
|
|
3488
|
-
)
|
|
4138
|
+
),
|
|
4139
|
+
previewStrokeStyle: strokeStyleState
|
|
3489
4140
|
}
|
|
3490
4141
|
),
|
|
4142
|
+
interactive && showStyleInspector && activeStyleToolId ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4143
|
+
reactNative.View,
|
|
4144
|
+
{
|
|
4145
|
+
pointerEvents: "box-none",
|
|
4146
|
+
style: styleInspectorPlacement === "top-left" ? {
|
|
4147
|
+
position: "absolute",
|
|
4148
|
+
left: 16,
|
|
4149
|
+
top: 104,
|
|
4150
|
+
alignItems: "flex-start"
|
|
4151
|
+
} : {
|
|
4152
|
+
position: "absolute",
|
|
4153
|
+
left: 16,
|
|
4154
|
+
right: 16,
|
|
4155
|
+
bottom: 84,
|
|
4156
|
+
alignItems: "center"
|
|
4157
|
+
},
|
|
4158
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4159
|
+
NativeVectorStyleInspector,
|
|
4160
|
+
{
|
|
4161
|
+
toolId: activeStyleToolId,
|
|
4162
|
+
value: strokeStyleState,
|
|
4163
|
+
onChange: patchCurrentStrokeStyle
|
|
4164
|
+
}
|
|
4165
|
+
)
|
|
4166
|
+
}
|
|
4167
|
+
) : null,
|
|
3491
4168
|
toolbar && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3492
4169
|
reactNative.View,
|
|
3493
4170
|
{
|
|
@@ -3511,14 +4188,18 @@ var NativeVectorViewport = react.forwardRef(function NativeVectorViewport2({
|
|
|
3511
4188
|
|
|
3512
4189
|
exports.DEFAULT_NATIVE_OVERFLOW_TOOL_IDS = DEFAULT_NATIVE_OVERFLOW_TOOL_IDS;
|
|
3513
4190
|
exports.DEFAULT_NATIVE_VECTOR_TOOLS = DEFAULT_NATIVE_VECTOR_TOOLS;
|
|
4191
|
+
exports.NATIVE_STYLE_PALETTE = NATIVE_STYLE_PALETTE;
|
|
3514
4192
|
exports.NativeInteractionOverlay = NativeInteractionOverlay;
|
|
3515
4193
|
exports.NativeSceneRenderer = NativeSceneRenderer;
|
|
3516
4194
|
exports.NativeShapeRenderer = NativeShapeRenderer;
|
|
4195
|
+
exports.NativeVectorStyleInspector = NativeVectorStyleInspector;
|
|
3517
4196
|
exports.NativeVectorToolbar = NativeVectorToolbar;
|
|
3518
4197
|
exports.NativeVectorViewport = NativeVectorViewport;
|
|
3519
4198
|
exports.createFreehandStrokeItem = createFreehandStrokeItem;
|
|
3520
4199
|
exports.createImageItem = createImageItem;
|
|
3521
4200
|
exports.createShapeId = createShapeId;
|
|
4201
|
+
exports.nativeStyleColorWithOpacity = nativeStyleColorWithOpacity;
|
|
4202
|
+
exports.normalizeNativeStyleHex = normalizeNativeStyleHex;
|
|
3522
4203
|
exports.parseSvgFragment = parseSvgFragment;
|
|
3523
4204
|
//# sourceMappingURL=native.cjs.map
|
|
3524
4205
|
//# sourceMappingURL=native.cjs.map
|