@umodeapporg/ui 0.23.0 → 0.23.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.
Files changed (65) hide show
  1. package/dist/assets/umode-ui.css +0 -105
  2. package/dist/components/Form/ControlledSelect.js +5 -3
  3. package/dist/components/Form/ControlledSelect.js.map +1 -1
  4. package/dist/locales/en/ui.json.d.ts +0 -67
  5. package/dist/locales/en/ui.json.js +0 -3
  6. package/dist/locales/en/ui.json.js.map +1 -1
  7. package/dist/locales/index.d.ts +0 -134
  8. package/dist/locales/pt/ui.json.d.ts +0 -67
  9. package/dist/locales/pt/ui.json.js +0 -3
  10. package/dist/locales/pt/ui.json.js.map +1 -1
  11. package/package.json +1 -10
  12. package/dist/components/UI/Flow/FlowBlockNode.d.ts +0 -35
  13. package/dist/components/UI/Flow/FlowBlockNode.js +0 -84
  14. package/dist/components/UI/Flow/FlowBlockNode.js.map +0 -1
  15. package/dist/components/UI/Flow/FlowCanvas.d.ts +0 -22
  16. package/dist/components/UI/Flow/FlowCanvas.js +0 -263
  17. package/dist/components/UI/Flow/FlowCanvas.js.map +0 -1
  18. package/dist/components/UI/Flow/FlowConnectionEdge.d.ts +0 -10
  19. package/dist/components/UI/Flow/FlowConnectionEdge.js +0 -54
  20. package/dist/components/UI/Flow/FlowConnectionEdge.js.map +0 -1
  21. package/dist/components/UI/Flow/FlowEditorProvider.d.ts +0 -17
  22. package/dist/components/UI/Flow/FlowEditorProvider.js +0 -27
  23. package/dist/components/UI/Flow/FlowEditorProvider.js.map +0 -1
  24. package/dist/components/UI/Flow/FlowInspector.d.ts +0 -11
  25. package/dist/components/UI/Flow/FlowInspector.js +0 -124
  26. package/dist/components/UI/Flow/FlowInspector.js.map +0 -1
  27. package/dist/components/UI/Flow/FlowIssueList.d.ts +0 -8
  28. package/dist/components/UI/Flow/FlowIssueList.js +0 -55
  29. package/dist/components/UI/Flow/FlowIssueList.js.map +0 -1
  30. package/dist/components/UI/Flow/FlowLaneMap.d.ts +0 -30
  31. package/dist/components/UI/Flow/FlowLaneMap.js +0 -206
  32. package/dist/components/UI/Flow/FlowLaneMap.js.map +0 -1
  33. package/dist/components/UI/Flow/FlowNodeShell.d.ts +0 -20
  34. package/dist/components/UI/Flow/FlowNodeShell.js +0 -81
  35. package/dist/components/UI/Flow/FlowNodeShell.js.map +0 -1
  36. package/dist/components/UI/Flow/FlowPalette.d.ts +0 -7
  37. package/dist/components/UI/Flow/FlowPalette.js +0 -90
  38. package/dist/components/UI/Flow/FlowPalette.js.map +0 -1
  39. package/dist/components/UI/Flow/FlowResizablePanel.d.ts +0 -20
  40. package/dist/components/UI/Flow/FlowResizablePanel.js +0 -117
  41. package/dist/components/UI/Flow/FlowResizablePanel.js.map +0 -1
  42. package/dist/components/UI/Flow/FlowZoomControls.d.ts +0 -12
  43. package/dist/components/UI/Flow/FlowZoomControls.js +0 -53
  44. package/dist/components/UI/Flow/FlowZoomControls.js.map +0 -1
  45. package/dist/components/UI/Flow/flowBlockRegistry.d.ts +0 -12
  46. package/dist/components/UI/Flow/flowBlockRegistry.js +0 -15
  47. package/dist/components/UI/Flow/flowBlockRegistry.js.map +0 -1
  48. package/dist/components/UI/Flow/flowConstants.d.ts +0 -2
  49. package/dist/components/UI/Flow/flowConstants.js +0 -5
  50. package/dist/components/UI/Flow/flowConstants.js.map +0 -1
  51. package/dist/components/UI/Flow/flowEditorStore.d.ts +0 -44
  52. package/dist/components/UI/Flow/flowEditorStore.js +0 -258
  53. package/dist/components/UI/Flow/flowEditorStore.js.map +0 -1
  54. package/dist/components/UI/Flow/flowGraph.d.ts +0 -32
  55. package/dist/components/UI/Flow/flowGraph.js +0 -156
  56. package/dist/components/UI/Flow/flowGraph.js.map +0 -1
  57. package/dist/components/UI/Flow/flowTypes.d.ts +0 -74
  58. package/dist/components/UI/Flow/flowTypes.js +0 -2
  59. package/dist/components/UI/Flow/flowTypes.js.map +0 -1
  60. package/dist/components/UI/Flow/index.d.ts +0 -14
  61. package/dist/components/UI/Flow/index.js +0 -43
  62. package/dist/components/UI/Flow/index.js.map +0 -1
  63. package/dist/components/UI/Flow/useFlowLabels.d.ts +0 -10
  64. package/dist/components/UI/Flow/useFlowLabels.js +0 -51
  65. package/dist/components/UI/Flow/useFlowLabels.js.map +0 -1
