canvu-react 0.3.38 → 0.3.40
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/{asset-hydration-EtEuBwb7.d.cts → asset-hydration-B7yMDQE-.d.cts} +2 -2
- package/dist/{asset-hydration-DrTOgDdd.d.ts → asset-hydration-CbwQVAwh.d.ts} +2 -2
- package/dist/{camera-Di5R_Rwl.d.cts → camera-CVVG7z56.d.cts} +1 -1
- package/dist/{camera-AoTwBSoE.d.ts → camera-CoRYN_IV.d.ts} +1 -1
- package/dist/chatbot.d.cts +4 -4
- package/dist/chatbot.d.ts +4 -4
- package/dist/index.cjs +164 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +159 -16
- package/dist/index.js.map +1 -1
- package/dist/native.cjs +57 -14
- package/dist/native.cjs.map +1 -1
- package/dist/native.d.cts +2 -2
- package/dist/native.d.ts +2 -2
- package/dist/native.js +57 -14
- package/dist/native.js.map +1 -1
- package/dist/react.cjs +731 -258
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +10 -10
- package/dist/react.d.ts +10 -10
- package/dist/react.js +731 -258
- package/dist/react.js.map +1 -1
- package/dist/realtime.cjs +36 -0
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +6 -6
- package/dist/realtime.d.ts +6 -6
- package/dist/realtime.js +36 -0
- package/dist/realtime.js.map +1 -1
- package/dist/{shape-builders-CsbSRZnQ.d.cts → shape-builders-BAWu-PxX.d.cts} +46 -4
- package/dist/{shape-builders-CsSXKCcs.d.ts → shape-builders-ClKv9tz9.d.ts} +46 -4
- package/dist/tldraw.cjs +189 -78
- package/dist/tldraw.cjs.map +1 -1
- package/dist/tldraw.d.cts +1 -1
- package/dist/tldraw.d.ts +1 -1
- package/dist/tldraw.js +189 -78
- package/dist/tldraw.js.map +1 -1
- package/dist/{types-DWGk2_GZ.d.cts → types-BC9Xgfu6.d.cts} +20 -6
- package/dist/{types-Bnq2HtHQ.d.cts → types-BCCvY6ie.d.cts} +2 -0
- package/dist/{types-Bnq2HtHQ.d.ts → types-BCCvY6ie.d.ts} +2 -0
- package/dist/{types-B2Na677H.d.cts → types-BUPc2Zgw.d.cts} +1 -1
- package/dist/{types-zmUah-vP.d.ts → types-CYtq9Pr9.d.ts} +1 -1
- package/dist/{types-B6PAYKzx.d.ts → types-DlSVGX0w.d.ts} +20 -6
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -91,6 +91,12 @@ function lineHeightFor(fontSize) {
|
|
|
91
91
|
function firstLineBaselineY(fontSize) {
|
|
92
92
|
return fontSize * FIRST_LINE_BASELINE_RATIO;
|
|
93
93
|
}
|
|
94
|
+
function textBaselineYFor(fontSize) {
|
|
95
|
+
return firstLineBaselineY(fontSize);
|
|
96
|
+
}
|
|
97
|
+
function textLineHeightFor(fontSize) {
|
|
98
|
+
return lineHeightFor(fontSize);
|
|
99
|
+
}
|
|
94
100
|
function measureTextBoundsLocal(content, fontSize = DEFAULT_TEXT_FONT_SIZE) {
|
|
95
101
|
const cacheKey = textMeasureCacheKey(content, fontSize);
|
|
96
102
|
const cached = textMeasureCache.get(cacheKey);
|
|
@@ -104,7 +110,7 @@ function measureTextBoundsLocal(content, fontSize = DEFAULT_TEXT_FONT_SIZE) {
|
|
|
104
110
|
let maxInnerW = 0;
|
|
105
111
|
const ctx = getSharedMeasureContext();
|
|
106
112
|
if (ctx) {
|
|
107
|
-
ctx.font = `${fontSize}px
|
|
113
|
+
ctx.font = `${fontSize}px ${TEXT_FONT_FAMILY}`;
|
|
108
114
|
for (const line of lines) {
|
|
109
115
|
const toMeasure = trimmed.length === 0 ? PLACEHOLDER : line.length === 0 ? " " : line;
|
|
110
116
|
maxInnerW = Math.max(maxInnerW, ctx.measureText(toMeasure).width);
|
|
@@ -120,22 +126,22 @@ function measureTextBoundsLocal(content, fontSize = DEFAULT_TEXT_FONT_SIZE) {
|
|
|
120
126
|
const width = Math.max(minW, TEXT_PAD_X * 2 + maxInnerW);
|
|
121
127
|
const height = Math.max(
|
|
122
128
|
MIN_TEXT_BOX_H,
|
|
123
|
-
baselineY + (lines.length - 1) * lh + Math.max(
|
|
129
|
+
baselineY + (lines.length - 1) * lh + Math.max(4, fontSize * BOTTOM_PAD_RATIO)
|
|
124
130
|
);
|
|
125
131
|
const measured = { width, height };
|
|
126
132
|
cacheMeasuredBounds(cacheKey, measured);
|
|
127
133
|
return measured;
|
|
128
134
|
}
|
|
129
|
-
function buildTextSvg(content, _width, _height, fillColor = "#
|
|
135
|
+
function buildTextSvg(content, _width, _height, fillColor = "#1d1d1d", fontSize = DEFAULT_TEXT_FONT_SIZE) {
|
|
130
136
|
const lh = lineHeightFor(fontSize);
|
|
131
137
|
const y0 = firstLineBaselineY(fontSize);
|
|
132
138
|
const trimmed = content.trim();
|
|
133
139
|
if (trimmed.length === 0) {
|
|
134
|
-
return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="
|
|
140
|
+
return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="${TEXT_FONT_FAMILY}" fill="#94a3b8" font-style="italic">${escapeSvgTextContent(PLACEHOLDER)}</text>`;
|
|
135
141
|
}
|
|
136
142
|
const lines = content.split("\n");
|
|
137
143
|
if (lines.length === 1) {
|
|
138
|
-
return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="
|
|
144
|
+
return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="${TEXT_FONT_FAMILY}" fill="${fillColor}">${escapeSvgTextContent(lines[0] ?? "")}</text>`;
|
|
139
145
|
}
|
|
140
146
|
const parts = [];
|
|
141
147
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -146,28 +152,32 @@ function buildTextSvg(content, _width, _height, fillColor = "#2563eb", fontSize
|
|
|
146
152
|
parts.push(`<tspan x="4" dy="${lh}">${escapeSvgTextContent(line)}</tspan>`);
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
|
-
return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="
|
|
155
|
+
return `<text x="4" y="${y0}" font-size="${fontSize}" font-family="${TEXT_FONT_FAMILY}" fill="${fillColor}">${parts.join("")}</text>`;
|
|
150
156
|
}
|
|
151
|
-
function buildTextFixedBoundsSvg(content, width, height, fillColor = "#
|
|
157
|
+
function buildTextFixedBoundsSvg(content, width, height, fillColor = "#1d1d1d", fontSize = DEFAULT_TEXT_FONT_SIZE) {
|
|
152
158
|
const w = Math.max(1, width);
|
|
153
159
|
const h = Math.max(1, height);
|
|
154
160
|
const lh = lineHeightFor(fontSize);
|
|
155
161
|
const trimmed = content.trim();
|
|
162
|
+
const padTop = EDIT_TOP_PAD_RATIO * fontSize;
|
|
156
163
|
if (trimmed.length === 0) {
|
|
157
|
-
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding
|
|
164
|
+
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:#94a3b8;font-style:italic">${escapeHtmlText(PLACEHOLDER)}</div></foreignObject>`;
|
|
158
165
|
}
|
|
159
166
|
const body = escapeHtmlText(content);
|
|
160
|
-
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding
|
|
167
|
+
return `<foreignObject width="${w}" height="${h}"><div xmlns="http://www.w3.org/1999/xhtml" style="box-sizing:border-box;width:100%;height:100%;margin:0;padding:${padTop}px 4px 0 4px;font-size:${fontSize}px;line-height:${lh}px;font-family:${TEXT_FONT_FAMILY};white-space:pre-wrap;word-wrap:break-word;overflow:hidden;color:${fillColor}">${body}</div></foreignObject>`;
|
|
161
168
|
}
|
|
162
|
-
var DEFAULT_TEXT_FONT_SIZE, LINE_HEIGHT_RATIO, FIRST_LINE_BASELINE_RATIO, PLACEHOLDER, MIN_TEXT_BOX_W, MIN_TEXT_BOX_H, TEXT_PAD_X, MAX_TEXT_MEASURE_CACHE_ENTRIES, sharedMeasureContext, textMeasureCache;
|
|
169
|
+
var DEFAULT_TEXT_FONT_SIZE, TEXT_FONT_FAMILY, LINE_HEIGHT_RATIO, FIRST_LINE_BASELINE_RATIO, EDIT_TOP_PAD_RATIO, BOTTOM_PAD_RATIO, PLACEHOLDER, MIN_TEXT_BOX_W, MIN_TEXT_BOX_H, TEXT_PAD_X, MAX_TEXT_MEASURE_CACHE_ENTRIES, sharedMeasureContext, textMeasureCache;
|
|
163
170
|
var init_text_svg = __esm({
|
|
164
171
|
"src/scene/text-svg.ts"() {
|
|
165
172
|
DEFAULT_TEXT_FONT_SIZE = 18;
|
|
173
|
+
TEXT_FONT_FAMILY = "Inter, -apple-system, BlinkMacSystemFont, ui-sans-serif, system-ui, sans-serif";
|
|
166
174
|
LINE_HEIGHT_RATIO = 22 / 18;
|
|
167
|
-
FIRST_LINE_BASELINE_RATIO =
|
|
175
|
+
FIRST_LINE_BASELINE_RATIO = 20 / 18;
|
|
176
|
+
EDIT_TOP_PAD_RATIO = 4 / 18;
|
|
177
|
+
BOTTOM_PAD_RATIO = 4 / 18;
|
|
168
178
|
PLACEHOLDER = "Tap to type";
|
|
169
179
|
MIN_TEXT_BOX_W = 40;
|
|
170
|
-
MIN_TEXT_BOX_H =
|
|
180
|
+
MIN_TEXT_BOX_H = 26;
|
|
171
181
|
TEXT_PAD_X = 4;
|
|
172
182
|
MAX_TEXT_MEASURE_CACHE_ENTRIES = 2e3;
|
|
173
183
|
textMeasureCache = /* @__PURE__ */ new Map();
|
|
@@ -280,7 +290,8 @@ function resolveStrokeStyle(item) {
|
|
|
280
290
|
return {
|
|
281
291
|
stroke: item.stroke ?? DEFAULT_STROKE_STYLE.stroke,
|
|
282
292
|
strokeWidth: item.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth,
|
|
283
|
-
strokeOpacity: item.strokeOpacity
|
|
293
|
+
strokeOpacity: item.strokeOpacity,
|
|
294
|
+
strokeDash: item.strokeDash
|
|
284
295
|
};
|
|
285
296
|
}
|
|
286
297
|
function strokeOpacityAttr(style) {
|
|
@@ -603,6 +614,30 @@ function buildDrawDotSvg(r, style = DEFAULT_STROKE_STYLE) {
|
|
|
603
614
|
const op = style.strokeOpacity != null ? ` fill-opacity="${style.strokeOpacity}"` : "";
|
|
604
615
|
return `<circle cx="${r}" cy="${r}" r="${r}" fill="${style.stroke}" shape-rendering="geometricPrecision"${op} />`;
|
|
605
616
|
}
|
|
617
|
+
function dashArrayForDrawStroke(strokeWidth) {
|
|
618
|
+
const dash = Math.max(strokeWidth * 1.8, 4);
|
|
619
|
+
const gap = Math.max(strokeWidth * 1.4, 3);
|
|
620
|
+
return `${dash} ${gap}`;
|
|
621
|
+
}
|
|
622
|
+
function buildSmoothedCenterlinePath(points) {
|
|
623
|
+
if (points.length < 2) return null;
|
|
624
|
+
const first = points[0];
|
|
625
|
+
if (!first) return null;
|
|
626
|
+
let d = `M ${first.x} ${first.y}`;
|
|
627
|
+
for (let i = 1; i < points.length - 1; i++) {
|
|
628
|
+
const a = points[i];
|
|
629
|
+
const b = points[i + 1];
|
|
630
|
+
if (!a || !b) continue;
|
|
631
|
+
const midX = (a.x + b.x) / 2;
|
|
632
|
+
const midY = (a.y + b.y) / 2;
|
|
633
|
+
d += ` Q ${a.x} ${a.y} ${midX} ${midY}`;
|
|
634
|
+
}
|
|
635
|
+
const last = points[points.length - 1];
|
|
636
|
+
if (last) {
|
|
637
|
+
d += ` L ${last.x} ${last.y}`;
|
|
638
|
+
}
|
|
639
|
+
return d;
|
|
640
|
+
}
|
|
606
641
|
function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeComplete = true) {
|
|
607
642
|
if (pathPointsLocal.length === 0) return null;
|
|
608
643
|
if (pathPointsLocal.length === 1) {
|
|
@@ -617,6 +652,18 @@ function computeFreehandSvgPayload(pathPointsLocal, style, toolKind, strokeCompl
|
|
|
617
652
|
fillOpacity: style.strokeOpacity
|
|
618
653
|
};
|
|
619
654
|
}
|
|
655
|
+
if (style.strokeDash === "dashed" && (toolKind === "draw" || toolKind === "pencil")) {
|
|
656
|
+
const d2 = buildSmoothedCenterlinePath(pathPointsLocal);
|
|
657
|
+
if (!d2) return null;
|
|
658
|
+
return {
|
|
659
|
+
kind: "strokePath",
|
|
660
|
+
d: d2,
|
|
661
|
+
stroke: style.stroke,
|
|
662
|
+
strokeWidth: style.strokeWidth,
|
|
663
|
+
strokeOpacity: style.strokeOpacity,
|
|
664
|
+
strokeDasharray: dashArrayForDrawStroke(style.strokeWidth)
|
|
665
|
+
};
|
|
666
|
+
}
|
|
620
667
|
const hasPressure = pathPointsLocal.some(
|
|
621
668
|
(p) => p.pressure != null && Number.isFinite(p.pressure)
|
|
622
669
|
);
|
|
@@ -659,7 +706,8 @@ function freehandPayloadToSvgString(payload) {
|
|
|
659
706
|
strokeWidth: payload.strokeWidth,
|
|
660
707
|
strokeOpacity: payload.strokeOpacity
|
|
661
708
|
});
|
|
662
|
-
|
|
709
|
+
const dash = payload.strokeDasharray ? ` stroke-dasharray="${payload.strokeDasharray}"` : "";
|
|
710
|
+
return `<path d="${payload.d}" fill="none" stroke="${payload.stroke}" stroke-width="${payload.strokeWidth}" stroke-linecap="round" stroke-linejoin="round" shape-rendering="geometricPrecision"${op}${dash} />`;
|
|
663
711
|
}
|
|
664
712
|
function buildFreehandPathSvg(pathPointsLocal, style, toolKind, strokeComplete = true) {
|
|
665
713
|
const payload = computeFreehandSvgPayload(
|
|
@@ -871,7 +919,7 @@ function createDrawDotItem(id, worldX, worldY, radius, style) {
|
|
|
871
919
|
childrenSvg: ""
|
|
872
920
|
});
|
|
873
921
|
}
|
|
874
|
-
function createTextItem(id, bounds, text = "", style) {
|
|
922
|
+
function createTextItem(id, bounds, text = "", style, textFontSize) {
|
|
875
923
|
const r = normalizeRect(bounds);
|
|
876
924
|
const s = { ...DEFAULT_STROKE_STYLE, ...style };
|
|
877
925
|
return rebuildItemSvg({
|
|
@@ -884,6 +932,7 @@ function createTextItem(id, bounds, text = "", style) {
|
|
|
884
932
|
stroke: s.stroke,
|
|
885
933
|
strokeWidth: s.strokeWidth,
|
|
886
934
|
...s.strokeOpacity != null ? { strokeOpacity: s.strokeOpacity } : {},
|
|
935
|
+
...textFontSize != null ? { textFontSize } : {},
|
|
887
936
|
childrenSvg: ""
|
|
888
937
|
});
|
|
889
938
|
}
|
|
@@ -920,6 +969,7 @@ function createFreehandStrokeItem(id, pointsWorld, toolKind, style) {
|
|
|
920
969
|
stroke: merged.stroke,
|
|
921
970
|
strokeWidth: merged.strokeWidth,
|
|
922
971
|
...merged.strokeOpacity != null ? { strokeOpacity: merged.strokeOpacity } : {},
|
|
972
|
+
...merged.strokeDash != null ? { strokeDash: merged.strokeDash } : {},
|
|
923
973
|
pathPointsLocal,
|
|
924
974
|
childrenSvg: ""
|
|
925
975
|
});
|
|
@@ -996,7 +1046,7 @@ var init_shape_builders = __esm({
|
|
|
996
1046
|
init_custom_shape();
|
|
997
1047
|
init_text_svg();
|
|
998
1048
|
DEFAULT_STROKE_STYLE = {
|
|
999
|
-
stroke: "#
|
|
1049
|
+
stroke: "#1d1d1d",
|
|
1000
1050
|
strokeWidth: 2
|
|
1001
1051
|
};
|
|
1002
1052
|
TOOL_FREEHAND_DEFAULTS = {
|
|
@@ -3488,33 +3538,416 @@ var DEFAULT_VECTOR_TOOLS = [
|
|
|
3488
3538
|
shortcutHint: "I"
|
|
3489
3539
|
}
|
|
3490
3540
|
];
|
|
3541
|
+
|
|
3542
|
+
// src/react/VectorSelectionInspector.tsx
|
|
3543
|
+
init_text_svg();
|
|
3491
3544
|
var shellLook = {
|
|
3492
3545
|
display: "flex",
|
|
3493
3546
|
flexDirection: "column",
|
|
3494
|
-
gap:
|
|
3495
|
-
minWidth:
|
|
3496
|
-
padding: "
|
|
3497
|
-
borderRadius:
|
|
3498
|
-
border: "1px solid rgba(0,0,0,0.
|
|
3499
|
-
background: "rgba(255,255,255,0.
|
|
3500
|
-
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
3547
|
+
gap: 10,
|
|
3548
|
+
minWidth: 200,
|
|
3549
|
+
padding: "12px 14px",
|
|
3550
|
+
borderRadius: 10,
|
|
3551
|
+
border: "1px solid rgba(0,0,0,0.1)",
|
|
3552
|
+
background: "rgba(255,255,255,0.97)",
|
|
3553
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.08)",
|
|
3501
3554
|
pointerEvents: "auto"
|
|
3502
3555
|
};
|
|
3503
|
-
var
|
|
3556
|
+
var sectionLabelStyle = {
|
|
3504
3557
|
display: "flex",
|
|
3505
3558
|
flexDirection: "column",
|
|
3506
|
-
gap:
|
|
3559
|
+
gap: 6,
|
|
3507
3560
|
fontSize: 11,
|
|
3508
3561
|
fontWeight: 600,
|
|
3509
|
-
color: "#52525b"
|
|
3562
|
+
color: "#52525b",
|
|
3563
|
+
letterSpacing: "0.02em",
|
|
3564
|
+
textTransform: "uppercase"
|
|
3565
|
+
};
|
|
3566
|
+
var mixedHintStyle = {
|
|
3567
|
+
fontWeight: 400,
|
|
3568
|
+
color: "#a1a1aa",
|
|
3569
|
+
textTransform: "none",
|
|
3570
|
+
letterSpacing: 0
|
|
3510
3571
|
};
|
|
3572
|
+
var TLDRAW_PALETTE = [
|
|
3573
|
+
{ name: "black", hex: "#1d1d1d" },
|
|
3574
|
+
{ name: "grey", hex: "#9fa8b2" },
|
|
3575
|
+
{ name: "light-violet", hex: "#e085f4" },
|
|
3576
|
+
{ name: "violet", hex: "#ae3ec9" },
|
|
3577
|
+
{ name: "blue", hex: "#4263eb" },
|
|
3578
|
+
{ name: "light-blue", hex: "#4dabf7" },
|
|
3579
|
+
{ name: "yellow", hex: "#ffc078" },
|
|
3580
|
+
{ name: "orange", hex: "#f76707" },
|
|
3581
|
+
{ name: "green", hex: "#099268" },
|
|
3582
|
+
{ name: "light-green", hex: "#40c057" },
|
|
3583
|
+
{ name: "light-red", hex: "#ff8787" },
|
|
3584
|
+
{ name: "red", hex: "#e03131" }
|
|
3585
|
+
];
|
|
3511
3586
|
function normalizeHex(stroke) {
|
|
3512
3587
|
if (/^#[0-9A-Fa-f]{6}$/.test(stroke)) return stroke;
|
|
3513
|
-
return "#
|
|
3588
|
+
return "#1d1d1d";
|
|
3589
|
+
}
|
|
3590
|
+
function hexesEqual(a, b) {
|
|
3591
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
3514
3592
|
}
|
|
3515
3593
|
function isStylableKind(tk) {
|
|
3516
3594
|
return tk === "rect" || tk === "ellipse" || tk === "architectural-cloud" || tk === "line" || tk === "arrow" || tk === "draw" || tk === "pencil" || tk === "brush" || tk === "marker" || tk === "text";
|
|
3517
3595
|
}
|
|
3596
|
+
var FONT_SIZE_PRESETS = [
|
|
3597
|
+
{ label: "S", size: 14 },
|
|
3598
|
+
{ label: "M", size: 18 },
|
|
3599
|
+
{ label: "L", size: 28 },
|
|
3600
|
+
{ label: "XL", size: 44 }
|
|
3601
|
+
];
|
|
3602
|
+
function ColorSwatch({ color, selected, onSelect }) {
|
|
3603
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3604
|
+
"button",
|
|
3605
|
+
{
|
|
3606
|
+
type: "button",
|
|
3607
|
+
"aria-label": color.name,
|
|
3608
|
+
"aria-pressed": selected,
|
|
3609
|
+
onClick: onSelect,
|
|
3610
|
+
onPointerDown: (e) => {
|
|
3611
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
3612
|
+
},
|
|
3613
|
+
style: {
|
|
3614
|
+
width: 24,
|
|
3615
|
+
height: 24,
|
|
3616
|
+
padding: 0,
|
|
3617
|
+
borderRadius: "50%",
|
|
3618
|
+
border: selected ? "2px solid #18181b" : "1px solid rgba(0,0,0,0.12)",
|
|
3619
|
+
background: color.hex,
|
|
3620
|
+
cursor: "pointer",
|
|
3621
|
+
outline: "none",
|
|
3622
|
+
boxShadow: selected ? "0 0 0 2px rgba(255,255,255,0.95) inset" : "none",
|
|
3623
|
+
transition: "transform 0.08s ease",
|
|
3624
|
+
transform: selected ? "scale(1.08)" : "scale(1)",
|
|
3625
|
+
WebkitTapHighlightColor: "transparent"
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
function ColorPalette({ value, onChange }) {
|
|
3631
|
+
const [showCustom, setShowCustom] = react.useState(false);
|
|
3632
|
+
const normalized = normalizeHex(value);
|
|
3633
|
+
const inPalette = TLDRAW_PALETTE.some((c) => hexesEqual(c.hex, normalized));
|
|
3634
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3635
|
+
"div",
|
|
3636
|
+
{
|
|
3637
|
+
style: {
|
|
3638
|
+
display: "grid",
|
|
3639
|
+
gridTemplateColumns: "repeat(6, 24px)",
|
|
3640
|
+
gap: 6,
|
|
3641
|
+
rowGap: 6,
|
|
3642
|
+
alignItems: "center"
|
|
3643
|
+
},
|
|
3644
|
+
children: [
|
|
3645
|
+
TLDRAW_PALETTE.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3646
|
+
ColorSwatch,
|
|
3647
|
+
{
|
|
3648
|
+
color: c,
|
|
3649
|
+
selected: hexesEqual(c.hex, normalized),
|
|
3650
|
+
onSelect: () => onChange(c.hex)
|
|
3651
|
+
},
|
|
3652
|
+
c.name
|
|
3653
|
+
)),
|
|
3654
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3655
|
+
"button",
|
|
3656
|
+
{
|
|
3657
|
+
type: "button",
|
|
3658
|
+
"aria-label": "Cor personalizada",
|
|
3659
|
+
"aria-pressed": showCustom || !inPalette,
|
|
3660
|
+
onClick: () => setShowCustom((v) => !v),
|
|
3661
|
+
onPointerDown: (e) => {
|
|
3662
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
3663
|
+
},
|
|
3664
|
+
style: {
|
|
3665
|
+
width: 24,
|
|
3666
|
+
height: 24,
|
|
3667
|
+
padding: 0,
|
|
3668
|
+
borderRadius: "50%",
|
|
3669
|
+
border: !inPalette || showCustom ? "2px solid #18181b" : "1px solid rgba(0,0,0,0.12)",
|
|
3670
|
+
background: "conic-gradient(from 0deg, #ff5757, #ffbd59, #59ff7e, #59ddff, #b259ff, #ff59c1, #ff5757)",
|
|
3671
|
+
cursor: "pointer",
|
|
3672
|
+
outline: "none",
|
|
3673
|
+
WebkitTapHighlightColor: "transparent"
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
),
|
|
3677
|
+
showCustom || !inPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3678
|
+
"label",
|
|
3679
|
+
{
|
|
3680
|
+
style: {
|
|
3681
|
+
gridColumn: "1 / -1",
|
|
3682
|
+
display: "flex",
|
|
3683
|
+
alignItems: "center",
|
|
3684
|
+
gap: 6,
|
|
3685
|
+
fontSize: 11,
|
|
3686
|
+
fontWeight: 500,
|
|
3687
|
+
color: "#71717a",
|
|
3688
|
+
textTransform: "none",
|
|
3689
|
+
letterSpacing: 0
|
|
3690
|
+
},
|
|
3691
|
+
children: [
|
|
3692
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3693
|
+
"input",
|
|
3694
|
+
{
|
|
3695
|
+
type: "color",
|
|
3696
|
+
value: normalized,
|
|
3697
|
+
onChange: (e) => onChange(e.target.value),
|
|
3698
|
+
style: {
|
|
3699
|
+
width: 28,
|
|
3700
|
+
height: 28,
|
|
3701
|
+
padding: 0,
|
|
3702
|
+
border: "1px solid rgba(0,0,0,0.12)",
|
|
3703
|
+
borderRadius: 6,
|
|
3704
|
+
cursor: "pointer",
|
|
3705
|
+
background: "transparent"
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
),
|
|
3709
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: normalized.toUpperCase() })
|
|
3710
|
+
]
|
|
3711
|
+
}
|
|
3712
|
+
) : null
|
|
3713
|
+
]
|
|
3714
|
+
}
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
function DashTabs({ value, onChange }) {
|
|
3718
|
+
const tabs = [
|
|
3719
|
+
{ id: "solid", label: "Cont\xEDnuo" },
|
|
3720
|
+
{ id: "dashed", label: "Tracejado" }
|
|
3721
|
+
];
|
|
3722
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3723
|
+
"div",
|
|
3724
|
+
{
|
|
3725
|
+
role: "radiogroup",
|
|
3726
|
+
"aria-label": "Estilo do tra\xE7o",
|
|
3727
|
+
style: {
|
|
3728
|
+
display: "flex",
|
|
3729
|
+
background: "rgba(24,24,27,0.06)",
|
|
3730
|
+
borderRadius: 7,
|
|
3731
|
+
padding: 2,
|
|
3732
|
+
gap: 2
|
|
3733
|
+
},
|
|
3734
|
+
children: tabs.map((t) => {
|
|
3735
|
+
const selected = value === t.id;
|
|
3736
|
+
return (
|
|
3737
|
+
// biome-ignore lint/a11y/useSemanticElements: styled segmented control needs button, not input[type=radio]
|
|
3738
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3739
|
+
"button",
|
|
3740
|
+
{
|
|
3741
|
+
type: "button",
|
|
3742
|
+
role: "radio",
|
|
3743
|
+
"aria-checked": selected,
|
|
3744
|
+
onClick: () => onChange(t.id),
|
|
3745
|
+
onPointerDown: (e) => {
|
|
3746
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
3747
|
+
},
|
|
3748
|
+
style: {
|
|
3749
|
+
flex: 1,
|
|
3750
|
+
display: "inline-flex",
|
|
3751
|
+
alignItems: "center",
|
|
3752
|
+
justifyContent: "center",
|
|
3753
|
+
padding: "5px 8px",
|
|
3754
|
+
fontSize: 11,
|
|
3755
|
+
fontWeight: 600,
|
|
3756
|
+
color: selected ? "#18181b" : "#71717a",
|
|
3757
|
+
background: selected ? "#fff" : "transparent",
|
|
3758
|
+
borderRadius: 5,
|
|
3759
|
+
border: "none",
|
|
3760
|
+
cursor: "pointer",
|
|
3761
|
+
boxShadow: selected ? "0 1px 2px rgba(0,0,0,0.1)" : "none",
|
|
3762
|
+
outline: "none",
|
|
3763
|
+
WebkitTapHighlightColor: "transparent"
|
|
3764
|
+
},
|
|
3765
|
+
children: [
|
|
3766
|
+
/* @__PURE__ */ jsxRuntime.jsx(DashTabSwatch, { dashed: t.id === "dashed" }),
|
|
3767
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { marginLeft: 6 }, children: t.label })
|
|
3768
|
+
]
|
|
3769
|
+
},
|
|
3770
|
+
t.id
|
|
3771
|
+
)
|
|
3772
|
+
);
|
|
3773
|
+
})
|
|
3774
|
+
}
|
|
3775
|
+
);
|
|
3776
|
+
}
|
|
3777
|
+
function DashTabSwatch({ dashed }) {
|
|
3778
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3779
|
+
"svg",
|
|
3780
|
+
{
|
|
3781
|
+
width: 26,
|
|
3782
|
+
height: 6,
|
|
3783
|
+
"aria-hidden": true,
|
|
3784
|
+
style: { flexShrink: 0, display: "block" },
|
|
3785
|
+
children: [
|
|
3786
|
+
/* @__PURE__ */ jsxRuntime.jsx("title", { children: dashed ? "dashed" : "solid" }),
|
|
3787
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3788
|
+
"line",
|
|
3789
|
+
{
|
|
3790
|
+
x1: 1,
|
|
3791
|
+
y1: 3,
|
|
3792
|
+
x2: 25,
|
|
3793
|
+
y2: 3,
|
|
3794
|
+
stroke: "currentColor",
|
|
3795
|
+
strokeWidth: 2,
|
|
3796
|
+
strokeLinecap: "round",
|
|
3797
|
+
strokeDasharray: dashed ? "4 3" : void 0
|
|
3798
|
+
}
|
|
3799
|
+
)
|
|
3800
|
+
]
|
|
3801
|
+
}
|
|
3802
|
+
);
|
|
3803
|
+
}
|
|
3804
|
+
function FontSizeTabs({ value, onChange }) {
|
|
3805
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3806
|
+
"div",
|
|
3807
|
+
{
|
|
3808
|
+
role: "radiogroup",
|
|
3809
|
+
"aria-label": "Tamanho do texto",
|
|
3810
|
+
style: {
|
|
3811
|
+
display: "flex",
|
|
3812
|
+
background: "rgba(24,24,27,0.06)",
|
|
3813
|
+
borderRadius: 7,
|
|
3814
|
+
padding: 2,
|
|
3815
|
+
gap: 2
|
|
3816
|
+
},
|
|
3817
|
+
children: FONT_SIZE_PRESETS.map((p) => {
|
|
3818
|
+
const selected = Math.abs(value - p.size) < 0.5;
|
|
3819
|
+
return (
|
|
3820
|
+
// biome-ignore lint/a11y/useSemanticElements: styled segmented control needs button, not input[type=radio]
|
|
3821
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3822
|
+
"button",
|
|
3823
|
+
{
|
|
3824
|
+
type: "button",
|
|
3825
|
+
role: "radio",
|
|
3826
|
+
"aria-checked": selected,
|
|
3827
|
+
"aria-label": `${p.label} (${p.size}px)`,
|
|
3828
|
+
onClick: () => onChange(p.size),
|
|
3829
|
+
onPointerDown: (e) => {
|
|
3830
|
+
if (e.pointerType === "mouse") e.preventDefault();
|
|
3831
|
+
},
|
|
3832
|
+
style: {
|
|
3833
|
+
flex: 1,
|
|
3834
|
+
padding: "5px 0",
|
|
3835
|
+
fontSize: p.label === "XL" ? 14 : p.label === "L" ? 13 : 12,
|
|
3836
|
+
fontWeight: 700,
|
|
3837
|
+
color: selected ? "#18181b" : "#71717a",
|
|
3838
|
+
background: selected ? "#fff" : "transparent",
|
|
3839
|
+
borderRadius: 5,
|
|
3840
|
+
border: "none",
|
|
3841
|
+
cursor: "pointer",
|
|
3842
|
+
boxShadow: selected ? "0 1px 2px rgba(0,0,0,0.1)" : "none",
|
|
3843
|
+
outline: "none",
|
|
3844
|
+
WebkitTapHighlightColor: "transparent",
|
|
3845
|
+
lineHeight: 1
|
|
3846
|
+
},
|
|
3847
|
+
children: p.label
|
|
3848
|
+
},
|
|
3849
|
+
p.label
|
|
3850
|
+
)
|
|
3851
|
+
);
|
|
3852
|
+
})
|
|
3853
|
+
}
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
function viewModelFromActiveTool(activeToolStyle) {
|
|
3857
|
+
const hex = normalizeHex(activeToolStyle.stroke);
|
|
3858
|
+
const strokeWidth = activeToolStyle.strokeWidth;
|
|
3859
|
+
const strokeOpacity = activeToolStyle.strokeOpacity ?? 1;
|
|
3860
|
+
const strokeDash = activeToolStyle.strokeDash === "dashed" ? "dashed" : "solid";
|
|
3861
|
+
const textFontSize = activeToolStyle.textFontSize ?? DEFAULT_TEXT_FONT_SIZE;
|
|
3862
|
+
const isText = activeToolStyle.toolKind === "text";
|
|
3863
|
+
const isDraw = activeToolStyle.toolKind === "draw";
|
|
3864
|
+
const isMarker = activeToolStyle.toolKind === "marker";
|
|
3865
|
+
const current = {
|
|
3866
|
+
stroke: hex,
|
|
3867
|
+
strokeWidth,
|
|
3868
|
+
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {},
|
|
3869
|
+
...activeToolStyle.strokeDash != null ? { strokeDash: activeToolStyle.strokeDash } : {},
|
|
3870
|
+
...isText ? { textFontSize } : {}
|
|
3871
|
+
};
|
|
3872
|
+
return {
|
|
3873
|
+
label: activeToolStyle.label ?? "Estilo da ferramenta",
|
|
3874
|
+
count: 0,
|
|
3875
|
+
hex,
|
|
3876
|
+
strokeWidth,
|
|
3877
|
+
strokeOpacity,
|
|
3878
|
+
strokeDash,
|
|
3879
|
+
textFontSize,
|
|
3880
|
+
showStrokeWidth: !isText,
|
|
3881
|
+
showDash: isDraw,
|
|
3882
|
+
showFontSize: isText,
|
|
3883
|
+
showMarkerOpacity: isMarker,
|
|
3884
|
+
mixedStroke: false,
|
|
3885
|
+
mixedWidth: false,
|
|
3886
|
+
mixedDash: false,
|
|
3887
|
+
mixedFontSize: false,
|
|
3888
|
+
mixedOpacity: false,
|
|
3889
|
+
current
|
|
3890
|
+
};
|
|
3891
|
+
}
|
|
3892
|
+
function viewModelFromSelection(stylable) {
|
|
3893
|
+
const first = stylable[0];
|
|
3894
|
+
if (!first) return null;
|
|
3895
|
+
const hex = normalizeHex(first.stroke ?? "#1d1d1d");
|
|
3896
|
+
const strokeWidth = first.strokeWidth ?? 2;
|
|
3897
|
+
const allSameStroke = stylable.every(
|
|
3898
|
+
(it) => (it.stroke ?? "#1d1d1d") === (first.stroke ?? "#1d1d1d")
|
|
3899
|
+
);
|
|
3900
|
+
const allSameWidth = stylable.every(
|
|
3901
|
+
(it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
|
|
3902
|
+
);
|
|
3903
|
+
const draws = stylable.filter(
|
|
3904
|
+
(it) => it.toolKind === "draw" || it.toolKind === "pencil"
|
|
3905
|
+
);
|
|
3906
|
+
const showDash = draws.length > 0;
|
|
3907
|
+
const firstDraw = draws[0];
|
|
3908
|
+
const strokeDash = firstDraw?.strokeDash === "dashed" ? "dashed" : "solid";
|
|
3909
|
+
const allSameDash = draws.length === 0 || draws.every((it) => (it.strokeDash ?? "solid") === strokeDash);
|
|
3910
|
+
const markers = stylable.filter((it) => it.toolKind === "marker");
|
|
3911
|
+
const showMarkerOpacity = markers.length > 0;
|
|
3912
|
+
const firstMarker = markers[0];
|
|
3913
|
+
const strokeOpacity = firstMarker?.strokeOpacity ?? 1;
|
|
3914
|
+
const allSameOpacity = markers.length === 0 || markers.every(
|
|
3915
|
+
(it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
|
|
3916
|
+
);
|
|
3917
|
+
const texts = stylable.filter((it) => it.toolKind === "text");
|
|
3918
|
+
const showFontSize = texts.length > 0;
|
|
3919
|
+
const firstText = texts[0];
|
|
3920
|
+
const textFontSize = firstText?.textFontSize ?? DEFAULT_TEXT_FONT_SIZE;
|
|
3921
|
+
const allSameFontSize = texts.length === 0 || texts.every(
|
|
3922
|
+
(it) => (it.textFontSize ?? DEFAULT_TEXT_FONT_SIZE) === textFontSize
|
|
3923
|
+
);
|
|
3924
|
+
const current = {
|
|
3925
|
+
stroke: hex,
|
|
3926
|
+
strokeWidth,
|
|
3927
|
+
...showMarkerOpacity ? { strokeOpacity } : {},
|
|
3928
|
+
...showDash ? { strokeDash } : {},
|
|
3929
|
+
...showFontSize ? { textFontSize } : {}
|
|
3930
|
+
};
|
|
3931
|
+
return {
|
|
3932
|
+
label: "Estilo da sele\xE7\xE3o",
|
|
3933
|
+
count: stylable.length,
|
|
3934
|
+
hex,
|
|
3935
|
+
strokeWidth,
|
|
3936
|
+
strokeOpacity,
|
|
3937
|
+
strokeDash,
|
|
3938
|
+
textFontSize,
|
|
3939
|
+
showStrokeWidth: true,
|
|
3940
|
+
showDash,
|
|
3941
|
+
showFontSize,
|
|
3942
|
+
showMarkerOpacity,
|
|
3943
|
+
mixedStroke: !allSameStroke,
|
|
3944
|
+
mixedWidth: !allSameWidth,
|
|
3945
|
+
mixedDash: !allSameDash,
|
|
3946
|
+
mixedFontSize: !allSameFontSize,
|
|
3947
|
+
mixedOpacity: !allSameOpacity,
|
|
3948
|
+
current
|
|
3949
|
+
};
|
|
3950
|
+
}
|
|
3518
3951
|
function VectorSelectionInspector({
|
|
3519
3952
|
items: itemsProp,
|
|
3520
3953
|
activeToolStyle: activeToolStyleProp,
|
|
@@ -3530,175 +3963,93 @@ function VectorSelectionInspector({
|
|
|
3530
3963
|
const activeToolStyle = activeToolStyleProp === void 0 ? ctx?.activeToolStyle ?? null : activeToolStyleProp;
|
|
3531
3964
|
const onChange = onChangeProp ?? ctx?.onSelectionStyleChange ?? null;
|
|
3532
3965
|
if (!onChange) return null;
|
|
3966
|
+
let vm = null;
|
|
3967
|
+
if (activeToolStyle) {
|
|
3968
|
+
vm = viewModelFromActiveTool(activeToolStyle);
|
|
3969
|
+
} else {
|
|
3970
|
+
const stylable = items.filter(
|
|
3971
|
+
(it) => !it.locked && it.toolKind && isStylableKind(it.toolKind)
|
|
3972
|
+
);
|
|
3973
|
+
if (stylable.length === 0) return null;
|
|
3974
|
+
vm = viewModelFromSelection(stylable);
|
|
3975
|
+
}
|
|
3976
|
+
if (!vm) return null;
|
|
3977
|
+
const apply = (changes) => onChange({ ...vm.current, ...changes });
|
|
3533
3978
|
const shell = {
|
|
3534
3979
|
...getBoardPositionStyle(position, inset, zIndex),
|
|
3535
3980
|
...shellLook,
|
|
3536
3981
|
...style
|
|
3537
3982
|
};
|
|
3538
|
-
|
|
3539
|
-
const stroke2 = activeToolStyle.stroke;
|
|
3540
|
-
const strokeWidth2 = activeToolStyle.strokeWidth;
|
|
3541
|
-
const hex2 = normalizeHex(stroke2);
|
|
3542
|
-
const showMarkerOpacity2 = activeToolStyle.toolKind === "marker";
|
|
3543
|
-
const opacityPct2 = Math.round((activeToolStyle.strokeOpacity ?? 1) * 100);
|
|
3544
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3545
|
-
"section",
|
|
3546
|
-
{
|
|
3547
|
-
"data-slot": "vector-selection-inspector",
|
|
3548
|
-
"data-position": position,
|
|
3549
|
-
className,
|
|
3550
|
-
"aria-label": activeToolStyle.label ?? "Estilo da ferramenta",
|
|
3551
|
-
style: shell,
|
|
3552
|
-
children: [
|
|
3553
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle2, children: [
|
|
3554
|
-
"Cor",
|
|
3555
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3556
|
-
"input",
|
|
3557
|
-
{
|
|
3558
|
-
type: "color",
|
|
3559
|
-
value: hex2,
|
|
3560
|
-
onChange: (e) => onChange({
|
|
3561
|
-
stroke: e.target.value,
|
|
3562
|
-
strokeWidth: strokeWidth2,
|
|
3563
|
-
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
3564
|
-
}),
|
|
3565
|
-
style: {
|
|
3566
|
-
width: "100%",
|
|
3567
|
-
height: 32,
|
|
3568
|
-
padding: 0,
|
|
3569
|
-
border: "none",
|
|
3570
|
-
cursor: "pointer",
|
|
3571
|
-
background: "transparent"
|
|
3572
|
-
}
|
|
3573
|
-
}
|
|
3574
|
-
)
|
|
3575
|
-
] }),
|
|
3576
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle2, children: [
|
|
3577
|
-
"Grossura",
|
|
3578
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3579
|
-
"input",
|
|
3580
|
-
{
|
|
3581
|
-
type: "range",
|
|
3582
|
-
min: 1,
|
|
3583
|
-
max: 48,
|
|
3584
|
-
value: strokeWidth2,
|
|
3585
|
-
onChange: (e) => onChange({
|
|
3586
|
-
stroke: hex2,
|
|
3587
|
-
strokeWidth: Number(e.target.value),
|
|
3588
|
-
...activeToolStyle.strokeOpacity != null ? { strokeOpacity: activeToolStyle.strokeOpacity } : {}
|
|
3589
|
-
})
|
|
3590
|
-
}
|
|
3591
|
-
)
|
|
3592
|
-
] }),
|
|
3593
|
-
showMarkerOpacity2 && /* @__PURE__ */ jsxRuntime.jsxs("label", { style: labelStyle2, children: [
|
|
3594
|
-
"Opacidade (marcador)",
|
|
3595
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3596
|
-
"input",
|
|
3597
|
-
{
|
|
3598
|
-
type: "range",
|
|
3599
|
-
min: 10,
|
|
3600
|
-
max: 100,
|
|
3601
|
-
value: opacityPct2,
|
|
3602
|
-
onChange: (e) => {
|
|
3603
|
-
const v = Number(e.target.value) / 100;
|
|
3604
|
-
onChange({
|
|
3605
|
-
stroke: hex2,
|
|
3606
|
-
strokeWidth: strokeWidth2,
|
|
3607
|
-
strokeOpacity: v
|
|
3608
|
-
});
|
|
3609
|
-
}
|
|
3610
|
-
}
|
|
3611
|
-
),
|
|
3612
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
3613
|
-
opacityPct2,
|
|
3614
|
-
"%"
|
|
3615
|
-
] })
|
|
3616
|
-
] })
|
|
3617
|
-
]
|
|
3618
|
-
}
|
|
3619
|
-
);
|
|
3620
|
-
}
|
|
3621
|
-
const stylable = items.filter(
|
|
3622
|
-
(it) => !it.locked && it.toolKind && isStylableKind(it.toolKind)
|
|
3623
|
-
);
|
|
3624
|
-
if (stylable.length === 0) return null;
|
|
3625
|
-
const first = stylable[0];
|
|
3626
|
-
if (!first) return null;
|
|
3627
|
-
const allSameStroke = stylable.every(
|
|
3628
|
-
(it) => (it.stroke ?? "#2563eb") === (first.stroke ?? "#2563eb")
|
|
3629
|
-
);
|
|
3630
|
-
const allSameWidth = stylable.every(
|
|
3631
|
-
(it) => (it.strokeWidth ?? 2) === (first.strokeWidth ?? 2)
|
|
3632
|
-
);
|
|
3633
|
-
const stroke = first.stroke ?? "#2563eb";
|
|
3634
|
-
const strokeWidth = first.strokeWidth ?? 2;
|
|
3635
|
-
const hex = normalizeHex(stroke);
|
|
3636
|
-
const markers = stylable.filter((it) => it.toolKind === "marker");
|
|
3637
|
-
const showMarkerOpacity = markers.length > 0;
|
|
3638
|
-
const firstMarker = markers[0];
|
|
3639
|
-
const allSameMarkerOpacity = markers.length > 0 && markers.every(
|
|
3640
|
-
(it) => (it.strokeOpacity ?? 1) === (firstMarker?.strokeOpacity ?? 1)
|
|
3641
|
-
);
|
|
3642
|
-
const opacityPct = firstMarker ? Math.round((firstMarker.strokeOpacity ?? 1) * 100) : 100;
|
|
3983
|
+
const opacityPct = Math.round(vm.strokeOpacity * 100);
|
|
3643
3984
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3644
3985
|
"section",
|
|
3645
3986
|
{
|
|
3646
3987
|
"data-slot": "vector-selection-inspector",
|
|
3647
3988
|
"data-position": position,
|
|
3648
3989
|
className,
|
|
3649
|
-
"aria-label":
|
|
3990
|
+
"aria-label": vm.label,
|
|
3650
3991
|
style: shell,
|
|
3651
3992
|
children: [
|
|
3652
|
-
|
|
3653
|
-
|
|
3993
|
+
vm.count > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { style: { margin: 0, fontSize: 11, color: "#71717a" }, children: [
|
|
3994
|
+
vm.count,
|
|
3654
3995
|
" objetos selecionados"
|
|
3655
|
-
] }),
|
|
3656
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
3657
|
-
"
|
|
3658
|
-
|
|
3659
|
-
" "
|
|
3660
|
-
"(valores misturados \u2014 novo valor aplica a todos)"
|
|
3996
|
+
] }) : null,
|
|
3997
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Cor", style: sectionLabelStyle, children: [
|
|
3998
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
3999
|
+
"Cor",
|
|
4000
|
+
vm.mixedStroke ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (valores misturados)" }) : null
|
|
3661
4001
|
] }),
|
|
3662
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3663
|
-
"input",
|
|
3664
|
-
{
|
|
3665
|
-
type: "color",
|
|
3666
|
-
value: hex,
|
|
3667
|
-
onChange: (e) => onChange({
|
|
3668
|
-
stroke: e.target.value,
|
|
3669
|
-
strokeWidth
|
|
3670
|
-
}),
|
|
3671
|
-
style: {
|
|
3672
|
-
width: "100%",
|
|
3673
|
-
height: 32,
|
|
3674
|
-
padding: 0,
|
|
3675
|
-
border: "none",
|
|
3676
|
-
cursor: "pointer",
|
|
3677
|
-
background: "transparent"
|
|
3678
|
-
}
|
|
3679
|
-
}
|
|
3680
|
-
)
|
|
4002
|
+
/* @__PURE__ */ jsxRuntime.jsx(ColorPalette, { value: vm.hex, onChange: (h) => apply({ stroke: h }) })
|
|
3681
4003
|
] }),
|
|
3682
|
-
/* @__PURE__ */ jsxRuntime.jsxs("label", { style:
|
|
4004
|
+
vm.showStrokeWidth ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: sectionLabelStyle, children: [
|
|
3683
4005
|
"Grossura",
|
|
3684
|
-
|
|
4006
|
+
vm.mixedWidth ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
|
|
3685
4007
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3686
4008
|
"input",
|
|
3687
4009
|
{
|
|
3688
4010
|
type: "range",
|
|
3689
4011
|
min: 1,
|
|
3690
4012
|
max: 48,
|
|
3691
|
-
value: strokeWidth,
|
|
3692
|
-
onChange: (e) =>
|
|
3693
|
-
stroke: hex,
|
|
3694
|
-
strokeWidth: Number(e.target.value)
|
|
3695
|
-
})
|
|
4013
|
+
value: vm.strokeWidth,
|
|
4014
|
+
onChange: (e) => apply({ strokeWidth: Number(e.target.value) })
|
|
3696
4015
|
}
|
|
3697
4016
|
)
|
|
3698
|
-
] }),
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
4017
|
+
] }) : null,
|
|
4018
|
+
vm.showDash ? (
|
|
4019
|
+
// biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
|
|
4020
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Tra\xE7o", style: sectionLabelStyle, children: [
|
|
4021
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4022
|
+
"Tra\xE7o",
|
|
4023
|
+
vm.mixedDash ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
|
|
4024
|
+
] }),
|
|
4025
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4026
|
+
DashTabs,
|
|
4027
|
+
{
|
|
4028
|
+
value: vm.strokeDash,
|
|
4029
|
+
onChange: (next) => apply({ strokeDash: next })
|
|
4030
|
+
}
|
|
4031
|
+
)
|
|
4032
|
+
] })
|
|
4033
|
+
) : null,
|
|
4034
|
+
vm.showFontSize ? (
|
|
4035
|
+
// biome-ignore lint/a11y/useSemanticElements: fieldset would impose default browser styling that breaks the inspector layout
|
|
4036
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": "Tamanho", style: sectionLabelStyle, children: [
|
|
4037
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
4038
|
+
"Tamanho",
|
|
4039
|
+
vm.mixedFontSize ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null
|
|
4040
|
+
] }),
|
|
4041
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4042
|
+
FontSizeTabs,
|
|
4043
|
+
{
|
|
4044
|
+
value: vm.textFontSize,
|
|
4045
|
+
onChange: (size) => apply({ textFontSize: size })
|
|
4046
|
+
}
|
|
4047
|
+
)
|
|
4048
|
+
] })
|
|
4049
|
+
) : null,
|
|
4050
|
+
vm.showMarkerOpacity ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: sectionLabelStyle, children: [
|
|
4051
|
+
"Opacidade",
|
|
4052
|
+
vm.mixedOpacity ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: mixedHintStyle, children: " (misturado)" }) : null,
|
|
3702
4053
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3703
4054
|
"input",
|
|
3704
4055
|
{
|
|
@@ -3706,21 +4057,14 @@ function VectorSelectionInspector({
|
|
|
3706
4057
|
min: 10,
|
|
3707
4058
|
max: 100,
|
|
3708
4059
|
value: opacityPct,
|
|
3709
|
-
onChange: (e) => {
|
|
3710
|
-
const v = Number(e.target.value) / 100;
|
|
3711
|
-
onChange({
|
|
3712
|
-
stroke: hex,
|
|
3713
|
-
strokeWidth,
|
|
3714
|
-
strokeOpacity: v
|
|
3715
|
-
});
|
|
3716
|
-
}
|
|
4060
|
+
onChange: (e) => apply({ strokeOpacity: Number(e.target.value) / 100 })
|
|
3717
4061
|
}
|
|
3718
4062
|
),
|
|
3719
4063
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontWeight: 500, color: "#71717a" }, children: [
|
|
3720
4064
|
opacityPct,
|
|
3721
4065
|
"%"
|
|
3722
4066
|
] })
|
|
3723
|
-
] })
|
|
4067
|
+
] }) : null
|
|
3724
4068
|
]
|
|
3725
4069
|
}
|
|
3726
4070
|
);
|
|
@@ -4199,6 +4543,24 @@ function splitToolbarTools(tools, overflowIds) {
|
|
|
4199
4543
|
const overflow = overflowIds.map((id) => tools.find((t) => t.id === id)).filter((t) => Boolean(t));
|
|
4200
4544
|
return { primary, overflow };
|
|
4201
4545
|
}
|
|
4546
|
+
function usePromotedOverflowTool({
|
|
4547
|
+
overflowTools,
|
|
4548
|
+
selectedId,
|
|
4549
|
+
initialPromotedId
|
|
4550
|
+
}) {
|
|
4551
|
+
const [lastOverflowToolId, setLastOverflowToolId] = react.useState(() => {
|
|
4552
|
+
if (initialPromotedId == null) return null;
|
|
4553
|
+
return overflowTools.some((t) => t.id === initialPromotedId) ? initialPromotedId : null;
|
|
4554
|
+
});
|
|
4555
|
+
react.useEffect(() => {
|
|
4556
|
+
if (overflowTools.some((t) => t.id === selectedId)) {
|
|
4557
|
+
setLastOverflowToolId(selectedId);
|
|
4558
|
+
}
|
|
4559
|
+
}, [selectedId, overflowTools]);
|
|
4560
|
+
const promotedTool = lastOverflowToolId ? overflowTools.find((t) => t.id === lastOverflowToolId) : void 0;
|
|
4561
|
+
const remainingOverflowTools = promotedTool ? overflowTools.filter((t) => t.id !== promotedTool.id) : overflowTools;
|
|
4562
|
+
return { promotedTool, remainingOverflowTools };
|
|
4563
|
+
}
|
|
4202
4564
|
var icOverflow = { size: 18, strokeWidth: 2 };
|
|
4203
4565
|
function useOverflowDropdown() {
|
|
4204
4566
|
const [open, setOpen] = react.useState(false);
|
|
@@ -4733,6 +5095,15 @@ function VectorToolbarComponent({
|
|
|
4733
5095
|
const pluginContext = react.useContext(CanvuPluginContext);
|
|
4734
5096
|
const runtimeTools = pluginContext?.resolvedTools;
|
|
4735
5097
|
const resolvedTools = tools ?? runtimeTools ?? DEFAULT_VECTOR_TOOLS;
|
|
5098
|
+
const { primary: primaryTools, overflow: overflowTools } = splitToolbarTools(
|
|
5099
|
+
resolvedTools,
|
|
5100
|
+
overflowToolIds
|
|
5101
|
+
);
|
|
5102
|
+
const { promotedTool, remainingOverflowTools } = usePromotedOverflowTool({
|
|
5103
|
+
overflowTools,
|
|
5104
|
+
selectedId: value,
|
|
5105
|
+
initialPromotedId: overflowToolIds[0] ?? null
|
|
5106
|
+
});
|
|
4736
5107
|
if (typeof children === "function") {
|
|
4737
5108
|
const ctx = {
|
|
4738
5109
|
tools: resolvedTools,
|
|
@@ -4777,11 +5148,8 @@ function VectorToolbarComponent({
|
|
|
4777
5148
|
)
|
|
4778
5149
|
] });
|
|
4779
5150
|
}
|
|
4780
|
-
const { primary: primaryTools, overflow: overflowTools } = splitToolbarTools(
|
|
4781
|
-
resolvedTools,
|
|
4782
|
-
overflowToolIds
|
|
4783
|
-
);
|
|
4784
5151
|
const showOverflowMenu = overflowTools.length > 0;
|
|
5152
|
+
const showOverflowDropdown = remainingOverflowTools.length > 0;
|
|
4785
5153
|
const inlineCtx = {
|
|
4786
5154
|
selectedId: value,
|
|
4787
5155
|
selectTool: onChange,
|
|
@@ -4832,6 +5200,7 @@ function VectorToolbarComponent({
|
|
|
4832
5200
|
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, style: toolLockDividerStyle })
|
|
4833
5201
|
] }) : null,
|
|
4834
5202
|
primaryTools.map((tool) => renderInlineToolButton(tool, inlineCtx)),
|
|
5203
|
+
promotedTool ? renderInlineToolButton(promotedTool, inlineCtx) : null,
|
|
4835
5204
|
showOverflowMenu && orientation === "horizontal" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4836
5205
|
"span",
|
|
4837
5206
|
{
|
|
@@ -4840,10 +5209,10 @@ function VectorToolbarComponent({
|
|
|
4840
5209
|
style: overflowSpacerStyle
|
|
4841
5210
|
}
|
|
4842
5211
|
) : null,
|
|
4843
|
-
|
|
5212
|
+
showOverflowDropdown ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4844
5213
|
ToolbarOverflowMenu,
|
|
4845
5214
|
{
|
|
4846
|
-
tools:
|
|
5215
|
+
tools: remainingOverflowTools,
|
|
4847
5216
|
selectedId: value,
|
|
4848
5217
|
onSelect: onChange,
|
|
4849
5218
|
density,
|
|
@@ -6463,6 +6832,18 @@ var SvgVectorRenderer = class {
|
|
|
6463
6832
|
}
|
|
6464
6833
|
};
|
|
6465
6834
|
|
|
6835
|
+
// src/scene/link-item.ts
|
|
6836
|
+
var LINK_PLUGIN_KEY = "canvuLink";
|
|
6837
|
+
var isCanvuLinkData = (value) => {
|
|
6838
|
+
if (!value || typeof value !== "object") return false;
|
|
6839
|
+
const candidate = value;
|
|
6840
|
+
return typeof candidate.href === "string" && candidate.href.length > 0;
|
|
6841
|
+
};
|
|
6842
|
+
function getLinkData(item) {
|
|
6843
|
+
const entry = item.pluginData?.[LINK_PLUGIN_KEY];
|
|
6844
|
+
return isCanvuLinkData(entry) ? entry : null;
|
|
6845
|
+
}
|
|
6846
|
+
|
|
6466
6847
|
// src/scene/scene.ts
|
|
6467
6848
|
var VectorScene = class {
|
|
6468
6849
|
items = [];
|
|
@@ -6482,6 +6863,7 @@ var VectorScene = class {
|
|
|
6482
6863
|
|
|
6483
6864
|
// src/react/VectorViewport.tsx
|
|
6484
6865
|
init_shape_builders();
|
|
6866
|
+
init_text_svg();
|
|
6485
6867
|
|
|
6486
6868
|
// src/react/blob-url-lifecycle.ts
|
|
6487
6869
|
var SVG_BLOB_HREF_PATTERN = /\b(?:href|xlink:href)=["'](blob:[^"']+)["']/g;
|
|
@@ -6687,6 +7069,9 @@ function InteractionOverlay({
|
|
|
6687
7069
|
);
|
|
6688
7070
|
} else if (placementPreview.kind === "rect" || placementPreview.kind === "ellipse" || placementPreview.kind === "architectural-cloud") {
|
|
6689
7071
|
const r = normalizeRect(placementPreview.rect);
|
|
7072
|
+
const shapeStroke = previewStrokeStyle?.stroke ?? "#1d1d1d";
|
|
7073
|
+
const shapeWidth = previewStrokeStyle?.strokeWidth ?? 2;
|
|
7074
|
+
const shapeOpacity = previewStrokeStyle?.strokeOpacity;
|
|
6690
7075
|
preview = placementPreview.kind === "rect" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6691
7076
|
"rect",
|
|
6692
7077
|
{
|
|
@@ -6694,11 +7079,11 @@ function InteractionOverlay({
|
|
|
6694
7079
|
y: r.y,
|
|
6695
7080
|
width: r.width,
|
|
6696
7081
|
height: r.height,
|
|
7082
|
+
rx: 4,
|
|
6697
7083
|
fill: "none",
|
|
6698
|
-
stroke:
|
|
6699
|
-
strokeWidth:
|
|
6700
|
-
|
|
6701
|
-
vectorEffect: "non-scaling-stroke"
|
|
7084
|
+
stroke: shapeStroke,
|
|
7085
|
+
strokeWidth: shapeWidth,
|
|
7086
|
+
strokeOpacity: shapeOpacity
|
|
6702
7087
|
}
|
|
6703
7088
|
) : placementPreview.kind === "ellipse" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6704
7089
|
"ellipse",
|
|
@@ -6708,29 +7093,30 @@ function InteractionOverlay({
|
|
|
6708
7093
|
rx: r.width / 2,
|
|
6709
7094
|
ry: r.height / 2,
|
|
6710
7095
|
fill: "none",
|
|
6711
|
-
stroke:
|
|
6712
|
-
strokeWidth:
|
|
6713
|
-
|
|
6714
|
-
vectorEffect: "non-scaling-stroke"
|
|
7096
|
+
stroke: shapeStroke,
|
|
7097
|
+
strokeWidth: shapeWidth,
|
|
7098
|
+
strokeOpacity: shapeOpacity
|
|
6715
7099
|
}
|
|
6716
7100
|
) : /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${r.x}, ${r.y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6717
7101
|
"path",
|
|
6718
7102
|
{
|
|
6719
|
-
d: buildArchitecturalCloudPathD(r.width, r.height,
|
|
7103
|
+
d: buildArchitecturalCloudPathD(r.width, r.height, shapeWidth),
|
|
6720
7104
|
fill: "none",
|
|
6721
|
-
stroke:
|
|
6722
|
-
strokeWidth:
|
|
6723
|
-
|
|
7105
|
+
stroke: shapeStroke,
|
|
7106
|
+
strokeWidth: shapeWidth,
|
|
7107
|
+
strokeOpacity: shapeOpacity,
|
|
6724
7108
|
strokeLinecap: "round",
|
|
6725
|
-
strokeLinejoin: "round"
|
|
6726
|
-
vectorEffect: "non-scaling-stroke"
|
|
7109
|
+
strokeLinejoin: "round"
|
|
6727
7110
|
}
|
|
6728
7111
|
) });
|
|
6729
7112
|
} else if (placementPreview.kind === "line" || placementPreview.kind === "arrow") {
|
|
6730
7113
|
const { start, end } = placementPreview;
|
|
7114
|
+
const shapeStroke = previewStrokeStyle?.stroke ?? "#1d1d1d";
|
|
7115
|
+
const shapeWidth = previewStrokeStyle?.strokeWidth ?? 2;
|
|
7116
|
+
const shapeOpacity = previewStrokeStyle?.strokeOpacity;
|
|
6731
7117
|
const geometry = placementPreview.kind === "arrow" ? computeStraightArrowGeometry(
|
|
6732
7118
|
{ x1: start.x, y1: start.y, x2: end.x, y2: end.y },
|
|
6733
|
-
|
|
7119
|
+
shapeWidth
|
|
6734
7120
|
) : null;
|
|
6735
7121
|
preview = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6736
7122
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6740,11 +7126,10 @@ function InteractionOverlay({
|
|
|
6740
7126
|
y1: start.y,
|
|
6741
7127
|
x2: geometry?.shaftEndX ?? end.x,
|
|
6742
7128
|
y2: geometry?.shaftEndY ?? end.y,
|
|
6743
|
-
stroke:
|
|
6744
|
-
strokeWidth:
|
|
6745
|
-
|
|
6746
|
-
strokeLinecap: "round"
|
|
6747
|
-
vectorEffect: "non-scaling-stroke"
|
|
7129
|
+
stroke: shapeStroke,
|
|
7130
|
+
strokeWidth: shapeWidth,
|
|
7131
|
+
strokeOpacity: shapeOpacity,
|
|
7132
|
+
strokeLinecap: "round"
|
|
6748
7133
|
}
|
|
6749
7134
|
),
|
|
6750
7135
|
placementPreview.kind === "arrow" && geometry ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6752,10 +7137,9 @@ function InteractionOverlay({
|
|
|
6752
7137
|
{
|
|
6753
7138
|
d: `M ${geometry.headLeftX} ${geometry.headLeftY} L ${geometry.headTipX} ${geometry.headTipY} L ${geometry.headRightX} ${geometry.headRightY}`,
|
|
6754
7139
|
fill: "none",
|
|
6755
|
-
stroke:
|
|
6756
|
-
strokeWidth:
|
|
6757
|
-
strokeOpacity:
|
|
6758
|
-
strokeDasharray: dashPattern,
|
|
7140
|
+
stroke: shapeStroke,
|
|
7141
|
+
strokeWidth: shapeWidth,
|
|
7142
|
+
strokeOpacity: shapeOpacity,
|
|
6759
7143
|
strokeLinecap: "round",
|
|
6760
7144
|
strokeLinejoin: "round"
|
|
6761
7145
|
}
|
|
@@ -6769,7 +7153,8 @@ function InteractionOverlay({
|
|
|
6769
7153
|
const previewStyle = {
|
|
6770
7154
|
stroke: isLaser ? "#f43f5e" : previewStrokeStyle?.stroke ?? "#64748b",
|
|
6771
7155
|
strokeWidth: isLaser ? 4 : previewStrokeStyle?.strokeWidth ?? (tool === "marker" ? 16 : 3),
|
|
6772
|
-
...previewStrokeStyle?.strokeOpacity != null && !isLaser ? { strokeOpacity: previewStrokeStyle.strokeOpacity } : {}
|
|
7156
|
+
...previewStrokeStyle?.strokeOpacity != null && !isLaser ? { strokeOpacity: previewStrokeStyle.strokeOpacity } : {},
|
|
7157
|
+
...previewStrokeStyle?.strokeDash != null && !isLaser ? { strokeDash: previewStrokeStyle.strokeDash } : {}
|
|
6773
7158
|
};
|
|
6774
7159
|
const payload = computeFreehandSvgPayload(
|
|
6775
7160
|
raw,
|
|
@@ -6813,6 +7198,7 @@ function InteractionOverlay({
|
|
|
6813
7198
|
strokeOpacity: isLaser ? 0.85 : payload.strokeOpacity,
|
|
6814
7199
|
strokeLinecap: "round",
|
|
6815
7200
|
strokeLinejoin: "round",
|
|
7201
|
+
strokeDasharray: payload.strokeDasharray,
|
|
6816
7202
|
shapeRendering: "geometricPrecision"
|
|
6817
7203
|
}
|
|
6818
7204
|
);
|
|
@@ -7218,6 +7604,9 @@ function TextEditOverlay({
|
|
|
7218
7604
|
const fontSize = fsWorld * z;
|
|
7219
7605
|
const lineHeight = fsWorld * (22 / 18) * z;
|
|
7220
7606
|
const fixedBox = !!item.textFixedBounds;
|
|
7607
|
+
const padTop = EDIT_TOP_PAD_RATIO * fsWorld * z;
|
|
7608
|
+
const padX = 4 * z;
|
|
7609
|
+
const textColor = item.stroke ?? "#1d1d1d";
|
|
7221
7610
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7222
7611
|
"div",
|
|
7223
7612
|
{
|
|
@@ -7248,14 +7637,14 @@ function TextEditOverlay({
|
|
|
7248
7637
|
width: "100%",
|
|
7249
7638
|
height: "100%",
|
|
7250
7639
|
margin: 0,
|
|
7251
|
-
padding:
|
|
7640
|
+
padding: `${padTop}px ${padX}px 0px ${padX}px`,
|
|
7252
7641
|
border: "none",
|
|
7253
7642
|
borderRadius: 0,
|
|
7254
7643
|
resize: "none",
|
|
7255
|
-
fontFamily:
|
|
7644
|
+
fontFamily: TEXT_FONT_FAMILY,
|
|
7256
7645
|
fontSize,
|
|
7257
7646
|
lineHeight: `${lineHeight}px`,
|
|
7258
|
-
color:
|
|
7647
|
+
color: textColor,
|
|
7259
7648
|
background: "transparent",
|
|
7260
7649
|
backgroundColor: "transparent",
|
|
7261
7650
|
outline: "none",
|
|
@@ -7263,7 +7652,6 @@ function TextEditOverlay({
|
|
|
7263
7652
|
appearance: "none",
|
|
7264
7653
|
WebkitAppearance: "none",
|
|
7265
7654
|
WebkitTapHighlightColor: "transparent",
|
|
7266
|
-
/* Auto-sized text: no soft wrap / scrollbar; fixed box: wrap like the SVG. */
|
|
7267
7655
|
whiteSpace: fixedBox ? "pre-wrap" : "pre",
|
|
7268
7656
|
overflow: fixedBox ? "auto" : "hidden",
|
|
7269
7657
|
overflowX: fixedBox ? "hidden" : "hidden",
|
|
@@ -7639,6 +8027,7 @@ var VectorViewport = react.forwardRef(
|
|
|
7639
8027
|
selectedIds: selectedIdsProp,
|
|
7640
8028
|
onSelectionChange,
|
|
7641
8029
|
onItemsChange: consumerOnItemsChange,
|
|
8030
|
+
onActivateLink,
|
|
7642
8031
|
onWorldPointerDown: consumerOnWorldPointerDown,
|
|
7643
8032
|
toolbar,
|
|
7644
8033
|
navMenu,
|
|
@@ -7819,6 +8208,8 @@ var VectorViewport = react.forwardRef(
|
|
|
7819
8208
|
const itemsRef = react.useRef(items);
|
|
7820
8209
|
const onWorldPointerDownRef = react.useRef(onWorldPointerDown);
|
|
7821
8210
|
const onItemsChangeRef = react.useRef(onItemsChange);
|
|
8211
|
+
const onActivateLinkRef = react.useRef(onActivateLink);
|
|
8212
|
+
onActivateLinkRef.current = onActivateLink;
|
|
7822
8213
|
const assetStoreRef = react.useRef(assetStore);
|
|
7823
8214
|
assetStoreRef.current = assetStore;
|
|
7824
8215
|
const customPlacementRef = react.useRef(customPlacement);
|
|
@@ -7950,14 +8341,32 @@ var VectorViewport = react.forwardRef(
|
|
|
7950
8341
|
imageStoreRef.current = new IndexedDbImageStore();
|
|
7951
8342
|
}
|
|
7952
8343
|
const rememberedImageBlobHrefsRef = react.useRef(/* @__PURE__ */ new Set());
|
|
7953
|
-
const strokeStyleRef = react.useRef({
|
|
8344
|
+
const strokeStyleRef = react.useRef({
|
|
8345
|
+
...DEFAULT_STROKE_STYLE,
|
|
8346
|
+
textFontSize: DEFAULT_TEXT_FONT_SIZE
|
|
8347
|
+
});
|
|
7954
8348
|
const [strokeStyleState, setStrokeStyleState] = react.useState({
|
|
7955
|
-
...DEFAULT_STROKE_STYLE
|
|
8349
|
+
...DEFAULT_STROKE_STYLE,
|
|
8350
|
+
textFontSize: DEFAULT_TEXT_FONT_SIZE
|
|
7956
8351
|
});
|
|
7957
8352
|
const setCurrentStrokeStyle = react.useCallback((next) => {
|
|
7958
8353
|
strokeStyleRef.current = next;
|
|
7959
8354
|
setStrokeStyleState(next);
|
|
7960
8355
|
}, []);
|
|
8356
|
+
const patchCurrentStrokeStyle = react.useCallback(
|
|
8357
|
+
(patch) => {
|
|
8358
|
+
const merged = { ...strokeStyleRef.current };
|
|
8359
|
+
for (const key of Object.keys(patch)) {
|
|
8360
|
+
const v = patch[key];
|
|
8361
|
+
if (v !== void 0) {
|
|
8362
|
+
merged[key] = v;
|
|
8363
|
+
}
|
|
8364
|
+
}
|
|
8365
|
+
strokeStyleRef.current = merged;
|
|
8366
|
+
setStrokeStyleState(merged);
|
|
8367
|
+
},
|
|
8368
|
+
[]
|
|
8369
|
+
);
|
|
7961
8370
|
const progressiveQueueRef = react.useRef(/* @__PURE__ */ new Set());
|
|
7962
8371
|
react.useEffect(() => {
|
|
7963
8372
|
const store = imageStoreRef.current;
|
|
@@ -8106,10 +8515,11 @@ var VectorViewport = react.forwardRef(
|
|
|
8106
8515
|
change(
|
|
8107
8516
|
exists ? replaceItem(itemsRef.current, id, item) : [...itemsRef.current, item]
|
|
8108
8517
|
);
|
|
8109
|
-
|
|
8518
|
+
patchCurrentStrokeStyle({
|
|
8110
8519
|
stroke: item.stroke ?? DEFAULT_STROKE_STYLE.stroke,
|
|
8111
8520
|
strokeWidth: item.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth,
|
|
8112
|
-
|
|
8521
|
+
strokeOpacity: item.strokeOpacity,
|
|
8522
|
+
strokeDash: item.strokeDash
|
|
8113
8523
|
});
|
|
8114
8524
|
}
|
|
8115
8525
|
if (!shouldKeepToolForContinuousPenInput(args.tool, args.pointerType)) {
|
|
@@ -8117,8 +8527,8 @@ var VectorViewport = react.forwardRef(
|
|
|
8117
8527
|
}
|
|
8118
8528
|
},
|
|
8119
8529
|
[
|
|
8530
|
+
patchCurrentStrokeStyle,
|
|
8120
8531
|
requestAutoResetTool,
|
|
8121
|
-
setCurrentStrokeStyle,
|
|
8122
8532
|
shouldKeepToolForContinuousPenInput
|
|
8123
8533
|
]
|
|
8124
8534
|
);
|
|
@@ -8854,24 +9264,28 @@ var VectorViewport = react.forwardRef(
|
|
|
8854
9264
|
renderFrame();
|
|
8855
9265
|
}, [renderFrame]);
|
|
8856
9266
|
react.useEffect(() => {
|
|
9267
|
+
const current = strokeStyleRef.current;
|
|
8857
9268
|
if (toolId === "marker") {
|
|
8858
9269
|
setCurrentStrokeStyle({
|
|
8859
|
-
...
|
|
9270
|
+
...current,
|
|
8860
9271
|
...MARKER_TOOL_STYLE
|
|
8861
9272
|
});
|
|
8862
9273
|
return;
|
|
8863
9274
|
}
|
|
8864
|
-
const current = strokeStyleRef.current;
|
|
8865
9275
|
if (isDefaultMarkerToolStyle(current)) {
|
|
8866
9276
|
setCurrentStrokeStyle({
|
|
8867
9277
|
stroke: DEFAULT_STROKE_STYLE.stroke,
|
|
8868
|
-
strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth
|
|
9278
|
+
strokeWidth: toolId === "draw" ? 10 : DEFAULT_STROKE_STYLE.strokeWidth,
|
|
9279
|
+
strokeDash: current.strokeDash,
|
|
9280
|
+
textFontSize: current.textFontSize
|
|
8869
9281
|
});
|
|
8870
9282
|
return;
|
|
8871
9283
|
}
|
|
8872
9284
|
setCurrentStrokeStyle({
|
|
8873
9285
|
stroke: current.stroke,
|
|
8874
|
-
strokeWidth: toolId === "draw" ? 10 : current.strokeWidth
|
|
9286
|
+
strokeWidth: toolId === "draw" ? 10 : current.strokeWidth,
|
|
9287
|
+
strokeDash: current.strokeDash,
|
|
9288
|
+
textFontSize: current.textFontSize
|
|
8875
9289
|
});
|
|
8876
9290
|
}, [setCurrentStrokeStyle, toolId]);
|
|
8877
9291
|
react.useEffect(() => {
|
|
@@ -8880,15 +9294,14 @@ var VectorViewport = react.forwardRef(
|
|
|
8880
9294
|
const it = items.find((i) => i.id === primaryId);
|
|
8881
9295
|
if (!it || it.locked) return;
|
|
8882
9296
|
if (it.toolKind === "image") return;
|
|
8883
|
-
|
|
9297
|
+
patchCurrentStrokeStyle({
|
|
8884
9298
|
stroke: it.stroke ?? DEFAULT_STROKE_STYLE.stroke,
|
|
8885
|
-
strokeWidth: it.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
}
|
|
8890
|
-
|
|
8891
|
-
}, [effectiveSelectedIds, items, setCurrentStrokeStyle]);
|
|
9299
|
+
strokeWidth: it.strokeWidth ?? DEFAULT_STROKE_STYLE.strokeWidth,
|
|
9300
|
+
strokeOpacity: it.strokeOpacity,
|
|
9301
|
+
strokeDash: it.strokeDash,
|
|
9302
|
+
...it.toolKind === "text" && it.textFontSize != null ? { textFontSize: it.textFontSize } : {}
|
|
9303
|
+
});
|
|
9304
|
+
}, [effectiveSelectedIds, items, patchCurrentStrokeStyle]);
|
|
8892
9305
|
const handleSelectionStyleChange = react.useCallback(
|
|
8893
9306
|
(patch) => {
|
|
8894
9307
|
const change = onItemsChangeRef.current;
|
|
@@ -8904,25 +9317,15 @@ var VectorViewport = react.forwardRef(
|
|
|
8904
9317
|
nextList = replaceItem(nextList, id, out);
|
|
8905
9318
|
}
|
|
8906
9319
|
change(nextList);
|
|
8907
|
-
|
|
8908
|
-
...strokeStyleRef.current,
|
|
8909
|
-
stroke: patch.stroke,
|
|
8910
|
-
strokeWidth: patch.strokeWidth,
|
|
8911
|
-
...patch.strokeOpacity != null ? { strokeOpacity: patch.strokeOpacity } : {}
|
|
8912
|
-
});
|
|
9320
|
+
patchCurrentStrokeStyle(patch);
|
|
8913
9321
|
},
|
|
8914
|
-
[
|
|
9322
|
+
[patchCurrentStrokeStyle]
|
|
8915
9323
|
);
|
|
8916
9324
|
const handleActiveToolStyleChange = react.useCallback(
|
|
8917
9325
|
(patch) => {
|
|
8918
|
-
|
|
8919
|
-
setCurrentStrokeStyle({
|
|
8920
|
-
stroke: patch.stroke,
|
|
8921
|
-
strokeWidth: patch.strokeWidth,
|
|
8922
|
-
...patch.strokeOpacity != null ? { strokeOpacity: patch.strokeOpacity } : current.strokeOpacity != null ? { strokeOpacity: current.strokeOpacity } : {}
|
|
8923
|
-
});
|
|
9326
|
+
patchCurrentStrokeStyle(patch);
|
|
8924
9327
|
},
|
|
8925
|
-
[
|
|
9328
|
+
[patchCurrentStrokeStyle]
|
|
8926
9329
|
);
|
|
8927
9330
|
const commitTextEdit = react.useCallback(() => {
|
|
8928
9331
|
const id = editingTextIdRef.current;
|
|
@@ -9094,13 +9497,27 @@ var VectorViewport = react.forwardRef(
|
|
|
9094
9497
|
);
|
|
9095
9498
|
const handleOverlayDoubleClick = react.useCallback(
|
|
9096
9499
|
(e) => {
|
|
9097
|
-
if (!interactiveRef.current || !onItemsChangeRef.current) return;
|
|
9098
|
-
if (toolIdRef.current !== "select") return;
|
|
9099
|
-
e.preventDefault();
|
|
9100
9500
|
const cam = cameraRef.current;
|
|
9101
9501
|
if (!cam) return;
|
|
9102
9502
|
const { worldX, worldY } = screenToWorld(e.clientX, e.clientY);
|
|
9103
9503
|
const lineHitWorld = 10 / cam.zoom;
|
|
9504
|
+
const linkHit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
|
|
9505
|
+
lineHitWorld,
|
|
9506
|
+
ignoreLocked: false
|
|
9507
|
+
});
|
|
9508
|
+
const link = linkHit ? getLinkData(linkHit) : null;
|
|
9509
|
+
if (linkHit && link) {
|
|
9510
|
+
e.preventDefault();
|
|
9511
|
+
if (onActivateLinkRef.current) {
|
|
9512
|
+
onActivateLinkRef.current({ link, item: linkHit });
|
|
9513
|
+
} else if (typeof window !== "undefined" && typeof window.open === "function") {
|
|
9514
|
+
window.open(link.href, "_blank", "noopener,noreferrer");
|
|
9515
|
+
}
|
|
9516
|
+
return;
|
|
9517
|
+
}
|
|
9518
|
+
if (!interactiveRef.current || !onItemsChangeRef.current) return;
|
|
9519
|
+
if (toolIdRef.current !== "select") return;
|
|
9520
|
+
e.preventDefault();
|
|
9104
9521
|
const hit = hitTestWorldPoint(resolvedItemsRef.current, worldX, worldY, {
|
|
9105
9522
|
lineHitWorld,
|
|
9106
9523
|
ignoreLocked: true
|
|
@@ -10041,16 +10458,21 @@ var VectorViewport = react.forwardRef(
|
|
|
10041
10458
|
const id = createShapeId();
|
|
10042
10459
|
const { x: worldX, y: worldY } = st.startWorld;
|
|
10043
10460
|
if (st.tool === "text") {
|
|
10461
|
+
const fs = strokeStyleRef.current.textFontSize;
|
|
10462
|
+
const baseline = textBaselineYFor(fs);
|
|
10463
|
+
const lh = textLineHeightFor(fs);
|
|
10464
|
+
const minH = Math.max(26, baseline + Math.max(4, lh * 0.2));
|
|
10044
10465
|
const newItem = createTextItem(
|
|
10045
10466
|
id,
|
|
10046
10467
|
{
|
|
10047
10468
|
x: worldX - 4,
|
|
10048
|
-
y: worldY -
|
|
10469
|
+
y: worldY - baseline,
|
|
10049
10470
|
width: 160,
|
|
10050
|
-
height:
|
|
10471
|
+
height: minH
|
|
10051
10472
|
},
|
|
10052
10473
|
"",
|
|
10053
|
-
strokeStyleRef.current
|
|
10474
|
+
strokeStyleRef.current,
|
|
10475
|
+
fs
|
|
10054
10476
|
);
|
|
10055
10477
|
editingTextSnapshotRef.current = {
|
|
10056
10478
|
...newItem,
|
|
@@ -10201,12 +10623,63 @@ var VectorViewport = react.forwardRef(
|
|
|
10201
10623
|
return effectiveSelectedIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
|
|
10202
10624
|
}, [effectiveSelectedIds, resolvedItems]);
|
|
10203
10625
|
const activeToolInspectorStyle = react.useMemo(() => {
|
|
10204
|
-
if (toolId
|
|
10205
|
-
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
10626
|
+
if (toolId === "draw") {
|
|
10627
|
+
return {
|
|
10628
|
+
toolKind: "draw",
|
|
10629
|
+
label: "Caneta",
|
|
10630
|
+
...strokeStyleState
|
|
10631
|
+
};
|
|
10632
|
+
}
|
|
10633
|
+
if (toolId === "marker") {
|
|
10634
|
+
return {
|
|
10635
|
+
toolKind: "marker",
|
|
10636
|
+
label: "Marcador",
|
|
10637
|
+
...strokeStyleState
|
|
10638
|
+
};
|
|
10639
|
+
}
|
|
10640
|
+
if (toolId === "text") {
|
|
10641
|
+
return {
|
|
10642
|
+
toolKind: "text",
|
|
10643
|
+
label: "Texto",
|
|
10644
|
+
...strokeStyleState
|
|
10645
|
+
};
|
|
10646
|
+
}
|
|
10647
|
+
if (toolId === "rect") {
|
|
10648
|
+
return {
|
|
10649
|
+
toolKind: "rect",
|
|
10650
|
+
label: "Ret\xE2ngulo",
|
|
10651
|
+
...strokeStyleState
|
|
10652
|
+
};
|
|
10653
|
+
}
|
|
10654
|
+
if (toolId === "ellipse") {
|
|
10655
|
+
return {
|
|
10656
|
+
toolKind: "ellipse",
|
|
10657
|
+
label: "Elipse",
|
|
10658
|
+
...strokeStyleState
|
|
10659
|
+
};
|
|
10660
|
+
}
|
|
10661
|
+
if (toolId === "architectural-cloud") {
|
|
10662
|
+
return {
|
|
10663
|
+
toolKind: "architectural-cloud",
|
|
10664
|
+
label: "Nuvem",
|
|
10665
|
+
...strokeStyleState
|
|
10666
|
+
};
|
|
10667
|
+
}
|
|
10668
|
+
if (toolId === "line") {
|
|
10669
|
+
return {
|
|
10670
|
+
toolKind: "line",
|
|
10671
|
+
label: "Linha",
|
|
10672
|
+
...strokeStyleState
|
|
10673
|
+
};
|
|
10674
|
+
}
|
|
10675
|
+
if (toolId === "arrow") {
|
|
10676
|
+
return {
|
|
10677
|
+
toolKind: "arrow",
|
|
10678
|
+
label: "Seta",
|
|
10679
|
+
...strokeStyleState
|
|
10680
|
+
};
|
|
10681
|
+
}
|
|
10682
|
+
return void 0;
|
|
10210
10683
|
}, [strokeStyleState, toolId]);
|
|
10211
10684
|
const eraserPreviewItemsForOverlay = react.useMemo(() => {
|
|
10212
10685
|
return eraserPreviewIds.map((id) => resolvedItems.find((i) => i.id === id)).filter((i) => i != null);
|