@bemedev/mind-flow 1.2.0 → 1.4.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/lib/helpers/clamp.d.ts +9 -0
- package/lib/helpers/clamp.d.ts.map +1 -1
- package/lib/index.cjs.js +1 -1
- package/lib/index.es.js +364 -341
- package/lib/output.css +1 -1
- package/lib/server.cjs.js +132 -61
- package/lib/server.es.js +132 -61
- package/lib/ui/components/EdgeComponent.d.ts.map +1 -1
- package/lib/ui/components/EditPanel.d.ts.map +1 -1
- package/lib/ui/components/FlowChart.d.ts.map +1 -1
- package/lib/ui/components/NodeComponent.d.ts.map +1 -1
- package/lib/ui/components/Panels.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/helpers/clamp.ts +9 -0
- package/src/ui/components/EdgeComponent.tsx +0 -7
- package/src/ui/components/EditPanel.tsx +6 -1
- package/src/ui/components/FlowChart.tsx +78 -14
- package/src/ui/components/NodeComponent.tsx +19 -15
- package/src/ui/components/Panels.tsx +38 -15
package/lib/server.es.js
CHANGED
|
@@ -272,6 +272,15 @@ var CLASSES = [
|
|
|
272
272
|
];
|
|
273
273
|
//#endregion
|
|
274
274
|
//#region src/helpers/clamp.ts
|
|
275
|
+
/**
|
|
276
|
+
* Restricts a number to be within the specified minimum and maximum bounds.
|
|
277
|
+
*
|
|
278
|
+
* @param value - The numerical value to clamp.
|
|
279
|
+
* @param min - The lower boundary limit.
|
|
280
|
+
* @param max - The upper boundary limit.
|
|
281
|
+
*
|
|
282
|
+
* @returns The clamped numerical value between `min` and `max`.
|
|
283
|
+
*/
|
|
275
284
|
var clamp = (value, min, max) => {
|
|
276
285
|
return Math.min(Math.max(value, min), max);
|
|
277
286
|
};
|
|
@@ -1005,13 +1014,6 @@ var _tmpl$2$5 = [
|
|
|
1005
1014
|
*
|
|
1006
1015
|
* @returns Offset distance in pixels for bezier curvature.
|
|
1007
1016
|
*/
|
|
1008
|
-
/**
|
|
1009
|
-
* Computes bezier curve control point offset based on horizontal distance.
|
|
1010
|
-
*
|
|
1011
|
-
* @param value - Absolute horizontal delta between start and end points.
|
|
1012
|
-
*
|
|
1013
|
-
* @returns Offset distance in pixels for bezier curvature.
|
|
1014
|
-
*/
|
|
1015
1017
|
var calculateOffset = (value) => value * 100 / 200;
|
|
1016
1018
|
/**
|
|
1017
1019
|
* Generates an SVG cubic bezier path `d` string between coordinates `(x0, y0)` and
|
|
@@ -1132,12 +1134,14 @@ var _tmpl$2$4 = [
|
|
|
1132
1134
|
"<!--/--><svg class=\"flex cursor-pointer items-center justify-center rounded-lg bg-blue-500 p-0.5 text-center font-bold text-white hover:bg-blue-600\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\" style=\"",
|
|
1133
1135
|
"\"><path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path></svg></div><div>",
|
|
1134
1136
|
"</div><div id=\"inputs\" class=\"pointer-events-none absolute top-0 z-10 flex cursor-default flex-col\" style=\"",
|
|
1135
|
-
"\"><div
|
|
1136
|
-
"
|
|
1137
|
-
"\"><div
|
|
1137
|
+
"\"><div data-handle-type=\"input\"",
|
|
1138
|
+
" class=\"cursor-default rounded-full bg-[#e38b29] shadow-md transition-transform duration-150 ease-in-out hover:scale-150\" style=\"",
|
|
1139
|
+
"\"></div></div><div id=\"outputs\" style=\"",
|
|
1140
|
+
"\" class=\"pointer-events-none absolute top-0 z-10 flex flex-col\"><div data-handle-type=\"output\"",
|
|
1141
|
+
" class=\"cursor-crosshair rounded-full bg-[#e38b29] shadow-md transition-transform duration-150 ease-in-out hover:scale-150\" style=\"",
|
|
1138
1142
|
"\"></div></div></div>"
|
|
1139
1143
|
];
|
|
1140
|
-
var _tmpl$3$
|
|
1144
|
+
var _tmpl$3$1 = [
|
|
1141
1145
|
"<div",
|
|
1142
1146
|
" class=\"p-3 select-none\"><!--$-->",
|
|
1143
1147
|
"<!--/--><!--$-->",
|
|
@@ -1194,7 +1198,7 @@ var NodeComponent = (props) => {
|
|
|
1194
1198
|
if (!edges) return false;
|
|
1195
1199
|
return edges.some(({ to }) => to === props.id);
|
|
1196
1200
|
} });
|
|
1197
|
-
return ssr(_tmpl$2$4, ssrHydrationKey(), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-100" : ""} ${!selected() ? "border border-[#e6d4be] z-1" : ""} min-w-48`, ssrStyleProperty("top:", `${escape(node().y, true)}px`) + ssrStyleProperty(";left:", `${escape(node().x, true)}px`), ssrAttribute("id", escape(props.id, true), false), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, ssrStyleProperty("overflow:", "visible") + ssrStyleProperty(";pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
|
|
1201
|
+
return ssr(_tmpl$2$4, ssrHydrationKey(), `flex flex-col absolute cursor-grab bg-white rounded-md shadow-md select-none transition-[border,box-shadow] duration-200 ease-in-out hover:shadow-lg draggable ${selected() ? "border border-[#e38c29] z-100" : ""} ${!selected() ? "border border-[#e6d4be] z-1" : ""} group min-w-48`, ssrStyleProperty("top:", `${escape(node().y, true)}px`) + ssrStyleProperty(";left:", `${escape(node().x, true)}px`), ssrAttribute("id", escape(props.id, true), false), `pointer-events-none absolute flex items-center justify-end -top-7.5 right-0 transition-all duration-200 ease-in-out space-x-2 ${selected() ? "w-full opacity-100" : ""} ${!selected() ? "w-0 -right-3 opacity-0 overflow-hidden" : ""}`, ssrStyleProperty("overflow:", "visible") + ssrStyleProperty(";pointer-events:", "all") + ssrStyleProperty(";width:", `${escape(12, true) * 2}px`) + ssrStyleProperty(";height:", `${escape(12, true) * 2}px`), escape(createComponent(Show, {
|
|
1198
1202
|
get when() {
|
|
1199
1203
|
return hasParent();
|
|
1200
1204
|
},
|
|
@@ -1206,7 +1210,7 @@ var NodeComponent = (props) => {
|
|
|
1206
1210
|
return props.children;
|
|
1207
1211
|
},
|
|
1208
1212
|
get fallback() {
|
|
1209
|
-
return ssr(_tmpl$3$
|
|
1213
|
+
return ssr(_tmpl$3$1, ssrHydrationKey(), escape(createComponent(Show, {
|
|
1210
1214
|
get when() {
|
|
1211
1215
|
return node().data.label;
|
|
1212
1216
|
},
|
|
@@ -1237,26 +1241,57 @@ var NodeComponent = (props) => {
|
|
|
1237
1241
|
return props.children;
|
|
1238
1242
|
} }, () => node().data));
|
|
1239
1243
|
}
|
|
1240
|
-
})), ssrStyleProperty("left:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"), ssrStyleProperty("right:", `-${escape(18, true)}px`), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"));
|
|
1244
|
+
})), ssrStyleProperty("left:", `-${escape(18, true)}px`), ssrAttribute("data-node-id", escape(props.id, true), false), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"), ssrStyleProperty("right:", `-${escape(18, true)}px`), ssrAttribute("data-node-id", escape(props.id, true), false), ssrStyleProperty("width:", `${escape(12, true)}px`) + ssrStyleProperty(";height:", `${escape(12, true)}px`) + ssrStyleProperty(";margin-top:", `${escape(12, true)}px`) + ssrStyleProperty(";pointer-events:", "all"));
|
|
1241
1245
|
};
|
|
1242
1246
|
//#endregion
|
|
1247
|
+
//#region src/ui/utils.ts
|
|
1248
|
+
/**
|
|
1249
|
+
* Merges conditional CSS class names using `clsx` and resolves Tailwind CSS class
|
|
1250
|
+
* conflicts with `twMerge`.
|
|
1251
|
+
*
|
|
1252
|
+
* @param inputs - Variable list of class values, objects, or arrays.
|
|
1253
|
+
*
|
|
1254
|
+
* @returns The resolved single class name string.
|
|
1255
|
+
*/
|
|
1256
|
+
function cn(...inputs) {
|
|
1257
|
+
return twMerge(clsx(inputs));
|
|
1258
|
+
}
|
|
1259
|
+
//#endregion
|
|
1243
1260
|
//#region src/ui/components/Panels.tsx
|
|
1244
1261
|
var _tmpl$$7 = [
|
|
1245
1262
|
"<div",
|
|
1246
|
-
" class=\"
|
|
1263
|
+
" class=\"",
|
|
1264
|
+
"\">",
|
|
1247
1265
|
"</div>"
|
|
1248
1266
|
];
|
|
1249
1267
|
var _tmpl$2$3 = [
|
|
1250
1268
|
"<div",
|
|
1251
|
-
" class=\"
|
|
1252
|
-
"
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
"<div",
|
|
1256
|
-
" class=\"absolute bottom-4 left-4 z-50\">",
|
|
1257
|
-
"</div>"
|
|
1269
|
+
" class=\"pointer-events-none absolute inset-0\"><!--$-->",
|
|
1270
|
+
"<!--/--><!--$-->",
|
|
1271
|
+
"<!--/--><!--$-->",
|
|
1272
|
+
"<!--/--></div>"
|
|
1258
1273
|
];
|
|
1259
1274
|
/**
|
|
1275
|
+
* Overlay slot component rendering an optional panel component with z-index
|
|
1276
|
+
* positioning.
|
|
1277
|
+
*
|
|
1278
|
+
* @param props - Panel properties of type {@linkcode PanelProps}.
|
|
1279
|
+
*
|
|
1280
|
+
* @returns The rendered panel container element or fallback.
|
|
1281
|
+
*
|
|
1282
|
+
* @see {@linkcode cn}
|
|
1283
|
+
*/
|
|
1284
|
+
var Panel = (props) => {
|
|
1285
|
+
const exists = isDefined(props.children);
|
|
1286
|
+
return createComponent(Show, {
|
|
1287
|
+
get when() {
|
|
1288
|
+
return props.children;
|
|
1289
|
+
},
|
|
1290
|
+
keyed: true,
|
|
1291
|
+
children: (Children) => ssr(_tmpl$$7, ssrHydrationKey(), `${escape(cn(props.class) || "", true)} ${exists ? "z-50" : ""}`, escape(createComponent(Children, {})))
|
|
1292
|
+
});
|
|
1293
|
+
};
|
|
1294
|
+
/**
|
|
1260
1295
|
* Overlay container rendering custom panel slots positioned around the flowchart
|
|
1261
1296
|
* canvas.
|
|
1262
1297
|
*
|
|
@@ -1265,29 +1300,22 @@ var _tmpl$3$1 = [
|
|
|
1265
1300
|
* @returns Rendered overlay panels JSX elements.
|
|
1266
1301
|
*/
|
|
1267
1302
|
var Panels = (props) => {
|
|
1268
|
-
return
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
get when() {
|
|
1285
|
-
return props.bottomLeft;
|
|
1286
|
-
},
|
|
1287
|
-
keyed: true,
|
|
1288
|
-
children: (Panel) => ssr(_tmpl$3$1, ssrHydrationKey(), escape(createComponent(Panel, {})))
|
|
1289
|
-
})
|
|
1290
|
-
];
|
|
1303
|
+
return ssr(_tmpl$2$3, ssrHydrationKey(), escape(createComponent(Panel, {
|
|
1304
|
+
get children() {
|
|
1305
|
+
return props.topLeft;
|
|
1306
|
+
},
|
|
1307
|
+
"class": "absolute top-4 left-4"
|
|
1308
|
+
})), escape(createComponent(Panel, {
|
|
1309
|
+
get children() {
|
|
1310
|
+
return props.topRight;
|
|
1311
|
+
},
|
|
1312
|
+
"class": "absolute top-4 right-4"
|
|
1313
|
+
})), escape(createComponent(Panel, {
|
|
1314
|
+
get children() {
|
|
1315
|
+
return props.bottomLeft;
|
|
1316
|
+
},
|
|
1317
|
+
"class": "absolute bottom-4 left-4"
|
|
1318
|
+
})));
|
|
1291
1319
|
};
|
|
1292
1320
|
//#endregion
|
|
1293
1321
|
//#region src/ui/components/NodesBoard.tsx
|
|
@@ -1500,7 +1528,7 @@ var FlowChart = (props) => {
|
|
|
1500
1528
|
const primaryNodes = props.config?.nodes ?? DEFAULT_NODES;
|
|
1501
1529
|
const primaryEdges = props.config?.edges;
|
|
1502
1530
|
const service = useFlow();
|
|
1503
|
-
const
|
|
1531
|
+
const fromSignal = createState(service, { selector: (s) => s.context.newEdge?.from });
|
|
1504
1532
|
onCleanup(service.pause);
|
|
1505
1533
|
onMount(() => {
|
|
1506
1534
|
service.resume();
|
|
@@ -1513,7 +1541,59 @@ var FlowChart = (props) => {
|
|
|
1513
1541
|
}
|
|
1514
1542
|
});
|
|
1515
1543
|
});
|
|
1516
|
-
|
|
1544
|
+
let added = false;
|
|
1545
|
+
let activeHandle = null;
|
|
1546
|
+
const clearActiveHandle = () => {
|
|
1547
|
+
if (activeHandle) {
|
|
1548
|
+
activeHandle.classList.remove("scale-150");
|
|
1549
|
+
activeHandle = null;
|
|
1550
|
+
added = false;
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
const handlePointerMove = (e) => {
|
|
1554
|
+
service.send({
|
|
1555
|
+
type: "MOVE_NEW_EDGE",
|
|
1556
|
+
payload: {
|
|
1557
|
+
x: e.clientX,
|
|
1558
|
+
y: e.clientY
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
const inputHandle = document.elementsFromPoint(e.clientX, e.clientY).map((el) => el.closest(".rounded-full[data-handle-type=\"input\"]")).find((handle) => Boolean(handle && handle.id !== "inputs"));
|
|
1562
|
+
if (activeHandle && activeHandle !== inputHandle) clearActiveHandle();
|
|
1563
|
+
if (inputHandle) {
|
|
1564
|
+
if (activeHandle !== inputHandle) {
|
|
1565
|
+
inputHandle.classList.add("scale-150");
|
|
1566
|
+
activeHandle = inputHandle;
|
|
1567
|
+
added = true;
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
};
|
|
1571
|
+
const createHandlePointerUp = (from) => (e) => {
|
|
1572
|
+
const inputHandle = document.elementsFromPoint(e.clientX, e.clientY).map((el) => el.closest(".rounded-full[data-handle-type=\"input\"]")).find((handle) => Boolean(handle && handle.id !== "inputs"));
|
|
1573
|
+
const to = inputHandle?.getAttribute("data-node-id");
|
|
1574
|
+
if (inputHandle && to) service.send({
|
|
1575
|
+
type: "ADD_EDGE",
|
|
1576
|
+
payload: {
|
|
1577
|
+
from,
|
|
1578
|
+
to
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
clearActiveHandle();
|
|
1582
|
+
service.send("CLEAR_NEW_EDGE");
|
|
1583
|
+
};
|
|
1584
|
+
createEffect(() => {
|
|
1585
|
+
const from = fromSignal();
|
|
1586
|
+
if (!from || added) return;
|
|
1587
|
+
const handlePointerUp = createHandlePointerUp(from);
|
|
1588
|
+
window.addEventListener("pointermove", handlePointerMove);
|
|
1589
|
+
window.addEventListener("pointerup", handlePointerUp);
|
|
1590
|
+
onCleanup(() => {
|
|
1591
|
+
clearActiveHandle();
|
|
1592
|
+
window.removeEventListener("pointermove", handlePointerMove);
|
|
1593
|
+
window.removeEventListener("pointerup", handlePointerUp);
|
|
1594
|
+
});
|
|
1595
|
+
});
|
|
1596
|
+
return ssr(_tmpl$$5, ssrHydrationKey(), ssrStyleProperty("cursor:", fromSignal() ? "inherit" : "crosshair"), escape(createComponent(NodesBoard, {
|
|
1517
1597
|
get panels() {
|
|
1518
1598
|
return props.panels;
|
|
1519
1599
|
},
|
|
@@ -1523,19 +1603,6 @@ var FlowChart = (props) => {
|
|
|
1523
1603
|
})));
|
|
1524
1604
|
};
|
|
1525
1605
|
//#endregion
|
|
1526
|
-
//#region src/ui/utils.ts
|
|
1527
|
-
/**
|
|
1528
|
-
* Merges conditional CSS class names using `clsx` and resolves Tailwind CSS class
|
|
1529
|
-
* conflicts with `twMerge`.
|
|
1530
|
-
*
|
|
1531
|
-
* @param inputs - Variable list of class values, objects, or arrays.
|
|
1532
|
-
*
|
|
1533
|
-
* @returns The resolved single class name string.
|
|
1534
|
-
*/
|
|
1535
|
-
function cn(...inputs) {
|
|
1536
|
-
return twMerge(clsx(inputs));
|
|
1537
|
-
}
|
|
1538
|
-
//#endregion
|
|
1539
1606
|
//#region src/ui/globals/helpers/espace.ts
|
|
1540
1607
|
/**
|
|
1541
1608
|
* Returns a zero-width space if string is empty, preserving layout flow.
|
|
@@ -2007,7 +2074,11 @@ var EditPanel = (props) => {
|
|
|
2007
2074
|
get when() {
|
|
2008
2075
|
return hooks.editing();
|
|
2009
2076
|
},
|
|
2010
|
-
children: (node) => ssr(_tmpl$, ssrHydrationKey(), `${escape(cn(`w-80 cursor-default rounded-2xl border border-gray-200 bg-white/95 p-4 shadow-xl backdrop-blur-md transition-all ${props.class ?? ""}`) || "", true)} ${escape(ssrClassList(
|
|
2077
|
+
children: (node) => ssr(_tmpl$, ssrHydrationKey(), `${escape(cn(`w-80 cursor-default rounded-2xl border border-gray-200 bg-white/95 p-4 shadow-xl backdrop-blur-md transition-all ${props.class ?? ""}`) || "", true)} ${escape(ssrClassList({
|
|
2078
|
+
...classList(),
|
|
2079
|
+
"pointer-events-none! -z-10": !hooks.editing(),
|
|
2080
|
+
"pointer-events-all! z-50": !!hooks.editing()
|
|
2081
|
+
}) || "", true)}`, ssrStyle(props.style), escape(createComponent(Show, {
|
|
2011
2082
|
get when() {
|
|
2012
2083
|
return props.header;
|
|
2013
2084
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EdgeComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/EdgeComponent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAQ,MAAM,UAAU,CAAC;AAM3C,0EAA0E;AAC1E,KAAK,KAAK,GAAG;IACX,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;
|
|
1
|
+
{"version":3,"file":"EdgeComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/EdgeComponent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAQ,MAAM,UAAU,CAAC;AAM3C,0EAA0E;AAC1E,KAAK,KAAK,GAAG;IACX,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,gEAAgE;IAChE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA2BF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,KAAK,CAwE1C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditPanel.d.ts","sourceRoot":"","sources":["../../../src/ui/components/EditPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAQ,MAAM,UAAU,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAK/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACrD,OAAO,cAAc,CAAC,CAAC,CAAC,KACvB,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"EditPanel.d.ts","sourceRoot":"","sources":["../../../src/ui/components/EditPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,GAAG,EAAQ,MAAM,UAAU,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAK/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACrD,OAAO,cAAc,CAAC,CAAC,CAAC,KACvB,GAAG,CAAC,OAoEN,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FlowChart.d.ts","sourceRoot":"","sources":["../../../src/ui/components/FlowChart.tsx"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"FlowChart.d.ts","sourceRoot":"","sources":["../../../src/ui/components/FlowChart.tsx"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,GAAG,EAAsB,MAAM,UAAU,CAAC;AAGtE,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAG1E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AAIpC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACrD,OAAO,SAAS,CAAC,CAAC,CAAC,KAClB,GAAG,CAAC,OAuGN,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/NodeComponent.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAO,KAAK,GAAG,EAAQ,MAAM,UAAU,CAAC;AAQ/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAK/D,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI;IAC9D,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACzD,OAAO,kBAAkB,CAAC,CAAC,CAAC,KAC3B,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"NodeComponent.d.ts","sourceRoot":"","sources":["../../../src/ui/components/NodeComponent.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAO,KAAK,GAAG,EAAQ,MAAM,UAAU,CAAC;AAQ/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAK/D,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,IAAI;IAC9D,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,iEAAiE;IACjE,QAAQ,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,QAAQ,GAAG,QAAQ,EACzD,OAAO,kBAAkB,CAAC,CAAC,CAAC,KAC3B,GAAG,CAAC,OA4NN,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Panels.d.ts","sourceRoot":"","sources":["../../../src/ui/components/Panels.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Panels.d.ts","sourceRoot":"","sources":["../../../src/ui/components/Panels.tsx"],"names":[],"mappings":"AACA,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAGhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAiCpD;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,CAAC,UAAU,CAQxC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bemedev/mind-flow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Flow Chart UI library for Solid.js applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"diagram",
|
|
@@ -65,13 +65,13 @@
|
|
|
65
65
|
"@bemedev/app": "^2.5.0",
|
|
66
66
|
"@bemedev/app-cli": "^2.5.0",
|
|
67
67
|
"@bemedev/app-solidjs": "^2.5.1",
|
|
68
|
-
"@bemedev/dev-utils": "^1.
|
|
68
|
+
"@bemedev/dev-utils": "^1.3.1",
|
|
69
69
|
"@rollup/plugin-babel": "^7.1.0",
|
|
70
70
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
71
71
|
"@size-limit/file": "^13.0.3",
|
|
72
72
|
"@tailwindcss/cli": "^4.3.3",
|
|
73
73
|
"@tailwindcss/vite": "^4.3.3",
|
|
74
|
-
"@types/node": "^26.
|
|
74
|
+
"@types/node": "^26.4.0",
|
|
75
75
|
"@vitest/coverage-v8": "^4.1.11",
|
|
76
76
|
"glob": "^13.0.6",
|
|
77
77
|
"globals": "^17.11.0",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"oxlint": "^1.80.0",
|
|
82
82
|
"pretty-quick": "^4.2.2",
|
|
83
83
|
"rimraf": "^6.1.3",
|
|
84
|
-
"rolldown": "^1.2.
|
|
84
|
+
"rolldown": "^1.2.6",
|
|
85
85
|
"rollup-preset-solid": "^3.0.0",
|
|
86
86
|
"size-limit": "^13.0.3",
|
|
87
87
|
"solid-js": "^1.9.15",
|
|
@@ -113,7 +113,8 @@
|
|
|
113
113
|
"node": ">=22"
|
|
114
114
|
},
|
|
115
115
|
"scripts": {
|
|
116
|
-
"build": "pnpm rm:lib && pnpm run build:
|
|
116
|
+
"build": "pnpm rm:lib && pnpm run build:dev",
|
|
117
|
+
"build:dev": "pnpm run build:server && pnpm run build:client && pnpm run build:css",
|
|
117
118
|
"build:client": "vite build",
|
|
118
119
|
"build:server": "vite build --mode server",
|
|
119
120
|
"build:css": "pnpx @tailwindcss/cli -i ./input.css -o ../lib/output.css --minify --cwd ./src",
|
package/src/helpers/clamp.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restricts a number to be within the specified minimum and maximum bounds.
|
|
3
|
+
*
|
|
4
|
+
* @param value - The numerical value to clamp.
|
|
5
|
+
* @param min - The lower boundary limit.
|
|
6
|
+
* @param max - The upper boundary limit.
|
|
7
|
+
*
|
|
8
|
+
* @returns The clamped numerical value between `min` and `max`.
|
|
9
|
+
*/
|
|
1
10
|
export const clamp = (value: number, min: number, max: number) => {
|
|
2
11
|
return Math.min(Math.max(value, min), max);
|
|
3
12
|
};
|
|
@@ -14,13 +14,6 @@ type Props = {
|
|
|
14
14
|
isNew?: boolean;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* Computes bezier curve control point offset based on horizontal distance.
|
|
19
|
-
*
|
|
20
|
-
* @param value - Absolute horizontal delta between start and end points.
|
|
21
|
-
*
|
|
22
|
-
* @returns Offset distance in pixels for bezier curvature.
|
|
23
|
-
*/
|
|
24
17
|
/**
|
|
25
18
|
* Computes bezier curve control point offset based on horizontal distance.
|
|
26
19
|
*
|
|
@@ -25,6 +25,7 @@ export const EditPanel = <D extends NodeData = NodeData>(
|
|
|
25
25
|
props: EditPanelProps<D>,
|
|
26
26
|
): JSX.Element => {
|
|
27
27
|
void clickOutside;
|
|
28
|
+
// const editing
|
|
28
29
|
const hooks = useHook<D>(props.timeout);
|
|
29
30
|
const classList = () =>
|
|
30
31
|
props.classList instanceof Function ? props.classList(hooks) : props.classList;
|
|
@@ -36,7 +37,11 @@ export const EditPanel = <D extends NodeData = NodeData>(
|
|
|
36
37
|
class={cn(
|
|
37
38
|
`w-80 cursor-default rounded-2xl border border-gray-200 bg-white/95 p-4 shadow-xl backdrop-blur-md transition-all ${props.class ?? ''}`,
|
|
38
39
|
)}
|
|
39
|
-
classList={
|
|
40
|
+
classList={{
|
|
41
|
+
...classList(),
|
|
42
|
+
'pointer-events-none! -z-10': !hooks.editing(),
|
|
43
|
+
'pointer-events-all! z-50': !!hooks.editing(),
|
|
44
|
+
}}
|
|
40
45
|
style={props.style}
|
|
41
46
|
onMouseDown={e => e.stopPropagation()}
|
|
42
47
|
use:clickOutside={hooks.close}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createState } from '@bemedev/app-solidjs';
|
|
2
|
-
import { type JSX, onCleanup, onMount } from 'solid-js';
|
|
2
|
+
import { createEffect, type JSX, onCleanup, onMount } from 'solid-js';
|
|
3
3
|
|
|
4
4
|
import { DEFAULT_NODES } from '#services/main.machine.data';
|
|
5
5
|
import type { NodeData, NodeProps } from '#services/main.machine.typings';
|
|
@@ -32,7 +32,9 @@ export const FlowChart = <D extends NodeData = NodeData>(
|
|
|
32
32
|
const primaryNodes = props.config?.nodes ?? DEFAULT_NODES;
|
|
33
33
|
const primaryEdges = props.config?.edges;
|
|
34
34
|
const service = useFlow();
|
|
35
|
-
const
|
|
35
|
+
const fromSignal = createState(service, {
|
|
36
|
+
selector: s => s.context.newEdge?.from,
|
|
37
|
+
});
|
|
36
38
|
onCleanup(service.pause);
|
|
37
39
|
|
|
38
40
|
onMount(() => {
|
|
@@ -47,21 +49,83 @@ export const FlowChart = <D extends NodeData = NodeData>(
|
|
|
47
49
|
});
|
|
48
50
|
});
|
|
49
51
|
|
|
52
|
+
let added = false;
|
|
53
|
+
|
|
54
|
+
// Track the currently enlarged handle during edge drag
|
|
55
|
+
let activeHandle: HTMLElement | null = null;
|
|
56
|
+
|
|
57
|
+
const clearActiveHandle = () => {
|
|
58
|
+
if (activeHandle) {
|
|
59
|
+
activeHandle.classList.remove('scale-150');
|
|
60
|
+
activeHandle = null;
|
|
61
|
+
added = false;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handlePointerMove = (e: MouseEvent | PointerEvent) => {
|
|
66
|
+
service.send({ type: 'MOVE_NEW_EDGE', payload: { x: e.clientX, y: e.clientY } });
|
|
67
|
+
|
|
68
|
+
const elements = document.elementsFromPoint(e.clientX, e.clientY);
|
|
69
|
+
const inputHandle = elements
|
|
70
|
+
.map(el => el.closest<HTMLElement>('.rounded-full[data-handle-type="input"]'))
|
|
71
|
+
.find((handle): handle is HTMLElement =>
|
|
72
|
+
Boolean(handle && handle.id !== 'inputs'),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// If cursor moved away from previous handle, reset its scale
|
|
76
|
+
if (activeHandle && activeHandle !== inputHandle) {
|
|
77
|
+
clearActiveHandle();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// If cursor entered a valid target handle, enlarge it
|
|
81
|
+
if (inputHandle) {
|
|
82
|
+
if (activeHandle !== inputHandle) {
|
|
83
|
+
inputHandle.classList.add('scale-150');
|
|
84
|
+
activeHandle = inputHandle;
|
|
85
|
+
added = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const createHandlePointerUp = (from: string) => (e: MouseEvent | PointerEvent) => {
|
|
91
|
+
const elements = document.elementsFromPoint(e.clientX, e.clientY);
|
|
92
|
+
const inputHandle = elements
|
|
93
|
+
.map(el => el.closest<HTMLElement>('.rounded-full[data-handle-type="input"]'))
|
|
94
|
+
.find((handle): handle is HTMLElement =>
|
|
95
|
+
Boolean(handle && handle.id !== 'inputs'),
|
|
96
|
+
);
|
|
97
|
+
const to = inputHandle?.getAttribute('data-node-id');
|
|
98
|
+
|
|
99
|
+
if (inputHandle && to) {
|
|
100
|
+
service.send({ type: 'ADD_EDGE', payload: { from, to } });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Cleanup on release
|
|
104
|
+
clearActiveHandle();
|
|
105
|
+
service.send('CLEAR_NEW_EDGE');
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
createEffect(() => {
|
|
109
|
+
const from = fromSignal();
|
|
110
|
+
if (!from || added) return;
|
|
111
|
+
|
|
112
|
+
const handlePointerUp = createHandlePointerUp(from);
|
|
113
|
+
window.addEventListener('pointermove', handlePointerMove);
|
|
114
|
+
window.addEventListener('pointerup', handlePointerUp);
|
|
115
|
+
|
|
116
|
+
onCleanup(() => {
|
|
117
|
+
// Cleanup on effect disposal
|
|
118
|
+
clearActiveHandle();
|
|
119
|
+
window.removeEventListener('pointermove', handlePointerMove);
|
|
120
|
+
window.removeEventListener('pointerup', handlePointerUp);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
50
124
|
return (
|
|
51
|
-
<div
|
|
52
|
-
class='relative h-full w-full'
|
|
53
|
-
onMouseUp={() => service.send('CLEAR_NEW_EDGE')}
|
|
54
|
-
onMouseMove={({ clientX, clientY }) => {
|
|
55
|
-
if (hasNewEdge())
|
|
56
|
-
service.send({
|
|
57
|
-
type: 'MOVE_NEW_EDGE',
|
|
58
|
-
payload: { x: clientX, y: clientY },
|
|
59
|
-
});
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
125
|
+
<div class='relative h-full w-full'>
|
|
62
126
|
<div
|
|
63
127
|
class='h-full w-full'
|
|
64
|
-
style={{ cursor:
|
|
128
|
+
style={{ cursor: fromSignal() ? 'inherit' : 'crosshair' }}
|
|
65
129
|
>
|
|
66
130
|
<NodesBoard panels={props.panels} component={props.component} />
|
|
67
131
|
</div>
|
|
@@ -78,7 +78,7 @@ export const NodeComponent = <D extends NodeData = NodeData>(
|
|
|
78
78
|
}}
|
|
79
79
|
|
|
80
80
|
style={{ top: `${node().y}px`, left: `${node().x}px` }}
|
|
81
|
-
class='min-w-48'
|
|
81
|
+
class='group min-w-48'
|
|
82
82
|
use:draggable={{ skipTransform: true }}
|
|
83
83
|
id={props.id}
|
|
84
84
|
|
|
@@ -204,7 +204,12 @@ export const NodeComponent = <D extends NodeData = NodeData>(
|
|
|
204
204
|
style={{ left: `-${HANDLE_CONTAINER_OFFSET_X}px` }}
|
|
205
205
|
>
|
|
206
206
|
<div
|
|
207
|
-
|
|
207
|
+
data-handle-type='input'
|
|
208
|
+
data-node-id={props.id}
|
|
209
|
+
onMouseDown={event => event.stopPropagation()}
|
|
210
|
+
onPointerDown={e => e.stopPropagation()}
|
|
211
|
+
|
|
212
|
+
class='cursor-default rounded-full bg-[#e38b29] shadow-md transition-transform duration-150 ease-in-out hover:scale-150'
|
|
208
213
|
|
|
209
214
|
style={{
|
|
210
215
|
width: `${HANDLE_SIZE}px`,
|
|
@@ -213,43 +218,42 @@ export const NodeComponent = <D extends NodeData = NodeData>(
|
|
|
213
218
|
'pointer-events': 'all',
|
|
214
219
|
}}
|
|
215
220
|
|
|
216
|
-
onMouseDown={event => {
|
|
217
|
-
event.stopPropagation();
|
|
218
|
-
}}
|
|
219
|
-
|
|
220
|
-
onPointerDown={e => e.stopPropagation()}
|
|
221
|
-
|
|
222
221
|
onMouseUp={event => {
|
|
223
222
|
event.stopPropagation();
|
|
224
223
|
const from = newEdge()?.from;
|
|
225
224
|
|
|
226
|
-
if (from) {
|
|
225
|
+
if (from && from !== props.id) {
|
|
227
226
|
service.send({ type: 'ADD_EDGE', payload: { from, to: props.id } });
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
}
|
|
228
|
+
service.send('CLEAR_NEW_EDGE');
|
|
230
229
|
}}
|
|
231
230
|
></div>
|
|
232
231
|
</div>
|
|
233
232
|
<div
|
|
234
233
|
id='outputs'
|
|
235
|
-
|
|
234
|
+
|
|
236
235
|
style={{ right: `-${HANDLE_CONTAINER_OFFSET_X}px` }}
|
|
236
|
+
|
|
237
|
+
class='pointer-events-none absolute top-0 z-10 flex flex-col'
|
|
237
238
|
>
|
|
238
239
|
<div
|
|
239
|
-
|
|
240
|
-
|
|
240
|
+
data-handle-type='output'
|
|
241
|
+
data-node-id={props.id}
|
|
242
|
+
class='cursor-crosshair rounded-full bg-[#e38b29] shadow-md transition-transform duration-150 ease-in-out hover:scale-150'
|
|
241
243
|
style={{
|
|
242
244
|
width: `${HANDLE_SIZE}px`,
|
|
243
245
|
height: `${HANDLE_SIZE}px`,
|
|
244
246
|
'margin-top': `${HANDLE_MARGIN_TOP}px`,
|
|
245
247
|
'pointer-events': 'all',
|
|
246
248
|
}}
|
|
247
|
-
|
|
248
249
|
onMouseDown={event => {
|
|
249
250
|
event.stopPropagation();
|
|
250
251
|
service.send('DESELECT');
|
|
251
252
|
service.send({ type: 'START_NEW_EDGE', payload: props.id });
|
|
252
253
|
}}
|
|
254
|
+
onPointerDown={event => {
|
|
255
|
+
event.stopPropagation();
|
|
256
|
+
}}
|
|
253
257
|
></div>
|
|
254
258
|
</div>
|
|
255
259
|
</div>
|
|
@@ -1,7 +1,40 @@
|
|
|
1
|
+
import { isDefined } from '@bemedev/app/bemedev';
|
|
1
2
|
import { Show, type Component } from 'solid-js';
|
|
2
3
|
|
|
4
|
+
import { cn } from '../utils';
|
|
3
5
|
import type { FlowPanels } from './FlowChart.types';
|
|
4
6
|
|
|
7
|
+
/** Properties for the internal {@linkcode Panel} wrapper component. */
|
|
8
|
+
type PanelProps = {
|
|
9
|
+
/** Optional component to render inside the panel container. */
|
|
10
|
+
children?: Component;
|
|
11
|
+
/** CSS class names for positioning and layout. */
|
|
12
|
+
class: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Overlay slot component rendering an optional panel component with z-index
|
|
17
|
+
* positioning.
|
|
18
|
+
*
|
|
19
|
+
* @param props - Panel properties of type {@linkcode PanelProps}.
|
|
20
|
+
*
|
|
21
|
+
* @returns The rendered panel container element or fallback.
|
|
22
|
+
*
|
|
23
|
+
* @see {@linkcode cn}
|
|
24
|
+
*/
|
|
25
|
+
const Panel: Component<PanelProps> = props => {
|
|
26
|
+
const exists = isDefined(props.children);
|
|
27
|
+
return (
|
|
28
|
+
<Show when={props.children} keyed>
|
|
29
|
+
{Children => (
|
|
30
|
+
<div class={cn(props.class)} classList={{ 'z-50': exists }}>
|
|
31
|
+
<Children />
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
</Show>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
5
38
|
/**
|
|
6
39
|
* Overlay container rendering custom panel slots positioned around the flowchart
|
|
7
40
|
* canvas.
|
|
@@ -12,20 +45,10 @@ import type { FlowPanels } from './FlowChart.types';
|
|
|
12
45
|
*/
|
|
13
46
|
export const Panels: Component<FlowPanels> = props => {
|
|
14
47
|
return (
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{/* Top-Right Panel */}
|
|
21
|
-
<Show when={props.topRight} keyed>
|
|
22
|
-
{Panel => <div class='absolute top-4 right-4 z-50'>{<Panel />}</div>}
|
|
23
|
-
</Show>
|
|
24
|
-
|
|
25
|
-
{/* Bottom-Left Panel */}
|
|
26
|
-
<Show when={props.bottomLeft} keyed>
|
|
27
|
-
{Panel => <div class='absolute bottom-4 left-4 z-50'>{<Panel />}</div>}
|
|
28
|
-
</Show>
|
|
29
|
-
</>
|
|
48
|
+
<div class='pointer-events-none absolute inset-0'>
|
|
49
|
+
<Panel children={props.topLeft} class='absolute top-4 left-4' />
|
|
50
|
+
<Panel children={props.topRight} class='absolute top-4 right-4' />
|
|
51
|
+
<Panel children={props.bottomLeft} class='absolute bottom-4 left-4' />
|
|
52
|
+
</div>
|
|
30
53
|
);
|
|
31
54
|
};
|