@@ -1,55 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useTranslation } from "react-i18next";
3
- import { twMerge } from "tailwind-merge";
4
- import ControlledButton from "../../Form/ControlledButton.js";
5
- import { MaterialSymbol } from "../MaterialSymbol/index.js";
6
- import NoDataFound from "../NoDataFound/index.js";
7
- import { useReactFlow } from "@xyflow/react";
8
- import { useFlowEditorStore, useFlowEditor } from "./FlowEditorProvider.js";
9
- import { useFlowLabels } from "./useFlowLabels.js";
10
- const issueKey = (issue) => `${issue.code}|${issue.nodeId ?? ""}|${issue.edgeId ?? ""}`;
11
- const FlowIssueList = ({ issues, className }) => {
12
- const { t } = useTranslation("ui");
13
- const labels = useFlowLabels();
14
- const store = useFlowEditorStore();
15
- const nodes = useFlowEditor((state) => state.graph.nodes);
16
- const { setCenter } = useReactFlow();
17
- const safeIssues = Array.isArray(issues) ? issues : [];
18
- const uniqueIssues = safeIssues.filter(
19
- (issue, index) => safeIssues.findIndex((other) => issueKey(other) === issueKey(issue)) === index
20
- );
21
- const focusNode = (nodeId) => {
22
- const node = nodes.find((candidate) => candidate.nodeId === nodeId);
23
- if (!node) return;
24
- store.getState().setSelection({ nodeIds: [node.nodeId], edgeIds: [] });
25
- void setCenter(node.position.x + 128, node.position.y + 48, { zoom: 1, duration: 200 });
26
- };
27
- return /* @__PURE__ */ jsxs("section", { className: twMerge("flex flex-col gap-y-2", className), children: [
28
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: t("flow.issues.title") }),
29
- uniqueIssues.length === 0 ? /* @__PURE__ */ jsx(NoDataFound, { compact: true, variant: "success", message: t("flow.issues.none") }) : /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-y-1", children: uniqueIssues.map((issue) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
30
- ControlledButton,
31
- {
32
- defaultHtmlButton: true,
33
- disabled: !issue.nodeId,
34
- onClick: () => focusNode(issue.nodeId),
35
- className: "interactive flex w-full items-start gap-x-2 rounded-lg px-2 py-1.5 text-left hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:bg-transparent",
36
- children: [
37
- /* @__PURE__ */ jsx(
38
- MaterialSymbol,
39
- {
40
- icon: issue.severity === "error" ? "error" : "warning",
41
- size: 16,
42
- className: issue.severity === "error" ? "text-danger-emphasis" : "text-warning-emphasis",
43
- "aria-hidden": true
44
- }
45
- ),
46
- /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground-muted", children: labels.issueMessage(issue) })
47
- ]
48
- }
49
- ) }, issueKey(issue))) })
50
- ] });
51
- };
52
- export {
53
- FlowIssueList as default
54
- };
55
- //# sourceMappingURL=FlowIssueList.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FlowIssueList.js","sources":["../../../../src/components/UI/Flow/FlowIssueList.tsx"],"sourcesContent":["import { useTranslation } from 'react-i18next';\nimport { twMerge } from 'tailwind-merge';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport NoDataFound from '@/components/UI/NoDataFound';\nimport { useReactFlow } from '@xyflow/react';\n\nimport { useFlowEditor, useFlowEditorStore } from './FlowEditorProvider';\nimport type { IFlowIssue } from './flowTypes';\nimport { useFlowLabels } from './useFlowLabels';\n\ninterface IFlowIssueListProps {\n issues: Array<IFlowIssue>;\n className?: string;\n}\n\nconst issueKey = (issue: IFlowIssue): string => `${issue.code}|${issue.nodeId ?? ''}|${issue.edgeId ?? ''}`;\n\n/** Every issue of the flow; clicking one selects and centers the block it points to. */\nconst FlowIssueList: React.FC<IFlowIssueListProps> = ({ issues, className }) => {\n const { t } = useTranslation('ui');\n const labels = useFlowLabels();\n const store = useFlowEditorStore();\n const nodes = useFlowEditor((state) => state.graph.nodes);\n const { setCenter } = useReactFlow();\n const safeIssues = Array.isArray(issues) ? issues : [];\n const uniqueIssues = safeIssues.filter(\n (issue, index) => safeIssues.findIndex((other) => issueKey(other) === issueKey(issue)) === index,\n );\n\n const focusNode = (nodeId: string | undefined) => {\n const node = nodes.find((candidate) => candidate.nodeId === nodeId);\n if (!node) return;\n store.getState().setSelection({ nodeIds: [node.nodeId], edgeIds: [] });\n void setCenter(node.position.x + 128, node.position.y + 48, { zoom: 1, duration: 200 });\n };\n\n return (\n <section className={twMerge('flex flex-col gap-y-2', className)}>\n <span className=\"text-sm font-semibold text-foreground\">{t('flow.issues.title')}</span>\n {uniqueIssues.length === 0 ? (\n <NoDataFound compact variant=\"success\" message={t('flow.issues.none')} />\n ) : (\n <ul className=\"flex flex-col gap-y-1\">\n {uniqueIssues.map((issue) => (\n <li key={issueKey(issue)}>\n <ControlledButton\n defaultHtmlButton\n disabled={!issue.nodeId}\n onClick={() => focusNode(issue.nodeId)}\n className=\"interactive flex w-full items-start gap-x-2 rounded-lg px-2 py-1.5 text-left hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:bg-transparent\"\n >\n <MaterialSymbol\n icon={issue.severity === 'error' ? 'error' : 'warning'}\n size={16}\n className={issue.severity === 'error' ? 'text-danger-emphasis' : 'text-warning-emphasis'}\n aria-hidden\n />\n <span className=\"text-xs text-foreground-muted\">{labels.issueMessage(issue)}</span>\n </ControlledButton>\n </li>\n ))}\n </ul>\n )}\n </section>\n );\n};\n\nexport default FlowIssueList;\n"],"names":[],"mappings":";;;;;;;;;AAiBA,MAAM,WAAW,CAAC,UAA8B,GAAG,MAAM,IAAI,IAAI,MAAM,UAAU,EAAE,IAAI,MAAM,UAAU,EAAE;AAGzG,MAAM,gBAA+C,CAAC,EAAE,QAAQ,gBAAgB;AAC9E,QAAM,EAAE,EAAA,IAAM,eAAe,IAAI;AACjC,QAAM,SAAS,cAAA;AACf,QAAM,QAAQ,mBAAA;AACd,QAAM,QAAQ,cAAc,CAAC,UAAU,MAAM,MAAM,KAAK;AACxD,QAAM,EAAE,UAAA,IAAc,aAAA;AACtB,QAAM,aAAa,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAA;AACpD,QAAM,eAAe,WAAW;AAAA,IAC9B,CAAC,OAAO,UAAU,WAAW,UAAU,CAAC,UAAU,SAAS,KAAK,MAAM,SAAS,KAAK,CAAC,MAAM;AAAA,EAAA;AAG7F,QAAM,YAAY,CAAC,WAA+B;AAChD,UAAM,OAAO,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM;AAClE,QAAI,CAAC,KAAM;AACX,UAAM,SAAA,EAAW,aAAa,EAAE,SAAS,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA,GAAI;AACrE,SAAK,UAAU,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,IAAI,EAAE,MAAM,GAAG,UAAU,KAAK;AAAA,EACxF;AAEA,8BACG,WAAA,EAAQ,WAAW,QAAQ,yBAAyB,SAAS,GAC5D,UAAA;AAAA,IAAA,oBAAC,QAAA,EAAK,WAAU,yCAAyC,UAAA,EAAE,mBAAmB,GAAE;AAAA,IAC/E,aAAa,WAAW,IACvB,oBAAC,eAAY,SAAO,MAAC,SAAQ,WAAU,SAAS,EAAE,kBAAkB,EAAA,CAAG,IAEvE,oBAAC,MAAA,EAAG,WAAU,yBACX,uBAAa,IAAI,CAAC,UACjB,oBAAC,MAAA,EACC,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,mBAAiB;AAAA,QACjB,UAAU,CAAC,MAAM;AAAA,QACjB,SAAS,MAAM,UAAU,MAAM,MAAM;AAAA,QACrC,WAAU;AAAA,QAEV,UAAA;AAAA,UAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAM,MAAM,aAAa,UAAU,UAAU;AAAA,cAC7C,MAAM;AAAA,cACN,WAAW,MAAM,aAAa,UAAU,yBAAyB;AAAA,cACjE,eAAW;AAAA,YAAA;AAAA,UAAA;AAAA,8BAEZ,QAAA,EAAK,WAAU,iCAAiC,UAAA,OAAO,aAAa,KAAK,EAAA,CAAE;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA,EAC9E,GAdO,SAAS,KAAK,CAevB,CACD,EAAA,CACH;AAAA,EAAA,GAEJ;AAEJ;"}
@@ -1,30 +0,0 @@
1
- import { ReactNode } from 'react';
2
- export interface IFlowLane {
3
- laneId: string;
4
- title: string;
5
- subtitle?: string;
6
- /** Counters or chips under the title (rules acting on entry, stage requirements). */
7
- badges?: ReactNode;
8
- }
9
- export type TFlowArcTone = 'default' | 'warning' | 'danger';
10
- export interface IFlowArc {
11
- arcId: string;
12
- sourceLaneId: string;
13
- targetLaneId: string;
14
- label: string;
15
- /** `over` arcs above the lanes, `under` below them; the host decides what each side means. */
16
- side: 'over' | 'under';
17
- tone?: TFlowArcTone;
18
- dashed?: boolean;
19
- }
20
- interface IFlowLaneMapProps {
21
- lanes: Array<IFlowLane>;
22
- arcs: Array<IFlowArc>;
23
- onArcClick?: (arcId: string) => void;
24
- onLaneClick?: (laneId: string) => void;
25
- selectedArcId?: string | null;
26
- ariaLabel?: string;
27
- className?: string;
28
- }
29
- declare const FlowLaneMap: React.FC<IFlowLaneMapProps>;
30
- export default FlowLaneMap;
@@ -1,206 +0,0 @@
1
- import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { useMemo } from "react";
3
- import classNames from "classnames";
4
- import { useTranslation } from "react-i18next";
5
- import { twMerge } from "tailwind-merge";
6
- import ControlledButton from "../../Form/ControlledButton.js";
7
- import { ReactFlowProvider, MarkerType, ReactFlow, Background, BackgroundVariant, BaseEdge, EdgeLabelRenderer, Handle, Position } from "@xyflow/react";
8
- import OutlinedChip from "../Chips/OutlinedChip.js";
9
- import FlowZoomControls from "./FlowZoomControls.js";
10
- import "@xyflow/react/dist/base.css";
11
- /* empty css */
12
- const LANE_WIDTH = 192;
13
- const LANE_HEIGHT = 168;
14
- const LANE_GAP = 80;
15
- const HIDDEN_HANDLE_CLASS = "!size-px !min-h-0 !min-w-0 !border-0 !bg-transparent";
16
- const LaneNode = ({ data }) => {
17
- const { lane, onLaneClick } = data;
18
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [
19
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: lane.title }),
20
- lane.subtitle && /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground-subtle", children: lane.subtitle }),
21
- lane.badges && /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-wrap gap-1", children: lane.badges })
22
- ] });
23
- return /* @__PURE__ */ jsxs("div", { style: { width: LANE_WIDTH, height: LANE_HEIGHT }, className: "relative", children: [
24
- /* @__PURE__ */ jsx(
25
- Handle,
26
- {
27
- id: "over-out",
28
- type: "source",
29
- position: Position.Top,
30
- isConnectable: false,
31
- className: HIDDEN_HANDLE_CLASS
32
- }
33
- ),
34
- /* @__PURE__ */ jsx(
35
- Handle,
36
- {
37
- id: "over-in",
38
- type: "target",
39
- position: Position.Top,
40
- isConnectable: false,
41
- className: HIDDEN_HANDLE_CLASS
42
- }
43
- ),
44
- onLaneClick ? /* @__PURE__ */ jsx(
45
- ControlledButton,
46
- {
47
- defaultHtmlButton: true,
48
- onClick: () => onLaneClick(lane.laneId),
49
- className: "interactive flex size-full flex-col gap-y-1 rounded-xl border border-border bg-surface p-3 text-left shadow-sm hover:border-border-emphasis focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
50
- children: content
51
- }
52
- ) : /* @__PURE__ */ jsx("div", { className: "flex size-full flex-col gap-y-1 rounded-xl border border-border bg-surface p-3 shadow-sm", children: content }),
53
- /* @__PURE__ */ jsx(
54
- Handle,
55
- {
56
- id: "under-out",
57
- type: "source",
58
- position: Position.Bottom,
59
- isConnectable: false,
60
- className: HIDDEN_HANDLE_CLASS
61
- }
62
- ),
63
- /* @__PURE__ */ jsx(
64
- Handle,
65
- {
66
- id: "under-in",
67
- type: "target",
68
- position: Position.Bottom,
69
- isConnectable: false,
70
- className: HIDDEN_HANDLE_CLASS
71
- }
72
- )
73
- ] });
74
- };
75
- const ArcEdge = ({
76
- id,
77
- sourceX,
78
- sourceY,
79
- targetX,
80
- targetY,
81
- data,
82
- markerEnd,
83
- selected
84
- }) => {
85
- const arc = data?.arc;
86
- const isOver = arc?.side !== "under";
87
- const isBackward = targetX < sourceX;
88
- const lift = 40 + Math.abs(targetX - sourceX) * 0.2 + (isBackward ? 32 : 0);
89
- const controlY = isOver ? Math.min(sourceY, targetY) - lift * 1.3 : Math.max(sourceY, targetY) + lift * 1.3;
90
- const path = `M ${sourceX},${sourceY} C ${sourceX},${controlY} ${targetX},${controlY} ${targetX},${targetY}`;
91
- const labelX = (sourceX + targetX) / 2;
92
- const labelY = 0.125 * (sourceY + targetY) + 0.75 * controlY;
93
- return /* @__PURE__ */ jsxs(Fragment, { children: [
94
- /* @__PURE__ */ jsx(
95
- BaseEdge,
96
- {
97
- id,
98
- path,
99
- markerEnd,
100
- interactionWidth: 16,
101
- className: classNames("umode-flow-arc", {
102
- "umode-flow-arc--warning": arc?.tone === "warning",
103
- "umode-flow-arc--danger": arc?.tone === "danger",
104
- "umode-flow-arc--dashed": arc?.dashed,
105
- "umode-flow-arc--selected": selected
106
- })
107
- }
108
- ),
109
- arc?.label && /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsx(
110
- "div",
111
- {
112
- className: "nodrag nopan pointer-events-none absolute whitespace-nowrap",
113
- style: { transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)` },
114
- children: /* @__PURE__ */ jsx(
115
- OutlinedChip,
116
- {
117
- label: arc.label,
118
- tone: arc.tone === "warning" ? "warning" : arc.tone === "danger" ? "danger" : "neutral",
119
- variant: "outlined",
120
- size: "xs",
121
- preserveLabelCase: true
122
- }
123
- )
124
- }
125
- ) })
126
- ] });
127
- };
128
- const ATTRIBUTION_POSITION = "bottom-left";
129
- const NODE_TYPES = { flowLane: LaneNode };
130
- const EDGE_TYPES = { flowArc: ArcEdge };
131
- const FlowLaneMapContent = ({
132
- lanes,
133
- arcs,
134
- onArcClick,
135
- onLaneClick,
136
- selectedArcId,
137
- ariaLabel,
138
- className
139
- }) => {
140
- const { t } = useTranslation("ui");
141
- const safeLanes = useMemo(() => Array.isArray(lanes) ? lanes : [], [lanes]);
142
- const safeArcs = useMemo(() => Array.isArray(arcs) ? arcs : [], [arcs]);
143
- const nodes = useMemo(
144
- () => safeLanes.map((lane, index) => ({
145
- id: lane.laneId,
146
- type: "flowLane",
147
- position: { x: index * (LANE_WIDTH + LANE_GAP), y: 0 },
148
- draggable: false,
149
- selectable: false,
150
- data: { lane, onLaneClick }
151
- })),
152
- [safeLanes, onLaneClick]
153
- );
154
- const laneIds = useMemo(() => new Set(safeLanes.map((lane) => lane.laneId)), [safeLanes]);
155
- const edges = useMemo(
156
- () => safeArcs.filter(
157
- (arc) => laneIds.has(arc.sourceLaneId) && laneIds.has(arc.targetLaneId) && arc.sourceLaneId !== arc.targetLaneId
158
- ).map((arc) => ({
159
- id: arc.arcId,
160
- type: "flowArc",
161
- source: arc.sourceLaneId,
162
- sourceHandle: `${arc.side}-out`,
163
- target: arc.targetLaneId,
164
- targetHandle: `${arc.side}-in`,
165
- selected: arc.arcId === selectedArcId,
166
- markerEnd: { type: MarkerType.ArrowClosed, width: 14, height: 14 },
167
- data: { arc }
168
- })),
169
- [safeArcs, laneIds, selectedArcId]
170
- );
171
- return /* @__PURE__ */ jsx(
172
- "div",
173
- {
174
- role: "region",
175
- "aria-label": ariaLabel ?? t("flow.laneMap.label"),
176
- className: twMerge("umode-flow relative h-full min-h-[24rem] w-full rounded-xl bg-canvas", className),
177
- children: /* @__PURE__ */ jsxs(
178
- ReactFlow,
179
- {
180
- nodes,
181
- edges,
182
- nodeTypes: NODE_TYPES,
183
- edgeTypes: EDGE_TYPES,
184
- nodesDraggable: false,
185
- nodesConnectable: false,
186
- deleteKeyCode: null,
187
- onEdgeClick: (_event, edge) => onArcClick?.(edge.id),
188
- minZoom: 0.25,
189
- maxZoom: 1.5,
190
- fitView: true,
191
- fitViewOptions: { padding: 0.15, minZoom: 0.75 },
192
- attributionPosition: ATTRIBUTION_POSITION,
193
- children: [
194
- /* @__PURE__ */ jsx(Background, { variant: BackgroundVariant.Dots, gap: 16, size: 1 }),
195
- /* @__PURE__ */ jsx(FlowZoomControls, {})
196
- ]
197
- }
198
- )
199
- }
200
- );
201
- };
202
- const FlowLaneMap = (props) => /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(FlowLaneMapContent, { ...props }) });
203
- export {
204
- FlowLaneMap as default
205
- };
206
- //# sourceMappingURL=FlowLaneMap.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FlowLaneMap.js","sources":["../../../../src/components/UI/Flow/FlowLaneMap.tsx"],"sourcesContent":["import { ReactNode, useMemo } from 'react';\nimport classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\nimport { twMerge } from 'tailwind-merge';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport {\n Background,\n BackgroundVariant,\n BaseEdge,\n type Edge,\n EdgeLabelRenderer,\n type EdgeProps,\n Handle,\n MarkerType,\n type Node,\n type NodeProps,\n type PanelPosition,\n Position,\n ReactFlow,\n ReactFlowProvider,\n} from '@xyflow/react';\n\nimport OutlinedChip from '../Chips/OutlinedChip';\n\nimport FlowZoomControls from './FlowZoomControls';\n\nimport '@xyflow/react/dist/base.css';\nimport '../../../styles/flow.css';\n\nexport interface IFlowLane {\n laneId: string;\n title: string;\n subtitle?: string;\n /** Counters or chips under the title (rules acting on entry, stage requirements). */\n badges?: ReactNode;\n}\n\nexport type TFlowArcTone = 'default' | 'warning' | 'danger';\n\nexport interface IFlowArc {\n arcId: string;\n sourceLaneId: string;\n targetLaneId: string;\n label: string;\n /** `over` arcs above the lanes, `under` below them; the host decides what each side means. */\n side: 'over' | 'under';\n tone?: TFlowArcTone;\n dashed?: boolean;\n}\n\ninterface IFlowLaneMapProps {\n lanes: Array<IFlowLane>;\n arcs: Array<IFlowArc>;\n onArcClick?: (arcId: string) => void;\n onLaneClick?: (laneId: string) => void;\n selectedArcId?: string | null;\n ariaLabel?: string;\n className?: string;\n}\n\nconst LANE_WIDTH = 192;\nconst LANE_HEIGHT = 168;\nconst LANE_GAP = 80;\n\ninterface ILaneNodeData extends Record<string, unknown> {\n lane: IFlowLane;\n onLaneClick?: (laneId: string) => void;\n}\n\ninterface IArcEdgeData extends Record<string, unknown> {\n arc: IFlowArc;\n}\n\ntype TLaneNode = Node<ILaneNodeData, 'flowLane'>;\ntype TArcEdge = Edge<IArcEdgeData, 'flowArc'>;\n\nconst HIDDEN_HANDLE_CLASS = '!size-px !min-h-0 !min-w-0 !border-0 !bg-transparent';\n\nconst LaneNode: React.FC<NodeProps<TLaneNode>> = ({ data }) => {\n const { lane, onLaneClick } = data;\n const content = (\n <>\n <span className=\"truncate text-sm font-semibold text-foreground\">{lane.title}</span>\n {lane.subtitle && <span className=\"truncate text-xs text-foreground-subtle\">{lane.subtitle}</span>}\n {lane.badges && <div className=\"mt-auto flex flex-wrap gap-1\">{lane.badges}</div>}\n </>\n );\n\n return (\n <div style={{ width: LANE_WIDTH, height: LANE_HEIGHT }} className=\"relative\">\n <Handle\n id=\"over-out\"\n type=\"source\"\n position={Position.Top}\n isConnectable={false}\n className={HIDDEN_HANDLE_CLASS}\n />\n <Handle\n id=\"over-in\"\n type=\"target\"\n position={Position.Top}\n isConnectable={false}\n className={HIDDEN_HANDLE_CLASS}\n />\n {onLaneClick ? (\n <ControlledButton\n defaultHtmlButton\n onClick={() => onLaneClick(lane.laneId)}\n className=\"interactive flex size-full flex-col gap-y-1 rounded-xl border border-border bg-surface p-3 text-left shadow-sm hover:border-border-emphasis focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {content}\n </ControlledButton>\n ) : (\n <div className=\"flex size-full flex-col gap-y-1 rounded-xl border border-border bg-surface p-3 shadow-sm\">\n {content}\n </div>\n )}\n <Handle\n id=\"under-out\"\n type=\"source\"\n position={Position.Bottom}\n isConnectable={false}\n className={HIDDEN_HANDLE_CLASS}\n />\n <Handle\n id=\"under-in\"\n type=\"target\"\n position={Position.Bottom}\n isConnectable={false}\n className={HIDDEN_HANDLE_CLASS}\n />\n </div>\n );\n};\n\n/** A curve between two lane edges whose height grows with the distance, so long arcs clear the short ones. */\nconst ArcEdge: React.FC<EdgeProps<TArcEdge>> = ({\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n data,\n markerEnd,\n selected,\n}) => {\n const arc = data?.arc;\n const isOver = arc?.side !== 'under';\n const isBackward = targetX < sourceX;\n const lift = 40 + Math.abs(targetX - sourceX) * 0.2 + (isBackward ? 32 : 0);\n const controlY = isOver ? Math.min(sourceY, targetY) - lift * 1.3 : Math.max(sourceY, targetY) + lift * 1.3;\n const path = `M ${sourceX},${sourceY} C ${sourceX},${controlY} ${targetX},${controlY} ${targetX},${targetY}`;\n const labelX = (sourceX + targetX) / 2;\n const labelY = 0.125 * (sourceY + targetY) + 0.75 * controlY;\n\n return (\n <>\n <BaseEdge\n id={id}\n path={path}\n markerEnd={markerEnd}\n interactionWidth={16}\n className={classNames('umode-flow-arc', {\n 'umode-flow-arc--warning': arc?.tone === 'warning',\n 'umode-flow-arc--danger': arc?.tone === 'danger',\n 'umode-flow-arc--dashed': arc?.dashed,\n 'umode-flow-arc--selected': selected,\n })}\n />\n {arc?.label && (\n <EdgeLabelRenderer>\n <div\n className=\"nodrag nopan pointer-events-none absolute whitespace-nowrap\"\n style={{ transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)` }}\n >\n <OutlinedChip\n label={arc.label}\n tone={arc.tone === 'warning' ? 'warning' : arc.tone === 'danger' ? 'danger' : 'neutral'}\n variant=\"outlined\"\n size=\"xs\"\n preserveLabelCase\n />\n </div>\n </EdgeLabelRenderer>\n )}\n </>\n );\n};\n\nconst ATTRIBUTION_POSITION: PanelPosition = 'bottom-left';\nconst NODE_TYPES = { flowLane: LaneNode };\nconst EDGE_TYPES = { flowArc: ArcEdge };\n\n/**\n * A read-only map of fixed columns joined by arcs: the shape of a pipeline seen from\n * above. Lanes keep the order they are given, so no graph layout is needed.\n */\nconst FlowLaneMapContent: React.FC<IFlowLaneMapProps> = ({\n lanes,\n arcs,\n onArcClick,\n onLaneClick,\n selectedArcId,\n ariaLabel,\n className,\n}) => {\n const { t } = useTranslation('ui');\n const safeLanes = useMemo(() => (Array.isArray(lanes) ? lanes : []), [lanes]);\n const safeArcs = useMemo(() => (Array.isArray(arcs) ? arcs : []), [arcs]);\n\n const nodes = useMemo<Array<TLaneNode>>(\n () =>\n safeLanes.map((lane, index) => ({\n id: lane.laneId,\n type: 'flowLane',\n position: { x: index * (LANE_WIDTH + LANE_GAP), y: 0 },\n draggable: false,\n selectable: false,\n data: { lane, onLaneClick },\n })),\n [safeLanes, onLaneClick],\n );\n\n const laneIds = useMemo(() => new Set(safeLanes.map((lane) => lane.laneId)), [safeLanes]);\n\n const edges = useMemo<Array<TArcEdge>>(\n () =>\n safeArcs\n .filter(\n (arc) =>\n laneIds.has(arc.sourceLaneId) && laneIds.has(arc.targetLaneId) && arc.sourceLaneId !== arc.targetLaneId,\n )\n .map((arc) => ({\n id: arc.arcId,\n type: 'flowArc',\n source: arc.sourceLaneId,\n sourceHandle: `${arc.side}-out`,\n target: arc.targetLaneId,\n targetHandle: `${arc.side}-in`,\n selected: arc.arcId === selectedArcId,\n markerEnd: { type: MarkerType.ArrowClosed, width: 14, height: 14 },\n data: { arc },\n })),\n [safeArcs, laneIds, selectedArcId],\n );\n\n return (\n <div\n role=\"region\"\n aria-label={ariaLabel ?? t('flow.laneMap.label')}\n className={twMerge('umode-flow relative h-full min-h-[24rem] w-full rounded-xl bg-canvas', className)}\n >\n <ReactFlow<TLaneNode, TArcEdge>\n nodes={nodes}\n edges={edges}\n nodeTypes={NODE_TYPES}\n edgeTypes={EDGE_TYPES}\n nodesDraggable={false}\n nodesConnectable={false}\n deleteKeyCode={null}\n onEdgeClick={(_event, edge) => onArcClick?.(edge.id)}\n minZoom={0.25}\n maxZoom={1.5}\n fitView\n fitViewOptions={{ padding: 0.15, minZoom: 0.75 }}\n attributionPosition={ATTRIBUTION_POSITION}\n >\n <Background variant={BackgroundVariant.Dots} gap={16} size={1} />\n <FlowZoomControls />\n </ReactFlow>\n </div>\n );\n};\n\nconst FlowLaneMap: React.FC<IFlowLaneMapProps> = (props) => (\n <ReactFlowProvider>\n <FlowLaneMapContent {...props} />\n </ReactFlowProvider>\n);\n\nexport default FlowLaneMap;\n"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { SymbolCodepoints } from '../MaterialSymbol';
3
- import { TFlowIssueSeverity, TFlowRunStepStatus } from './flowTypes';
4
- export interface IFlowNodeShellProps {
5
- icon: SymbolCodepoints;
6
- title: string;
7
- summary?: ReactNode;
8
- categoryLabel?: string;
9
- selected?: boolean;
10
- disabled?: boolean;
11
- issueSeverity?: TFlowIssueSeverity;
12
- issueCount?: number;
13
- runStatus?: TFlowRunStepStatus;
14
- /** The ports, rendered by the canvas at the block's edges. */
15
- children?: ReactNode;
16
- className?: string;
17
- }
18
- /** The look of one block: icon, title, a one-line summary and its status badges. */
19
- declare const FlowNodeShell: React.FC<IFlowNodeShellProps>;
20
- export default FlowNodeShell;
@@ -1,81 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import classNames from "classnames";
3
- import { useTranslation } from "react-i18next";
4
- import OutlinedChip from "../Chips/OutlinedChip.js";
5
- import { MaterialSymbol } from "../MaterialSymbol/index.js";
6
- import { useFlowLabels, FLOW_RUN_STATUS_TONE } from "./useFlowLabels.js";
7
- const RUN_BORDER_CLASS = {
8
- passed: "border-success-border",
9
- wouldRun: "border-info-border",
10
- skipped: "border-border",
11
- blocked: "border-warning-border",
12
- failed: "border-danger-border",
13
- error: "border-danger-border"
14
- };
15
- const FlowNodeShell = ({
16
- icon,
17
- title,
18
- summary,
19
- categoryLabel,
20
- selected = false,
21
- disabled = false,
22
- issueSeverity,
23
- issueCount = 0,
24
- runStatus,
25
- children,
26
- className
27
- }) => {
28
- const { t } = useTranslation("ui");
29
- const labels = useFlowLabels();
30
- const borderClass = runStatus ? RUN_BORDER_CLASS[runStatus] : issueSeverity === "error" ? "border-danger-border" : issueSeverity === "warning" ? "border-warning-border" : "border-border";
31
- return /* @__PURE__ */ jsxs(
32
- "div",
33
- {
34
- className: classNames(
35
- "relative flex w-64 flex-col gap-y-2 rounded-xl border-2 bg-surface px-4 py-3 shadow-sm transition-colors duration-fast",
36
- borderClass,
37
- { "ring-2 ring-ring ring-offset-2 ring-offset-canvas": selected, "opacity-60": disabled },
38
- className
39
- ),
40
- children: [
41
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
42
- /* @__PURE__ */ jsx("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground-muted", children: /* @__PURE__ */ jsx(MaterialSymbol, { icon, size: 18, "aria-hidden": true }) }),
43
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col", children: [
44
- categoryLabel && /* @__PURE__ */ jsx("span", { className: "text-xxs font-semibold uppercase tracking-wide text-foreground-subtle", children: categoryLabel }),
45
- /* @__PURE__ */ jsx("span", { className: "truncate text-sm font-semibold text-foreground", children: title })
46
- ] })
47
- ] }),
48
- summary && /* @__PURE__ */ jsx("div", { className: "line-clamp-2 text-xs text-foreground-muted", children: summary }),
49
- (runStatus || issueCount > 0 || disabled) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1", children: [
50
- runStatus && /* @__PURE__ */ jsx(
51
- OutlinedChip,
52
- {
53
- size: "xs",
54
- variant: "soft",
55
- tone: FLOW_RUN_STATUS_TONE[runStatus],
56
- label: labels.runStatus[runStatus],
57
- preserveLabelCase: true,
58
- dot: true
59
- }
60
- ),
61
- issueCount > 0 && /* @__PURE__ */ jsx(
62
- OutlinedChip,
63
- {
64
- size: "xs",
65
- variant: "soft",
66
- tone: issueSeverity === "error" ? "danger" : "warning",
67
- label: t("flow.node.issueCount", { count: issueCount }),
68
- preserveLabelCase: true
69
- }
70
- ),
71
- disabled && /* @__PURE__ */ jsx(OutlinedChip, { size: "xs", variant: "soft", tone: "neutral", label: t("flow.node.disabled"), preserveLabelCase: true })
72
- ] }),
73
- children
74
- ]
75
- }
76
- );
77
- };
78
- export {
79
- FlowNodeShell as default
80
- };
81
- //# sourceMappingURL=FlowNodeShell.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FlowNodeShell.js","sources":["../../../../src/components/UI/Flow/FlowNodeShell.tsx"],"sourcesContent":["import { ReactNode } from 'react';\nimport classNames from 'classnames';\nimport { useTranslation } from 'react-i18next';\n\nimport OutlinedChip from '@/components/UI/Chips/OutlinedChip';\nimport { MaterialSymbol, SymbolCodepoints } from '@/components/UI/MaterialSymbol';\n\nimport type { TFlowIssueSeverity, TFlowRunStepStatus } from './flowTypes';\nimport { FLOW_RUN_STATUS_TONE, useFlowLabels } from './useFlowLabels';\n\nexport interface IFlowNodeShellProps {\n icon: SymbolCodepoints;\n title: string;\n summary?: ReactNode;\n categoryLabel?: string;\n selected?: boolean;\n disabled?: boolean;\n issueSeverity?: TFlowIssueSeverity;\n issueCount?: number;\n runStatus?: TFlowRunStepStatus;\n /** The ports, rendered by the canvas at the block's edges. */\n children?: ReactNode;\n className?: string;\n}\n\nconst RUN_BORDER_CLASS: Record<TFlowRunStepStatus, string> = {\n passed: 'border-success-border',\n wouldRun: 'border-info-border',\n skipped: 'border-border',\n blocked: 'border-warning-border',\n failed: 'border-danger-border',\n error: 'border-danger-border',\n};\n\n/** The look of one block: icon, title, a one-line summary and its status badges. */\nconst FlowNodeShell: React.FC<IFlowNodeShellProps> = ({\n icon,\n title,\n summary,\n categoryLabel,\n selected = false,\n disabled = false,\n issueSeverity,\n issueCount = 0,\n runStatus,\n children,\n className,\n}) => {\n const { t } = useTranslation('ui');\n const labels = useFlowLabels();\n\n const borderClass = runStatus\n ? RUN_BORDER_CLASS[runStatus]\n : issueSeverity === 'error'\n ? 'border-danger-border'\n : issueSeverity === 'warning'\n ? 'border-warning-border'\n : 'border-border';\n\n return (\n <div\n className={classNames(\n 'relative flex w-64 flex-col gap-y-2 rounded-xl border-2 bg-surface px-4 py-3 shadow-sm transition-colors duration-fast',\n borderClass,\n { 'ring-2 ring-ring ring-offset-2 ring-offset-canvas': selected, 'opacity-60': disabled },\n className,\n )}\n >\n <div className=\"flex items-center gap-x-2\">\n <span className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground-muted\">\n <MaterialSymbol icon={icon} size={18} aria-hidden />\n </span>\n <div className=\"flex min-w-0 flex-col\">\n {categoryLabel && (\n <span className=\"text-xxs font-semibold uppercase tracking-wide text-foreground-subtle\">\n {categoryLabel}\n </span>\n )}\n <span className=\"truncate text-sm font-semibold text-foreground\">{title}</span>\n </div>\n </div>\n {summary && <div className=\"line-clamp-2 text-xs text-foreground-muted\">{summary}</div>}\n {(runStatus || issueCount > 0 || disabled) && (\n <div className=\"flex flex-wrap items-center gap-1\">\n {runStatus && (\n <OutlinedChip\n size=\"xs\"\n variant=\"soft\"\n tone={FLOW_RUN_STATUS_TONE[runStatus]}\n label={labels.runStatus[runStatus]}\n preserveLabelCase\n dot\n />\n )}\n {issueCount > 0 && (\n <OutlinedChip\n size=\"xs\"\n variant=\"soft\"\n tone={issueSeverity === 'error' ? 'danger' : 'warning'}\n label={t('flow.node.issueCount', { count: issueCount })}\n preserveLabelCase\n />\n )}\n {disabled && (\n <OutlinedChip size=\"xs\" variant=\"soft\" tone=\"neutral\" label={t('flow.node.disabled')} preserveLabelCase />\n )}\n </div>\n )}\n {children}\n </div>\n );\n};\n\nexport default FlowNodeShell;\n"],"names":[],"mappings":";;;;;;AAyBA,MAAM,mBAAuD;AAAA,EAC3D,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,OAAO;AACT;AAGA,MAAM,gBAA+C,CAAC;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AAAA,EACA,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,EAAE,EAAA,IAAM,eAAe,IAAI;AACjC,QAAM,SAAS,cAAA;AAEf,QAAM,cAAc,YAChB,iBAAiB,SAAS,IAC1B,kBAAkB,UAChB,yBACA,kBAAkB,YAChB,0BACA;AAER,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA,EAAE,qDAAqD,UAAU,cAAc,SAAA;AAAA,QAC/E;AAAA,MAAA;AAAA,MAGF,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,6BACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,8FACd,UAAA,oBAAC,gBAAA,EAAe,MAAY,MAAM,IAAI,eAAW,KAAA,CAAC,EAAA,CACpD;AAAA,UACA,qBAAC,OAAA,EAAI,WAAU,yBACZ,UAAA;AAAA,YAAA,iBACC,oBAAC,QAAA,EAAK,WAAU,yEACb,UAAA,eACH;AAAA,YAEF,oBAAC,QAAA,EAAK,WAAU,kDAAkD,UAAA,MAAA,CAAM;AAAA,UAAA,EAAA,CAC1E;AAAA,QAAA,GACF;AAAA,QACC,WAAW,oBAAC,OAAA,EAAI,WAAU,8CAA8C,UAAA,SAAQ;AAAA,SAC/E,aAAa,aAAa,KAAK,aAC/B,qBAAC,OAAA,EAAI,WAAU,qCACZ,UAAA;AAAA,UAAA,aACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAM,qBAAqB,SAAS;AAAA,cACpC,OAAO,OAAO,UAAU,SAAS;AAAA,cACjC,mBAAiB;AAAA,cACjB,KAAG;AAAA,YAAA;AAAA,UAAA;AAAA,UAGN,aAAa,KACZ;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAM,kBAAkB,UAAU,WAAW;AAAA,cAC7C,OAAO,EAAE,wBAAwB,EAAE,OAAO,YAAY;AAAA,cACtD,mBAAiB;AAAA,YAAA;AAAA,UAAA;AAAA,UAGpB,YACC,oBAAC,cAAA,EAAa,MAAK,MAAK,SAAQ,QAAO,MAAK,WAAU,OAAO,EAAE,oBAAoB,GAAG,mBAAiB,KAAA,CAAC;AAAA,QAAA,GAE5G;AAAA,QAED;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGP;"}
@@ -1,7 +0,0 @@
1
- interface IFlowPaletteProps {
2
- readOnly?: boolean;
3
- className?: string;
4
- }
5
- /** The blocks a flow can use, grouped by category. Drag one onto the canvas, or click to add it below the selection. */
6
- declare const FlowPalette: React.FC<IFlowPaletteProps>;
7
- export default FlowPalette;
@@ -1,90 +0,0 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useState, useMemo } from "react";
3
- import { useTranslation } from "react-i18next";
4
- import { twMerge } from "tailwind-merge";
5
- import ControlledButton from "../../Form/ControlledButton.js";
6
- import UnifiedSearchBar from "../../Search/UnifiedSearchBar.js";
7
- import { MaterialSymbol } from "../MaterialSymbol/index.js";
8
- import NoDataFound from "../NoDataFound/index.js";
9
- import { FLOW_BLOCK_DRAG_TYPE } from "./flowConstants.js";
10
- import { useFlowBlockRegistry, useFlowEditorStore, useFlowEditor } from "./FlowEditorProvider.js";
11
- import { useFlowLabels } from "./useFlowLabels.js";
12
- const CATEGORY_ORDER = ["trigger", "condition", "action"];
13
- const FlowPalette = ({ readOnly = false, className }) => {
14
- const { t } = useTranslation("ui");
15
- const labels = useFlowLabels();
16
- const registry = useFlowBlockRegistry();
17
- const store = useFlowEditorStore();
18
- const nodes = useFlowEditor((state) => state.graph.nodes);
19
- const [visibleBlocks, setVisibleBlocks] = useState(registry.list);
20
- const instancesByType = useMemo(() => {
21
- const counts = /* @__PURE__ */ new Map();
22
- nodes.forEach((node) => counts.set(node.type, (counts.get(node.type) ?? 0) + 1));
23
- return counts;
24
- }, [nodes]);
25
- const startDrag = (event, type) => {
26
- event.dataTransfer.setData(FLOW_BLOCK_DRAG_TYPE, type);
27
- event.dataTransfer.effectAllowed = "copy";
28
- };
29
- return /* @__PURE__ */ jsxs(
30
- "aside",
31
- {
32
- className: twMerge(
33
- "flex h-full min-h-0 flex-col gap-y-3 rounded-xl border border-border bg-surface p-3",
34
- className
35
- ),
36
- children: [
37
- /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-foreground", children: t("flow.palette.title") }),
38
- /* @__PURE__ */ jsx(
39
- UnifiedSearchBar,
40
- {
41
- mode: "default",
42
- label: t("flow.palette.search"),
43
- searchableItems: registry.list,
44
- searchKeys: ["label", "description"],
45
- setFilteredItems: setVisibleBlocks,
46
- minSearchLength: 1,
47
- debounceTime: 150
48
- }
49
- ),
50
- /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-y-4 overflow-y-auto", children: [
51
- visibleBlocks.length === 0 && /* @__PURE__ */ jsx(NoDataFound, { compact: true, variant: "search", message: t("flow.palette.empty") }),
52
- CATEGORY_ORDER.map((category) => {
53
- const blocks = visibleBlocks.filter((block) => block.category === category);
54
- if (blocks.length === 0) return null;
55
- return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-y-1", children: [
56
- /* @__PURE__ */ jsx("span", { className: "text-xxs font-semibold uppercase tracking-wide text-foreground-subtle", children: labels.categories[category] }),
57
- blocks.map((block) => {
58
- const limitReached = block.maxInstances !== void 0 && (instancesByType.get(block.type) ?? 0) >= block.maxInstances;
59
- const disabled = readOnly || limitReached;
60
- return /* @__PURE__ */ jsxs(
61
- ControlledButton,
62
- {
63
- defaultHtmlButton: true,
64
- draggable: !disabled,
65
- disabled,
66
- onDragStart: (event) => startDrag(event, block.type),
67
- onClick: () => store.getState().addNode(block.type),
68
- className: "interactive flex items-start gap-x-2 rounded-lg border border-transparent px-2 py-2 text-left hover:border-border hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-60",
69
- children: [
70
- /* @__PURE__ */ jsx("span", { className: "flex size-7 shrink-0 items-center justify-center rounded-md bg-muted text-foreground-muted", children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: block.icon, size: 16, "aria-hidden": true }) }),
71
- /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col", children: [
72
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: block.label }),
73
- limitReached ? /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground-subtle", children: t("flow.palette.limitReached") }) : block.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground-subtle", children: block.description })
74
- ] })
75
- ]
76
- },
77
- block.type
78
- );
79
- })
80
- ] }, category);
81
- })
82
- ] })
83
- ]
84
- }
85
- );
86
- };
87
- export {
88
- FlowPalette as default
89
- };
90
- //# sourceMappingURL=FlowPalette.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FlowPalette.js","sources":["../../../../src/components/UI/Flow/FlowPalette.tsx"],"sourcesContent":["import { DragEvent, useMemo, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { twMerge } from 'tailwind-merge';\n\nimport ControlledButton from '@/components/Form/ControlledButton';\nimport UnifiedSearchBar from '@/components/Search/UnifiedSearchBar';\nimport { MaterialSymbol } from '@/components/UI/MaterialSymbol';\nimport NoDataFound from '@/components/UI/NoDataFound';\n\nimport { FLOW_BLOCK_DRAG_TYPE } from './flowConstants';\nimport { useFlowBlockRegistry, useFlowEditor, useFlowEditorStore } from './FlowEditorProvider';\nimport type { IFlowBlockDefinition, TFlowBlockCategory } from './flowTypes';\nimport { useFlowLabels } from './useFlowLabels';\n\nconst CATEGORY_ORDER: Array<TFlowBlockCategory> = ['trigger', 'condition', 'action'];\n\ninterface IFlowPaletteProps {\n readOnly?: boolean;\n className?: string;\n}\n\n/** The blocks a flow can use, grouped by category. Drag one onto the canvas, or click to add it below the selection. */\nconst FlowPalette: React.FC<IFlowPaletteProps> = ({ readOnly = false, className }) => {\n const { t } = useTranslation('ui');\n const labels = useFlowLabels();\n const registry = useFlowBlockRegistry();\n const store = useFlowEditorStore();\n const nodes = useFlowEditor((state) => state.graph.nodes);\n const [visibleBlocks, setVisibleBlocks] = useState<Array<IFlowBlockDefinition>>(registry.list);\n\n const instancesByType = useMemo(() => {\n const counts = new Map<string, number>();\n nodes.forEach((node) => counts.set(node.type, (counts.get(node.type) ?? 0) + 1));\n return counts;\n }, [nodes]);\n\n const startDrag = (event: DragEvent<HTMLButtonElement>, type: string) => {\n event.dataTransfer.setData(FLOW_BLOCK_DRAG_TYPE, type);\n event.dataTransfer.effectAllowed = 'copy';\n };\n\n return (\n <aside\n className={twMerge(\n 'flex h-full min-h-0 flex-col gap-y-3 rounded-xl border border-border bg-surface p-3',\n className,\n )}\n >\n <span className=\"text-sm font-semibold text-foreground\">{t('flow.palette.title')}</span>\n <UnifiedSearchBar<IFlowBlockDefinition>\n mode=\"default\"\n label={t('flow.palette.search')}\n searchableItems={registry.list}\n searchKeys={['label', 'description']}\n setFilteredItems={setVisibleBlocks}\n minSearchLength={1}\n debounceTime={150}\n />\n <div className=\"flex min-h-0 flex-1 flex-col gap-y-4 overflow-y-auto\">\n {visibleBlocks.length === 0 && <NoDataFound compact variant=\"search\" message={t('flow.palette.empty')} />}\n {CATEGORY_ORDER.map((category) => {\n const blocks = visibleBlocks.filter((block) => block.category === category);\n if (blocks.length === 0) return null;\n return (\n <section key={category} className=\"flex flex-col gap-y-1\">\n <span className=\"text-xxs font-semibold uppercase tracking-wide text-foreground-subtle\">\n {labels.categories[category]}\n </span>\n {blocks.map((block) => {\n const limitReached =\n block.maxInstances !== undefined && (instancesByType.get(block.type) ?? 0) >= block.maxInstances;\n const disabled = readOnly || limitReached;\n return (\n <ControlledButton\n key={block.type}\n defaultHtmlButton\n draggable={!disabled}\n disabled={disabled}\n onDragStart={(event) => startDrag(event, block.type)}\n onClick={() => store.getState().addNode(block.type)}\n className=\"interactive flex items-start gap-x-2 rounded-lg border border-transparent px-2 py-2 text-left hover:border-border hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-60\"\n >\n <span className=\"flex size-7 shrink-0 items-center justify-center rounded-md bg-muted text-foreground-muted\">\n <MaterialSymbol icon={block.icon} size={16} aria-hidden />\n </span>\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"text-sm font-medium text-foreground\">{block.label}</span>\n {limitReached ? (\n <span className=\"text-xs text-foreground-subtle\">{t('flow.palette.limitReached')}</span>\n ) : (\n block.description && <span className=\"text-xs text-foreground-subtle\">{block.description}</span>\n )}\n </span>\n </ControlledButton>\n );\n })}\n </section>\n );\n })}\n </div>\n </aside>\n );\n};\n\nexport default FlowPalette;\n"],"names":[],"mappings":"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"}
@@ -1,20 +0,0 @@
1
- import { ReactNode } from 'react';
2
- export interface IFlowResizablePanelProps {
3
- /** Which edge carries the drag handle: the one that faces the canvas. */
4
- handleSide: 'left' | 'right';
5
- /** Widths in px, like any pointer-driven geometry. */
6
- defaultWidth: number;
7
- minWidth: number;
8
- maxWidth: number;
9
- /** Remembers the width the user chose, per panel, across visits. */
10
- storageKey?: string;
11
- className?: string;
12
- children: ReactNode;
13
- }
14
- /**
15
- * A side panel of the flow editor that the user can widen or narrow by dragging the edge facing
16
- * the canvas (arrow keys on the focused handle, double click resets). Below `lg` it takes the full
17
- * width and the handle hides: stacked under the canvas there is nothing to resize against.
18
- */
19
- declare const FlowResizablePanel: React.FC<IFlowResizablePanelProps>;
20
- export default FlowResizablePanel;