@umodeapporg/ui 0.30.1 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/umode-ui.css +32 -0
- package/dist/components/UI/Flow/FlowAnnotationDraftPreview.d.ts +6 -0
- package/dist/components/UI/Flow/FlowAnnotationDraftPreview.js +65 -0
- package/dist/components/UI/Flow/FlowAnnotationDraftPreview.js.map +1 -0
- package/dist/components/UI/Flow/FlowAnnotationNode.d.ts +34 -0
- package/dist/components/UI/Flow/FlowAnnotationNode.js +231 -0
- package/dist/components/UI/Flow/FlowAnnotationNode.js.map +1 -0
- package/dist/components/UI/Flow/FlowAnnotationToolbar.d.ts +32 -0
- package/dist/components/UI/Flow/FlowAnnotationToolbar.js +298 -0
- package/dist/components/UI/Flow/FlowAnnotationToolbar.js.map +1 -0
- package/dist/components/UI/Flow/FlowCanvas.d.ts +2 -0
- package/dist/components/UI/Flow/FlowCanvas.js +278 -20
- package/dist/components/UI/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/UI/Flow/FlowInspector.js +6 -5
- package/dist/components/UI/Flow/FlowInspector.js.map +1 -1
- package/dist/components/UI/Flow/FlowSelectionBar.d.ts +10 -0
- package/dist/components/UI/Flow/FlowSelectionBar.js +56 -0
- package/dist/components/UI/Flow/FlowSelectionBar.js.map +1 -0
- package/dist/components/UI/Flow/flowAnnotations.d.ts +38 -0
- package/dist/components/UI/Flow/flowAnnotations.js +123 -0
- package/dist/components/UI/Flow/flowAnnotations.js.map +1 -0
- package/dist/components/UI/Flow/flowEditorStore.d.ts +29 -5
- package/dist/components/UI/Flow/flowEditorStore.js +123 -23
- package/dist/components/UI/Flow/flowEditorStore.js.map +1 -1
- package/dist/components/UI/Flow/flowTypes.d.ts +30 -0
- package/dist/components/UI/Flow/index.d.ts +2 -1
- package/dist/components/UI/Flow/index.js +5 -1
- package/dist/components/UI/Flow/index.js.map +1 -1
- package/dist/components/UI/Flow/useFlowAnnotationDrawing.d.ts +25 -0
- package/dist/components/UI/Flow/useFlowAnnotationDrawing.js +117 -0
- package/dist/components/UI/Flow/useFlowAnnotationDrawing.js.map +1 -0
- package/dist/locales/en/ui.json.d.ts +41 -0
- package/dist/locales/en/ui.json.js +1 -1
- package/dist/locales/index.d.ts +82 -0
- package/dist/locales/pt/ui.json.d.ts +41 -0
- package/dist/locales/pt/ui.json.js +1 -1
- package/package.json +1 -1
- package/src/assets/fonts/LICENSE-Virgil.txt +45 -0
- package/src/assets/fonts/virgil.woff2 +0 -0
- package/src/assets/styles/base.css +10 -0
package/dist/assets/umode-ui.css
CHANGED
|
@@ -433,3 +433,35 @@
|
|
|
433
433
|
stroke: rgb(var(--color-border-strong));
|
|
434
434
|
fill: rgb(var(--color-border-strong));
|
|
435
435
|
}
|
|
436
|
+
|
|
437
|
+
.umode-flow .react-flow__selection {
|
|
438
|
+
background: rgb(var(--color-primary) / 0.08);
|
|
439
|
+
border: 1px solid rgb(var(--color-primary) / 0.6);
|
|
440
|
+
border-radius: 0.25rem;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/* The box xyflow draws around a marquee selection sits over the blocks and swallows the click that
|
|
444
|
+
should pick one of them. Dragging any selected block already moves the whole selection. */
|
|
445
|
+
.umode-flow .react-flow__nodesselection {
|
|
446
|
+
display: none;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.umode-flow--drawing .react-flow__pane {
|
|
450
|
+
cursor: crosshair;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.umode-flow .umode-flow-annotation-handle {
|
|
454
|
+
width: 0.5rem;
|
|
455
|
+
height: 0.5rem;
|
|
456
|
+
border: 1.5px solid rgb(var(--color-primary));
|
|
457
|
+
border-radius: 0.125rem;
|
|
458
|
+
background: rgb(var(--color-surface));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.umode-flow .umode-flow-annotation-line {
|
|
462
|
+
border-color: transparent;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.umode-flow .umode-flow-annotation-text {
|
|
466
|
+
font-family: 'Virgil', 'Segoe Print', 'Bradley Hand', cursive;
|
|
467
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { IFlowAnnotationDraft } from './useFlowAnnotationDrawing';
|
|
2
|
+
/** The shape under the pointer while it is drawn, in canvas coordinates so it pans and zooms along. */
|
|
3
|
+
declare const FlowAnnotationDraftPreview: React.FC<{
|
|
4
|
+
draft: IFlowAnnotationDraft;
|
|
5
|
+
}>;
|
|
6
|
+
export default FlowAnnotationDraftPreview;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ViewportPortal } from "@xyflow/react";
|
|
3
|
+
import { flowAnnotationMarkerUrl, FlowAnnotationArrowMarker, FLOW_ANNOTATION_STROKE_CLASS } from "./FlowAnnotationNode.js";
|
|
4
|
+
import { boxBetween, smoothStrokePath, strokeWidthOf } from "./flowAnnotations.js";
|
|
5
|
+
const DRAFT_MARKER_ID = "umode-flow-annotation-draft-arrow";
|
|
6
|
+
const FlowAnnotationDraftPreview = ({ draft }) => {
|
|
7
|
+
const { kind, start, current } = draft;
|
|
8
|
+
const box = boxBetween(start, current);
|
|
9
|
+
const shape = (() => {
|
|
10
|
+
if (kind === "rectangle")
|
|
11
|
+
return /* @__PURE__ */ jsx("rect", { x: box.position.x, y: box.position.y, width: box.size.width, height: box.size.height, rx: 4 });
|
|
12
|
+
if (kind === "ellipse")
|
|
13
|
+
return /* @__PURE__ */ jsx(
|
|
14
|
+
"ellipse",
|
|
15
|
+
{
|
|
16
|
+
cx: box.position.x + box.size.width / 2,
|
|
17
|
+
cy: box.position.y + box.size.height / 2,
|
|
18
|
+
rx: box.size.width / 2,
|
|
19
|
+
ry: box.size.height / 2
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
if (kind === "arrow")
|
|
23
|
+
return /* @__PURE__ */ jsx(
|
|
24
|
+
"line",
|
|
25
|
+
{
|
|
26
|
+
x1: start.x,
|
|
27
|
+
y1: start.y,
|
|
28
|
+
x2: current.x,
|
|
29
|
+
y2: current.y,
|
|
30
|
+
markerEnd: flowAnnotationMarkerUrl(DRAFT_MARKER_ID)
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
if (kind === "line") return /* @__PURE__ */ jsx("line", { x1: start.x, y1: start.y, x2: current.x, y2: current.y });
|
|
34
|
+
if (kind === "drawing") return /* @__PURE__ */ jsx("path", { d: smoothStrokePath(draft.points) });
|
|
35
|
+
return null;
|
|
36
|
+
})();
|
|
37
|
+
if (!shape) return null;
|
|
38
|
+
return /* @__PURE__ */ jsx(ViewportPortal, { children: /* @__PURE__ */ jsxs(
|
|
39
|
+
"svg",
|
|
40
|
+
{
|
|
41
|
+
"aria-hidden": true,
|
|
42
|
+
width: 1,
|
|
43
|
+
height: 1,
|
|
44
|
+
fill: "none",
|
|
45
|
+
className: "pointer-events-none absolute left-0 top-0 overflow-visible",
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ jsx(FlowAnnotationArrowMarker, { markerId: DRAFT_MARKER_ID, tone: draft.tone }),
|
|
48
|
+
/* @__PURE__ */ jsx(
|
|
49
|
+
"g",
|
|
50
|
+
{
|
|
51
|
+
className: FLOW_ANNOTATION_STROKE_CLASS[draft.tone],
|
|
52
|
+
strokeWidth: strokeWidthOf(draft),
|
|
53
|
+
strokeLinecap: "round",
|
|
54
|
+
strokeLinejoin: "round",
|
|
55
|
+
children: shape
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
) });
|
|
61
|
+
};
|
|
62
|
+
export {
|
|
63
|
+
FlowAnnotationDraftPreview as default
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=FlowAnnotationDraftPreview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlowAnnotationDraftPreview.js","sources":["../../../../src/components/UI/Flow/FlowAnnotationDraftPreview.tsx"],"sourcesContent":["import { ViewportPortal } from '@xyflow/react';\n\nimport { FLOW_ANNOTATION_STROKE_CLASS, FlowAnnotationArrowMarker, flowAnnotationMarkerUrl } from './FlowAnnotationNode';\nimport { boxBetween, smoothStrokePath, strokeWidthOf } from './flowAnnotations';\nimport type { IFlowAnnotationDraft } from './useFlowAnnotationDrawing';\n\nconst DRAFT_MARKER_ID = 'umode-flow-annotation-draft-arrow';\n\n/** The shape under the pointer while it is drawn, in canvas coordinates so it pans and zooms along. */\nconst FlowAnnotationDraftPreview: React.FC<{ draft: IFlowAnnotationDraft }> = ({ draft }) => {\n const { kind, start, current } = draft;\n const box = boxBetween(start, current);\n\n const shape = (() => {\n if (kind === 'rectangle')\n return <rect x={box.position.x} y={box.position.y} width={box.size.width} height={box.size.height} rx={4} />;\n if (kind === 'ellipse')\n return (\n <ellipse\n cx={box.position.x + box.size.width / 2}\n cy={box.position.y + box.size.height / 2}\n rx={box.size.width / 2}\n ry={box.size.height / 2}\n />\n );\n if (kind === 'arrow')\n return (\n <line\n x1={start.x}\n y1={start.y}\n x2={current.x}\n y2={current.y}\n markerEnd={flowAnnotationMarkerUrl(DRAFT_MARKER_ID)}\n />\n );\n if (kind === 'line') return <line x1={start.x} y1={start.y} x2={current.x} y2={current.y} />;\n if (kind === 'drawing') return <path d={smoothStrokePath(draft.points)} />;\n return null;\n })();\n\n if (!shape) return null;\n\n return (\n <ViewportPortal>\n <svg\n aria-hidden\n width={1}\n height={1}\n fill=\"none\"\n className=\"pointer-events-none absolute left-0 top-0 overflow-visible\"\n >\n <FlowAnnotationArrowMarker markerId={DRAFT_MARKER_ID} tone={draft.tone} />\n <g\n className={FLOW_ANNOTATION_STROKE_CLASS[draft.tone]}\n strokeWidth={strokeWidthOf(draft)}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n {shape}\n </g>\n </svg>\n </ViewportPortal>\n );\n};\n\nexport default FlowAnnotationDraftPreview;\n"],"names":[],"mappings":";;;;AAMA,MAAM,kBAAkB;AAGxB,MAAM,6BAAwE,CAAC,EAAE,YAAY;AAC3F,QAAM,EAAE,MAAM,OAAO,QAAA,IAAY;AACjC,QAAM,MAAM,WAAW,OAAO,OAAO;AAErC,QAAM,SAAS,MAAM;AACnB,QAAI,SAAS;AACX,iCAAQ,QAAA,EAAK,GAAG,IAAI,SAAS,GAAG,GAAG,IAAI,SAAS,GAAG,OAAO,IAAI,KAAK,OAAO,QAAQ,IAAI,KAAK,QAAQ,IAAI,GAAG;AAC5G,QAAI,SAAS;AACX,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAI,IAAI,SAAS,IAAI,IAAI,KAAK,QAAQ;AAAA,UACtC,IAAI,IAAI,SAAS,IAAI,IAAI,KAAK,SAAS;AAAA,UACvC,IAAI,IAAI,KAAK,QAAQ;AAAA,UACrB,IAAI,IAAI,KAAK,SAAS;AAAA,QAAA;AAAA,MAAA;AAG5B,QAAI,SAAS;AACX,aACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACC,IAAI,MAAM;AAAA,UACV,IAAI,MAAM;AAAA,UACV,IAAI,QAAQ;AAAA,UACZ,IAAI,QAAQ;AAAA,UACZ,WAAW,wBAAwB,eAAe;AAAA,QAAA;AAAA,MAAA;AAGxD,QAAI,SAAS,OAAQ,QAAO,oBAAC,QAAA,EAAK,IAAI,MAAM,GAAG,IAAI,MAAM,GAAG,IAAI,QAAQ,GAAG,IAAI,QAAQ,GAAG;AAC1F,QAAI,SAAS,UAAW,QAAO,oBAAC,UAAK,GAAG,iBAAiB,MAAM,MAAM,GAAG;AACxE,WAAO;AAAA,EACT,GAAA;AAEA,MAAI,CAAC,MAAO,QAAO;AAEnB,6BACG,gBAAA,EACC,UAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,eAAW;AAAA,MACX,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAK;AAAA,MACL,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA,oBAAC,2BAAA,EAA0B,UAAU,iBAAiB,MAAM,MAAM,MAAM;AAAA,QACxE;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW,6BAA6B,MAAM,IAAI;AAAA,YAClD,aAAa,cAAc,KAAK;AAAA,YAChC,eAAc;AAAA,YACd,gBAAe;AAAA,YAEd,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Node, NodeProps } from '@xyflow/react';
|
|
2
|
+
import { IFlowAnnotationGeometry } from './flowEditorStore';
|
|
3
|
+
import { IFlowAnnotation, TFlowAnnotationStroke, TFlowAnnotationTone } from './flowTypes';
|
|
4
|
+
/** Text ink: the tone's `-emphasis`, the variant validated for reading. */
|
|
5
|
+
export declare const FLOW_ANNOTATION_TONE_CLASS: Record<TFlowAnnotationTone, string>;
|
|
6
|
+
/** Strokes are graphics, not text: they take the tone's fill colour, validated at 3:1 for exactly that. */
|
|
7
|
+
export declare const FLOW_ANNOTATION_STROKE_CLASS: Record<TFlowAnnotationTone, string>;
|
|
8
|
+
export declare const FLOW_ANNOTATION_SWATCH_CLASS: Record<TFlowAnnotationTone, string>;
|
|
9
|
+
export declare const FLOW_ANNOTATION_TEXT_SIZE_CLASS: Record<TFlowAnnotationStroke, string>;
|
|
10
|
+
export declare const flowAnnotationMarkerUrl: (markerId: string) => string;
|
|
11
|
+
/** The arrowhead, painted with the stroke colour of the arrow that uses it. */
|
|
12
|
+
export declare const FlowAnnotationArrowMarker: React.FC<{
|
|
13
|
+
markerId: string;
|
|
14
|
+
tone: TFlowAnnotationTone;
|
|
15
|
+
}>;
|
|
16
|
+
export interface IFlowAnnotationNodeData extends Record<string, unknown> {
|
|
17
|
+
annotation: IFlowAnnotation;
|
|
18
|
+
readOnly: boolean;
|
|
19
|
+
editing: boolean;
|
|
20
|
+
placeholder: string;
|
|
21
|
+
onTextChange: (text: string) => void;
|
|
22
|
+
onEditStart: () => void;
|
|
23
|
+
onEditEnd: () => void;
|
|
24
|
+
onResizeStart: () => void;
|
|
25
|
+
onResize: (geometry: IFlowAnnotationGeometry) => void;
|
|
26
|
+
}
|
|
27
|
+
export type TFlowAnnotationNode = Node<IFlowAnnotationNodeData, 'flowAnnotation'>;
|
|
28
|
+
/**
|
|
29
|
+
* One annotation on the canvas. Shapes only catch the pointer on their stroke, so a frame drawn
|
|
30
|
+
* around blocks never steals the click meant for what is inside it; once selected, the whole box
|
|
31
|
+
* can be dragged. Text is written in Virgil, the hand-drawn face of Excalidraw.
|
|
32
|
+
*/
|
|
33
|
+
declare const FlowAnnotationNode: React.FC<NodeProps<TFlowAnnotationNode>>;
|
|
34
|
+
export default FlowAnnotationNode;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useEffect } from "react";
|
|
3
|
+
import { mergeClasses } from "../../../helpers/mergeClasses.js";
|
|
4
|
+
import { NodeResizer } from "@xyflow/react";
|
|
5
|
+
import { FLOW_ANNOTATION_MIN_SIZE, strokeWidthOf, smoothStrokePath } from "./flowAnnotations.js";
|
|
6
|
+
const FLOW_ANNOTATION_TONE_CLASS = {
|
|
7
|
+
neutral: "text-foreground-muted",
|
|
8
|
+
brand: "text-brand-emphasis",
|
|
9
|
+
info: "text-info-emphasis",
|
|
10
|
+
success: "text-success-emphasis",
|
|
11
|
+
warning: "text-warning-emphasis",
|
|
12
|
+
danger: "text-danger-emphasis"
|
|
13
|
+
};
|
|
14
|
+
const FLOW_ANNOTATION_STROKE_CLASS = {
|
|
15
|
+
neutral: "stroke-foreground-muted",
|
|
16
|
+
brand: "stroke-brand",
|
|
17
|
+
info: "stroke-info",
|
|
18
|
+
success: "stroke-success",
|
|
19
|
+
warning: "stroke-warning",
|
|
20
|
+
danger: "stroke-danger"
|
|
21
|
+
};
|
|
22
|
+
const ARROWHEAD_CLASS = {
|
|
23
|
+
neutral: "fill-foreground-muted",
|
|
24
|
+
brand: "fill-brand",
|
|
25
|
+
info: "fill-info",
|
|
26
|
+
success: "fill-success",
|
|
27
|
+
warning: "fill-warning",
|
|
28
|
+
danger: "fill-danger"
|
|
29
|
+
};
|
|
30
|
+
const FLOW_ANNOTATION_SWATCH_CLASS = {
|
|
31
|
+
neutral: "bg-foreground-muted",
|
|
32
|
+
brand: "bg-brand",
|
|
33
|
+
info: "bg-info",
|
|
34
|
+
success: "bg-success",
|
|
35
|
+
warning: "bg-warning",
|
|
36
|
+
danger: "bg-danger"
|
|
37
|
+
};
|
|
38
|
+
const STEP_TONE_CLASS = {
|
|
39
|
+
neutral: "bg-ink text-ink-fg",
|
|
40
|
+
brand: "bg-brand text-brand-fg",
|
|
41
|
+
info: "bg-info text-info-fg",
|
|
42
|
+
success: "bg-success text-success-fg",
|
|
43
|
+
warning: "bg-warning text-warning-fg",
|
|
44
|
+
danger: "bg-danger text-danger-fg"
|
|
45
|
+
};
|
|
46
|
+
const FLOW_ANNOTATION_TEXT_SIZE_CLASS = {
|
|
47
|
+
thin: "text-base",
|
|
48
|
+
medium: "text-xl",
|
|
49
|
+
thick: "text-3xl"
|
|
50
|
+
};
|
|
51
|
+
const HIT_BAND_PX = 12;
|
|
52
|
+
const HIT_POINTER_EVENTS = "stroke";
|
|
53
|
+
const MARKER_ORIENT = "auto-start-reverse";
|
|
54
|
+
const RESIZE_HANDLE_CLASS = "umode-flow-annotation-handle";
|
|
55
|
+
const RESIZE_LINE_CLASS = "umode-flow-annotation-line";
|
|
56
|
+
const TEXTAREA_WRAP = "off";
|
|
57
|
+
const FOCUS_ATTEMPTS = 10;
|
|
58
|
+
const EMPTY_LINE_FILLER = "";
|
|
59
|
+
const flowAnnotationMarkerUrl = (markerId) => `url(#${markerId})`;
|
|
60
|
+
const FlowAnnotationArrowMarker = ({
|
|
61
|
+
markerId,
|
|
62
|
+
tone
|
|
63
|
+
}) => /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("marker", { id: markerId, viewBox: "0 0 10 10", refX: 7, refY: 5, markerWidth: 4, markerHeight: 4, orient: MARKER_ORIENT, children: /* @__PURE__ */ jsx("path", { d: "M 0 0 L 10 5 L 0 10 z", className: ARROWHEAD_CLASS[tone] }) }) });
|
|
64
|
+
const AnnotationShape = ({ annotation, markerId }) => {
|
|
65
|
+
const { width, height } = annotation.size;
|
|
66
|
+
const strokeWidth = strokeWidthOf(annotation);
|
|
67
|
+
const inset = strokeWidth / 2;
|
|
68
|
+
const [start, end] = annotation.points ?? [];
|
|
69
|
+
const hasArrowhead = annotation.kind === "arrow";
|
|
70
|
+
const shape = (() => {
|
|
71
|
+
if (annotation.kind === "rectangle")
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
"rect",
|
|
74
|
+
{
|
|
75
|
+
x: inset,
|
|
76
|
+
y: inset,
|
|
77
|
+
width: Math.max(width - strokeWidth, 0),
|
|
78
|
+
height: Math.max(height - strokeWidth, 0),
|
|
79
|
+
rx: 4
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
if (annotation.kind === "ellipse")
|
|
83
|
+
return /* @__PURE__ */ jsx(
|
|
84
|
+
"ellipse",
|
|
85
|
+
{
|
|
86
|
+
cx: width / 2,
|
|
87
|
+
cy: height / 2,
|
|
88
|
+
rx: Math.max(width / 2 - inset, 0),
|
|
89
|
+
ry: Math.max(height / 2 - inset, 0)
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
if ((annotation.kind === "arrow" || annotation.kind === "line") && start && end)
|
|
93
|
+
return /* @__PURE__ */ jsx("line", { x1: start.x, y1: start.y, x2: end.x, y2: end.y });
|
|
94
|
+
return /* @__PURE__ */ jsx("path", { d: smoothStrokePath(annotation.points ?? []) });
|
|
95
|
+
})();
|
|
96
|
+
return /* @__PURE__ */ jsxs("svg", { "aria-hidden": true, className: "pointer-events-none absolute inset-0 h-full w-full overflow-visible", fill: "none", children: [
|
|
97
|
+
hasArrowhead && /* @__PURE__ */ jsx(FlowAnnotationArrowMarker, { markerId, tone: annotation.tone }),
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
"g",
|
|
100
|
+
{
|
|
101
|
+
className: FLOW_ANNOTATION_STROKE_CLASS[annotation.tone],
|
|
102
|
+
strokeWidth,
|
|
103
|
+
strokeLinecap: "round",
|
|
104
|
+
strokeLinejoin: "round",
|
|
105
|
+
markerEnd: hasArrowhead ? flowAnnotationMarkerUrl(markerId) : void 0,
|
|
106
|
+
children: shape
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
/* @__PURE__ */ jsx("g", { stroke: "transparent", strokeWidth: strokeWidth + HIT_BAND_PX, pointerEvents: HIT_POINTER_EVENTS, children: shape })
|
|
110
|
+
] });
|
|
111
|
+
};
|
|
112
|
+
const FlowAnnotationNode = ({ id, data, selected }) => {
|
|
113
|
+
const { annotation, readOnly, editing, placeholder } = data;
|
|
114
|
+
const textareaRef = useRef(null);
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
if (!editing) return void 0;
|
|
117
|
+
let frame = 0;
|
|
118
|
+
let attemptsLeft = FOCUS_ATTEMPTS;
|
|
119
|
+
const focusTextarea = () => {
|
|
120
|
+
const textarea = textareaRef.current;
|
|
121
|
+
if (!textarea) return;
|
|
122
|
+
textarea.focus();
|
|
123
|
+
if (document.activeElement === textarea) {
|
|
124
|
+
textarea.setSelectionRange(textarea.value.length, textarea.value.length);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
attemptsLeft -= 1;
|
|
128
|
+
if (attemptsLeft > 0) frame = window.requestAnimationFrame(focusTextarea);
|
|
129
|
+
};
|
|
130
|
+
focusTextarea();
|
|
131
|
+
return () => window.cancelAnimationFrame(frame);
|
|
132
|
+
}, [editing]);
|
|
133
|
+
const markerId = `umode-flow-annotation-arrow-${id}`;
|
|
134
|
+
const isText = annotation.kind === "text";
|
|
135
|
+
const toneClass = FLOW_ANNOTATION_TONE_CLASS[annotation.tone] ?? FLOW_ANNOTATION_TONE_CLASS.neutral;
|
|
136
|
+
const textSizeClass = FLOW_ANNOTATION_TEXT_SIZE_CLASS[annotation.stroke ?? "medium"];
|
|
137
|
+
const textMetricsClass = mergeClasses("umode-flow-annotation-text whitespace-pre", textSizeClass, "leading-snug");
|
|
138
|
+
const canResize = !readOnly && selected && !editing && !isText && annotation.kind !== "step";
|
|
139
|
+
const content = (() => {
|
|
140
|
+
if (annotation.kind === "step")
|
|
141
|
+
return /* @__PURE__ */ jsx(
|
|
142
|
+
"div",
|
|
143
|
+
{
|
|
144
|
+
className: mergeClasses(
|
|
145
|
+
"umode-flow-annotation-text pointer-events-auto flex h-full w-full items-center justify-center rounded-full text-lg leading-none shadow-sm",
|
|
146
|
+
STEP_TONE_CLASS[annotation.tone] ?? STEP_TONE_CLASS.danger
|
|
147
|
+
),
|
|
148
|
+
children: annotation.step
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
if (!isText) return /* @__PURE__ */ jsx(AnnotationShape, { annotation, markerId });
|
|
152
|
+
const text = annotation.text ?? "";
|
|
153
|
+
return /* @__PURE__ */ jsxs(
|
|
154
|
+
"div",
|
|
155
|
+
{
|
|
156
|
+
role: "presentation",
|
|
157
|
+
onDoubleClick: readOnly || editing ? void 0 : data.onEditStart,
|
|
158
|
+
className: mergeClasses(
|
|
159
|
+
"umode-flow-annotation-text pointer-events-auto relative inline-block whitespace-pre leading-snug",
|
|
160
|
+
textSizeClass
|
|
161
|
+
),
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
"span",
|
|
165
|
+
{
|
|
166
|
+
"aria-hidden": editing,
|
|
167
|
+
className: mergeClasses(textMetricsClass, "block min-w-[0.5rem]", editing && "invisible"),
|
|
168
|
+
children: `${text}${EMPTY_LINE_FILLER}`
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
editing && /* @__PURE__ */ jsx(
|
|
172
|
+
"textarea",
|
|
173
|
+
{
|
|
174
|
+
ref: textareaRef,
|
|
175
|
+
"aria-label": placeholder,
|
|
176
|
+
value: text,
|
|
177
|
+
wrap: TEXTAREA_WRAP,
|
|
178
|
+
onChange: (event) => data.onTextChange(event.target.value),
|
|
179
|
+
onBlur: data.onEditEnd,
|
|
180
|
+
onKeyDown: (event) => {
|
|
181
|
+
if (event.key === "Escape") event.currentTarget.blur();
|
|
182
|
+
},
|
|
183
|
+
className: mergeClasses(
|
|
184
|
+
textMetricsClass,
|
|
185
|
+
"nodrag nopan nowheel absolute inset-0 h-full w-full select-text resize-none overflow-hidden border-0 bg-transparent p-0 text-current focus:outline-none focus:ring-0"
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
})();
|
|
193
|
+
return /* @__PURE__ */ jsxs(
|
|
194
|
+
"div",
|
|
195
|
+
{
|
|
196
|
+
className: mergeClasses(
|
|
197
|
+
isText ? "relative inline-block" : "relative h-full w-full",
|
|
198
|
+
toneClass,
|
|
199
|
+
selected && !editing && "rounded-md outline-dashed outline-2 outline-offset-4 outline-border-emphasis"
|
|
200
|
+
),
|
|
201
|
+
children: [
|
|
202
|
+
/* @__PURE__ */ jsx(
|
|
203
|
+
NodeResizer,
|
|
204
|
+
{
|
|
205
|
+
isVisible: canResize,
|
|
206
|
+
minWidth: FLOW_ANNOTATION_MIN_SIZE,
|
|
207
|
+
minHeight: FLOW_ANNOTATION_MIN_SIZE,
|
|
208
|
+
handleClassName: RESIZE_HANDLE_CLASS,
|
|
209
|
+
lineClassName: RESIZE_LINE_CLASS,
|
|
210
|
+
onResizeStart: data.onResizeStart,
|
|
211
|
+
onResize: (_event, params) => data.onResize({
|
|
212
|
+
position: { x: params.x, y: params.y },
|
|
213
|
+
size: { width: params.width, height: params.height }
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
content
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
};
|
|
222
|
+
export {
|
|
223
|
+
FLOW_ANNOTATION_STROKE_CLASS,
|
|
224
|
+
FLOW_ANNOTATION_SWATCH_CLASS,
|
|
225
|
+
FLOW_ANNOTATION_TEXT_SIZE_CLASS,
|
|
226
|
+
FLOW_ANNOTATION_TONE_CLASS,
|
|
227
|
+
FlowAnnotationArrowMarker,
|
|
228
|
+
FlowAnnotationNode as default,
|
|
229
|
+
flowAnnotationMarkerUrl
|
|
230
|
+
};
|
|
231
|
+
//# sourceMappingURL=FlowAnnotationNode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FlowAnnotationNode.js","sources":["../../../../src/components/UI/Flow/FlowAnnotationNode.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nimport { mergeClasses } from '@/helpers/mergeClasses';\nimport { type Node, type NodeProps, NodeResizer } from '@xyflow/react';\n\nimport { FLOW_ANNOTATION_MIN_SIZE, smoothStrokePath, strokeWidthOf } from './flowAnnotations';\nimport type { IFlowAnnotationGeometry } from './flowEditorStore';\nimport type { IFlowAnnotation, TFlowAnnotationStroke, TFlowAnnotationTone } from './flowTypes';\n\n/** Text ink: the tone's `-emphasis`, the variant validated for reading. */\nexport const FLOW_ANNOTATION_TONE_CLASS: Record<TFlowAnnotationTone, string> = {\n neutral: 'text-foreground-muted',\n brand: 'text-brand-emphasis',\n info: 'text-info-emphasis',\n success: 'text-success-emphasis',\n warning: 'text-warning-emphasis',\n danger: 'text-danger-emphasis',\n};\n\n/** Strokes are graphics, not text: they take the tone's fill colour, validated at 3:1 for exactly that. */\nexport const FLOW_ANNOTATION_STROKE_CLASS: Record<TFlowAnnotationTone, string> = {\n neutral: 'stroke-foreground-muted',\n brand: 'stroke-brand',\n info: 'stroke-info',\n success: 'stroke-success',\n warning: 'stroke-warning',\n danger: 'stroke-danger',\n};\n\nconst ARROWHEAD_CLASS: Record<TFlowAnnotationTone, string> = {\n neutral: 'fill-foreground-muted',\n brand: 'fill-brand',\n info: 'fill-info',\n success: 'fill-success',\n warning: 'fill-warning',\n danger: 'fill-danger',\n};\n\nexport const FLOW_ANNOTATION_SWATCH_CLASS: Record<TFlowAnnotationTone, string> = {\n neutral: 'bg-foreground-muted',\n brand: 'bg-brand',\n info: 'bg-info',\n success: 'bg-success',\n warning: 'bg-warning',\n danger: 'bg-danger',\n};\n\n/** A step is a solid badge: the fill is the tone and the number takes the tone's own ink. */\nconst STEP_TONE_CLASS: Record<TFlowAnnotationTone, string> = {\n neutral: 'bg-ink text-ink-fg',\n brand: 'bg-brand text-brand-fg',\n info: 'bg-info text-info-fg',\n success: 'bg-success text-success-fg',\n warning: 'bg-warning text-warning-fg',\n danger: 'bg-danger text-danger-fg',\n};\n\nexport const FLOW_ANNOTATION_TEXT_SIZE_CLASS: Record<TFlowAnnotationStroke, string> = {\n thin: 'text-base',\n medium: 'text-xl',\n thick: 'text-3xl',\n};\n\n/** The invisible band around a stroke that still catches the pointer: a thin line is hard to grab. */\nconst HIT_BAND_PX = 12;\nconst HIT_POINTER_EVENTS = 'stroke';\nconst MARKER_ORIENT = 'auto-start-reverse';\nconst RESIZE_HANDLE_CLASS = 'umode-flow-annotation-handle';\nconst RESIZE_LINE_CLASS = 'umode-flow-annotation-line';\nconst TEXTAREA_WRAP = 'off';\nconst FOCUS_ATTEMPTS = 10;\n/** Keeps a trailing empty line (or an empty note) one line tall in the hidden mirror. */\nconst EMPTY_LINE_FILLER = '';\n\nexport const flowAnnotationMarkerUrl = (markerId: string): string => `url(#${markerId})`;\n\n/** The arrowhead, painted with the stroke colour of the arrow that uses it. */\nexport const FlowAnnotationArrowMarker: React.FC<{ markerId: string; tone: TFlowAnnotationTone }> = ({\n markerId,\n tone,\n}) => (\n <defs>\n <marker id={markerId} viewBox=\"0 0 10 10\" refX={7} refY={5} markerWidth={4} markerHeight={4} orient={MARKER_ORIENT}>\n <path d=\"M 0 0 L 10 5 L 0 10 z\" className={ARROWHEAD_CLASS[tone]} />\n </marker>\n </defs>\n);\n\nexport interface IFlowAnnotationNodeData extends Record<string, unknown> {\n annotation: IFlowAnnotation;\n readOnly: boolean;\n editing: boolean;\n placeholder: string;\n onTextChange: (text: string) => void;\n onEditStart: () => void;\n onEditEnd: () => void;\n onResizeStart: () => void;\n onResize: (geometry: IFlowAnnotationGeometry) => void;\n}\n\nexport type TFlowAnnotationNode = Node<IFlowAnnotationNodeData, 'flowAnnotation'>;\n\ninterface IAnnotationShapeProps {\n annotation: IFlowAnnotation;\n markerId: string;\n}\n\n/** Draws the stroke twice: once visible, once wide and transparent to catch the pointer. */\nconst AnnotationShape: React.FC<IAnnotationShapeProps> = ({ annotation, markerId }) => {\n const { width, height } = annotation.size;\n const strokeWidth = strokeWidthOf(annotation);\n const inset = strokeWidth / 2;\n const [start, end] = annotation.points ?? [];\n const hasArrowhead = annotation.kind === 'arrow';\n\n const shape = (() => {\n if (annotation.kind === 'rectangle')\n return (\n <rect\n x={inset}\n y={inset}\n width={Math.max(width - strokeWidth, 0)}\n height={Math.max(height - strokeWidth, 0)}\n rx={4}\n />\n );\n if (annotation.kind === 'ellipse')\n return (\n <ellipse\n cx={width / 2}\n cy={height / 2}\n rx={Math.max(width / 2 - inset, 0)}\n ry={Math.max(height / 2 - inset, 0)}\n />\n );\n if ((annotation.kind === 'arrow' || annotation.kind === 'line') && start && end)\n return <line x1={start.x} y1={start.y} x2={end.x} y2={end.y} />;\n return <path d={smoothStrokePath(annotation.points ?? [])} />;\n })();\n\n return (\n <svg aria-hidden className=\"pointer-events-none absolute inset-0 h-full w-full overflow-visible\" fill=\"none\">\n {hasArrowhead && <FlowAnnotationArrowMarker markerId={markerId} tone={annotation.tone} />}\n <g\n className={FLOW_ANNOTATION_STROKE_CLASS[annotation.tone]}\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n markerEnd={hasArrowhead ? flowAnnotationMarkerUrl(markerId) : undefined}\n >\n {shape}\n </g>\n <g stroke=\"transparent\" strokeWidth={strokeWidth + HIT_BAND_PX} pointerEvents={HIT_POINTER_EVENTS}>\n {shape}\n </g>\n </svg>\n );\n};\n\n/**\n * One annotation on the canvas. Shapes only catch the pointer on their stroke, so a frame drawn\n * around blocks never steals the click meant for what is inside it; once selected, the whole box\n * can be dragged. Text is written in Virgil, the hand-drawn face of Excalidraw.\n */\nconst FlowAnnotationNode: React.FC<NodeProps<TFlowAnnotationNode>> = ({ id, data, selected }) => {\n const { annotation, readOnly, editing, placeholder } = data;\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // xyflow keeps a new node `visibility: hidden` until it is measured, and a hidden field refuses\n // focus: keep trying for a few frames, or the keys typed next land on the canvas as tool shortcuts.\n useEffect(() => {\n if (!editing) return undefined;\n let frame = 0;\n let attemptsLeft = FOCUS_ATTEMPTS;\n const focusTextarea = () => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n textarea.focus();\n if (document.activeElement === textarea) {\n textarea.setSelectionRange(textarea.value.length, textarea.value.length);\n return;\n }\n attemptsLeft -= 1;\n if (attemptsLeft > 0) frame = window.requestAnimationFrame(focusTextarea);\n };\n focusTextarea();\n return () => window.cancelAnimationFrame(frame);\n }, [editing]);\n\n const markerId = `umode-flow-annotation-arrow-${id}`;\n const isText = annotation.kind === 'text';\n const toneClass = FLOW_ANNOTATION_TONE_CLASS[annotation.tone] ?? FLOW_ANNOTATION_TONE_CLASS.neutral;\n const textSizeClass = FLOW_ANNOTATION_TEXT_SIZE_CLASS[annotation.stroke ?? 'medium'];\n // The mirror and the field must measure the same: one face, one size, one line height, no wrap.\n const textMetricsClass = mergeClasses('umode-flow-annotation-text whitespace-pre', textSizeClass, 'leading-snug');\n const canResize = !readOnly && selected && !editing && !isText && annotation.kind !== 'step';\n\n const content = (() => {\n if (annotation.kind === 'step')\n return (\n <div\n className={mergeClasses(\n 'umode-flow-annotation-text pointer-events-auto flex h-full w-full items-center justify-center rounded-full text-lg leading-none shadow-sm',\n STEP_TONE_CLASS[annotation.tone] ?? STEP_TONE_CLASS.danger,\n )}\n >\n {annotation.step}\n </div>\n );\n if (!isText) return <AnnotationShape annotation={annotation} markerId={markerId} />;\n\n // Like Excalidraw: no box, no placeholder, only the caret. The hidden mirror gives the note the\n // size of what is typed, so it grows as you write and only breaks a line on Enter.\n const text = annotation.text ?? '';\n return (\n <div\n role=\"presentation\"\n onDoubleClick={readOnly || editing ? undefined : data.onEditStart}\n className={mergeClasses(\n 'umode-flow-annotation-text pointer-events-auto relative inline-block whitespace-pre leading-snug',\n textSizeClass,\n )}\n >\n <span\n aria-hidden={editing}\n className={mergeClasses(textMetricsClass, 'block min-w-[0.5rem]', editing && 'invisible')}\n >\n {`${text}${EMPTY_LINE_FILLER}`}\n </span>\n {editing && (\n <textarea\n ref={textareaRef}\n aria-label={placeholder}\n value={text}\n wrap={TEXTAREA_WRAP}\n onChange={(event) => data.onTextChange(event.target.value)}\n onBlur={data.onEditEnd}\n onKeyDown={(event) => {\n if (event.key === 'Escape') event.currentTarget.blur();\n }}\n className={mergeClasses(\n textMetricsClass,\n 'nodrag nopan nowheel absolute inset-0 h-full w-full select-text resize-none overflow-hidden border-0 bg-transparent p-0 text-current focus:outline-none focus:ring-0',\n )}\n />\n )}\n </div>\n );\n })();\n\n return (\n <div\n className={mergeClasses(\n isText ? 'relative inline-block' : 'relative h-full w-full',\n toneClass,\n selected && !editing && 'rounded-md outline-dashed outline-2 outline-offset-4 outline-border-emphasis',\n )}\n >\n <NodeResizer\n isVisible={canResize}\n minWidth={FLOW_ANNOTATION_MIN_SIZE}\n minHeight={FLOW_ANNOTATION_MIN_SIZE}\n handleClassName={RESIZE_HANDLE_CLASS}\n lineClassName={RESIZE_LINE_CLASS}\n onResizeStart={data.onResizeStart}\n onResize={(_event, params) =>\n data.onResize({\n position: { x: params.x, y: params.y },\n size: { width: params.width, height: params.height },\n })\n }\n />\n {content}\n </div>\n );\n};\n\nexport default FlowAnnotationNode;\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SymbolCodepoints } from '../MaterialSymbol';
|
|
2
|
+
import { TFlowAnnotationStroke, TFlowAnnotationTone, TFlowCanvasTool, TFlowShapeTool } from './flowTypes';
|
|
3
|
+
interface IFlowAnnotationToolbarProps {
|
|
4
|
+
tool: TFlowCanvasTool;
|
|
5
|
+
/** The shape the shape button draws: the last one picked in its menu. */
|
|
6
|
+
shapeTool: TFlowShapeTool;
|
|
7
|
+
onToolChange: (tool: TFlowCanvasTool) => void;
|
|
8
|
+
tone: TFlowAnnotationTone;
|
|
9
|
+
onToneChange: (tone: TFlowAnnotationTone) => void;
|
|
10
|
+
stroke: TFlowAnnotationStroke;
|
|
11
|
+
onStrokeChange: (stroke: TFlowAnnotationStroke) => void;
|
|
12
|
+
canUndo: boolean;
|
|
13
|
+
canRedo: boolean;
|
|
14
|
+
onUndo: () => void;
|
|
15
|
+
onRedo: () => void;
|
|
16
|
+
canDelete: boolean;
|
|
17
|
+
onDelete: () => void;
|
|
18
|
+
onClose: () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare const FLOW_TOOL_ICON: Record<TFlowCanvasTool, SymbolCodepoints>;
|
|
21
|
+
/** Single keys, as in Figma and Excalidraw. */
|
|
22
|
+
export declare const FLOW_TOOL_SHORTCUT: Partial<Record<TFlowCanvasTool, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* The annotation toolbar, laid out like the annotation bar of a screenshot tool: what to draw, its
|
|
25
|
+
* colour and weight, history, delete, and close.
|
|
26
|
+
*/
|
|
27
|
+
declare const FlowAnnotationToolbar: React.FC<IFlowAnnotationToolbarProps>;
|
|
28
|
+
/** What the closed toolbar leaves on the canvas: one button that opens it again. */
|
|
29
|
+
export declare const FlowAnnotationLauncher: React.FC<{
|
|
30
|
+
onOpen: () => void;
|
|
31
|
+
}>;
|
|
32
|
+
export default FlowAnnotationToolbar;
|