@zuilib/text-editor 0.6.0 → 0.7.1
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/CHANGELOG.md +31 -0
- package/README.md +35 -0
- package/dist/index.js +142 -130
- package/dist/styles.css +136 -162
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog — @zuilib/text-editor
|
|
2
2
|
|
|
3
|
+
## 0.7.1
|
|
4
|
+
|
|
5
|
+
- Fix: free `text` shapes could not be typed into. The text tool's editor
|
|
6
|
+
opened on pointerdown and was blurred by the same click; it now opens on
|
|
7
|
+
pointerup like the card editors
|
|
8
|
+
- Fix: selecting a shape no longer shifts the drawing. The properties row
|
|
9
|
+
is always rendered (showing the defaults for the next shape when nothing
|
|
10
|
+
is selected) with a fixed minimum height, so click-to-edit lands where
|
|
11
|
+
the pointer is
|
|
12
|
+
- Fix: keyboard shortcuts keep working after committing text (the canvas
|
|
13
|
+
regains focus); `Home`/`End` work inside the inline text editor
|
|
14
|
+
|
|
15
|
+
## 0.7.0
|
|
16
|
+
|
|
17
|
+
**Themed drawing canvas.** The canvas chrome now follows the host theme
|
|
18
|
+
instead of shipping its own look.
|
|
19
|
+
|
|
20
|
+
- Tool row and contextual property row are a flat header in the editor's
|
|
21
|
+
own toolbar idiom (in flow, no floating panels over the drawing)
|
|
22
|
+
- Every color, radius, shadow and font of the canvas is a
|
|
23
|
+
`--zui-drawing-*` custom property defaulting to the core theme tokens
|
|
24
|
+
(`--primary`, `--foreground`, `--border`, `--popover`, `--radius`,
|
|
25
|
+
`--shadow-medium`, …), so light and dark mode come from the tokens;
|
|
26
|
+
override them on `.zui-drawing-canvas` or an ancestor. See README,
|
|
27
|
+
"Theming the canvas"
|
|
28
|
+
- Selection frame, handles, marquee, bind highlight and connector label
|
|
29
|
+
plates are drawn in `currentColor`/theme variables; the table selection
|
|
30
|
+
tint uses `--primary` too, so the editor shares one accent
|
|
31
|
+
- `--zui-drawing-dark-filter` exposes the dark-mode inversion of the
|
|
32
|
+
drawing area (`none` opts out)
|
|
33
|
+
|
|
3
34
|
## 0.6.0
|
|
4
35
|
|
|
5
36
|
**Text measure and `text` block width.** Additive: markdown, JSON, class
|
package/README.md
CHANGED
|
@@ -353,6 +353,41 @@ The full skeleton spec (attach sides, explicit positions, free texts) is
|
|
|
353
353
|
in [DRAWING_FORMAT.md](./DRAWING_FORMAT.md). Programmatic access:
|
|
354
354
|
`parseDrawingSkeleton`, `expandSkeleton`, `DRAWING_SKELETON_JSON_SCHEMA`.
|
|
355
355
|
|
|
356
|
+
### Theming the canvas
|
|
357
|
+
|
|
358
|
+
The canvas chrome (header, property row, popover, selection handles) is
|
|
359
|
+
styled entirely through `--zui-drawing-*` custom properties. They default
|
|
360
|
+
to the ZUI core tokens (`--primary`, `--foreground`, `--border`,
|
|
361
|
+
`--popover`, `--radius`, `--shadow-medium`, …), so the canvas follows the
|
|
362
|
+
host theme in light and dark mode. Override any of them on
|
|
363
|
+
`.zui-drawing-canvas` or an ancestor:
|
|
364
|
+
|
|
365
|
+
```css
|
|
366
|
+
.my-app .zui-drawing-canvas {
|
|
367
|
+
--zui-drawing-accent: #0f766e; /* selection, active tool, handles */
|
|
368
|
+
--zui-drawing-surface: #fbfbf7; /* drawing area (pre dark-mode filter) */
|
|
369
|
+
--zui-drawing-grid: rgba(0, 0, 0, 0.12);
|
|
370
|
+
--zui-drawing-grid-size: 16px;
|
|
371
|
+
--zui-drawing-font: 'Inter', sans-serif;
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
| Variable | Default | Used for |
|
|
376
|
+
|----------|---------|----------|
|
|
377
|
+
| `--zui-drawing-accent` | `var(--primary)` | Selection frame and handles, active tool, focus ring, bind highlight |
|
|
378
|
+
| `--zui-drawing-foreground` | `var(--foreground)` | Header text and icons |
|
|
379
|
+
| `--zui-drawing-muted-foreground` | `var(--muted-foreground)` | Swatch labels, empty-state hint |
|
|
380
|
+
| `--zui-drawing-border` | `var(--border)` | Block border, header divider, popover border |
|
|
381
|
+
| `--zui-drawing-chrome` | `var(--background)` | Header and property row background |
|
|
382
|
+
| `--zui-drawing-popover` / `-foreground` | `var(--popover)` / `var(--popover-foreground)` | "More shapes" menu |
|
|
383
|
+
| `--zui-drawing-danger` / `--zui-drawing-success` | `var(--destructive)` / `var(--success)` | Delete button, "copied" state |
|
|
384
|
+
| `--zui-drawing-radius` / `--zui-drawing-control-radius` | `var(--radius-lg)` / `var(--radius)` | Block corners / buttons and menus |
|
|
385
|
+
| `--zui-drawing-shadow` | `var(--shadow-medium)` | Popover |
|
|
386
|
+
| `--zui-drawing-font` | system sans | Shape text and the inline editor |
|
|
387
|
+
| `--zui-drawing-surface` / `--zui-drawing-grid` / `--zui-drawing-grid-size` | white / 9% black / `20px` | Drawing area and its dot grid, before the dark filter |
|
|
388
|
+
| `--zui-drawing-dark-filter` | `invert(93%) hue-rotate(180deg)` | Applied to the drawing area under `.dark`; set to `none` to opt out |
|
|
389
|
+
| `--zui-drawing-hover` / `--zui-drawing-active` | `currentColor` 8% / 12% | Button hover and pressed backgrounds |
|
|
390
|
+
|
|
356
391
|
## Outline & section folding
|
|
357
392
|
|
|
358
393
|
`outline` docks a collapsible table-of-contents sidebar: live heading list
|
package/dist/index.js
CHANGED
|
@@ -1652,7 +1652,6 @@ function PropertyBar({
|
|
|
1652
1652
|
|
|
1653
1653
|
// src/drawing/canvas/SelectionOverlay.tsx
|
|
1654
1654
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1655
|
-
var SELECTION_COLOR = "#6965db";
|
|
1656
1655
|
var HANDLE = 7;
|
|
1657
1656
|
function Handle({
|
|
1658
1657
|
x,
|
|
@@ -1664,8 +1663,6 @@ function Handle({
|
|
|
1664
1663
|
}) {
|
|
1665
1664
|
const props = {
|
|
1666
1665
|
className: "zui-drawing-handle",
|
|
1667
|
-
fill: "#fff",
|
|
1668
|
-
stroke: SELECTION_COLOR,
|
|
1669
1666
|
strokeWidth: 1.5,
|
|
1670
1667
|
style: { cursor },
|
|
1671
1668
|
onPointerDown
|
|
@@ -1694,12 +1691,10 @@ function SelectionOverlay({
|
|
|
1694
1691
|
ghosts.map(({ index, x, y }) => /* @__PURE__ */ jsx3(
|
|
1695
1692
|
"circle",
|
|
1696
1693
|
{
|
|
1697
|
-
className: "zui-drawing-ghost",
|
|
1694
|
+
className: "zui-drawing-handle zui-drawing-ghost",
|
|
1698
1695
|
cx: x,
|
|
1699
1696
|
cy: y,
|
|
1700
1697
|
r: 4.5,
|
|
1701
|
-
fill: "#fff",
|
|
1702
|
-
stroke: SELECTION_COLOR,
|
|
1703
1698
|
strokeWidth: 1.5,
|
|
1704
1699
|
strokeDasharray: "2 2",
|
|
1705
1700
|
style: { cursor: "copy" },
|
|
@@ -1795,7 +1790,7 @@ function SelectionFrame({ rect, pad }) {
|
|
|
1795
1790
|
height: rect.h + pad * 2,
|
|
1796
1791
|
rx: 6,
|
|
1797
1792
|
fill: "none",
|
|
1798
|
-
stroke:
|
|
1793
|
+
stroke: "currentColor",
|
|
1799
1794
|
strokeWidth: 1,
|
|
1800
1795
|
strokeDasharray: "5 4",
|
|
1801
1796
|
pointerEvents: "none"
|
|
@@ -1826,7 +1821,7 @@ function GroupSelectionOverlay({
|
|
|
1826
1821
|
height: r.h + 6,
|
|
1827
1822
|
rx: 4,
|
|
1828
1823
|
fill: "none",
|
|
1829
|
-
stroke:
|
|
1824
|
+
stroke: "currentColor",
|
|
1830
1825
|
strokeWidth: 1,
|
|
1831
1826
|
opacity: 0.45
|
|
1832
1827
|
},
|
|
@@ -1839,14 +1834,15 @@ function MarqueeOverlay({ rect }) {
|
|
|
1839
1834
|
return /* @__PURE__ */ jsx3(
|
|
1840
1835
|
"rect",
|
|
1841
1836
|
{
|
|
1837
|
+
className: "zui-drawing-selection",
|
|
1842
1838
|
x: rect.x,
|
|
1843
1839
|
y: rect.y,
|
|
1844
1840
|
width: rect.w,
|
|
1845
1841
|
height: rect.h,
|
|
1846
1842
|
rx: 3,
|
|
1847
|
-
fill:
|
|
1843
|
+
fill: "currentColor",
|
|
1848
1844
|
fillOpacity: 0.08,
|
|
1849
|
-
stroke:
|
|
1845
|
+
stroke: "currentColor",
|
|
1850
1846
|
strokeWidth: 1,
|
|
1851
1847
|
pointerEvents: "none"
|
|
1852
1848
|
}
|
|
@@ -2000,7 +1996,6 @@ function BoxGeometry({
|
|
|
2000
1996
|
// src/drawing/canvas/ShapeView.tsx
|
|
2001
1997
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
2002
1998
|
import { createElement } from "react";
|
|
2003
|
-
var FONT_FAMILY = "ui-sans-serif, system-ui, sans-serif";
|
|
2004
1999
|
var CONNECTOR_FONT_SIZE = 12;
|
|
2005
2000
|
var CONNECTOR_LABEL_MAX_WIDTH = 160;
|
|
2006
2001
|
function slotLayout(shape) {
|
|
@@ -2036,7 +2031,6 @@ function BoxTexts({
|
|
|
2036
2031
|
const mainStart = area.y + area.h / 2 - (mainLines.length - 1) * mainLineH / 2 + FONT_SIZE * 0.35;
|
|
2037
2032
|
const common = {
|
|
2038
2033
|
fill: shape.stroke,
|
|
2039
|
-
fontFamily: FONT_FAMILY,
|
|
2040
2034
|
textAnchor: "middle"
|
|
2041
2035
|
};
|
|
2042
2036
|
return /* @__PURE__ */ jsxs5("g", { children: [
|
|
@@ -2134,7 +2128,7 @@ function ConnectorLabel({
|
|
|
2134
2128
|
width: size.w + 10,
|
|
2135
2129
|
height: size.h + 6,
|
|
2136
2130
|
rx: 5,
|
|
2137
|
-
|
|
2131
|
+
className: "zui-drawing-label-plate",
|
|
2138
2132
|
opacity: 0.94
|
|
2139
2133
|
}
|
|
2140
2134
|
),
|
|
@@ -2145,7 +2139,6 @@ function ConnectorLabel({
|
|
|
2145
2139
|
y: startY + i * lineH,
|
|
2146
2140
|
fill: shape.stroke,
|
|
2147
2141
|
fontSize: CONNECTOR_FONT_SIZE,
|
|
2148
|
-
fontFamily: FONT_FAMILY,
|
|
2149
2142
|
textAnchor: "middle",
|
|
2150
2143
|
style: textStyle,
|
|
2151
2144
|
children: line
|
|
@@ -2190,7 +2183,6 @@ function ShapeView({
|
|
|
2190
2183
|
y: shape.y + FONT_SIZE,
|
|
2191
2184
|
fill: shape.stroke,
|
|
2192
2185
|
fontSize: FONT_SIZE,
|
|
2193
|
-
fontFamily: FONT_FAMILY,
|
|
2194
2186
|
style: { userSelect: "none", whiteSpace: "pre" },
|
|
2195
2187
|
children: (shape.text ?? "").split("\n").map((line, i) => /* @__PURE__ */ jsx5("tspan", { x: shape.x, dy: i === 0 ? 0 : FONT_SIZE * LINE_HEIGHT, children: line }, i))
|
|
2196
2188
|
}
|
|
@@ -2242,6 +2234,14 @@ function TextEditOverlay({
|
|
|
2242
2234
|
el.select();
|
|
2243
2235
|
const onKeyDown = (e) => {
|
|
2244
2236
|
e.stopPropagation();
|
|
2237
|
+
if ((e.key === "Home" || e.key === "End") && !e.shiftKey && !e.metaKey && !e.ctrlKey) {
|
|
2238
|
+
e.preventDefault();
|
|
2239
|
+
const text = el.value;
|
|
2240
|
+
const caret = e.key === "Home" ? el.selectionStart : el.selectionEnd;
|
|
2241
|
+
const pos = e.key === "Home" ? text.lastIndexOf("\n", caret - 1) + 1 : (text.indexOf("\n", caret) + 1 || text.length + 1) - 1;
|
|
2242
|
+
el.setSelectionRange(pos, pos);
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
2245
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
2246
2246
|
e.preventDefault();
|
|
2247
2247
|
onCommit(shape.id, field, valueRef.current);
|
|
@@ -2262,7 +2262,7 @@ function TextEditOverlay({
|
|
|
2262
2262
|
}, []);
|
|
2263
2263
|
const style = {
|
|
2264
2264
|
color: shape.stroke,
|
|
2265
|
-
fontFamily:
|
|
2265
|
+
fontFamily: "var(--zui-drawing-font)",
|
|
2266
2266
|
lineHeight: LINE_HEIGHT
|
|
2267
2267
|
};
|
|
2268
2268
|
if (shape.type === "text") {
|
|
@@ -2273,7 +2273,9 @@ function TextEditOverlay({
|
|
|
2273
2273
|
fontSize: FONT_SIZE,
|
|
2274
2274
|
width: Math.max(80, size.w + 20),
|
|
2275
2275
|
height: size.h + 2,
|
|
2276
|
-
|
|
2276
|
+
// `pre` would keep the lines, but Chrome ignores Home/End in a
|
|
2277
|
+
// `white-space: pre` textarea; the width tracks the content anyway
|
|
2278
|
+
whiteSpace: "pre-wrap"
|
|
2277
2279
|
});
|
|
2278
2280
|
} else if (isConnectorType(shape.type)) {
|
|
2279
2281
|
const lines = wrapText(value, CONNECTOR_LABEL_MAX_WIDTH, CONNECTOR_FONT_SIZE);
|
|
@@ -2545,6 +2547,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
2545
2547
|
const rootRef = useRef4(null);
|
|
2546
2548
|
const svgRef = useRef4(null);
|
|
2547
2549
|
const dragRef = useRef4(null);
|
|
2550
|
+
const pendingTextEditRef = useRef4(null);
|
|
2548
2551
|
const pendingPointRef = useRef4(null);
|
|
2549
2552
|
const frameRef = useRef4(null);
|
|
2550
2553
|
const lastCommittedRef = useRef4(serializeDrawingData(data));
|
|
@@ -2671,7 +2674,8 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
2671
2674
|
}
|
|
2672
2675
|
]);
|
|
2673
2676
|
setTool("select");
|
|
2674
|
-
|
|
2677
|
+
setSelectedIds(/* @__PURE__ */ new Set([id2]));
|
|
2678
|
+
pendingTextEditRef.current = id2;
|
|
2675
2679
|
return;
|
|
2676
2680
|
}
|
|
2677
2681
|
const id = createShapeId();
|
|
@@ -2775,6 +2779,12 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
2775
2779
|
}
|
|
2776
2780
|
pendingPointRef.current = null;
|
|
2777
2781
|
setHoverBoxId(null);
|
|
2782
|
+
if (pendingTextEditRef.current) {
|
|
2783
|
+
const id = pendingTextEditRef.current;
|
|
2784
|
+
pendingTextEditRef.current = null;
|
|
2785
|
+
startTextEditing(id, "text");
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2778
2788
|
if (!drag) return;
|
|
2779
2789
|
const point = getPoint(e);
|
|
2780
2790
|
if (drag.mode === "marquee") {
|
|
@@ -2958,6 +2968,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
2958
2968
|
const commitText = useCallback(
|
|
2959
2969
|
(id, field, value) => {
|
|
2960
2970
|
setEditingText(null);
|
|
2971
|
+
rootRef.current?.focus({ preventScroll: true });
|
|
2961
2972
|
updateShapes(
|
|
2962
2973
|
(prev) => prev.flatMap((s) => {
|
|
2963
2974
|
if (s.id !== id) return [s];
|
|
@@ -2982,7 +2993,6 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
2982
2993
|
}, []);
|
|
2983
2994
|
const editingShape = shapes.find((s) => s.id === editingText?.id) ?? null;
|
|
2984
2995
|
const hoverBox = hoverBoxId ? shapes.find((s) => s.id === hoverBoxId) : null;
|
|
2985
|
-
const showProps = isEditable && !editingText && (selection.length > 0 || tool !== "select");
|
|
2986
2996
|
const propStroke = single?.stroke ?? selection[0]?.stroke ?? stroke;
|
|
2987
2997
|
const propFill = single?.fill ?? selection.find((s) => isBoxType(s.type))?.fill ?? fill;
|
|
2988
2998
|
const canvasCursor = tool === "select" ? "default" : tool === "text" ? "text" : "crosshair";
|
|
@@ -3012,7 +3022,7 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
3012
3022
|
onCopyMermaid: copyMermaid
|
|
3013
3023
|
}
|
|
3014
3024
|
),
|
|
3015
|
-
/* @__PURE__ */ jsx9("div", { className:
|
|
3025
|
+
/* @__PURE__ */ jsx9("div", { className: "zui-drawing-props-host", children: /* @__PURE__ */ jsx9(
|
|
3016
3026
|
PropertyBar,
|
|
3017
3027
|
{
|
|
3018
3028
|
selection,
|
|
@@ -3027,118 +3037,120 @@ function DrawingCanvas({ nodeKey, data }) {
|
|
|
3027
3037
|
}
|
|
3028
3038
|
) })
|
|
3029
3039
|
] }),
|
|
3030
|
-
/* @__PURE__ */ jsxs8(
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
},
|
|
3060
|
-
children: [
|
|
3061
|
-
shapes.map((shape) => /* @__PURE__ */ jsxs8(
|
|
3062
|
-
"g",
|
|
3063
|
-
{
|
|
3064
|
-
"data-shape-id": shape.id,
|
|
3065
|
-
className: `zui-drawing-shape ${selectedIds.has(shape.id) ? "is-selected" : ""}`,
|
|
3066
|
-
style: { cursor: isEditable && tool === "select" ? "move" : void 0 },
|
|
3067
|
-
onPointerDown: (e) => handleShapePointerDown(e, shape),
|
|
3068
|
-
children: [
|
|
3069
|
-
/* @__PURE__ */ jsx9(HitArea, { shape, points: paths.get(shape.id) }),
|
|
3070
|
-
shape.id === editingText?.id && shape.type === "text" ? null : /* @__PURE__ */ jsx9(
|
|
3071
|
-
ShapeView,
|
|
3072
|
-
{
|
|
3073
|
-
shape,
|
|
3074
|
-
points: paths.get(shape.id),
|
|
3075
|
-
hideField: shape.id === editingText?.id ? editingText.field : null,
|
|
3076
|
-
showHints: isEditable && tool === "select" && single?.id === shape.id && shape.id !== editingText?.id
|
|
3077
|
-
}
|
|
3078
|
-
)
|
|
3079
|
-
]
|
|
3080
|
-
},
|
|
3081
|
-
shape.id
|
|
3082
|
-
)),
|
|
3083
|
-
hoverBox && /* @__PURE__ */ jsx9(
|
|
3084
|
-
"polygon",
|
|
3085
|
-
{
|
|
3086
|
-
className: "zui-drawing-bind-target",
|
|
3087
|
-
points: boxOutline(hoverBox).map((p) => `${p.x},${p.y}`).join(" "),
|
|
3088
|
-
fill: SELECTION_COLOR,
|
|
3089
|
-
fillOpacity: 0.06,
|
|
3090
|
-
stroke: SELECTION_COLOR,
|
|
3091
|
-
strokeWidth: 2,
|
|
3092
|
-
strokeLinejoin: "round",
|
|
3093
|
-
pointerEvents: "none"
|
|
3094
|
-
}
|
|
3095
|
-
),
|
|
3096
|
-
single && isEditable && !editingText && /* @__PURE__ */ jsx9(
|
|
3097
|
-
SelectionOverlay,
|
|
3098
|
-
{
|
|
3099
|
-
shape: single,
|
|
3100
|
-
points: paths.get(single.id) ?? [],
|
|
3101
|
-
onHandlePointerDown: handleHandlePointerDown,
|
|
3102
|
-
onWaypointAdd: addWaypoint,
|
|
3103
|
-
onWaypointRemove: removeWaypoint
|
|
3040
|
+
/* @__PURE__ */ jsxs8("div", { className: "zui-drawing-stage", children: [
|
|
3041
|
+
/* @__PURE__ */ jsxs8(
|
|
3042
|
+
"svg",
|
|
3043
|
+
{
|
|
3044
|
+
ref: svgRef,
|
|
3045
|
+
className: "zui-drawing-surface",
|
|
3046
|
+
style: {
|
|
3047
|
+
height: logicalWidth ? void 0 : canvasHeight,
|
|
3048
|
+
width: logicalWidth ?? void 0,
|
|
3049
|
+
maxWidth: "100%",
|
|
3050
|
+
aspectRatio: logicalWidth ? `${logicalWidth} / ${canvasHeight}` : void 0,
|
|
3051
|
+
cursor: canvasCursor
|
|
3052
|
+
},
|
|
3053
|
+
viewBox: logicalWidth ? `0 0 ${logicalWidth} ${canvasHeight}` : void 0,
|
|
3054
|
+
preserveAspectRatio: "xMinYMin meet",
|
|
3055
|
+
onPointerDown: handleBackgroundPointerDown,
|
|
3056
|
+
onPointerMove: handlePointerMove,
|
|
3057
|
+
onPointerUp: handlePointerUp,
|
|
3058
|
+
onPointerCancel: handlePointerUp,
|
|
3059
|
+
onDoubleClick: (e) => {
|
|
3060
|
+
if (!isEditable) return;
|
|
3061
|
+
const target = e.target.closest("[data-shape-id]");
|
|
3062
|
+
const id = target?.getAttribute("data-shape-id");
|
|
3063
|
+
const shape = id ? shapes.find((s) => s.id === id) : null;
|
|
3064
|
+
if (!shape) return;
|
|
3065
|
+
if (shape.type === "text" || isConnectorType(shape.type)) {
|
|
3066
|
+
startTextEditing(shape.id, "text");
|
|
3067
|
+
} else {
|
|
3068
|
+
startTextEditing(shape.id, slotAt(shape, getPoint(e).y));
|
|
3104
3069
|
}
|
|
3105
|
-
),
|
|
3106
|
-
selection.length > 1 && isEditable && /* @__PURE__ */ jsx9(GroupSelectionOverlay, { shapes: selection, paths }),
|
|
3107
|
-
marquee && /* @__PURE__ */ jsx9(MarqueeOverlay, { rect: marqueeRect(marquee.origin, marquee.current) })
|
|
3108
|
-
]
|
|
3109
|
-
}
|
|
3110
|
-
),
|
|
3111
|
-
isEditable && shapes.length === 0 && /* @__PURE__ */ jsx9("div", { className: "zui-drawing-empty", "aria-hidden": "true", children: "Pick a shape above, then click or drag on the canvas" }),
|
|
3112
|
-
editingShape && editingText && /* @__PURE__ */ jsx9(
|
|
3113
|
-
"div",
|
|
3114
|
-
{
|
|
3115
|
-
className: "zui-drawing-overlay",
|
|
3116
|
-
style: {
|
|
3117
|
-
transform: `scale(${scale})`,
|
|
3118
|
-
width: logicalWidth ?? void 0,
|
|
3119
|
-
height: logicalWidth ? canvasHeight : void 0
|
|
3120
|
-
},
|
|
3121
|
-
children: /* @__PURE__ */ jsx9(
|
|
3122
|
-
TextEditOverlay,
|
|
3123
|
-
{
|
|
3124
|
-
shape: editingShape,
|
|
3125
|
-
field: editingText.field,
|
|
3126
|
-
points: paths.get(editingShape.id),
|
|
3127
|
-
onCommit: commitText
|
|
3128
3070
|
},
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3071
|
+
children: [
|
|
3072
|
+
shapes.map((shape) => /* @__PURE__ */ jsxs8(
|
|
3073
|
+
"g",
|
|
3074
|
+
{
|
|
3075
|
+
"data-shape-id": shape.id,
|
|
3076
|
+
className: `zui-drawing-shape ${selectedIds.has(shape.id) ? "is-selected" : ""}`,
|
|
3077
|
+
style: { cursor: isEditable && tool === "select" ? "move" : void 0 },
|
|
3078
|
+
onPointerDown: (e) => handleShapePointerDown(e, shape),
|
|
3079
|
+
children: [
|
|
3080
|
+
/* @__PURE__ */ jsx9(HitArea, { shape, points: paths.get(shape.id) }),
|
|
3081
|
+
shape.id === editingText?.id && shape.type === "text" ? null : /* @__PURE__ */ jsx9(
|
|
3082
|
+
ShapeView,
|
|
3083
|
+
{
|
|
3084
|
+
shape,
|
|
3085
|
+
points: paths.get(shape.id),
|
|
3086
|
+
hideField: shape.id === editingText?.id ? editingText.field : null,
|
|
3087
|
+
showHints: isEditable && tool === "select" && single?.id === shape.id && shape.id !== editingText?.id
|
|
3088
|
+
}
|
|
3089
|
+
)
|
|
3090
|
+
]
|
|
3091
|
+
},
|
|
3092
|
+
shape.id
|
|
3093
|
+
)),
|
|
3094
|
+
hoverBox && /* @__PURE__ */ jsx9(
|
|
3095
|
+
"polygon",
|
|
3096
|
+
{
|
|
3097
|
+
className: "zui-drawing-selection zui-drawing-bind-target",
|
|
3098
|
+
points: boxOutline(hoverBox).map((p) => `${p.x},${p.y}`).join(" "),
|
|
3099
|
+
fill: "currentColor",
|
|
3100
|
+
fillOpacity: 0.06,
|
|
3101
|
+
stroke: "currentColor",
|
|
3102
|
+
strokeWidth: 2,
|
|
3103
|
+
strokeLinejoin: "round",
|
|
3104
|
+
pointerEvents: "none"
|
|
3105
|
+
}
|
|
3106
|
+
),
|
|
3107
|
+
single && isEditable && !editingText && /* @__PURE__ */ jsx9(
|
|
3108
|
+
SelectionOverlay,
|
|
3109
|
+
{
|
|
3110
|
+
shape: single,
|
|
3111
|
+
points: paths.get(single.id) ?? [],
|
|
3112
|
+
onHandlePointerDown: handleHandlePointerDown,
|
|
3113
|
+
onWaypointAdd: addWaypoint,
|
|
3114
|
+
onWaypointRemove: removeWaypoint
|
|
3115
|
+
}
|
|
3116
|
+
),
|
|
3117
|
+
selection.length > 1 && isEditable && /* @__PURE__ */ jsx9(GroupSelectionOverlay, { shapes: selection, paths }),
|
|
3118
|
+
marquee && /* @__PURE__ */ jsx9(MarqueeOverlay, { rect: marqueeRect(marquee.origin, marquee.current) })
|
|
3119
|
+
]
|
|
3120
|
+
}
|
|
3121
|
+
),
|
|
3122
|
+
isEditable && shapes.length === 0 && /* @__PURE__ */ jsx9("div", { className: "zui-drawing-empty", "aria-hidden": "true", children: "Pick a shape above, then click or drag on the canvas" }),
|
|
3123
|
+
editingShape && editingText && /* @__PURE__ */ jsx9(
|
|
3124
|
+
"div",
|
|
3125
|
+
{
|
|
3126
|
+
className: "zui-drawing-overlay",
|
|
3127
|
+
style: {
|
|
3128
|
+
transform: `scale(${scale})`,
|
|
3129
|
+
width: logicalWidth ?? void 0,
|
|
3130
|
+
height: logicalWidth ? canvasHeight : void 0
|
|
3131
|
+
},
|
|
3132
|
+
children: /* @__PURE__ */ jsx9(
|
|
3133
|
+
TextEditOverlay,
|
|
3134
|
+
{
|
|
3135
|
+
shape: editingShape,
|
|
3136
|
+
field: editingText.field,
|
|
3137
|
+
points: paths.get(editingShape.id),
|
|
3138
|
+
onCommit: commitText
|
|
3139
|
+
},
|
|
3140
|
+
`${editingShape.id}:${editingText.field}`
|
|
3141
|
+
)
|
|
3142
|
+
}
|
|
3143
|
+
),
|
|
3144
|
+
isEditable && /* @__PURE__ */ jsx9(
|
|
3145
|
+
HeightHandle,
|
|
3146
|
+
{
|
|
3147
|
+
height: canvasHeight,
|
|
3148
|
+
scale,
|
|
3149
|
+
onChange: setCanvasHeight,
|
|
3150
|
+
onCommit: (h) => commit(shapesRef.current, { height: h })
|
|
3151
|
+
}
|
|
3152
|
+
)
|
|
3153
|
+
] })
|
|
3142
3154
|
]
|
|
3143
3155
|
}
|
|
3144
3156
|
);
|
package/dist/styles.css
CHANGED
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
.zui-table-selected,
|
|
363
363
|
.zui-table-cell.zui-table-selected {
|
|
364
364
|
outline: none;
|
|
365
|
-
background: color-mix(in srgb, #6965db 14%, transparent);
|
|
365
|
+
background: color-mix(in srgb, var(--primary, #6965db) 14%, transparent);
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
/*
|
|
@@ -391,24 +391,57 @@
|
|
|
391
391
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
/*
|
|
394
|
+
/*
|
|
395
|
+
* Drawing canvas.
|
|
396
|
+
*
|
|
397
|
+
* Every color, radius and shadow of the canvas chrome comes from a
|
|
398
|
+
* `--zui-drawing-*` custom property. They default to the ZUI core theme
|
|
399
|
+
* tokens (`--primary`, `--foreground`, `--border`, …) so the canvas follows
|
|
400
|
+
* the host theme, light and dark, and consumers override them on
|
|
401
|
+
* `.zui-drawing-canvas` (or any ancestor) to restyle without touching
|
|
402
|
+
* selectors. `--zui-drawing-surface` and `--zui-drawing-grid` are the
|
|
403
|
+
* pre-filter colors of the drawing area: in dark mode the whole area is
|
|
404
|
+
* passed through `--zui-drawing-dark-filter` so stored shape colors stay
|
|
405
|
+
* theme-agnostic (set the filter to `none` to opt out).
|
|
406
|
+
*/
|
|
395
407
|
.zui-drawing {
|
|
396
408
|
margin-block: 1rem;
|
|
397
409
|
}
|
|
398
410
|
|
|
399
411
|
.zui-drawing-canvas {
|
|
400
|
-
--zui-drawing-accent: #
|
|
412
|
+
--zui-drawing-accent: var(--primary, #3b82f6);
|
|
413
|
+
--zui-drawing-foreground: var(--foreground, #0a0a0a);
|
|
414
|
+
--zui-drawing-muted-foreground: var(--muted-foreground, #71717a);
|
|
415
|
+
--zui-drawing-border: var(--border, #e4e4e7);
|
|
416
|
+
--zui-drawing-chrome: var(--background, #ffffff);
|
|
417
|
+
--zui-drawing-popover: var(--popover, #ffffff);
|
|
418
|
+
--zui-drawing-popover-foreground: var(--popover-foreground, var(--zui-drawing-foreground));
|
|
419
|
+
--zui-drawing-danger: var(--destructive, #ef4444);
|
|
420
|
+
--zui-drawing-success: var(--success, #22c55e);
|
|
421
|
+
--zui-drawing-radius: var(--radius-lg, 0.75rem);
|
|
422
|
+
--zui-drawing-control-radius: var(--radius, 0.375rem);
|
|
423
|
+
--zui-drawing-shadow: var(--shadow-medium, 0 4px 6px -1px rgba(0, 0, 0, 0.1));
|
|
424
|
+
--zui-drawing-font: ui-sans-serif, system-ui, sans-serif;
|
|
425
|
+
--zui-drawing-surface: #ffffff;
|
|
426
|
+
--zui-drawing-grid: rgba(0, 0, 0, 0.09);
|
|
427
|
+
--zui-drawing-grid-size: 20px;
|
|
428
|
+
--zui-drawing-dark-filter: invert(93%) hue-rotate(180deg);
|
|
429
|
+
--zui-drawing-hover: color-mix(in srgb, currentColor 8%, transparent);
|
|
430
|
+
--zui-drawing-active: color-mix(in srgb, currentColor 12%, transparent);
|
|
431
|
+
|
|
401
432
|
position: relative;
|
|
402
|
-
border: 1px solid
|
|
403
|
-
border-radius:
|
|
404
|
-
background:
|
|
433
|
+
border: 1px solid var(--zui-drawing-border);
|
|
434
|
+
border-radius: var(--zui-drawing-radius);
|
|
435
|
+
background: var(--zui-drawing-chrome);
|
|
436
|
+
color: var(--zui-drawing-foreground);
|
|
437
|
+
font-family: var(--zui-drawing-font);
|
|
405
438
|
outline: none;
|
|
406
439
|
overflow: hidden;
|
|
407
|
-
transition: box-shadow 0.
|
|
440
|
+
transition: box-shadow 0.15s ease, border-color 0.15s ease;
|
|
408
441
|
}
|
|
409
442
|
|
|
410
443
|
.zui-drawing-canvas.is-editable:focus-within {
|
|
411
|
-
border-color: color-mix(in srgb, var(--zui-drawing-accent)
|
|
444
|
+
border-color: color-mix(in srgb, var(--zui-drawing-accent) 50%, var(--zui-drawing-border));
|
|
412
445
|
box-shadow: 0 0 0 3px color-mix(in srgb, var(--zui-drawing-accent) 12%, transparent);
|
|
413
446
|
}
|
|
414
447
|
|
|
@@ -417,25 +450,30 @@
|
|
|
417
450
|
max-width: 100%;
|
|
418
451
|
}
|
|
419
452
|
|
|
453
|
+
.zui-drawing-stage {
|
|
454
|
+
position: relative;
|
|
455
|
+
}
|
|
456
|
+
|
|
420
457
|
.zui-drawing-surface {
|
|
421
458
|
display: block;
|
|
422
459
|
width: 100%;
|
|
423
460
|
max-width: 100%;
|
|
424
461
|
touch-action: none;
|
|
425
|
-
|
|
426
|
-
background-
|
|
427
|
-
background-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
.zui-drawing-shape {
|
|
431
|
-
transition: filter 0.12s ease;
|
|
462
|
+
font-family: var(--zui-drawing-font);
|
|
463
|
+
background-color: var(--zui-drawing-surface);
|
|
464
|
+
background-image: radial-gradient(circle, var(--zui-drawing-grid) 1px, transparent 1px);
|
|
465
|
+
background-size: var(--zui-drawing-grid-size) var(--zui-drawing-grid-size);
|
|
432
466
|
}
|
|
433
467
|
|
|
434
|
-
|
|
435
|
-
|
|
468
|
+
/* Selection chrome inside the SVG: everything is drawn in `currentColor` */
|
|
469
|
+
.zui-drawing-selection,
|
|
470
|
+
.zui-drawing-bind-target {
|
|
471
|
+
color: var(--zui-drawing-accent);
|
|
436
472
|
}
|
|
437
473
|
|
|
438
474
|
.zui-drawing-handle {
|
|
475
|
+
fill: var(--zui-drawing-surface);
|
|
476
|
+
stroke: currentColor;
|
|
439
477
|
filter: drop-shadow(0 1px 1.5px rgba(0, 0, 0, 0.25));
|
|
440
478
|
}
|
|
441
479
|
|
|
@@ -448,6 +486,18 @@
|
|
|
448
486
|
opacity: 1;
|
|
449
487
|
}
|
|
450
488
|
|
|
489
|
+
.zui-drawing-label-plate {
|
|
490
|
+
fill: var(--zui-drawing-surface);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.zui-drawing-shape {
|
|
494
|
+
transition: filter 0.12s ease;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
.zui-drawing-canvas.is-editable .zui-drawing-shape:hover:not(.is-selected) {
|
|
498
|
+
filter: drop-shadow(0 0 2.5px color-mix(in srgb, var(--zui-drawing-accent) 55%, transparent));
|
|
499
|
+
}
|
|
500
|
+
|
|
451
501
|
.zui-drawing-bind-target {
|
|
452
502
|
animation: zui-drawing-pulse 0.9s ease-in-out infinite alternate;
|
|
453
503
|
}
|
|
@@ -464,43 +514,29 @@
|
|
|
464
514
|
@keyframes zui-drawing-pop {
|
|
465
515
|
from {
|
|
466
516
|
opacity: 0;
|
|
467
|
-
transform: translateY(
|
|
517
|
+
transform: translateY(-3px);
|
|
468
518
|
}
|
|
469
519
|
to {
|
|
470
520
|
opacity: 1;
|
|
471
|
-
transform: translateY(0)
|
|
521
|
+
transform: translateY(0);
|
|
472
522
|
}
|
|
473
523
|
}
|
|
474
524
|
|
|
475
|
-
/*
|
|
525
|
+
/* Header: tools row (+ contextual properties row), same idiom as the editor toolbar */
|
|
476
526
|
.zui-drawing-toolbar {
|
|
477
|
-
position: absolute;
|
|
478
|
-
top: 0.625rem;
|
|
479
|
-
left: 0.625rem;
|
|
480
|
-
right: 0.625rem;
|
|
481
|
-
z-index: 5;
|
|
482
527
|
display: flex;
|
|
483
528
|
flex-wrap: wrap;
|
|
484
|
-
align-items:
|
|
485
|
-
gap: 0.5rem;
|
|
486
|
-
|
|
529
|
+
align-items: center;
|
|
530
|
+
gap: 0.125rem 0.5rem;
|
|
531
|
+
padding: 0.375rem 0.625rem;
|
|
532
|
+
border-bottom: 1px solid var(--zui-drawing-border);
|
|
533
|
+
background: var(--zui-drawing-chrome);
|
|
487
534
|
}
|
|
488
535
|
|
|
489
|
-
.zui-drawing-toolbar-group
|
|
490
|
-
.zui-drawing-props {
|
|
536
|
+
.zui-drawing-toolbar-group {
|
|
491
537
|
display: flex;
|
|
492
538
|
align-items: center;
|
|
493
539
|
gap: 0.125rem;
|
|
494
|
-
padding: 0.25rem;
|
|
495
|
-
border: 1px solid rgba(0, 0, 0, 0.07);
|
|
496
|
-
border-radius: 0.625rem;
|
|
497
|
-
background: rgba(255, 255, 255, 0.88);
|
|
498
|
-
backdrop-filter: blur(10px);
|
|
499
|
-
-webkit-backdrop-filter: blur(10px);
|
|
500
|
-
box-shadow:
|
|
501
|
-
0 1px 2px rgba(0, 0, 0, 0.05),
|
|
502
|
-
0 6px 20px -8px rgba(0, 0, 0, 0.18);
|
|
503
|
-
pointer-events: auto;
|
|
504
540
|
}
|
|
505
541
|
|
|
506
542
|
.zui-drawing-toolbar-group-end {
|
|
@@ -510,8 +546,8 @@
|
|
|
510
546
|
.zui-drawing-toolbar-divider {
|
|
511
547
|
width: 1px;
|
|
512
548
|
height: 1.125rem;
|
|
513
|
-
margin: 0 0.
|
|
514
|
-
background:
|
|
549
|
+
margin: 0 0.375rem;
|
|
550
|
+
background: color-mix(in srgb, currentColor 15%, transparent);
|
|
515
551
|
}
|
|
516
552
|
|
|
517
553
|
.zui-drawing-tool {
|
|
@@ -519,57 +555,56 @@
|
|
|
519
555
|
display: inline-flex;
|
|
520
556
|
align-items: center;
|
|
521
557
|
justify-content: center;
|
|
522
|
-
width: 1.
|
|
523
|
-
height: 1.
|
|
558
|
+
width: 1.75rem;
|
|
559
|
+
height: 1.75rem;
|
|
524
560
|
border: none;
|
|
525
|
-
border-radius:
|
|
561
|
+
border-radius: var(--zui-drawing-control-radius);
|
|
526
562
|
background: transparent;
|
|
527
|
-
color:
|
|
563
|
+
color: inherit;
|
|
564
|
+
opacity: 0.65;
|
|
528
565
|
cursor: pointer;
|
|
529
566
|
transition:
|
|
530
567
|
background-color 0.12s ease,
|
|
531
|
-
|
|
532
|
-
transform 0.12s ease;
|
|
568
|
+
opacity 0.12s ease;
|
|
533
569
|
}
|
|
534
570
|
|
|
535
571
|
.zui-drawing-tool:hover {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
.zui-drawing-tool:active {
|
|
540
|
-
transform: scale(0.94);
|
|
572
|
+
opacity: 1;
|
|
573
|
+
background: var(--zui-drawing-hover);
|
|
541
574
|
}
|
|
542
575
|
|
|
543
576
|
.zui-drawing-tool.is-active {
|
|
544
|
-
|
|
545
|
-
color:
|
|
577
|
+
opacity: 1;
|
|
578
|
+
color: var(--zui-drawing-accent);
|
|
579
|
+
background: color-mix(in srgb, var(--zui-drawing-accent) 14%, transparent);
|
|
546
580
|
}
|
|
547
581
|
|
|
548
582
|
.zui-drawing-tool.is-success {
|
|
549
|
-
|
|
583
|
+
opacity: 1;
|
|
584
|
+
color: var(--zui-drawing-success);
|
|
550
585
|
}
|
|
551
586
|
|
|
552
587
|
.zui-drawing-tool-danger {
|
|
553
|
-
color:
|
|
588
|
+
color: var(--zui-drawing-danger);
|
|
554
589
|
}
|
|
555
590
|
|
|
556
591
|
.zui-drawing-tool-danger:hover {
|
|
557
|
-
background:
|
|
592
|
+
background: color-mix(in srgb, var(--zui-drawing-danger) 12%, transparent);
|
|
558
593
|
}
|
|
559
594
|
|
|
560
595
|
.zui-drawing-tool-more {
|
|
561
|
-
width: 2.
|
|
596
|
+
width: 2.25rem;
|
|
562
597
|
}
|
|
563
598
|
|
|
564
599
|
.zui-drawing-caret {
|
|
565
600
|
position: absolute;
|
|
566
|
-
right: 0.
|
|
567
|
-
bottom: 0.
|
|
601
|
+
right: 0.25rem;
|
|
602
|
+
bottom: 0.25rem;
|
|
568
603
|
width: 0;
|
|
569
604
|
height: 0;
|
|
570
605
|
border-left: 4px solid transparent;
|
|
571
606
|
border-top: 4px solid currentColor;
|
|
572
|
-
opacity: 0.
|
|
607
|
+
opacity: 0.7;
|
|
573
608
|
}
|
|
574
609
|
|
|
575
610
|
.zui-drawing-more {
|
|
@@ -578,7 +613,7 @@
|
|
|
578
613
|
|
|
579
614
|
.zui-drawing-popover {
|
|
580
615
|
position: absolute;
|
|
581
|
-
top: calc(100% + 0.
|
|
616
|
+
top: calc(100% + 0.25rem);
|
|
582
617
|
left: 0;
|
|
583
618
|
z-index: 7;
|
|
584
619
|
display: flex;
|
|
@@ -586,14 +621,12 @@
|
|
|
586
621
|
gap: 0.125rem;
|
|
587
622
|
min-width: 9.5rem;
|
|
588
623
|
padding: 0.25rem;
|
|
589
|
-
border: 1px solid
|
|
590
|
-
border-radius:
|
|
591
|
-
background:
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
animation: zui-drawing-pop 0.14s ease-out;
|
|
596
|
-
transform-origin: top left;
|
|
624
|
+
border: 1px solid var(--zui-drawing-border);
|
|
625
|
+
border-radius: var(--zui-drawing-control-radius);
|
|
626
|
+
background: var(--zui-drawing-popover);
|
|
627
|
+
color: var(--zui-drawing-popover-foreground);
|
|
628
|
+
box-shadow: var(--zui-drawing-shadow);
|
|
629
|
+
animation: zui-drawing-pop 0.12s ease-out;
|
|
597
630
|
}
|
|
598
631
|
|
|
599
632
|
.zui-drawing-popover-item {
|
|
@@ -602,9 +635,9 @@
|
|
|
602
635
|
gap: 0.5rem;
|
|
603
636
|
padding: 0.375rem 0.5rem;
|
|
604
637
|
border: none;
|
|
605
|
-
border-radius:
|
|
638
|
+
border-radius: var(--zui-drawing-control-radius);
|
|
606
639
|
background: transparent;
|
|
607
|
-
color:
|
|
640
|
+
color: inherit;
|
|
608
641
|
font-size: 0.8125rem;
|
|
609
642
|
text-align: left;
|
|
610
643
|
cursor: pointer;
|
|
@@ -612,36 +645,32 @@
|
|
|
612
645
|
}
|
|
613
646
|
|
|
614
647
|
.zui-drawing-popover-item:hover {
|
|
615
|
-
background:
|
|
648
|
+
background: var(--zui-drawing-hover);
|
|
616
649
|
}
|
|
617
650
|
|
|
618
651
|
.zui-drawing-popover-item.is-active {
|
|
619
|
-
|
|
620
|
-
|
|
652
|
+
color: var(--zui-drawing-accent);
|
|
653
|
+
background: color-mix(in srgb, var(--zui-drawing-accent) 14%, transparent);
|
|
621
654
|
}
|
|
622
655
|
|
|
623
|
-
/*
|
|
656
|
+
/*
|
|
657
|
+
* Properties row: colors for the selection (or for the next shape when
|
|
658
|
+
* nothing is selected) plus contextual connector options. Always rendered
|
|
659
|
+
* with a fixed minimum height so the drawing never shifts under the
|
|
660
|
+
* pointer when the selection changes.
|
|
661
|
+
*/
|
|
624
662
|
.zui-drawing-props-host {
|
|
625
663
|
flex-basis: 100%;
|
|
626
664
|
display: flex;
|
|
627
|
-
opacity: 0;
|
|
628
|
-
transform: translateY(-4px);
|
|
629
|
-
pointer-events: none;
|
|
630
|
-
transition:
|
|
631
|
-
opacity 0.16s ease,
|
|
632
|
-
transform 0.16s ease;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
.zui-drawing-props-host.is-visible {
|
|
636
|
-
opacity: 1;
|
|
637
|
-
transform: translateY(0);
|
|
638
|
-
pointer-events: auto;
|
|
639
665
|
}
|
|
640
666
|
|
|
641
667
|
.zui-drawing-props {
|
|
668
|
+
display: flex;
|
|
642
669
|
flex-wrap: wrap;
|
|
670
|
+
align-items: center;
|
|
643
671
|
gap: 0.25rem;
|
|
644
|
-
|
|
672
|
+
min-height: 1.75rem;
|
|
673
|
+
padding: 0.125rem 0 0;
|
|
645
674
|
}
|
|
646
675
|
|
|
647
676
|
.zui-drawing-props-group {
|
|
@@ -651,24 +680,24 @@
|
|
|
651
680
|
}
|
|
652
681
|
|
|
653
682
|
.zui-drawing-props-group + .zui-drawing-props-group {
|
|
654
|
-
padding-left: 0.
|
|
655
|
-
border-left: 1px solid
|
|
683
|
+
padding-left: 0.5rem;
|
|
684
|
+
border-left: 1px solid color-mix(in srgb, currentColor 15%, transparent);
|
|
656
685
|
}
|
|
657
686
|
|
|
658
687
|
.zui-drawing-swatch-label {
|
|
659
688
|
margin: 0 0.25rem 0 0.125rem;
|
|
660
689
|
font-size: 0.6875rem;
|
|
661
690
|
letter-spacing: 0.02em;
|
|
662
|
-
color:
|
|
691
|
+
color: var(--zui-drawing-muted-foreground);
|
|
663
692
|
user-select: none;
|
|
664
693
|
}
|
|
665
694
|
|
|
666
695
|
.zui-drawing-swatch {
|
|
667
|
-
width:
|
|
668
|
-
height:
|
|
696
|
+
width: 1rem;
|
|
697
|
+
height: 1rem;
|
|
669
698
|
margin: 0 0.125rem;
|
|
670
699
|
padding: 0;
|
|
671
|
-
border: 1px solid rgba(0, 0, 0, 0.
|
|
700
|
+
border: 1px solid rgba(0, 0, 0, 0.18);
|
|
672
701
|
border-radius: 999px;
|
|
673
702
|
cursor: pointer;
|
|
674
703
|
transition:
|
|
@@ -682,7 +711,7 @@
|
|
|
682
711
|
|
|
683
712
|
.zui-drawing-swatch.is-active {
|
|
684
713
|
box-shadow:
|
|
685
|
-
0 0 0 2px
|
|
714
|
+
0 0 0 2px var(--zui-drawing-chrome),
|
|
686
715
|
0 0 0 3.5px var(--zui-drawing-accent);
|
|
687
716
|
}
|
|
688
717
|
|
|
@@ -703,9 +732,8 @@
|
|
|
703
732
|
display: flex;
|
|
704
733
|
align-items: center;
|
|
705
734
|
justify-content: center;
|
|
706
|
-
padding-top: 2rem;
|
|
707
735
|
font-size: 0.8125rem;
|
|
708
|
-
color:
|
|
736
|
+
color: var(--zui-drawing-muted-foreground);
|
|
709
737
|
pointer-events: none;
|
|
710
738
|
user-select: none;
|
|
711
739
|
}
|
|
@@ -728,10 +756,11 @@
|
|
|
728
756
|
padding: 0;
|
|
729
757
|
border: 0;
|
|
730
758
|
border-radius: 3px;
|
|
731
|
-
outline: 1.5px dashed var(--zui-drawing-accent
|
|
759
|
+
outline: 1.5px dashed var(--zui-drawing-accent);
|
|
732
760
|
outline-offset: 3px;
|
|
733
|
-
background:
|
|
761
|
+
background: color-mix(in srgb, var(--zui-drawing-surface) 85%, transparent);
|
|
734
762
|
color: inherit;
|
|
763
|
+
font-family: var(--zui-drawing-font);
|
|
735
764
|
resize: none;
|
|
736
765
|
overflow: hidden;
|
|
737
766
|
box-sizing: content-box;
|
|
@@ -753,7 +782,7 @@
|
|
|
753
782
|
justify-content: center;
|
|
754
783
|
width: 1.375rem;
|
|
755
784
|
height: 1.375rem;
|
|
756
|
-
border-radius:
|
|
785
|
+
border-radius: var(--zui-drawing-control-radius);
|
|
757
786
|
color: #1e1e1e;
|
|
758
787
|
opacity: 0.35;
|
|
759
788
|
cursor: ns-resize;
|
|
@@ -769,70 +798,15 @@
|
|
|
769
798
|
}
|
|
770
799
|
|
|
771
800
|
/*
|
|
772
|
-
* Dark mode (zui `.dark` class convention). The
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
* near-black stroke renders light on the dark canvas, and the swatches
|
|
776
|
-
* preview exactly what will be drawn.
|
|
801
|
+
* Dark mode (zui `.dark` class convention). The chrome follows the theme
|
|
802
|
+
* tokens on its own; the drawing area, the swatches and the inline text
|
|
803
|
+
* input are inverted so stored shape colors stay theme-agnostic.
|
|
777
804
|
*/
|
|
778
|
-
.dark .zui-drawing-canvas {
|
|
779
|
-
background: #18181b;
|
|
780
|
-
border-color: rgba(255, 255, 255, 0.1);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
805
|
.dark .zui-drawing-surface,
|
|
784
806
|
.dark .zui-drawing-swatch,
|
|
785
|
-
.dark .zui-drawing-text-input
|
|
786
|
-
filter: invert(93%) hue-rotate(180deg);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
.dark .zui-drawing-toolbar-group,
|
|
790
|
-
.dark .zui-drawing-props,
|
|
791
|
-
.dark .zui-drawing-popover {
|
|
792
|
-
background: rgba(35, 35, 41, 0.9);
|
|
793
|
-
border-color: rgba(255, 255, 255, 0.1);
|
|
794
|
-
box-shadow:
|
|
795
|
-
0 1px 2px rgba(0, 0, 0, 0.3),
|
|
796
|
-
0 8px 24px -8px rgba(0, 0, 0, 0.6);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
.dark .zui-drawing-tool,
|
|
800
|
-
.dark .zui-drawing-popover-item,
|
|
807
|
+
.dark .zui-drawing-text-input,
|
|
801
808
|
.dark .zui-drawing-resize {
|
|
802
|
-
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
.dark .zui-drawing-tool:hover,
|
|
806
|
-
.dark .zui-drawing-popover-item:hover,
|
|
807
|
-
.dark .zui-drawing-resize:hover {
|
|
808
|
-
background: rgba(255, 255, 255, 0.08);
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
.dark .zui-drawing-tool.is-active,
|
|
812
|
-
.dark .zui-drawing-popover-item.is-active {
|
|
813
|
-
background: #45437a;
|
|
814
|
-
color: #dcdaff;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
.dark .zui-drawing-toolbar-divider,
|
|
818
|
-
.dark .zui-drawing-props-group + .zui-drawing-props-group {
|
|
819
|
-
border-color: rgba(255, 255, 255, 0.12);
|
|
820
|
-
background-color: rgba(255, 255, 255, 0.12);
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
.dark .zui-drawing-props-group + .zui-drawing-props-group {
|
|
824
|
-
background-color: transparent;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
.dark .zui-drawing-swatch-label,
|
|
828
|
-
.dark .zui-drawing-empty {
|
|
829
|
-
color: rgba(255, 255, 255, 0.55);
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
.dark .zui-drawing-swatch.is-active {
|
|
833
|
-
box-shadow:
|
|
834
|
-
0 0 0 2px #232329,
|
|
835
|
-
0 0 0 3.5px #8f8bff;
|
|
809
|
+
filter: var(--zui-drawing-dark-filter);
|
|
836
810
|
}
|
|
837
811
|
|
|
838
812
|
.zui-text-editor-textarea {
|