@leavepulse/ui 0.22.11 → 0.22.12

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.
@@ -6,6 +6,7 @@ import { MiniMap } from "@vue-flow/minimap";
6
6
  import { _ as _sfc_main$3 } from "./LpInfraNode-qIL3l7IO.js";
7
7
  import { _ as _sfc_main$1 } from "./LpLaneNode-HtnQf2FF.js";
8
8
  import { _ as _sfc_main$2 } from "./LpServiceNode-D8uJ8sGk.js";
9
+ import { _ as _sfc_main$4 } from "./LpWaypointEdge-D8yHlQiw.js";
9
10
  const _hoisted_1 = { class: "flex flex-col gap-1.5 rounded-card border border-line bg-surface-overlay/90 px-3 py-2 text-[11px] backdrop-blur" };
10
11
  const _hoisted_2 = {
11
12
  width: "22",
@@ -38,6 +39,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
38
39
  service: markRaw(_sfc_main$2),
39
40
  lane: markRaw(_sfc_main$1)
40
41
  };
42
+ const edgeTypes = { waypoints: markRaw(_sfc_main$4) };
41
43
  const laneNodes = computed(
42
44
  () => props.lanes.map(
43
45
  (l) => ({
@@ -107,6 +109,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
107
109
  source: e.source,
108
110
  target: e.target,
109
111
  type: props.edgeType ?? "default",
112
+ data: { waypoints: e.waypoints },
110
113
  animated: obs !== "applied" && !e.dimmed,
111
114
  label: e.kind,
112
115
  labelBgStyle: { fill: "var(--color-surface)" },
@@ -189,6 +192,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
189
192
  nodes: flowNodes.value,
190
193
  edges: flowEdges.value,
191
194
  "node-types": nodeTypes,
195
+ "edge-types": edgeTypes,
192
196
  "nodes-connectable": __props.connectable,
193
197
  "selection-on-drag": __props.selectable,
194
198
  "pan-on-drag": __props.selectable ? [1, 2] : true,
@@ -0,0 +1,67 @@
1
+ import { defineComponent, computed, openBlock, createBlock, unref, normalizeStyle } from "vue";
2
+ import { getBezierPath, Position, BaseEdge } from "@vue-flow/core";
3
+ const _sfc_main = /* @__PURE__ */ defineComponent({
4
+ __name: "LpWaypointEdge",
5
+ props: {
6
+ id: {},
7
+ sourceX: {},
8
+ sourceY: {},
9
+ targetX: {},
10
+ targetY: {},
11
+ sourcePosition: {},
12
+ targetPosition: {},
13
+ data: {},
14
+ markerEnd: {},
15
+ style: {}
16
+ },
17
+ setup(__props) {
18
+ const props = __props;
19
+ function splineThrough(points) {
20
+ if (points.length < 2) return "";
21
+ const p = [points[0], ...points, points[points.length - 1]];
22
+ let d = `M${points[0].x},${points[0].y}`;
23
+ for (let i = 1; i < p.length - 2; i++) {
24
+ const p0 = p[i - 1];
25
+ const p1 = p[i];
26
+ const p2 = p[i + 1];
27
+ const p3 = p[i + 2];
28
+ const c1x = p1.x + (p2.x - p0.x) / 6;
29
+ const c1y = p1.y + (p2.y - p0.y) / 6;
30
+ const c2x = p2.x - (p3.x - p1.x) / 6;
31
+ const c2y = p2.y - (p3.y - p1.y) / 6;
32
+ d += ` C${c1x},${c1y} ${c2x},${c2y} ${p2.x},${p2.y}`;
33
+ }
34
+ return d;
35
+ }
36
+ const path = computed(() => {
37
+ const via = props.data?.waypoints ?? [];
38
+ if (!via.length) {
39
+ const [d] = getBezierPath({
40
+ sourceX: props.sourceX,
41
+ sourceY: props.sourceY,
42
+ targetX: props.targetX,
43
+ targetY: props.targetY,
44
+ sourcePosition: props.sourcePosition ?? Position.Right,
45
+ targetPosition: props.targetPosition ?? Position.Left
46
+ });
47
+ return d;
48
+ }
49
+ return splineThrough([
50
+ { x: props.sourceX, y: props.sourceY },
51
+ ...via,
52
+ { x: props.targetX, y: props.targetY }
53
+ ]);
54
+ });
55
+ return (_ctx, _cache) => {
56
+ return openBlock(), createBlock(unref(BaseEdge), {
57
+ id: __props.id,
58
+ path: path.value,
59
+ "marker-end": __props.markerEnd,
60
+ style: normalizeStyle(__props.style)
61
+ }, null, 8, ["id", "path", "marker-end", "style"]);
62
+ };
63
+ }
64
+ });
65
+ export {
66
+ _sfc_main as _
67
+ };
@@ -1 +1 @@
1
- export declare const COMPONENT_NAMES: readonly ["LayoutCanvas", "LayoutNode", "LpAddressInput", "LpAlert", "LpAppShell", "LpAutocomplete", "LpAvatar", "LpBadge", "LpBreadcrumbs", "LpButton", "LpCalendar", "LpCard", "LpCheckbox", "LpCodeBlock", "LpCommandPalette", "LpConfirmDialog", "LpContextMenu", "LpDatePicker", "LpDisclosure", "LpDivider", "LpDrawer", "LpDropdownMenu", "LpEmptyState", "LpFileTree", "LpFileTreeNode", "LpFormField", "LpIcon", "LpInfraNode", "LpInput", "LpLaneNode", "LpLightbox", "LpLink", "LpLogViewer", "LpMapPicker", "LpModal", "LpNotificationBell", "LpNumberField", "LpOtpInput", "LpPagination", "LpPasswordInput", "LpPhoneInput", "LpPopover", "LpProgress", "LpRadio", "LpRadioGroup", "LpScrollArea", "LpSegmented", "LpSelect", "LpServiceNode", "LpShortcutsDialog", "LpSidebar", "LpSidebarNav", "LpSkeleton", "LpSlider", "LpStat", "LpStepper", "LpSwitch", "LpTable", "LpTableOfContents", "LpTabs", "LpTextarea", "LpThemeSwitcher", "LpTilt", "LpToaster", "LpTooltip", "LpTopologyCanvas", "LpUptimeBar"];
1
+ export declare const COMPONENT_NAMES: readonly ["LayoutCanvas", "LayoutNode", "LpAddressInput", "LpAlert", "LpAppShell", "LpAutocomplete", "LpAvatar", "LpBadge", "LpBreadcrumbs", "LpButton", "LpCalendar", "LpCard", "LpCheckbox", "LpCodeBlock", "LpCommandPalette", "LpConfirmDialog", "LpContextMenu", "LpDatePicker", "LpDisclosure", "LpDivider", "LpDrawer", "LpDropdownMenu", "LpEmptyState", "LpFileTree", "LpFileTreeNode", "LpFormField", "LpIcon", "LpInfraNode", "LpInput", "LpLaneNode", "LpLightbox", "LpLink", "LpLogViewer", "LpMapPicker", "LpModal", "LpNotificationBell", "LpNumberField", "LpOtpInput", "LpPagination", "LpPasswordInput", "LpPhoneInput", "LpPopover", "LpProgress", "LpRadio", "LpRadioGroup", "LpScrollArea", "LpSegmented", "LpSelect", "LpServiceNode", "LpShortcutsDialog", "LpSidebar", "LpSidebarNav", "LpSkeleton", "LpSlider", "LpStat", "LpStepper", "LpSwitch", "LpTable", "LpTableOfContents", "LpTabs", "LpTextarea", "LpThemeSwitcher", "LpTilt", "LpToaster", "LpTooltip", "LpTopologyCanvas", "LpUptimeBar", "LpWaypointEdge"];
@@ -46,6 +46,14 @@ export interface TopologyEdge {
46
46
  * every unrelated line at full strength.
47
47
  */
48
48
  dimmed?: boolean;
49
+ /**
50
+ * Points the edge should curve through, when the host's layout knows where
51
+ * the gaps are. Requires `edgeType: "waypoints"`; ignored otherwise.
52
+ */
53
+ waypoints?: {
54
+ x: number;
55
+ y: number;
56
+ }[];
49
57
  }
50
58
  /**
51
59
  * A background swimlane: a sized zone (one per project / overlay network) drawn
@@ -96,7 +104,7 @@ type __VLS_Props = {
96
104
  * LAYERED one — a long edge crosses the cards standing between its columns.
97
105
  * `smoothstep` routes orthogonally through the gaps instead.
98
106
  */
99
- edgeType?: "default" | "smoothstep" | "step" | "straight";
107
+ edgeType?: "default" | "smoothstep" | "step" | "straight" | "waypoints";
100
108
  /** Rename legend rows to match what this graph's edges actually mean. */
101
109
  legendLabels?: Partial<Record<"network" | "structural" | "pending" | "drift", string>>;
102
110
  /**
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTopologyCanvas-BTzQE78D.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTopologyCanvas-ebP67CLx.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -0,0 +1,21 @@
1
+ import { Position } from '@vue-flow/core';
2
+ type __VLS_Props = {
3
+ id: string;
4
+ sourceX: number;
5
+ sourceY: number;
6
+ targetX: number;
7
+ targetY: number;
8
+ sourcePosition?: Position;
9
+ targetPosition?: Position;
10
+ data?: {
11
+ waypoints?: {
12
+ x: number;
13
+ y: number;
14
+ }[];
15
+ };
16
+ markerEnd?: string;
17
+ style?: Record<string, unknown>;
18
+ };
19
+ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
20
+ declare const _default: typeof __VLS_export;
21
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/LpWaypointEdge-D8yHlQiw.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
package/dist/index.js CHANGED
@@ -64,7 +64,7 @@ import { D, _ as _56, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v }
64
64
  import { _ as _57 } from "./chunks/LpTilt-DuyKbpkA.js";
65
65
  import { _ as _58 } from "./chunks/LpToaster-gDB1EaU3.js";
66
66
  import { _ as _59 } from "./chunks/LpTooltip-D1hbjQWG.js";
67
- import { _ as _60 } from "./chunks/LpTopologyCanvas-BTzQE78D.js";
67
+ import { _ as _60 } from "./chunks/LpTopologyCanvas-ebP67CLx.js";
68
68
  import { _ as _61 } from "./chunks/LpUptimeBar-CB366k0O.js";
69
69
  import { deserializeLayout, serializeLayout } from "./layout/tree.js";
70
70
  import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.22.11",
3
+ "version": "0.22.12",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -68,5 +68,6 @@ export const COMPONENT_NAMES = [
68
68
  "LpToaster",
69
69
  "LpTooltip",
70
70
  "LpTopologyCanvas",
71
- "LpUptimeBar"
71
+ "LpUptimeBar",
72
+ "LpWaypointEdge"
72
73
  ] as const
@@ -26,6 +26,7 @@ import { computed, markRaw, watch, type Component } from "vue"
26
26
  import LpInfraNode, { type InfraNodeData } from "./LpInfraNode.vue"
27
27
  import LpLaneNode from "./LpLaneNode.vue"
28
28
  import LpServiceNode, { type ServiceNodeData } from "./LpServiceNode.vue"
29
+ import LpWaypointEdge from "./LpWaypointEdge.vue"
29
30
 
30
31
  // Vue Flow base CSS + canvas chrome theming is shipped via the kit's
31
32
  // "@leavepulse/ui/canvas.css" export (a plain CSS file). Component-local CSS
@@ -77,6 +78,11 @@ export interface TopologyEdge {
77
78
  * every unrelated line at full strength.
78
79
  */
79
80
  dimmed?: boolean
81
+ /**
82
+ * Points the edge should curve through, when the host's layout knows where
83
+ * the gaps are. Requires `edgeType: "waypoints"`; ignored otherwise.
84
+ */
85
+ waypoints?: { x: number; y: number }[]
80
86
  }
81
87
 
82
88
  /**
@@ -131,7 +137,7 @@ const props = withDefaults(
131
137
  * LAYERED one — a long edge crosses the cards standing between its columns.
132
138
  * `smoothstep` routes orthogonally through the gaps instead.
133
139
  */
134
- edgeType?: "default" | "smoothstep" | "step" | "straight"
140
+ edgeType?: "default" | "smoothstep" | "step" | "straight" | "waypoints"
135
141
  /** Rename legend rows to match what this graph's edges actually mean. */
136
142
  legendLabels?: Partial<
137
143
  Record<"network" | "structural" | "pending" | "drift", string>
@@ -192,6 +198,9 @@ const nodeTypes: NodeTypesObject = {
192
198
  service: markRaw(LpServiceNode) as Component,
193
199
  lane: markRaw(LpLaneNode) as Component,
194
200
  }
201
+ // `waypoints` curves through the points an edge carries, for layouts that know
202
+ // where the gaps are; with none it falls back to the plain bezier.
203
+ const edgeTypes = { waypoints: markRaw(LpWaypointEdge) as Component }
195
204
 
196
205
  // Lanes render as non-interactive Vue Flow nodes BENEATH the graph: prepended
197
206
  // so they paint first, with selecting/dragging/connecting off and a low z so
@@ -276,6 +285,7 @@ const flowEdges = computed(() =>
276
285
  source: e.source,
277
286
  target: e.target,
278
287
  type: props.edgeType ?? "default",
288
+ data: { waypoints: e.waypoints },
279
289
  animated: obs !== "applied" && !e.dimmed,
280
290
  label: e.kind,
281
291
  labelBgStyle: { fill: "var(--color-surface)" },
@@ -382,6 +392,7 @@ defineExpose({ fitView, setViewport: applyViewport })
382
392
  :nodes="flowNodes"
383
393
  :edges="flowEdges"
384
394
  :node-types="nodeTypes"
395
+ :edge-types="edgeTypes"
385
396
  :nodes-connectable="connectable"
386
397
  :selection-on-drag="selectable"
387
398
  :pan-on-drag="selectable ? [1, 2] : true"
@@ -0,0 +1,77 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * An edge that curves THROUGH a list of waypoints.
4
+ *
5
+ * The built-in shapes each fail one way in a layered graph: a bezier bulges off
6
+ * the line between its ends and cuts through whatever stands between the
7
+ * columns, and orthogonal routing clears the cards but draws the graph as a
8
+ * grid of right angles. Given the gaps a layout already knows about, a curve
9
+ * threaded through them is both — clear of the cards and still a curve.
10
+ *
11
+ * Waypoints ride on the edge's `data.waypoints`; with none, this is a plain
12
+ * bezier, so the type is safe to use for every edge in a graph.
13
+ */
14
+ import { BaseEdge, getBezierPath, Position } from "@vue-flow/core"
15
+ import { computed } from "vue"
16
+
17
+ const props = defineProps<{
18
+ id: string
19
+ sourceX: number
20
+ sourceY: number
21
+ targetX: number
22
+ targetY: number
23
+ sourcePosition?: Position
24
+ targetPosition?: Position
25
+ data?: { waypoints?: { x: number; y: number }[] }
26
+ markerEnd?: string
27
+ style?: Record<string, unknown>
28
+ }>()
29
+
30
+ /*
31
+ * Catmull-Rom through the points, converted to cubic beziers — the standard way
32
+ * to get a curve that actually passes through its control points rather than
33
+ * being pulled towards them. Tension 0.5 keeps it taut enough not to loop back
34
+ * on itself between closely spaced waypoints.
35
+ */
36
+ function splineThrough(points: { x: number; y: number }[]): string {
37
+ if (points.length < 2) return ""
38
+ const p = [points[0]!, ...points, points[points.length - 1]!]
39
+ let d = `M${points[0]!.x},${points[0]!.y}`
40
+ for (let i = 1; i < p.length - 2; i++) {
41
+ const p0 = p[i - 1]!
42
+ const p1 = p[i]!
43
+ const p2 = p[i + 1]!
44
+ const p3 = p[i + 2]!
45
+ const c1x = p1.x + (p2.x - p0.x) / 6
46
+ const c1y = p1.y + (p2.y - p0.y) / 6
47
+ const c2x = p2.x - (p3.x - p1.x) / 6
48
+ const c2y = p2.y - (p3.y - p1.y) / 6
49
+ d += ` C${c1x},${c1y} ${c2x},${c2y} ${p2.x},${p2.y}`
50
+ }
51
+ return d
52
+ }
53
+
54
+ const path = computed(() => {
55
+ const via = props.data?.waypoints ?? []
56
+ if (!via.length) {
57
+ const [d] = getBezierPath({
58
+ sourceX: props.sourceX,
59
+ sourceY: props.sourceY,
60
+ targetX: props.targetX,
61
+ targetY: props.targetY,
62
+ sourcePosition: props.sourcePosition ?? Position.Right,
63
+ targetPosition: props.targetPosition ?? Position.Left,
64
+ })
65
+ return d
66
+ }
67
+ return splineThrough([
68
+ { x: props.sourceX, y: props.sourceY },
69
+ ...via,
70
+ { x: props.targetX, y: props.targetY },
71
+ ])
72
+ })
73
+ </script>
74
+
75
+ <template>
76
+ <BaseEdge :id="id" :path="path" :marker-end="markerEnd" :style="style" />
77
+ </template>