@upbound/monarch-blocks 0.4.1 → 0.6.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/README.md +17 -0
- package/dist/cel-filter-bar.js +29 -15
- package/dist/cel-filter-bar.js.map +1 -1
- package/dist/chart.js.map +1 -1
- package/dist/chat-interface.js +1 -1
- package/dist/chat-interface.js.map +1 -1
- package/dist/code-block.js.map +1 -1
- package/dist/data-table.d.ts +33 -180
- package/dist/data-table.js +452 -307
- package/dist/data-table.js.map +1 -1
- package/dist/empty-view-DO36LNvk.d.ts +179 -0
- package/dist/filter-bar.d.ts +6 -2
- package/dist/filter-bar.js +102 -54
- package/dist/filter-bar.js.map +1 -1
- package/dist/floating-widget.js.map +1 -1
- package/dist/graph.css +73 -0
- package/dist/graph.d.ts +182 -0
- package/dist/graph.js +1780 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1444 -369
- package/dist/index.js.map +1 -1
- package/dist/page-header.js.map +1 -1
- package/dist/relative-time.js.map +1 -1
- package/dist/section-header.js.map +1 -1
- package/dist/section-nav.js.map +1 -1
- package/dist/timeline.js.map +1 -1
- package/package.json +7 -3
package/dist/graph.js
ADDED
|
@@ -0,0 +1,1780 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __objRest = (source, exclude) => {
|
|
21
|
+
var target = {};
|
|
22
|
+
for (var prop in source)
|
|
23
|
+
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
|
24
|
+
target[prop] = source[prop];
|
|
25
|
+
if (source != null && __getOwnPropSymbols)
|
|
26
|
+
for (var prop of __getOwnPropSymbols(source)) {
|
|
27
|
+
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
|
28
|
+
target[prop] = source[prop];
|
|
29
|
+
}
|
|
30
|
+
return target;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/graph/graph-actions-provider.tsx
|
|
34
|
+
import * as React2 from "react";
|
|
35
|
+
|
|
36
|
+
// src/graph/hooks/useGraphActionsContext.ts
|
|
37
|
+
import { createContext, useContext } from "react";
|
|
38
|
+
var GraphActionsContext = createContext(null);
|
|
39
|
+
var useGraphActionsContext = () => {
|
|
40
|
+
const context = useContext(GraphActionsContext);
|
|
41
|
+
if (!context) {
|
|
42
|
+
throw new Error("useGraphActionsContext must be used within GraphActionsProvider");
|
|
43
|
+
}
|
|
44
|
+
return context;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// src/graph/hooks/useGraphActions.ts
|
|
48
|
+
import { useCallback as useCallback2, useState } from "react";
|
|
49
|
+
|
|
50
|
+
// src/graph/graph-node.tsx
|
|
51
|
+
import * as React from "react";
|
|
52
|
+
import { getIncomers, getOutgoers, Handle, Position as Position2 } from "reactflow";
|
|
53
|
+
import { Badge, Card, Icon, Spinner, Tooltip, TooltipContent, TooltipTrigger } from "@upbound/monarch-core";
|
|
54
|
+
|
|
55
|
+
// src/lib/utils.ts
|
|
56
|
+
import { clsx } from "clsx";
|
|
57
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
58
|
+
var twMerge = extendTailwindMerge({
|
|
59
|
+
extend: {
|
|
60
|
+
classGroups: {
|
|
61
|
+
"font-size": [
|
|
62
|
+
{
|
|
63
|
+
text: [
|
|
64
|
+
"display-hero",
|
|
65
|
+
"display-kpi-sm",
|
|
66
|
+
"display-kpi",
|
|
67
|
+
"display-kpi-lg",
|
|
68
|
+
"display-feature",
|
|
69
|
+
"h1",
|
|
70
|
+
"h2",
|
|
71
|
+
"h3",
|
|
72
|
+
"h4",
|
|
73
|
+
"body-lg",
|
|
74
|
+
"body",
|
|
75
|
+
"body-sm",
|
|
76
|
+
"caption",
|
|
77
|
+
"eyebrow"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
function cn(...inputs) {
|
|
85
|
+
return twMerge(clsx(inputs));
|
|
86
|
+
}
|
|
87
|
+
function sortBy(items, getKey) {
|
|
88
|
+
return [...items].sort((a, b) => getKey(a).localeCompare(getKey(b)));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/graph/consts.ts
|
|
92
|
+
var GRAPH_NODE_WIDTH = 260;
|
|
93
|
+
var GRAPH_NODE_HEIGHT = 106;
|
|
94
|
+
var GRAPH_NODE_COLLAPSED_HEIGHT = 47;
|
|
95
|
+
var GRAPH_NODE_SEP = 22;
|
|
96
|
+
var GRAPH_RANK_SEP = 54;
|
|
97
|
+
|
|
98
|
+
// src/graph/graph-edge.tsx
|
|
99
|
+
import { getSmoothStepPath, getStraightPath, Position } from "reactflow";
|
|
100
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
101
|
+
var GraphEdge = ({
|
|
102
|
+
id,
|
|
103
|
+
sourceX,
|
|
104
|
+
sourceY,
|
|
105
|
+
targetX,
|
|
106
|
+
targetY,
|
|
107
|
+
targetPosition
|
|
108
|
+
}) => {
|
|
109
|
+
const halfDistanceWidth = (targetX - sourceX) / 2;
|
|
110
|
+
const [straightEdgePath] = getStraightPath({
|
|
111
|
+
sourceX: sourceX - 4,
|
|
112
|
+
sourceY,
|
|
113
|
+
targetX: Math.max(sourceX + halfDistanceWidth, GRAPH_NODE_WIDTH / 2),
|
|
114
|
+
targetY: sourceY
|
|
115
|
+
});
|
|
116
|
+
const [edgePath] = getSmoothStepPath({
|
|
117
|
+
sourceX: Math.max(sourceX + halfDistanceWidth, GRAPH_NODE_WIDTH / 2),
|
|
118
|
+
sourceY,
|
|
119
|
+
sourcePosition: Position.Bottom,
|
|
120
|
+
targetX: targetX + 4,
|
|
121
|
+
targetY,
|
|
122
|
+
targetPosition,
|
|
123
|
+
borderRadius: 8,
|
|
124
|
+
offset: 0
|
|
125
|
+
});
|
|
126
|
+
return /* @__PURE__ */ jsxs("g", { id, className: "react-flow__edge-path", children: [
|
|
127
|
+
/* @__PURE__ */ jsx("path", { d: straightEdgePath, className: "stroke-border fill-transparent stroke-2" }),
|
|
128
|
+
/* @__PURE__ */ jsx("path", { d: edgePath, className: "stroke-border fill-transparent stroke-2" })
|
|
129
|
+
] });
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// src/graph/graph-node.tsx
|
|
133
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
134
|
+
function isDefaultNode(node) {
|
|
135
|
+
return node.type !== "loading";
|
|
136
|
+
}
|
|
137
|
+
function GraphNode(_a) {
|
|
138
|
+
var _b = _a, { id, data } = _b, props = __objRest(_b, ["id", "data"]);
|
|
139
|
+
var _a2, _b2;
|
|
140
|
+
const { nodes: originalNodes, edges: originalEdges } = useGraphActionsContext();
|
|
141
|
+
const nodes = originalNodes;
|
|
142
|
+
const edges = originalEdges;
|
|
143
|
+
const handleClick = React.useCallback(() => {
|
|
144
|
+
var _a3;
|
|
145
|
+
(_a3 = data == null ? void 0 : data.onClick) == null ? void 0 : _a3.call(data, id, data);
|
|
146
|
+
}, [data, id]);
|
|
147
|
+
const compact = (_a2 = data == null ? void 0 : data.compact) != null ? _a2 : false;
|
|
148
|
+
const state = (_b2 = data == null ? void 0 : data.state) != null ? _b2 : "default";
|
|
149
|
+
const compactBadges = React.useMemo(() => {
|
|
150
|
+
const badges = data.badges.compact || [];
|
|
151
|
+
if (state === "paused") {
|
|
152
|
+
return [
|
|
153
|
+
{ children: /* @__PURE__ */ jsx2(Icon, { name: "pause", size: "xs" }), variant: "secondary", tooltipContent: "Resource is paused" },
|
|
154
|
+
...badges
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
return badges;
|
|
158
|
+
}, [data.badges.compact, state]);
|
|
159
|
+
const defaultBadges = React.useMemo(() => {
|
|
160
|
+
const badges = data.badges.default || [];
|
|
161
|
+
if (state === "paused") {
|
|
162
|
+
return [
|
|
163
|
+
{
|
|
164
|
+
children: /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
165
|
+
/* @__PURE__ */ jsx2(Icon, { name: "pause", size: "xs", className: "mr-1" }),
|
|
166
|
+
" Paused"
|
|
167
|
+
] }),
|
|
168
|
+
variant: "secondary",
|
|
169
|
+
tooltipContent: "Resource is paused"
|
|
170
|
+
},
|
|
171
|
+
...badges
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
return badges;
|
|
175
|
+
}, [data.badges.default, state]);
|
|
176
|
+
const currentNode = React.useMemo(
|
|
177
|
+
() => ({
|
|
178
|
+
id,
|
|
179
|
+
position: { x: 0, y: 0 },
|
|
180
|
+
// Position not needed for edge calculations
|
|
181
|
+
data,
|
|
182
|
+
type: props.type
|
|
183
|
+
}),
|
|
184
|
+
[id, data, props.type]
|
|
185
|
+
);
|
|
186
|
+
const _hasIncomingEdge = React.useMemo(() => {
|
|
187
|
+
return getIncomers(currentNode, nodes, edges).length > 0;
|
|
188
|
+
}, [currentNode, nodes, edges]);
|
|
189
|
+
const hasOutgoingEdge = React.useMemo(() => {
|
|
190
|
+
return getOutgoers(currentNode, nodes, edges).length > 0;
|
|
191
|
+
}, [currentNode, nodes, edges]);
|
|
192
|
+
const targetX = GRAPH_NODE_WIDTH / 2 - 4;
|
|
193
|
+
const sourceX = 4;
|
|
194
|
+
return /* @__PURE__ */ jsxs2(
|
|
195
|
+
Card,
|
|
196
|
+
{
|
|
197
|
+
className: cn(
|
|
198
|
+
"relative gap-1.5 overflow-visible rounded-lg py-0",
|
|
199
|
+
`flex h-[106px]! w-[260px]! flex-col pt-2.5! pb-0!`,
|
|
200
|
+
`hover:shadow-md motion-safe:transition-shadow`,
|
|
201
|
+
compact && `h-[47px]!`
|
|
202
|
+
),
|
|
203
|
+
style: { height: compact ? GRAPH_NODE_COLLAPSED_HEIGHT : GRAPH_NODE_HEIGHT, width: GRAPH_NODE_WIDTH },
|
|
204
|
+
children: [
|
|
205
|
+
/* @__PURE__ */ jsx2(
|
|
206
|
+
Handle,
|
|
207
|
+
{
|
|
208
|
+
type: "target",
|
|
209
|
+
position: Position2.Left,
|
|
210
|
+
style: {
|
|
211
|
+
visibility: "hidden",
|
|
212
|
+
width: 0
|
|
213
|
+
},
|
|
214
|
+
isConnectable: false
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex w-full flex-1 cursor-pointer flex-col", onClick: handleClick, children: [
|
|
218
|
+
/* @__PURE__ */ jsxs2("div", { className: cn("flex w-full justify-between gap-1 px-2.5", compact && "flex-row-reverse items-start"), children: [
|
|
219
|
+
!!compact && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
220
|
+
compactBadges.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex shrink-0 flex-row items-center gap-1.5 overflow-hidden", children: compactBadges.map((_c, index) => {
|
|
221
|
+
var _d = _c, { tooltipContent, variant } = _d, badge = __objRest(_d, ["tooltipContent", "variant"]);
|
|
222
|
+
return /* @__PURE__ */ jsxs2(Tooltip, { children: [
|
|
223
|
+
/* @__PURE__ */ jsx2(TooltipTrigger, { children: /* @__PURE__ */ jsx2(
|
|
224
|
+
Badge,
|
|
225
|
+
__spreadProps(__spreadValues({
|
|
226
|
+
variant
|
|
227
|
+
}, badge), {
|
|
228
|
+
className: "flex size-[20px]! cursor-default! items-center justify-center p-0!",
|
|
229
|
+
children: badge.children
|
|
230
|
+
})
|
|
231
|
+
) }),
|
|
232
|
+
tooltipContent && /* @__PURE__ */ jsx2(TooltipContent, { children: tooltipContent })
|
|
233
|
+
] }, index);
|
|
234
|
+
}) }),
|
|
235
|
+
state === "progressing" && /* @__PURE__ */ jsx2("div", { className: "flex shrink-0 flex-row items-center gap-1.5 overflow-hidden", children: /* @__PURE__ */ jsx2("div", { className: "flex size-[20px]! items-center justify-center", children: /* @__PURE__ */ jsx2(Spinner, { size: "sm" }) }) })
|
|
236
|
+
] }),
|
|
237
|
+
/* @__PURE__ */ jsxs2("div", { className: "mr-auto grow-0 overflow-hidden", children: [
|
|
238
|
+
/* @__PURE__ */ jsx2("h5", { className: "text-body-sm/tight truncate font-bold", children: data.title }),
|
|
239
|
+
/* @__PURE__ */ jsx2("p", { className: "text-body-sm truncate leading-none", children: data.subtitle }),
|
|
240
|
+
!compact && /* @__PURE__ */ jsx2("p", { className: "text-body-sm text-muted-foreground mt-1 truncate", children: data.name })
|
|
241
|
+
] }),
|
|
242
|
+
/* @__PURE__ */ jsx2(
|
|
243
|
+
"span",
|
|
244
|
+
{
|
|
245
|
+
className: cn(
|
|
246
|
+
"bg-foreground text-background inline-flex h-[26px] w-auto shrink-0 items-center justify-center rounded-md px-[7px] text-[10px] font-semibold",
|
|
247
|
+
["progressing", "paused"].includes(state) && "opacity-30",
|
|
248
|
+
data.typeDescriptors.className
|
|
249
|
+
),
|
|
250
|
+
children: data.typeDescriptors.default
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
] }),
|
|
254
|
+
!compact && /* @__PURE__ */ jsx2(Fragment, { children: (defaultBadges.length > 0 || state === "progressing") && /* @__PURE__ */ jsxs2("div", { className: "mt-auto flex h-8 items-center border-t px-2.5", children: [
|
|
255
|
+
/* @__PURE__ */ jsx2("div", { className: "flex gap-1.5 overflow-hidden overflow-x-auto", children: defaultBadges.map((_e, index) => {
|
|
256
|
+
var _f = _e, { tooltipContent, variant } = _f, badge = __objRest(_f, ["tooltipContent", "variant"]);
|
|
257
|
+
return /* @__PURE__ */ jsxs2(Tooltip, { children: [
|
|
258
|
+
/* @__PURE__ */ jsx2(TooltipTrigger, { children: /* @__PURE__ */ jsx2(Badge, __spreadProps(__spreadValues({ variant }, badge), { className: "cursor-default! py-px! whitespace-nowrap", children: badge.children })) }),
|
|
259
|
+
tooltipContent && /* @__PURE__ */ jsx2(TooltipContent, { children: tooltipContent })
|
|
260
|
+
] }, index);
|
|
261
|
+
}) }),
|
|
262
|
+
state === "progressing" && /* @__PURE__ */ jsx2("div", { className: "ml-auto flex size-[20px]! items-center justify-center", children: /* @__PURE__ */ jsx2(Spinner, { size: "sm" }) })
|
|
263
|
+
] }) })
|
|
264
|
+
] }),
|
|
265
|
+
!!hasOutgoingEdge && /* @__PURE__ */ jsxs2(
|
|
266
|
+
"div",
|
|
267
|
+
{
|
|
268
|
+
className: "absolute top-0 right-0 h-full w-auto translate-x-full",
|
|
269
|
+
style: {
|
|
270
|
+
width: GRAPH_RANK_SEP
|
|
271
|
+
},
|
|
272
|
+
children: [
|
|
273
|
+
/* @__PURE__ */ jsx2("svg", { height: 20, width: GRAPH_NODE_SEP, className: "absolute top-1/2 -translate-y-1/2", children: /* @__PURE__ */ jsx2(
|
|
274
|
+
GraphEdge,
|
|
275
|
+
{
|
|
276
|
+
id: `${id}-expadable`,
|
|
277
|
+
sourceX,
|
|
278
|
+
sourceY: 10,
|
|
279
|
+
targetX,
|
|
280
|
+
targetY: 10,
|
|
281
|
+
sourcePosition: Position2.Right,
|
|
282
|
+
targetPosition: Position2.Left,
|
|
283
|
+
source: id,
|
|
284
|
+
target: id,
|
|
285
|
+
stopOpacity: 0
|
|
286
|
+
}
|
|
287
|
+
) }),
|
|
288
|
+
/* @__PURE__ */ jsxs2(Tooltip, { children: [
|
|
289
|
+
/* @__PURE__ */ jsx2(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx2(
|
|
290
|
+
"button",
|
|
291
|
+
{
|
|
292
|
+
type: "button",
|
|
293
|
+
"aria-label": data.expanded ? "Collapse" : "Expand",
|
|
294
|
+
className: "bg-foreground text-background hover:bg-foreground/90 focus-visible:ring-ring/30 absolute top-[calc(50%)] left-[calc(50%)] inline-flex size-4 -translate-1/2 items-center justify-center rounded-full p-0 text-center text-[10px] leading-none duration-150 outline-none focus-visible:ring-2 motion-safe:transition-colors",
|
|
295
|
+
onClick: (e) => {
|
|
296
|
+
var _a3;
|
|
297
|
+
e.stopPropagation();
|
|
298
|
+
(_a3 = data.onExpandToggle) == null ? void 0 : _a3.call(data, id);
|
|
299
|
+
},
|
|
300
|
+
children: /* @__PURE__ */ jsx2(Icon, { name: data.expanded ? "minus" : "plus", size: "xs" })
|
|
301
|
+
}
|
|
302
|
+
) }),
|
|
303
|
+
/* @__PURE__ */ jsx2(TooltipContent, { children: data.expanded ? "Collapse" : "Expand" })
|
|
304
|
+
] })
|
|
305
|
+
]
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
/* @__PURE__ */ jsx2(
|
|
309
|
+
Handle,
|
|
310
|
+
{
|
|
311
|
+
type: "source",
|
|
312
|
+
position: Position2.Right,
|
|
313
|
+
style: {
|
|
314
|
+
visibility: "hidden",
|
|
315
|
+
width: 0
|
|
316
|
+
},
|
|
317
|
+
isConnectable: false
|
|
318
|
+
}
|
|
319
|
+
)
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
function GraphNodeSkeleton(_props) {
|
|
325
|
+
const compact = false;
|
|
326
|
+
return /* @__PURE__ */ jsxs2(
|
|
327
|
+
Card,
|
|
328
|
+
{
|
|
329
|
+
className: cn(
|
|
330
|
+
`flex h-[106px]! w-[260px]! flex-col gap-1.5 rounded-lg px-0 py-0 pt-2.5! pb-0! hover:shadow-md motion-safe:transition-shadow`,
|
|
331
|
+
compact && `h-[47px]!`
|
|
332
|
+
),
|
|
333
|
+
children: [
|
|
334
|
+
/* @__PURE__ */ jsx2(
|
|
335
|
+
Handle,
|
|
336
|
+
{
|
|
337
|
+
type: "target",
|
|
338
|
+
position: Position2.Left,
|
|
339
|
+
style: {
|
|
340
|
+
visibility: "hidden",
|
|
341
|
+
width: 0
|
|
342
|
+
},
|
|
343
|
+
isConnectable: false
|
|
344
|
+
}
|
|
345
|
+
),
|
|
346
|
+
/* @__PURE__ */ jsxs2("div", { className: cn("flex w-full gap-1 px-2.5", compact && "flex-row-reverse"), children: [
|
|
347
|
+
/* @__PURE__ */ jsxs2("div", { className: cn("grow-0 overflow-hidden", compact && "mr-auto"), children: [
|
|
348
|
+
/* @__PURE__ */ jsx2("div", { className: "bg-muted mb-1 h-3 w-16 animate-pulse rounded-sm" }),
|
|
349
|
+
/* @__PURE__ */ jsx2("div", { className: "bg-muted h-3 w-40 animate-pulse rounded-sm" }),
|
|
350
|
+
!compact && /* @__PURE__ */ jsx2("div", { className: "bg-muted mt-2 h-3 w-48 animate-pulse rounded-sm" })
|
|
351
|
+
] }),
|
|
352
|
+
/* @__PURE__ */ jsx2("div", { className: cn("bg-muted size-[26px] shrink-0 animate-pulse rounded-md", !compact && "ml-auto") })
|
|
353
|
+
] }),
|
|
354
|
+
!compact && /* @__PURE__ */ jsx2("div", { className: "mt-auto flex h-8 items-center border-t px-2.5", children: /* @__PURE__ */ jsxs2("div", { className: "flex gap-1.5 overflow-hidden overflow-x-auto", children: [
|
|
355
|
+
/* @__PURE__ */ jsx2("div", { className: "bg-muted h-4 w-12 animate-pulse rounded-sm" }),
|
|
356
|
+
/* @__PURE__ */ jsx2("div", { className: "bg-muted h-4 w-12 animate-pulse rounded-sm" })
|
|
357
|
+
] }) }),
|
|
358
|
+
/* @__PURE__ */ jsx2(
|
|
359
|
+
Handle,
|
|
360
|
+
{
|
|
361
|
+
type: "source",
|
|
362
|
+
position: Position2.Right,
|
|
363
|
+
style: {
|
|
364
|
+
visibility: "hidden",
|
|
365
|
+
width: 0
|
|
366
|
+
},
|
|
367
|
+
isConnectable: false
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/graph/hooks/useGraphActions.ts
|
|
376
|
+
var useGraphActions = (initialNodes = [], initialEdges = []) => {
|
|
377
|
+
const [nodes, setNodes] = useState(initialNodes);
|
|
378
|
+
const [edges, setEdges] = useState(initialEdges);
|
|
379
|
+
const addNodes = useCallback2((newNodes) => {
|
|
380
|
+
setNodes((prevNodes) => {
|
|
381
|
+
const existingIds = new Set(prevNodes.map((node) => node.id));
|
|
382
|
+
const uniqueNewNodes = newNodes.filter((node) => !existingIds.has(node.id));
|
|
383
|
+
return [...prevNodes, ...uniqueNewNodes];
|
|
384
|
+
});
|
|
385
|
+
}, []);
|
|
386
|
+
const addEdges = useCallback2((newEdges) => {
|
|
387
|
+
setEdges((prevEdges) => {
|
|
388
|
+
const existingIds = new Set(prevEdges.map((edge) => edge.id));
|
|
389
|
+
const uniqueNewEdges = newEdges.filter((edge) => !existingIds.has(edge.id));
|
|
390
|
+
return [...prevEdges, ...uniqueNewEdges];
|
|
391
|
+
});
|
|
392
|
+
}, []);
|
|
393
|
+
const removeNodes = useCallback2((nodeIds) => {
|
|
394
|
+
const nodeIdSet = new Set(nodeIds);
|
|
395
|
+
setNodes((prevNodes) => prevNodes.filter((node) => !nodeIdSet.has(node.id)));
|
|
396
|
+
setEdges((prevEdges) => prevEdges.filter((edge) => !nodeIdSet.has(edge.source) && !nodeIdSet.has(edge.target)));
|
|
397
|
+
}, []);
|
|
398
|
+
const removeEdges = useCallback2((edgeIds) => {
|
|
399
|
+
const edgeIdSet = new Set(edgeIds);
|
|
400
|
+
setEdges((prevEdges) => prevEdges.filter((edge) => !edgeIdSet.has(edge.id)));
|
|
401
|
+
}, []);
|
|
402
|
+
const resetGraph = useCallback2(() => {
|
|
403
|
+
setNodes(initialNodes);
|
|
404
|
+
setEdges(initialEdges);
|
|
405
|
+
}, [initialNodes, initialEdges]);
|
|
406
|
+
const updateNode = useCallback2((nodeId, node) => {
|
|
407
|
+
setNodes((prevNodes) => prevNodes.map((n) => n.id === nodeId ? node : n));
|
|
408
|
+
}, []);
|
|
409
|
+
const toggleNodeExpansion = useCallback2((nodeId) => {
|
|
410
|
+
setNodes(
|
|
411
|
+
(prevNodes) => prevNodes.map((node) => {
|
|
412
|
+
if (node.id === nodeId && isDefaultNode(node)) {
|
|
413
|
+
return __spreadProps(__spreadValues({}, node), {
|
|
414
|
+
data: __spreadProps(__spreadValues({}, node.data), {
|
|
415
|
+
expanded: !node.data.expanded
|
|
416
|
+
})
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
return node;
|
|
420
|
+
})
|
|
421
|
+
);
|
|
422
|
+
}, []);
|
|
423
|
+
return {
|
|
424
|
+
nodes,
|
|
425
|
+
edges,
|
|
426
|
+
addNodes,
|
|
427
|
+
addEdges,
|
|
428
|
+
removeNodes,
|
|
429
|
+
removeEdges,
|
|
430
|
+
setNodes,
|
|
431
|
+
setEdges,
|
|
432
|
+
resetGraph,
|
|
433
|
+
updateNode,
|
|
434
|
+
toggleNodeExpansion
|
|
435
|
+
};
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// src/graph/graph-actions-provider.tsx
|
|
439
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
440
|
+
var GraphActionsProvider = React2.forwardRef(
|
|
441
|
+
({ initialNodes = [], initialEdges = [], children }, ref) => {
|
|
442
|
+
const graphActions = useGraphActions(initialNodes, initialEdges);
|
|
443
|
+
React2.useImperativeHandle(
|
|
444
|
+
ref,
|
|
445
|
+
() => ({
|
|
446
|
+
addNodes: graphActions.addNodes,
|
|
447
|
+
addEdges: graphActions.addEdges,
|
|
448
|
+
setNodes: graphActions.setNodes,
|
|
449
|
+
setEdges: graphActions.setEdges,
|
|
450
|
+
toggleNodeExpansion: graphActions.toggleNodeExpansion,
|
|
451
|
+
updateNode: graphActions.updateNode,
|
|
452
|
+
reset: graphActions.resetGraph
|
|
453
|
+
}),
|
|
454
|
+
[graphActions]
|
|
455
|
+
);
|
|
456
|
+
return /* @__PURE__ */ jsx3(GraphActionsContext.Provider, { value: graphActions, children });
|
|
457
|
+
}
|
|
458
|
+
);
|
|
459
|
+
GraphActionsProvider.displayName = "GraphActionsProvider";
|
|
460
|
+
|
|
461
|
+
// src/graph/graph.tsx
|
|
462
|
+
import * as React6 from "react";
|
|
463
|
+
import {
|
|
464
|
+
getCoreRowModel,
|
|
465
|
+
getFacetedRowModel,
|
|
466
|
+
getFacetedUniqueValues,
|
|
467
|
+
getFilteredRowModel,
|
|
468
|
+
useReactTable
|
|
469
|
+
} from "@tanstack/react-table";
|
|
470
|
+
import { Icon as Icon4, InputGroup, InputGroupAddon, InputGroupInput } from "@upbound/monarch-core";
|
|
471
|
+
|
|
472
|
+
// src/lib/data-table/contextual-filters.ts
|
|
473
|
+
function getColumnFilterCount(filterValue, filter) {
|
|
474
|
+
const isCheckboxValue = (value) => value === void 0 || Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
475
|
+
const isRadioValue = (value) => value === void 0 || typeof value === "string";
|
|
476
|
+
const isDateRangeValue = (value) => value === void 0 || typeof value === "object" && value !== null && "startDate" in value && "endDate" in value;
|
|
477
|
+
if ((filter == null ? void 0 : filter.type) === "checkbox") {
|
|
478
|
+
return isCheckboxValue(filterValue) ? (filterValue || []).length : 0;
|
|
479
|
+
}
|
|
480
|
+
if ((filter == null ? void 0 : filter.type) === "radio") {
|
|
481
|
+
return isRadioValue(filterValue) && filterValue !== void 0 ? 1 : 0;
|
|
482
|
+
}
|
|
483
|
+
if ((filter == null ? void 0 : filter.type) === "dateRange") {
|
|
484
|
+
if (!isDateRangeValue(filterValue)) return 0;
|
|
485
|
+
return Number(!!(filterValue == null ? void 0 : filterValue.startDate)) + Number(!!(filterValue == null ? void 0 : filterValue.endDate));
|
|
486
|
+
}
|
|
487
|
+
if (isCheckboxValue(filterValue)) {
|
|
488
|
+
return (filterValue || []).length;
|
|
489
|
+
}
|
|
490
|
+
if (isRadioValue(filterValue) && filterValue !== void 0) {
|
|
491
|
+
return 1;
|
|
492
|
+
}
|
|
493
|
+
if (isDateRangeValue(filterValue)) {
|
|
494
|
+
return Number(!!(filterValue == null ? void 0 : filterValue.startDate)) + Number(!!(filterValue == null ? void 0 : filterValue.endDate));
|
|
495
|
+
}
|
|
496
|
+
return 0;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/lib/data-table/utils.ts
|
|
500
|
+
function isFilterOnlyColumn(column) {
|
|
501
|
+
var _a;
|
|
502
|
+
return ((_a = column.columnDef.meta) == null ? void 0 : _a.filterOnly) === true;
|
|
503
|
+
}
|
|
504
|
+
function hasFilterableColumns(columns) {
|
|
505
|
+
return columns.some(
|
|
506
|
+
(column) => {
|
|
507
|
+
var _a;
|
|
508
|
+
return !isFilterOnlyColumn(column) && column.getCanFilter() && !!((_a = column.columnDef.meta) == null ? void 0 : _a.filter);
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
function getTableState({
|
|
513
|
+
error,
|
|
514
|
+
loading,
|
|
515
|
+
rowCount
|
|
516
|
+
}) {
|
|
517
|
+
if (error) return "error";
|
|
518
|
+
if (loading) return "loading";
|
|
519
|
+
return rowCount === 0 ? "empty" : "success";
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// src/data-table/empty-view.tsx
|
|
523
|
+
import { Alert, AlertDescription } from "@upbound/monarch-core";
|
|
524
|
+
import { Button } from "@upbound/monarch-core";
|
|
525
|
+
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@upbound/monarch-core";
|
|
526
|
+
import { Icon as Icon2 } from "@upbound/monarch-core";
|
|
527
|
+
import { IconTile } from "@upbound/monarch-core";
|
|
528
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
529
|
+
var defaultErrorConfig = {
|
|
530
|
+
icon: "circle-exclamation",
|
|
531
|
+
header: "Something went wrong",
|
|
532
|
+
subheader: "We couldn't load this data. Try again in a moment."
|
|
533
|
+
};
|
|
534
|
+
var defaultEmptyConfig = {
|
|
535
|
+
icon: "inbox",
|
|
536
|
+
header: "No results",
|
|
537
|
+
subheader: "There's nothing here yet."
|
|
538
|
+
};
|
|
539
|
+
var defaultNoFilterFoundConfig = {
|
|
540
|
+
icon: "magnifying-glass",
|
|
541
|
+
header: "No matching results",
|
|
542
|
+
subheader: "Try adjusting your search or filters."
|
|
543
|
+
};
|
|
544
|
+
function DataTableEmptyView({
|
|
545
|
+
emptyView,
|
|
546
|
+
tableState,
|
|
547
|
+
filterCount,
|
|
548
|
+
resetFilters,
|
|
549
|
+
className
|
|
550
|
+
}) {
|
|
551
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
552
|
+
const isFilteredEmpty = tableState === "empty" && filterCount > 0;
|
|
553
|
+
const config = tableState === "error" ? (_a = emptyView == null ? void 0 : emptyView.error) != null ? _a : defaultErrorConfig : tableState === "empty" ? isFilteredEmpty ? (_b = emptyView == null ? void 0 : emptyView.filter) != null ? _b : defaultNoFilterFoundConfig : (_c = emptyView == null ? void 0 : emptyView.empty) != null ? _c : defaultEmptyConfig : null;
|
|
554
|
+
const wrapperClassName = tableState === "error" ? (_f = (_d = emptyView == null ? void 0 : emptyView.error) == null ? void 0 : _d.wrapperClassName) != null ? _f : (_e = emptyView == null ? void 0 : emptyView.empty) == null ? void 0 : _e.wrapperClassName : config == null ? void 0 : config.wrapperClassName;
|
|
555
|
+
const defaultAction = isFilteredEmpty && !(config == null ? void 0 : config.action) ? /* @__PURE__ */ jsxs3(Button, { variant: "outline", size: "sm", onClick: resetFilters, children: [
|
|
556
|
+
/* @__PURE__ */ jsx4(Icon2, { name: "xmark" }),
|
|
557
|
+
"Clear filters"
|
|
558
|
+
] }) : null;
|
|
559
|
+
return /* @__PURE__ */ jsx4("div", { className: cn("flex size-full flex-col justify-center p-6", wrapperClassName, className), children: config && // The error state ignores its own config's `className` for outer
|
|
560
|
+
// padding, matching the source's forced `pt-0` on that branch.
|
|
561
|
+
/* @__PURE__ */ jsxs3(Empty, { className: tableState === "error" ? "pt-0" : config.className, children: [
|
|
562
|
+
/* @__PURE__ */ jsxs3(EmptyHeader, { children: [
|
|
563
|
+
config.icon && /* @__PURE__ */ jsx4(EmptyMedia, { children: /* @__PURE__ */ jsx4(IconTile, { variant: "branded", children: /* @__PURE__ */ jsx4(Icon2, { name: config.icon }) }) }),
|
|
564
|
+
config.header && /* @__PURE__ */ jsx4(EmptyTitle, { children: config.header }),
|
|
565
|
+
config.subheader && /* @__PURE__ */ jsx4(EmptyDescription, { children: config.subheader })
|
|
566
|
+
] }),
|
|
567
|
+
(config.details || config.action || defaultAction) && /* @__PURE__ */ jsxs3(EmptyContent, { children: [
|
|
568
|
+
config.details && /* @__PURE__ */ jsx4(Alert, { children: /* @__PURE__ */ jsx4(AlertDescription, { children: config.details }) }),
|
|
569
|
+
(_g = config.action) != null ? _g : defaultAction
|
|
570
|
+
] })
|
|
571
|
+
] }) });
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/data-table/view-filter.tsx
|
|
575
|
+
import * as React4 from "react";
|
|
576
|
+
|
|
577
|
+
// src/filter-bar/filter-bar.tsx
|
|
578
|
+
import * as React3 from "react";
|
|
579
|
+
import { Icon as Icon3 } from "@upbound/monarch-core";
|
|
580
|
+
import { Badge as Badge2 } from "@upbound/monarch-core";
|
|
581
|
+
import { Button as Button2 } from "@upbound/monarch-core";
|
|
582
|
+
import { ButtonGroup, ButtonGroupText } from "@upbound/monarch-core";
|
|
583
|
+
import { Calendar } from "@upbound/monarch-core";
|
|
584
|
+
import {
|
|
585
|
+
Combobox,
|
|
586
|
+
ComboboxChip,
|
|
587
|
+
ComboboxClearAll,
|
|
588
|
+
ComboboxContent,
|
|
589
|
+
ComboboxEmpty,
|
|
590
|
+
ComboboxInput,
|
|
591
|
+
ComboboxItem,
|
|
592
|
+
ComboboxList,
|
|
593
|
+
ComboboxSelectedChips,
|
|
594
|
+
ComboboxSeparator,
|
|
595
|
+
ComboboxTrigger,
|
|
596
|
+
ComboboxValue
|
|
597
|
+
} from "@upbound/monarch-core";
|
|
598
|
+
import {
|
|
599
|
+
DropdownMenu,
|
|
600
|
+
DropdownMenuCheckboxItem,
|
|
601
|
+
DropdownMenuContent,
|
|
602
|
+
DropdownMenuRadioGroup,
|
|
603
|
+
DropdownMenuRadioItem,
|
|
604
|
+
DropdownMenuTrigger
|
|
605
|
+
} from "@upbound/monarch-core";
|
|
606
|
+
import { Input } from "@upbound/monarch-core";
|
|
607
|
+
import { Label } from "@upbound/monarch-core";
|
|
608
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@upbound/monarch-core";
|
|
609
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
610
|
+
function asCheckboxValue(value) {
|
|
611
|
+
return Array.isArray(value) ? value : [];
|
|
612
|
+
}
|
|
613
|
+
function asRadioValue(value) {
|
|
614
|
+
return typeof value === "string" ? value : "";
|
|
615
|
+
}
|
|
616
|
+
function asTextValue(value) {
|
|
617
|
+
return typeof value === "string" ? value : "";
|
|
618
|
+
}
|
|
619
|
+
function asMultiTextValue(value) {
|
|
620
|
+
return Array.isArray(value) ? value : [];
|
|
621
|
+
}
|
|
622
|
+
function asDateValue(value) {
|
|
623
|
+
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
624
|
+
return { startDate: null, endDate: null };
|
|
625
|
+
}
|
|
626
|
+
function hasExplicitTime(date) {
|
|
627
|
+
if (!date) return false;
|
|
628
|
+
return date.getHours() !== 0 || date.getMinutes() !== 0;
|
|
629
|
+
}
|
|
630
|
+
function formatTimeForInput(date) {
|
|
631
|
+
if (!date) return "";
|
|
632
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
633
|
+
}
|
|
634
|
+
function applyTimeToDate(date, time) {
|
|
635
|
+
const [hours, minutes] = time.split(":").map(Number);
|
|
636
|
+
const next = new Date(date);
|
|
637
|
+
next.setHours(hours || 0, minutes || 0, 0, 0);
|
|
638
|
+
return next;
|
|
639
|
+
}
|
|
640
|
+
function formatDateLabel(date) {
|
|
641
|
+
return date.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
642
|
+
}
|
|
643
|
+
function composeRefs(...refs) {
|
|
644
|
+
return (node) => {
|
|
645
|
+
for (const ref of refs) {
|
|
646
|
+
if (typeof ref === "function") ref(node);
|
|
647
|
+
else if (ref) ref.current = node;
|
|
648
|
+
}
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function hasOrder(filter) {
|
|
652
|
+
return typeof filter.order === "number";
|
|
653
|
+
}
|
|
654
|
+
function FilterBar(_a) {
|
|
655
|
+
var _b = _a, {
|
|
656
|
+
filters,
|
|
657
|
+
activeFilters,
|
|
658
|
+
onAddFilter,
|
|
659
|
+
onRemoveFilter,
|
|
660
|
+
onChangeFilter,
|
|
661
|
+
className,
|
|
662
|
+
children,
|
|
663
|
+
ref
|
|
664
|
+
} = _b, props = __objRest(_b, [
|
|
665
|
+
"filters",
|
|
666
|
+
"activeFilters",
|
|
667
|
+
"onAddFilter",
|
|
668
|
+
"onRemoveFilter",
|
|
669
|
+
"onChangeFilter",
|
|
670
|
+
"className",
|
|
671
|
+
"children",
|
|
672
|
+
"ref"
|
|
673
|
+
]);
|
|
674
|
+
const usedKeys = new Set(activeFilters.map((f) => f.key));
|
|
675
|
+
const remainingFilters = filters.filter((f) => !usedKeys.has(f.key));
|
|
676
|
+
const availableFilters = filters.some(hasOrder) ? remainingFilters : sortBy(remainingFilters, (f) => f.label);
|
|
677
|
+
const shouldShowFilters = availableFilters.length > 0 || activeFilters.length > 0;
|
|
678
|
+
const [pendingOpenKey, setPendingOpenKey] = React3.useState(null);
|
|
679
|
+
function handleAddFilter(key) {
|
|
680
|
+
onAddFilter(key);
|
|
681
|
+
setPendingOpenKey(key);
|
|
682
|
+
}
|
|
683
|
+
const containerRef = React3.useRef(null);
|
|
684
|
+
return /* @__PURE__ */ jsxs4(
|
|
685
|
+
"div",
|
|
686
|
+
__spreadProps(__spreadValues({
|
|
687
|
+
"data-slot": "filter-bar",
|
|
688
|
+
"data-visible-filters": shouldShowFilters,
|
|
689
|
+
className: cn("flex flex-wrap items-center gap-2", className),
|
|
690
|
+
ref: composeRefs(containerRef, ref)
|
|
691
|
+
}, props), {
|
|
692
|
+
children: [
|
|
693
|
+
availableFilters.length > 0 && /* @__PURE__ */ jsxs4(
|
|
694
|
+
Combobox,
|
|
695
|
+
{
|
|
696
|
+
items: availableFilters,
|
|
697
|
+
itemToStringValue: (item) => item.label,
|
|
698
|
+
itemToStringLabel: (item) => item.label,
|
|
699
|
+
onValueChange: (filter) => {
|
|
700
|
+
if (filter) handleAddFilter(filter.key);
|
|
701
|
+
},
|
|
702
|
+
children: [
|
|
703
|
+
/* @__PURE__ */ jsxs4(ComboboxTrigger, { "aria-label": "Add filter", render: /* @__PURE__ */ jsx5(Button2, { variant: "outline" }), children: [
|
|
704
|
+
/* @__PURE__ */ jsx5(Icon3, { name: "plus", "data-icon": "inline-start" }),
|
|
705
|
+
"Add filter"
|
|
706
|
+
] }),
|
|
707
|
+
/* @__PURE__ */ jsxs4(ComboboxContent, { container: containerRef, align: "start", className: "w-48", children: [
|
|
708
|
+
/* @__PURE__ */ jsx5(ComboboxInput, { showTrigger: false, placeholder: "Search filters\u2026" }),
|
|
709
|
+
/* @__PURE__ */ jsx5(ComboboxEmpty, { children: "No filters found." }),
|
|
710
|
+
/* @__PURE__ */ jsx5(ComboboxList, { children: (filter) => /* @__PURE__ */ jsx5(ComboboxItem, { value: filter, children: filter.label }, filter.key) })
|
|
711
|
+
] })
|
|
712
|
+
]
|
|
713
|
+
}
|
|
714
|
+
),
|
|
715
|
+
activeFilters.map((active) => {
|
|
716
|
+
const definition = filters.find((f) => f.key === active.key);
|
|
717
|
+
if (!definition) return null;
|
|
718
|
+
return /* @__PURE__ */ jsx5(
|
|
719
|
+
FilterChip,
|
|
720
|
+
{
|
|
721
|
+
definition,
|
|
722
|
+
value: active.value,
|
|
723
|
+
autoOpen: active.key === pendingOpenKey,
|
|
724
|
+
container: containerRef,
|
|
725
|
+
onChangeValue: (value) => onChangeFilter(active.key, value),
|
|
726
|
+
onRemove: () => onRemoveFilter(active.key)
|
|
727
|
+
},
|
|
728
|
+
active.key
|
|
729
|
+
);
|
|
730
|
+
}),
|
|
731
|
+
children
|
|
732
|
+
]
|
|
733
|
+
})
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
function FilterChip({ definition, value, autoOpen, container, onChangeValue, onRemove }) {
|
|
737
|
+
var _a;
|
|
738
|
+
const type = (_a = definition.type) != null ? _a : "checkbox";
|
|
739
|
+
switch (type) {
|
|
740
|
+
case "checkbox":
|
|
741
|
+
return /* @__PURE__ */ jsx5(
|
|
742
|
+
CheckboxFilterChip,
|
|
743
|
+
{
|
|
744
|
+
definition,
|
|
745
|
+
value,
|
|
746
|
+
autoOpen,
|
|
747
|
+
container,
|
|
748
|
+
onChangeValue,
|
|
749
|
+
onRemove
|
|
750
|
+
}
|
|
751
|
+
);
|
|
752
|
+
case "radio":
|
|
753
|
+
return /* @__PURE__ */ jsx5(
|
|
754
|
+
RadioFilterChip,
|
|
755
|
+
{
|
|
756
|
+
definition,
|
|
757
|
+
value,
|
|
758
|
+
autoOpen,
|
|
759
|
+
container,
|
|
760
|
+
onChangeValue,
|
|
761
|
+
onRemove
|
|
762
|
+
}
|
|
763
|
+
);
|
|
764
|
+
case "text":
|
|
765
|
+
return /* @__PURE__ */ jsx5(
|
|
766
|
+
TextFilterChip,
|
|
767
|
+
{
|
|
768
|
+
definition,
|
|
769
|
+
value,
|
|
770
|
+
autoOpen,
|
|
771
|
+
onChangeValue,
|
|
772
|
+
onRemove
|
|
773
|
+
}
|
|
774
|
+
);
|
|
775
|
+
case "multiText":
|
|
776
|
+
return /* @__PURE__ */ jsx5(
|
|
777
|
+
MultiTextFilterChip,
|
|
778
|
+
{
|
|
779
|
+
definition,
|
|
780
|
+
value,
|
|
781
|
+
autoOpen,
|
|
782
|
+
onChangeValue,
|
|
783
|
+
onRemove
|
|
784
|
+
}
|
|
785
|
+
);
|
|
786
|
+
case "date":
|
|
787
|
+
return /* @__PURE__ */ jsx5(
|
|
788
|
+
DateFilterChip,
|
|
789
|
+
{
|
|
790
|
+
definition,
|
|
791
|
+
value,
|
|
792
|
+
autoOpen,
|
|
793
|
+
onChangeValue,
|
|
794
|
+
onRemove
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
function FilterChipRemoveButton({ label, onRemove }) {
|
|
800
|
+
return /* @__PURE__ */ jsx5(Button2, { variant: "outline", size: "icon", "aria-label": `Remove ${label} filter`, onClick: onRemove, children: /* @__PURE__ */ jsx5(Icon3, { name: "xmark" }) });
|
|
801
|
+
}
|
|
802
|
+
function CheckboxFilterChip({
|
|
803
|
+
definition,
|
|
804
|
+
value,
|
|
805
|
+
autoOpen,
|
|
806
|
+
container,
|
|
807
|
+
onChangeValue,
|
|
808
|
+
onRemove
|
|
809
|
+
}) {
|
|
810
|
+
var _a, _b;
|
|
811
|
+
const { label, searchable = false } = definition;
|
|
812
|
+
const options = sortBy(definition.options, (o) => o.label);
|
|
813
|
+
const values = asCheckboxValue(value);
|
|
814
|
+
const displayLabel = values.length === 0 ? "Any" : values.length === 1 ? (_b = (_a = options.find((o) => o.value === values[0])) == null ? void 0 : _a.label) != null ? _b : values[0] : `${values.length} selected`;
|
|
815
|
+
function toggle(optionValue) {
|
|
816
|
+
const next = values.includes(optionValue) ? values.filter((v) => v !== optionValue) : [...values, optionValue];
|
|
817
|
+
onChangeValue(next);
|
|
818
|
+
}
|
|
819
|
+
return /* @__PURE__ */ jsxs4(ButtonGroup, { children: [
|
|
820
|
+
/* @__PURE__ */ jsx5(ButtonGroupText, { children: label }),
|
|
821
|
+
searchable ? /* @__PURE__ */ jsxs4(
|
|
822
|
+
Combobox,
|
|
823
|
+
{
|
|
824
|
+
items: options,
|
|
825
|
+
itemToStringValue: (item) => item.label,
|
|
826
|
+
multiple: true,
|
|
827
|
+
defaultOpen: autoOpen,
|
|
828
|
+
value: options.filter((o) => values.includes(o.value)),
|
|
829
|
+
onValueChange: (selected) => onChangeValue(selected.map((s) => s.value)),
|
|
830
|
+
children: [
|
|
831
|
+
/* @__PURE__ */ jsx5(
|
|
832
|
+
ComboboxTrigger,
|
|
833
|
+
{
|
|
834
|
+
render: /* @__PURE__ */ jsx5(Button2, { variant: "outline", className: "gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)" }),
|
|
835
|
+
children: /* @__PURE__ */ jsx5("span", { className: values.length === 0 ? "text-muted-foreground" : "", children: displayLabel })
|
|
836
|
+
}
|
|
837
|
+
),
|
|
838
|
+
/* @__PURE__ */ jsxs4(
|
|
839
|
+
ComboboxContent,
|
|
840
|
+
{
|
|
841
|
+
container,
|
|
842
|
+
className: "min-w-64",
|
|
843
|
+
collisionAvoidance: { side: "none", align: "shift" },
|
|
844
|
+
children: [
|
|
845
|
+
/* @__PURE__ */ jsx5(ComboboxInput, { showTrigger: false, placeholder: `Search ${label.toLowerCase()}\u2026` }),
|
|
846
|
+
values.length > 0 && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
847
|
+
/* @__PURE__ */ jsxs4(ComboboxSelectedChips, { children: [
|
|
848
|
+
/* @__PURE__ */ jsx5(ComboboxValue, { children: (selected) => /* @__PURE__ */ jsx5(Fragment2, { children: selected.map((item) => /* @__PURE__ */ jsx5(ComboboxChip, { children: item.label }, item.value)) }) }),
|
|
849
|
+
/* @__PURE__ */ jsx5(ComboboxClearAll, { onClick: () => onChangeValue([]) })
|
|
850
|
+
] }),
|
|
851
|
+
/* @__PURE__ */ jsx5(ComboboxSeparator, {})
|
|
852
|
+
] }),
|
|
853
|
+
/* @__PURE__ */ jsxs4(ComboboxEmpty, { children: [
|
|
854
|
+
"No ",
|
|
855
|
+
label.toLowerCase(),
|
|
856
|
+
" found."
|
|
857
|
+
] }),
|
|
858
|
+
/* @__PURE__ */ jsx5(ComboboxList, { children: (item) => /* @__PURE__ */ jsxs4(ComboboxItem, { value: item, children: [
|
|
859
|
+
item.icon,
|
|
860
|
+
item.label
|
|
861
|
+
] }, item.value) })
|
|
862
|
+
]
|
|
863
|
+
}
|
|
864
|
+
)
|
|
865
|
+
]
|
|
866
|
+
}
|
|
867
|
+
) : /* @__PURE__ */ jsxs4(DropdownMenu, { defaultOpen: autoOpen, children: [
|
|
868
|
+
/* @__PURE__ */ jsx5(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(Button2, { variant: "outline", className: "gap-1 font-normal", children: [
|
|
869
|
+
/* @__PURE__ */ jsx5("span", { className: values.length === 0 ? "text-muted-foreground" : "", children: displayLabel }),
|
|
870
|
+
/* @__PURE__ */ jsx5(Icon3, { name: "chevron-down", size: "sm", className: "text-muted-foreground" })
|
|
871
|
+
] }) }),
|
|
872
|
+
/* @__PURE__ */ jsx5(DropdownMenuContent, { align: "start", className: "w-48", children: options.map((opt) => /* @__PURE__ */ jsxs4(
|
|
873
|
+
DropdownMenuCheckboxItem,
|
|
874
|
+
{
|
|
875
|
+
checked: values.includes(opt.value),
|
|
876
|
+
onCheckedChange: () => toggle(opt.value),
|
|
877
|
+
children: [
|
|
878
|
+
opt.icon,
|
|
879
|
+
opt.label
|
|
880
|
+
]
|
|
881
|
+
},
|
|
882
|
+
opt.value
|
|
883
|
+
)) })
|
|
884
|
+
] }),
|
|
885
|
+
/* @__PURE__ */ jsx5(FilterChipRemoveButton, { label, onRemove })
|
|
886
|
+
] });
|
|
887
|
+
}
|
|
888
|
+
function RadioFilterChip({
|
|
889
|
+
definition,
|
|
890
|
+
value,
|
|
891
|
+
autoOpen,
|
|
892
|
+
container,
|
|
893
|
+
onChangeValue,
|
|
894
|
+
onRemove
|
|
895
|
+
}) {
|
|
896
|
+
var _a, _b, _c;
|
|
897
|
+
const { label, searchable = false } = definition;
|
|
898
|
+
const options = sortBy(definition.options, (o) => o.label);
|
|
899
|
+
const selectedValue = asRadioValue(value);
|
|
900
|
+
const displayLabel = selectedValue ? (_b = (_a = options.find((o) => o.value === selectedValue)) == null ? void 0 : _a.label) != null ? _b : selectedValue : "Any";
|
|
901
|
+
const selected = (_c = options.find((o) => o.value === selectedValue)) != null ? _c : null;
|
|
902
|
+
return /* @__PURE__ */ jsxs4(ButtonGroup, { children: [
|
|
903
|
+
/* @__PURE__ */ jsx5(ButtonGroupText, { children: label }),
|
|
904
|
+
searchable ? /* @__PURE__ */ jsxs4(
|
|
905
|
+
Combobox,
|
|
906
|
+
{
|
|
907
|
+
items: options,
|
|
908
|
+
itemToStringValue: (item) => item.label,
|
|
909
|
+
defaultOpen: autoOpen,
|
|
910
|
+
value: selected,
|
|
911
|
+
onValueChange: (next) => {
|
|
912
|
+
var _a2;
|
|
913
|
+
return onChangeValue((_a2 = next == null ? void 0 : next.value) != null ? _a2 : "");
|
|
914
|
+
},
|
|
915
|
+
children: [
|
|
916
|
+
/* @__PURE__ */ jsx5(
|
|
917
|
+
ComboboxTrigger,
|
|
918
|
+
{
|
|
919
|
+
render: /* @__PURE__ */ jsx5(Button2, { variant: "outline", className: "gap-1 font-normal [&>svg:last-child]:size-(--icon-sm)" }),
|
|
920
|
+
children: /* @__PURE__ */ jsx5("span", { className: selectedValue ? "" : "text-muted-foreground", children: displayLabel })
|
|
921
|
+
}
|
|
922
|
+
),
|
|
923
|
+
/* @__PURE__ */ jsxs4(
|
|
924
|
+
ComboboxContent,
|
|
925
|
+
{
|
|
926
|
+
container,
|
|
927
|
+
className: "min-w-64",
|
|
928
|
+
collisionAvoidance: { side: "none", align: "shift" },
|
|
929
|
+
children: [
|
|
930
|
+
/* @__PURE__ */ jsx5(ComboboxInput, { showTrigger: false, placeholder: `Search ${label.toLowerCase()}\u2026` }),
|
|
931
|
+
/* @__PURE__ */ jsxs4(ComboboxEmpty, { children: [
|
|
932
|
+
"No ",
|
|
933
|
+
label.toLowerCase(),
|
|
934
|
+
" found."
|
|
935
|
+
] }),
|
|
936
|
+
/* @__PURE__ */ jsx5(ComboboxList, { children: (item) => /* @__PURE__ */ jsxs4(ComboboxItem, { value: item, children: [
|
|
937
|
+
item.icon,
|
|
938
|
+
item.label
|
|
939
|
+
] }, item.value) })
|
|
940
|
+
]
|
|
941
|
+
}
|
|
942
|
+
)
|
|
943
|
+
]
|
|
944
|
+
}
|
|
945
|
+
) : /* @__PURE__ */ jsxs4(DropdownMenu, { defaultOpen: autoOpen, children: [
|
|
946
|
+
/* @__PURE__ */ jsx5(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs4(Button2, { variant: "outline", className: "gap-1 font-normal", children: [
|
|
947
|
+
/* @__PURE__ */ jsx5("span", { className: selectedValue ? "" : "text-muted-foreground", children: displayLabel }),
|
|
948
|
+
/* @__PURE__ */ jsx5(Icon3, { name: "chevron-down", size: "sm", className: "text-muted-foreground" })
|
|
949
|
+
] }) }),
|
|
950
|
+
/* @__PURE__ */ jsx5(DropdownMenuContent, { align: "start", className: "w-48", children: /* @__PURE__ */ jsx5(DropdownMenuRadioGroup, { value: selectedValue, onValueChange: onChangeValue, children: options.map((opt) => /* @__PURE__ */ jsxs4(DropdownMenuRadioItem, { value: opt.value, children: [
|
|
951
|
+
opt.icon,
|
|
952
|
+
opt.label
|
|
953
|
+
] }, opt.value)) }) })
|
|
954
|
+
] }),
|
|
955
|
+
/* @__PURE__ */ jsx5(FilterChipRemoveButton, { label, onRemove })
|
|
956
|
+
] });
|
|
957
|
+
}
|
|
958
|
+
function TextFilterChip({
|
|
959
|
+
definition,
|
|
960
|
+
value,
|
|
961
|
+
autoOpen,
|
|
962
|
+
onChangeValue,
|
|
963
|
+
onRemove
|
|
964
|
+
}) {
|
|
965
|
+
const { label, placeholder } = definition;
|
|
966
|
+
const text = asTextValue(value);
|
|
967
|
+
const [open, setOpen] = React3.useState(!!autoOpen);
|
|
968
|
+
return /* @__PURE__ */ jsxs4(ButtonGroup, { children: [
|
|
969
|
+
/* @__PURE__ */ jsx5(ButtonGroupText, { children: label }),
|
|
970
|
+
/* @__PURE__ */ jsxs4(Popover, { open, onOpenChange: setOpen, children: [
|
|
971
|
+
/* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(Button2, { variant: "outline", className: "gap-1 font-normal", children: /* @__PURE__ */ jsx5("span", { className: text ? "" : "text-muted-foreground", children: text || "Any" }) }) }),
|
|
972
|
+
/* @__PURE__ */ jsx5(PopoverContent, { align: "start", children: /* @__PURE__ */ jsx5(
|
|
973
|
+
Input,
|
|
974
|
+
{
|
|
975
|
+
autoFocus: true,
|
|
976
|
+
value: text,
|
|
977
|
+
placeholder: placeholder != null ? placeholder : "Enter a value\u2026",
|
|
978
|
+
onChange: (e) => onChangeValue(e.target.value),
|
|
979
|
+
onKeyDown: (e) => {
|
|
980
|
+
if (e.key === "Enter") setOpen(false);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
) })
|
|
984
|
+
] }),
|
|
985
|
+
/* @__PURE__ */ jsx5(FilterChipRemoveButton, { label, onRemove })
|
|
986
|
+
] });
|
|
987
|
+
}
|
|
988
|
+
function MultiTextFilterChip({
|
|
989
|
+
definition,
|
|
990
|
+
value,
|
|
991
|
+
autoOpen,
|
|
992
|
+
onChangeValue,
|
|
993
|
+
onRemove
|
|
994
|
+
}) {
|
|
995
|
+
const { label, placeholder } = definition;
|
|
996
|
+
const tokens = asMultiTextValue(value);
|
|
997
|
+
const [draft, setDraft] = React3.useState("");
|
|
998
|
+
const displayLabel = tokens.length === 0 ? "Any" : tokens.length === 1 ? tokens[0] : `${tokens.length} values`;
|
|
999
|
+
function addToken() {
|
|
1000
|
+
const token = draft.trim();
|
|
1001
|
+
if (!token || tokens.includes(token)) {
|
|
1002
|
+
setDraft("");
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
onChangeValue([...tokens, token]);
|
|
1006
|
+
setDraft("");
|
|
1007
|
+
}
|
|
1008
|
+
function removeToken(token) {
|
|
1009
|
+
onChangeValue(tokens.filter((t) => t !== token));
|
|
1010
|
+
}
|
|
1011
|
+
return /* @__PURE__ */ jsxs4(ButtonGroup, { children: [
|
|
1012
|
+
/* @__PURE__ */ jsx5(ButtonGroupText, { children: label }),
|
|
1013
|
+
/* @__PURE__ */ jsxs4(Popover, { defaultOpen: autoOpen, children: [
|
|
1014
|
+
/* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(Button2, { variant: "outline", className: "gap-1 font-normal", children: /* @__PURE__ */ jsx5("span", { className: tokens.length === 0 ? "text-muted-foreground" : "", children: displayLabel }) }) }),
|
|
1015
|
+
/* @__PURE__ */ jsx5(PopoverContent, { align: "start", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
1016
|
+
tokens.length > 0 && /* @__PURE__ */ jsx5("div", { className: "flex flex-wrap gap-1", children: tokens.map((token) => /* @__PURE__ */ jsxs4(Badge2, { variant: "secondary", className: "gap-1", children: [
|
|
1017
|
+
token,
|
|
1018
|
+
/* @__PURE__ */ jsx5(
|
|
1019
|
+
"button",
|
|
1020
|
+
{
|
|
1021
|
+
type: "button",
|
|
1022
|
+
"aria-label": `Remove ${token}`,
|
|
1023
|
+
onClick: () => removeToken(token),
|
|
1024
|
+
className: "cursor-pointer",
|
|
1025
|
+
children: /* @__PURE__ */ jsx5(Icon3, { name: "xmark", size: "xs" })
|
|
1026
|
+
}
|
|
1027
|
+
)
|
|
1028
|
+
] }, token)) }),
|
|
1029
|
+
/* @__PURE__ */ jsx5(
|
|
1030
|
+
Input,
|
|
1031
|
+
{
|
|
1032
|
+
autoFocus: true,
|
|
1033
|
+
value: draft,
|
|
1034
|
+
placeholder: placeholder != null ? placeholder : "Type a value, press Enter\u2026",
|
|
1035
|
+
onChange: (e) => setDraft(e.target.value),
|
|
1036
|
+
onKeyDown: (e) => {
|
|
1037
|
+
if (e.key === "Enter") {
|
|
1038
|
+
e.preventDefault();
|
|
1039
|
+
addToken();
|
|
1040
|
+
} else if (e.key === "Backspace" && draft === "" && tokens.length > 0) {
|
|
1041
|
+
removeToken(tokens[tokens.length - 1]);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
)
|
|
1046
|
+
] }) })
|
|
1047
|
+
] }),
|
|
1048
|
+
/* @__PURE__ */ jsx5(FilterChipRemoveButton, { label, onRemove })
|
|
1049
|
+
] });
|
|
1050
|
+
}
|
|
1051
|
+
function DateFilterChip({
|
|
1052
|
+
definition,
|
|
1053
|
+
value,
|
|
1054
|
+
autoOpen,
|
|
1055
|
+
onChangeValue,
|
|
1056
|
+
onRemove
|
|
1057
|
+
}) {
|
|
1058
|
+
const { label, includeTime } = definition;
|
|
1059
|
+
const { startDate, endDate } = asDateValue(value);
|
|
1060
|
+
const showTimeInputs = includeTime != null ? includeTime : hasExplicitTime(startDate) || hasExplicitTime(endDate);
|
|
1061
|
+
const displayLabel = !startDate ? "Any" : !endDate || startDate.getTime() === endDate.getTime() ? formatDateLabel(startDate) : `${formatDateLabel(startDate)} \u2013 ${formatDateLabel(endDate)}`;
|
|
1062
|
+
function pickRange(range) {
|
|
1063
|
+
var _a;
|
|
1064
|
+
if (!(range == null ? void 0 : range.from)) {
|
|
1065
|
+
onChangeValue({ startDate: null, endDate: null });
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
onChangeValue({ startDate: range.from, endDate: (_a = range.to) != null ? _a : range.from });
|
|
1069
|
+
}
|
|
1070
|
+
function pickStartTime(time) {
|
|
1071
|
+
if (!startDate) return;
|
|
1072
|
+
onChangeValue({ startDate: applyTimeToDate(startDate, time), endDate });
|
|
1073
|
+
}
|
|
1074
|
+
function pickEndTime(time) {
|
|
1075
|
+
if (!endDate) return;
|
|
1076
|
+
onChangeValue({ startDate, endDate: applyTimeToDate(endDate, time) });
|
|
1077
|
+
}
|
|
1078
|
+
return /* @__PURE__ */ jsxs4(ButtonGroup, { children: [
|
|
1079
|
+
/* @__PURE__ */ jsx5(ButtonGroupText, { children: label }),
|
|
1080
|
+
/* @__PURE__ */ jsxs4(Popover, { defaultOpen: autoOpen, children: [
|
|
1081
|
+
/* @__PURE__ */ jsx5(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx5(Button2, { variant: "outline", className: "gap-1 font-normal", children: /* @__PURE__ */ jsx5("span", { className: startDate ? "" : "text-muted-foreground", children: displayLabel }) }) }),
|
|
1082
|
+
/* @__PURE__ */ jsx5(PopoverContent, { align: "start", className: "w-fit", children: /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
1083
|
+
/* @__PURE__ */ jsx5(
|
|
1084
|
+
Calendar,
|
|
1085
|
+
{
|
|
1086
|
+
mode: "range",
|
|
1087
|
+
selected: { from: startDate != null ? startDate : void 0, to: endDate != null ? endDate : void 0 },
|
|
1088
|
+
onSelect: pickRange
|
|
1089
|
+
}
|
|
1090
|
+
),
|
|
1091
|
+
showTimeInputs && /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-2", children: [
|
|
1092
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex w-full items-center gap-2", children: [
|
|
1093
|
+
/* @__PURE__ */ jsx5(
|
|
1094
|
+
Label,
|
|
1095
|
+
{
|
|
1096
|
+
htmlFor: `${definition.key}-start-time`,
|
|
1097
|
+
className: "text-body-sm text-muted-foreground w-10 shrink-0",
|
|
1098
|
+
children: "Start"
|
|
1099
|
+
}
|
|
1100
|
+
),
|
|
1101
|
+
/* @__PURE__ */ jsx5(
|
|
1102
|
+
Input,
|
|
1103
|
+
{
|
|
1104
|
+
id: `${definition.key}-start-time`,
|
|
1105
|
+
type: "time",
|
|
1106
|
+
className: "w-full",
|
|
1107
|
+
value: formatTimeForInput(startDate),
|
|
1108
|
+
onChange: (e) => pickStartTime(e.target.value),
|
|
1109
|
+
disabled: !startDate
|
|
1110
|
+
}
|
|
1111
|
+
)
|
|
1112
|
+
] }),
|
|
1113
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex w-full items-center gap-2", children: [
|
|
1114
|
+
/* @__PURE__ */ jsx5(
|
|
1115
|
+
Label,
|
|
1116
|
+
{
|
|
1117
|
+
htmlFor: `${definition.key}-end-time`,
|
|
1118
|
+
className: "text-body-sm text-muted-foreground w-10 shrink-0",
|
|
1119
|
+
children: "End"
|
|
1120
|
+
}
|
|
1121
|
+
),
|
|
1122
|
+
/* @__PURE__ */ jsx5(
|
|
1123
|
+
Input,
|
|
1124
|
+
{
|
|
1125
|
+
id: `${definition.key}-end-time`,
|
|
1126
|
+
type: "time",
|
|
1127
|
+
className: "w-full",
|
|
1128
|
+
value: formatTimeForInput(endDate),
|
|
1129
|
+
onChange: (e) => pickEndTime(e.target.value),
|
|
1130
|
+
disabled: !endDate
|
|
1131
|
+
}
|
|
1132
|
+
)
|
|
1133
|
+
] })
|
|
1134
|
+
] })
|
|
1135
|
+
] }) })
|
|
1136
|
+
] }),
|
|
1137
|
+
/* @__PURE__ */ jsx5(FilterChipRemoveButton, { label, onRemove })
|
|
1138
|
+
] });
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// src/lib/data-table/filter-types.ts
|
|
1142
|
+
var isDateRangeColumnFilter = (definition) => definition.type === "dateRange";
|
|
1143
|
+
var isRadioColumnFilter = (definition) => definition.type === "radio";
|
|
1144
|
+
var isCheckboxColumnFilter = (definition) => definition.type === "checkbox";
|
|
1145
|
+
var isMultiTextColumnFilter = (definition) => definition.type === "multiText";
|
|
1146
|
+
|
|
1147
|
+
// src/data-table/view-filter.tsx
|
|
1148
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1149
|
+
function toFilterOption(opt) {
|
|
1150
|
+
return typeof opt === "string" ? { value: opt, label: opt } : { value: opt.value, label: opt.label };
|
|
1151
|
+
}
|
|
1152
|
+
function toFilterBarDefinition(key, filter) {
|
|
1153
|
+
var _a, _b;
|
|
1154
|
+
const base = { key, label: filter.title, order: filter.order };
|
|
1155
|
+
if (isCheckboxColumnFilter(filter)) {
|
|
1156
|
+
const options = "options" in filter && filter.options ? filter.options : [];
|
|
1157
|
+
return __spreadProps(__spreadValues({}, base), {
|
|
1158
|
+
type: "checkbox",
|
|
1159
|
+
options: options.map(toFilterOption),
|
|
1160
|
+
searchable: (_a = filter.searchable) != null ? _a : true
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
if (isRadioColumnFilter(filter)) {
|
|
1164
|
+
return __spreadProps(__spreadValues({}, base), {
|
|
1165
|
+
type: "radio",
|
|
1166
|
+
options: filter.options.map(toFilterOption),
|
|
1167
|
+
searchable: (_b = filter.searchable) != null ? _b : true
|
|
1168
|
+
});
|
|
1169
|
+
}
|
|
1170
|
+
if (isDateRangeColumnFilter(filter)) {
|
|
1171
|
+
return __spreadProps(__spreadValues({}, base), { type: "date", includeTime: filter.includeTime });
|
|
1172
|
+
}
|
|
1173
|
+
if (isMultiTextColumnFilter(filter)) {
|
|
1174
|
+
return __spreadProps(__spreadValues({}, base), { type: "multiText", placeholder: filter.placeholder });
|
|
1175
|
+
}
|
|
1176
|
+
return __spreadProps(__spreadValues({}, base), { type: "text", placeholder: filter.placeholder });
|
|
1177
|
+
}
|
|
1178
|
+
function emptyValueFor(filter) {
|
|
1179
|
+
if (isCheckboxColumnFilter(filter)) return [];
|
|
1180
|
+
if (isRadioColumnFilter(filter)) return void 0;
|
|
1181
|
+
if (isDateRangeColumnFilter(filter)) return { startDate: null, endDate: null };
|
|
1182
|
+
if (isMultiTextColumnFilter(filter)) return [];
|
|
1183
|
+
return void 0;
|
|
1184
|
+
}
|
|
1185
|
+
function ViewFilter({
|
|
1186
|
+
columns,
|
|
1187
|
+
columnFilters,
|
|
1188
|
+
onColumnFiltersChange,
|
|
1189
|
+
className
|
|
1190
|
+
}) {
|
|
1191
|
+
const filterableColumns = React4.useMemo(
|
|
1192
|
+
() => columns.filter((column) => !isFilterOnlyColumn(column) && column.getCanFilter()).flatMap((column) => {
|
|
1193
|
+
var _a;
|
|
1194
|
+
const filter = (_a = column.columnDef.meta) == null ? void 0 : _a.filter;
|
|
1195
|
+
return filter ? [{ key: column.id, filter }] : [];
|
|
1196
|
+
}),
|
|
1197
|
+
[columns]
|
|
1198
|
+
);
|
|
1199
|
+
const filterDefinitions = React4.useMemo(
|
|
1200
|
+
() => filterableColumns.map(({ key, filter }) => toFilterBarDefinition(key, filter)).sort((a, b) => {
|
|
1201
|
+
if (a.order == null && b.order == null) return 0;
|
|
1202
|
+
if (a.order == null) return 1;
|
|
1203
|
+
if (b.order == null) return -1;
|
|
1204
|
+
return a.order - b.order;
|
|
1205
|
+
}),
|
|
1206
|
+
[filterableColumns]
|
|
1207
|
+
);
|
|
1208
|
+
const emptyValueByKey = React4.useMemo(
|
|
1209
|
+
() => new Map(filterableColumns.map(({ key, filter }) => [key, emptyValueFor(filter)])),
|
|
1210
|
+
[filterableColumns]
|
|
1211
|
+
);
|
|
1212
|
+
const activeFilters = React4.useMemo(
|
|
1213
|
+
() => columnFilters.map((filter) => ({ key: filter.id, value: filter.value })),
|
|
1214
|
+
[columnFilters]
|
|
1215
|
+
);
|
|
1216
|
+
return /* @__PURE__ */ jsx6(
|
|
1217
|
+
FilterBar,
|
|
1218
|
+
{
|
|
1219
|
+
className,
|
|
1220
|
+
filters: filterDefinitions,
|
|
1221
|
+
activeFilters,
|
|
1222
|
+
onAddFilter: (key) => onColumnFiltersChange((prev) => [...prev.filter((f) => f.id !== key), { id: key, value: emptyValueByKey.get(key) }]),
|
|
1223
|
+
onRemoveFilter: (key) => onColumnFiltersChange((prev) => prev.filter((f) => f.id !== key)),
|
|
1224
|
+
onChangeFilter: (key, value) => onColumnFiltersChange((prev) => [...prev.filter((f) => f.id !== key), { id: key, value }])
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// src/section-header.tsx
|
|
1230
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1231
|
+
function SectionHeader(_a) {
|
|
1232
|
+
var _b = _a, { className, children } = _b, props = __objRest(_b, ["className", "children"]);
|
|
1233
|
+
return /* @__PURE__ */ jsx7(
|
|
1234
|
+
"div",
|
|
1235
|
+
__spreadProps(__spreadValues({
|
|
1236
|
+
"data-slot": "section-header",
|
|
1237
|
+
className: cn("flex w-full items-start justify-between gap-4", className)
|
|
1238
|
+
}, props), {
|
|
1239
|
+
children
|
|
1240
|
+
})
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
function SectionHeaderContent(_a) {
|
|
1244
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1245
|
+
return /* @__PURE__ */ jsx7("div", __spreadValues({ "data-slot": "section-header-content", className: cn("flex flex-col gap-1", className) }, props));
|
|
1246
|
+
}
|
|
1247
|
+
function SectionHeaderTitle(_a) {
|
|
1248
|
+
var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
|
|
1249
|
+
return /* @__PURE__ */ jsx7("h2", __spreadValues({ "data-slot": "section-header-title", className: cn("text-h3 text-foreground", className) }, props));
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/graph/graph-view.tsx
|
|
1253
|
+
import * as React5 from "react";
|
|
1254
|
+
import {
|
|
1255
|
+
Background,
|
|
1256
|
+
Controls,
|
|
1257
|
+
MiniMap,
|
|
1258
|
+
ReactFlow,
|
|
1259
|
+
ReactFlowProvider,
|
|
1260
|
+
useReactFlow
|
|
1261
|
+
} from "reactflow";
|
|
1262
|
+
|
|
1263
|
+
// src/graph/hooks/useAutoLayout.ts
|
|
1264
|
+
import { useMemo as useMemo3 } from "react";
|
|
1265
|
+
import { Position as Position3 } from "reactflow";
|
|
1266
|
+
var TILE_HORIZONTAL_DISTANCE = GRAPH_RANK_SEP;
|
|
1267
|
+
var TILE_VERTICAL_DISTANCE = GRAPH_NODE_SEP;
|
|
1268
|
+
var TILE_HEIGHT = GRAPH_NODE_HEIGHT;
|
|
1269
|
+
var TILE_WIDTH = GRAPH_NODE_WIDTH;
|
|
1270
|
+
var getNodeDepth = (nodeId, edges, visited = /* @__PURE__ */ new Set(), maxDepth = 100) => {
|
|
1271
|
+
if (visited.has(nodeId)) {
|
|
1272
|
+
console.warn(`Cycle detected in graph at node: ${nodeId}. Returning depth 0 to prevent infinite recursion.`);
|
|
1273
|
+
return 0;
|
|
1274
|
+
}
|
|
1275
|
+
if (visited.size >= maxDepth) {
|
|
1276
|
+
console.warn(`Maximum depth limit (${maxDepth}) reached for node: ${nodeId}.`);
|
|
1277
|
+
return visited.size;
|
|
1278
|
+
}
|
|
1279
|
+
const parentEdge = edges.find((edge) => edge.target === nodeId);
|
|
1280
|
+
if (!parentEdge) {
|
|
1281
|
+
return 0;
|
|
1282
|
+
}
|
|
1283
|
+
const newVisited = /* @__PURE__ */ new Set([...visited, nodeId]);
|
|
1284
|
+
return 1 + getNodeDepth(parentEdge.source, edges, newVisited, maxDepth);
|
|
1285
|
+
};
|
|
1286
|
+
var flatNodeTree = (nodeTree) => {
|
|
1287
|
+
const { node, children } = nodeTree;
|
|
1288
|
+
if (!children) {
|
|
1289
|
+
return [node];
|
|
1290
|
+
}
|
|
1291
|
+
return [node, ...children.flatMap(flatNodeTree)];
|
|
1292
|
+
};
|
|
1293
|
+
var getChildren = (nodeId, nodes, edges) => {
|
|
1294
|
+
const nodeEdges = edges.filter((edge) => edge.source === nodeId);
|
|
1295
|
+
return nodeEdges.map((edge) => nodes.find((node) => node.id === edge.target)).filter((node) => node !== void 0).map((node) => ({
|
|
1296
|
+
node,
|
|
1297
|
+
children: getChildren(node.id, nodes, edges)
|
|
1298
|
+
}));
|
|
1299
|
+
};
|
|
1300
|
+
var getTrees = (nodes, edges) => nodes.filter((node) => !edges.some((edge) => edge.target === node.id)).map((node) => ({
|
|
1301
|
+
node,
|
|
1302
|
+
children: getChildren(node.id, nodes, edges)
|
|
1303
|
+
}));
|
|
1304
|
+
var getLowestPositionYInSubtree = (nodeTree) => {
|
|
1305
|
+
const { node, children } = nodeTree;
|
|
1306
|
+
const lowestY = node.position.y;
|
|
1307
|
+
if (!(children == null ? void 0 : children.length)) {
|
|
1308
|
+
return lowestY;
|
|
1309
|
+
}
|
|
1310
|
+
const visibleChildren = children.filter(({ node: { hidden } }) => !hidden);
|
|
1311
|
+
if (visibleChildren.length === 0) {
|
|
1312
|
+
return lowestY;
|
|
1313
|
+
}
|
|
1314
|
+
const childLowestYPositions = visibleChildren.map((child) => getLowestPositionYInSubtree(child));
|
|
1315
|
+
const lowestChildY = Math.max(...childLowestYPositions);
|
|
1316
|
+
return Math.max(lowestY, lowestChildY);
|
|
1317
|
+
};
|
|
1318
|
+
var positionNodes = (treeNodes, rootFlattenTree, allEdges, parentY) => {
|
|
1319
|
+
const flattenTree = rootFlattenTree || treeNodes.flatMap(flatNodeTree);
|
|
1320
|
+
return treeNodes.reduce((actualTreeNodes, nodeTree, treeNodeIndex) => {
|
|
1321
|
+
const { node, children } = nodeTree;
|
|
1322
|
+
const depth = allEdges ? getNodeDepth(node.id, allEdges) : 0;
|
|
1323
|
+
const getPositionY = () => {
|
|
1324
|
+
if (treeNodeIndex > 0) {
|
|
1325
|
+
const previousSibling = actualTreeNodes[treeNodeIndex - 1];
|
|
1326
|
+
const lowestYInPreviousSubtree = getLowestPositionYInSubtree(previousSibling);
|
|
1327
|
+
return lowestYInPreviousSubtree + TILE_HEIGHT + TILE_VERTICAL_DISTANCE;
|
|
1328
|
+
}
|
|
1329
|
+
if (parentY !== void 0) {
|
|
1330
|
+
return parentY;
|
|
1331
|
+
}
|
|
1332
|
+
return (TILE_HEIGHT + TILE_VERTICAL_DISTANCE) * treeNodeIndex;
|
|
1333
|
+
};
|
|
1334
|
+
const position = {
|
|
1335
|
+
x: depth * (TILE_WIDTH + TILE_HORIZONTAL_DISTANCE),
|
|
1336
|
+
y: getPositionY()
|
|
1337
|
+
};
|
|
1338
|
+
const positionedNode = __spreadProps(__spreadValues({}, node), {
|
|
1339
|
+
targetPosition: Position3.Left,
|
|
1340
|
+
sourcePosition: Position3.Right,
|
|
1341
|
+
position
|
|
1342
|
+
});
|
|
1343
|
+
if (!(children == null ? void 0 : children.length)) {
|
|
1344
|
+
return [
|
|
1345
|
+
...actualTreeNodes,
|
|
1346
|
+
__spreadProps(__spreadValues({}, nodeTree), {
|
|
1347
|
+
node: positionedNode
|
|
1348
|
+
})
|
|
1349
|
+
];
|
|
1350
|
+
}
|
|
1351
|
+
const nodeWithChildren = __spreadProps(__spreadValues({}, nodeTree), {
|
|
1352
|
+
node: positionedNode,
|
|
1353
|
+
children: positionNodes(
|
|
1354
|
+
children.sort((a, b) => {
|
|
1355
|
+
return a.node.id.localeCompare(b.node.id);
|
|
1356
|
+
}),
|
|
1357
|
+
flattenTree,
|
|
1358
|
+
allEdges,
|
|
1359
|
+
position.y
|
|
1360
|
+
// Pass parent's Y position to children
|
|
1361
|
+
)
|
|
1362
|
+
});
|
|
1363
|
+
return [...actualTreeNodes, nodeWithChildren];
|
|
1364
|
+
}, []);
|
|
1365
|
+
};
|
|
1366
|
+
var getLaidOutElements = (nodes, edges) => {
|
|
1367
|
+
const nodeTrees = getTrees(nodes, edges);
|
|
1368
|
+
const positionedNodes = positionNodes(nodeTrees, void 0, edges).flatMap(flatNodeTree);
|
|
1369
|
+
return { nodes: positionedNodes, edges };
|
|
1370
|
+
};
|
|
1371
|
+
var useAutoLayout = (nodes, edges, { viewportFocusNodeId }) => {
|
|
1372
|
+
const laidOutElements = useMemo3(() => {
|
|
1373
|
+
if (nodes.length === 0) {
|
|
1374
|
+
return { nodes, edges, nodeMap: /* @__PURE__ */ new Map() };
|
|
1375
|
+
}
|
|
1376
|
+
const visibleNodes = nodes.filter((node) => !node.hidden);
|
|
1377
|
+
const visibleNodeIds = new Set(visibleNodes.map((node) => node.id));
|
|
1378
|
+
const visibleEdges = edges.filter((edge) => visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target));
|
|
1379
|
+
const { nodes: laidOutNodes, edges: laidOutEdges } = getLaidOutElements(visibleNodes, visibleEdges);
|
|
1380
|
+
const laidOutNodeMap = new Map(laidOutNodes.map((node) => [node.id, node]));
|
|
1381
|
+
if (viewportFocusNodeId) {
|
|
1382
|
+
const highlightedNode = laidOutNodeMap.get(viewportFocusNodeId);
|
|
1383
|
+
if (highlightedNode) {
|
|
1384
|
+
laidOutNodeMap.set(viewportFocusNodeId, __spreadProps(__spreadValues({}, highlightedNode), {
|
|
1385
|
+
data: __spreadProps(__spreadValues({}, highlightedNode.data), { isHighlighted: true })
|
|
1386
|
+
}));
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
const finalNodes = nodes.filter((node) => !node.hidden).map((node) => laidOutNodeMap.get(node.id) || node);
|
|
1390
|
+
return {
|
|
1391
|
+
nodes: finalNodes,
|
|
1392
|
+
edges: laidOutEdges,
|
|
1393
|
+
nodeMap: laidOutNodeMap
|
|
1394
|
+
};
|
|
1395
|
+
}, [nodes, edges, viewportFocusNodeId]);
|
|
1396
|
+
return laidOutElements;
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
// src/graph/hooks/useNodeVisibility.ts
|
|
1400
|
+
import { useCallback as useCallback3, useMemo as useMemo4 } from "react";
|
|
1401
|
+
var useNodeVisibility = (nodes, edges) => {
|
|
1402
|
+
const hasRealChildren = useCallback3(
|
|
1403
|
+
(nodeId) => {
|
|
1404
|
+
const childEdges = edges.filter((edge) => edge.source === nodeId);
|
|
1405
|
+
const childNodes = childEdges.map((edge) => nodes.find((node) => node.id === edge.target)).filter(Boolean);
|
|
1406
|
+
return childNodes.some((child) => child && child.type !== "loading");
|
|
1407
|
+
},
|
|
1408
|
+
[nodes, edges]
|
|
1409
|
+
);
|
|
1410
|
+
const areAllAncestorsExpanded = useCallback3(
|
|
1411
|
+
(nodeId) => {
|
|
1412
|
+
const parentEdges = edges.filter((edge) => edge.target === nodeId);
|
|
1413
|
+
if (parentEdges.length === 0) {
|
|
1414
|
+
return true;
|
|
1415
|
+
}
|
|
1416
|
+
for (const parentEdge of parentEdges) {
|
|
1417
|
+
const parentNode = nodes.find((node) => node.id === parentEdge.source);
|
|
1418
|
+
if (!parentNode) {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
const isParentExpanded = isDefaultNode(parentNode) ? Boolean(parentNode.data.expanded) : true;
|
|
1422
|
+
if (!isParentExpanded) {
|
|
1423
|
+
return false;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
return parentEdges.every((parentEdge) => areAllAncestorsExpanded(parentEdge.source));
|
|
1427
|
+
},
|
|
1428
|
+
[nodes, edges]
|
|
1429
|
+
);
|
|
1430
|
+
const visibleNodes = useMemo4(() => {
|
|
1431
|
+
return nodes.map((node) => {
|
|
1432
|
+
const shouldBeVisible = areAllAncestorsExpanded(node.id);
|
|
1433
|
+
if (node.type === "loading") {
|
|
1434
|
+
const loadingNode = {
|
|
1435
|
+
id: node.id,
|
|
1436
|
+
position: node.position,
|
|
1437
|
+
type: "loading",
|
|
1438
|
+
hidden: !shouldBeVisible,
|
|
1439
|
+
data: node.data
|
|
1440
|
+
};
|
|
1441
|
+
return loadingNode;
|
|
1442
|
+
}
|
|
1443
|
+
if (isDefaultNode(node)) {
|
|
1444
|
+
const expandedNode = {
|
|
1445
|
+
id: node.id,
|
|
1446
|
+
position: node.position,
|
|
1447
|
+
type: node.type,
|
|
1448
|
+
hidden: !shouldBeVisible,
|
|
1449
|
+
data: __spreadProps(__spreadValues({}, node.data), {
|
|
1450
|
+
expanded: Boolean(node.data.expanded),
|
|
1451
|
+
isExpandable: hasRealChildren(node.id)
|
|
1452
|
+
// onExpandToggle will be added later in GraphView
|
|
1453
|
+
})
|
|
1454
|
+
};
|
|
1455
|
+
return expandedNode;
|
|
1456
|
+
}
|
|
1457
|
+
return node;
|
|
1458
|
+
});
|
|
1459
|
+
}, [nodes, areAllAncestorsExpanded, hasRealChildren]);
|
|
1460
|
+
const visibleEdges = useMemo4(() => {
|
|
1461
|
+
const visibleNodeIds = new Set(visibleNodes.filter((node) => !node.hidden).map((node) => node.id));
|
|
1462
|
+
return edges.filter((edge) => visibleNodeIds.has(edge.source) && visibleNodeIds.has(edge.target));
|
|
1463
|
+
}, [visibleNodes, edges]);
|
|
1464
|
+
return { nodes: visibleNodes, edges: visibleEdges };
|
|
1465
|
+
};
|
|
1466
|
+
|
|
1467
|
+
// src/graph/graph-view.tsx
|
|
1468
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1469
|
+
var GraphViewInternal = ({ nodeTypes: parentNodeTypes, edgeTypes: parentEdgeTypes, children, viewportFocusNodeId }) => {
|
|
1470
|
+
const { nodes, edges, toggleNodeExpansion } = useGraphActionsContext();
|
|
1471
|
+
const nodeTypes = React5.useMemo(
|
|
1472
|
+
() => __spreadValues({
|
|
1473
|
+
default: GraphNode,
|
|
1474
|
+
loading: GraphNodeSkeleton
|
|
1475
|
+
}, parentNodeTypes),
|
|
1476
|
+
[parentNodeTypes]
|
|
1477
|
+
);
|
|
1478
|
+
const edgeTypes = React5.useMemo(
|
|
1479
|
+
() => __spreadValues({
|
|
1480
|
+
default: GraphEdge
|
|
1481
|
+
}, parentEdgeTypes),
|
|
1482
|
+
[parentEdgeTypes]
|
|
1483
|
+
);
|
|
1484
|
+
const { nodes: visibleNodes, edges: visibleEdges } = useNodeVisibility(nodes, edges);
|
|
1485
|
+
const nodesWithExpansion = React5.useMemo(() => {
|
|
1486
|
+
return visibleNodes.map((node) => {
|
|
1487
|
+
if (isDefaultNode(node) && !node.data.onExpandToggle) {
|
|
1488
|
+
return __spreadProps(__spreadValues({}, node), {
|
|
1489
|
+
data: __spreadProps(__spreadValues({}, node.data), {
|
|
1490
|
+
onExpandToggle: toggleNodeExpansion
|
|
1491
|
+
})
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
return node;
|
|
1495
|
+
});
|
|
1496
|
+
}, [visibleNodes, toggleNodeExpansion]);
|
|
1497
|
+
const {
|
|
1498
|
+
nodes: layoutedNodes,
|
|
1499
|
+
edges: layoutedEdges,
|
|
1500
|
+
nodeMap
|
|
1501
|
+
} = useAutoLayout(nodesWithExpansion, visibleEdges, {
|
|
1502
|
+
viewportFocusNodeId
|
|
1503
|
+
});
|
|
1504
|
+
useViewportFocus(nodeMap, viewportFocusNodeId);
|
|
1505
|
+
return /* @__PURE__ */ jsxs5(
|
|
1506
|
+
ReactFlow,
|
|
1507
|
+
{
|
|
1508
|
+
nodeTypes,
|
|
1509
|
+
edgeTypes,
|
|
1510
|
+
nodes: layoutedNodes,
|
|
1511
|
+
edges: layoutedEdges,
|
|
1512
|
+
nodesConnectable: false,
|
|
1513
|
+
nodesDraggable: false,
|
|
1514
|
+
fitView: viewportFocusNodeId ? false : true,
|
|
1515
|
+
fitViewOptions: {
|
|
1516
|
+
maxZoom: 1
|
|
1517
|
+
},
|
|
1518
|
+
proOptions: {
|
|
1519
|
+
hideAttribution: true
|
|
1520
|
+
},
|
|
1521
|
+
panOnScroll: true,
|
|
1522
|
+
selectionOnDrag: true,
|
|
1523
|
+
children: [
|
|
1524
|
+
/* @__PURE__ */ jsx8(Background, { className: "bg-muted text-muted" }),
|
|
1525
|
+
/* @__PURE__ */ jsx8(Controls, { showInteractive: false }),
|
|
1526
|
+
/* @__PURE__ */ jsx8(MiniMap, { zoomable: true, pannable: true }),
|
|
1527
|
+
children
|
|
1528
|
+
]
|
|
1529
|
+
}
|
|
1530
|
+
);
|
|
1531
|
+
};
|
|
1532
|
+
var GraphView = React5.forwardRef(({ defaultNodes = [], defaultEdges = [], nodeTypes, edgeTypes, children, viewportFocusNodeId }, ref) => {
|
|
1533
|
+
return /* @__PURE__ */ jsx8(ReactFlowProvider, { children: /* @__PURE__ */ jsx8(GraphActionsProvider, { ref, initialNodes: defaultNodes, initialEdges: defaultEdges, children: /* @__PURE__ */ jsx8(GraphViewInternal, { nodeTypes, edgeTypes, viewportFocusNodeId, children }) }) });
|
|
1534
|
+
});
|
|
1535
|
+
GraphView.displayName = "GraphView";
|
|
1536
|
+
function useViewportFocus(nodeMap, viewportFocusNodeId) {
|
|
1537
|
+
const flow = useReactFlow();
|
|
1538
|
+
React5.useEffect(() => {
|
|
1539
|
+
if (viewportFocusNodeId) {
|
|
1540
|
+
const focusNode = nodeMap.get(viewportFocusNodeId);
|
|
1541
|
+
if (focusNode) {
|
|
1542
|
+
flow.setCenter(focusNode.position.x, focusNode.position.y, { zoom: 1 });
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}, [viewportFocusNodeId, flow, nodeMap]);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/graph/graph.tsx
|
|
1549
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1550
|
+
var defaultGraphErrorConfig = {
|
|
1551
|
+
icon: "triangle-exclamation",
|
|
1552
|
+
header: "An error occurred",
|
|
1553
|
+
subheader: "There was an error fetching the data for this graph. Please refresh the screen."
|
|
1554
|
+
};
|
|
1555
|
+
var defaultGraphEmptyConfig = {
|
|
1556
|
+
icon: "layer-group",
|
|
1557
|
+
header: "No resources found",
|
|
1558
|
+
subheader: "We couldn't find any resources to display in this graph. If you need help getting started you can learn more in the Upbound Documentation."
|
|
1559
|
+
};
|
|
1560
|
+
var defaultGraphNoFilterFoundConfig = {
|
|
1561
|
+
icon: "magnifying-glass",
|
|
1562
|
+
header: "No results found",
|
|
1563
|
+
subheader: "We couldn't find any results that match your search term or filtering criteria. Try using different keywords, checking for typos or adjusting your filters and try again."
|
|
1564
|
+
};
|
|
1565
|
+
function Graph({
|
|
1566
|
+
id,
|
|
1567
|
+
viewportFocusNodeId,
|
|
1568
|
+
config,
|
|
1569
|
+
columnDefinitions = [],
|
|
1570
|
+
title,
|
|
1571
|
+
emptyView,
|
|
1572
|
+
showError,
|
|
1573
|
+
className,
|
|
1574
|
+
loading = false,
|
|
1575
|
+
ActionsComponent: Actions,
|
|
1576
|
+
disableSearch = false,
|
|
1577
|
+
disableFilters = false,
|
|
1578
|
+
searchTerm: parentGlobalFilter,
|
|
1579
|
+
onSearchTermChange: parentSetGlobalFilter,
|
|
1580
|
+
filters: parentColumnFilters,
|
|
1581
|
+
onFiltersChange: parentSetColumnFilters,
|
|
1582
|
+
children
|
|
1583
|
+
}) {
|
|
1584
|
+
var _a, _b, _c;
|
|
1585
|
+
const graphRef = React6.useRef(null);
|
|
1586
|
+
const [innerGlobalFilter, setInnerGlobalFilter] = React6.useState("");
|
|
1587
|
+
const [innerColumnFilters, setInnerColumnFilters] = React6.useState([]);
|
|
1588
|
+
const globalFilter = parentGlobalFilter != null ? parentGlobalFilter : innerGlobalFilter;
|
|
1589
|
+
const setGlobalFilter = parentSetGlobalFilter != null ? parentSetGlobalFilter : setInnerGlobalFilter;
|
|
1590
|
+
React6.useEffect(() => {
|
|
1591
|
+
if (parentGlobalFilter !== void 0) {
|
|
1592
|
+
setInnerGlobalFilter(parentGlobalFilter);
|
|
1593
|
+
}
|
|
1594
|
+
}, [parentGlobalFilter]);
|
|
1595
|
+
const columnFilters = parentColumnFilters != null ? parentColumnFilters : innerColumnFilters;
|
|
1596
|
+
const setColumnFilters = parentSetColumnFilters != null ? parentSetColumnFilters : setInnerColumnFilters;
|
|
1597
|
+
const table = useReactTable({
|
|
1598
|
+
data: config.data,
|
|
1599
|
+
columns: columnDefinitions,
|
|
1600
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1601
|
+
getFilteredRowModel: getFilteredRowModel(),
|
|
1602
|
+
getFacetedRowModel: getFacetedRowModel(),
|
|
1603
|
+
getFacetedUniqueValues: getFacetedUniqueValues(),
|
|
1604
|
+
state: { columnFilters, globalFilter },
|
|
1605
|
+
onColumnFiltersChange: setColumnFilters,
|
|
1606
|
+
onGlobalFilterChange: setGlobalFilter,
|
|
1607
|
+
manualFiltering: false
|
|
1608
|
+
});
|
|
1609
|
+
const flatColumns = table.getAllFlatColumns();
|
|
1610
|
+
const filteredRows = table.getFilteredRowModel().rows;
|
|
1611
|
+
const filteredData = React6.useMemo(() => filteredRows.map((row) => row.original), [filteredRows]);
|
|
1612
|
+
const filterCount = columnFilters.map(
|
|
1613
|
+
({ id: columnFilterId, value }) => {
|
|
1614
|
+
var _a2, _b2;
|
|
1615
|
+
return getColumnFilterCount(value, (_b2 = (_a2 = table.getColumn(columnFilterId)) == null ? void 0 : _a2.columnDef.meta) == null ? void 0 : _b2.filter);
|
|
1616
|
+
}
|
|
1617
|
+
).reduce((prev, curr) => prev + curr, 0);
|
|
1618
|
+
const graphState = getTableState({ error: !!showError, loading, rowCount: filteredData.length });
|
|
1619
|
+
const shouldDisable = graphState === "error";
|
|
1620
|
+
const shouldRenderGraphContent = graphState !== "error" && graphState !== "empty";
|
|
1621
|
+
const resetFilters = () => {
|
|
1622
|
+
setColumnFilters([]);
|
|
1623
|
+
setGlobalFilter("");
|
|
1624
|
+
};
|
|
1625
|
+
const memoizedNodeTypes = React6.useMemo(() => config.nodeTypes, [config.nodeTypes]);
|
|
1626
|
+
const memoizedEdgeTypes = React6.useMemo(() => config.edgeTypes, [config.edgeTypes]);
|
|
1627
|
+
const memoizedConfig = React6.useMemo(() => config, [config]);
|
|
1628
|
+
React6.useEffect(() => {
|
|
1629
|
+
if (!graphRef.current) {
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
const { setNodes, setEdges } = graphRef.current;
|
|
1633
|
+
const nodes = filteredData.map((item) => memoizedConfig.nodeTransform(item, filteredData)).flat() || [];
|
|
1634
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
1635
|
+
const edges = (filteredData.map(memoizedConfig.edgeTransform).filter(Boolean).flat() || []).filter(
|
|
1636
|
+
(edge) => Boolean(edge && edge.hasOwnProperty("source") && edge.hasOwnProperty("target")) && nodeIds.has(edge.source) && nodeIds.has(edge.target)
|
|
1637
|
+
);
|
|
1638
|
+
setNodes(nodes);
|
|
1639
|
+
setEdges(edges);
|
|
1640
|
+
}, [memoizedConfig, filteredData]);
|
|
1641
|
+
const showFilters = !disableFilters && hasFilterableColumns(flatColumns);
|
|
1642
|
+
const showToolbar = !disableSearch || !!Actions;
|
|
1643
|
+
const resolvedEmptyView = {
|
|
1644
|
+
error: (_a = emptyView == null ? void 0 : emptyView.error) != null ? _a : defaultGraphErrorConfig,
|
|
1645
|
+
empty: (_b = emptyView == null ? void 0 : emptyView.empty) != null ? _b : defaultGraphEmptyConfig,
|
|
1646
|
+
filter: (_c = emptyView == null ? void 0 : emptyView.filter) != null ? _c : defaultGraphNoFilterFoundConfig
|
|
1647
|
+
};
|
|
1648
|
+
return /* @__PURE__ */ jsxs6("div", { id: `graph-${id}`, className: cn("flex size-full flex-1 flex-col", className), children: [
|
|
1649
|
+
!!title && /* @__PURE__ */ jsx9(SectionHeader, { className: "mb-4", children: /* @__PURE__ */ jsx9(SectionHeaderContent, { children: /* @__PURE__ */ jsx9(SectionHeaderTitle, { children: title }) }) }),
|
|
1650
|
+
showToolbar && /* @__PURE__ */ jsxs6("div", { className: "mb-4 flex flex-row gap-2", children: [
|
|
1651
|
+
!disableSearch && /* @__PURE__ */ jsxs6(InputGroup, { className: "w-70", children: [
|
|
1652
|
+
/* @__PURE__ */ jsx9(InputGroupAddon, { align: "inline-start", children: /* @__PURE__ */ jsx9(Icon4, { name: "magnifying-glass" }) }),
|
|
1653
|
+
/* @__PURE__ */ jsx9(
|
|
1654
|
+
InputGroupInput,
|
|
1655
|
+
{
|
|
1656
|
+
"aria-label": "Search",
|
|
1657
|
+
placeholder: "Search\u2026",
|
|
1658
|
+
disabled: shouldDisable,
|
|
1659
|
+
value: globalFilter,
|
|
1660
|
+
onChange: (e) => setGlobalFilter(e.target.value)
|
|
1661
|
+
}
|
|
1662
|
+
)
|
|
1663
|
+
] }),
|
|
1664
|
+
!!Actions && /* @__PURE__ */ jsx9("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsx9(Actions, { state: graphState }) })
|
|
1665
|
+
] }),
|
|
1666
|
+
showFilters && /* @__PURE__ */ jsx9(
|
|
1667
|
+
ViewFilter,
|
|
1668
|
+
{
|
|
1669
|
+
columns: flatColumns,
|
|
1670
|
+
columnFilters,
|
|
1671
|
+
onColumnFiltersChange: setColumnFilters,
|
|
1672
|
+
className: "mb-4"
|
|
1673
|
+
}
|
|
1674
|
+
),
|
|
1675
|
+
shouldRenderGraphContent ? /* @__PURE__ */ jsxs6("div", { className: "relative size-full flex-1", children: [
|
|
1676
|
+
graphState === "loading" && /* @__PURE__ */ jsx9(Icon4, { name: "circle-notch", className: "absolute top-4 left-4 z-10 animate-spin" }),
|
|
1677
|
+
/* @__PURE__ */ jsx9(
|
|
1678
|
+
GraphView,
|
|
1679
|
+
{
|
|
1680
|
+
viewportFocusNodeId,
|
|
1681
|
+
ref: graphRef,
|
|
1682
|
+
nodeTypes: memoizedNodeTypes,
|
|
1683
|
+
edgeTypes: memoizedEdgeTypes,
|
|
1684
|
+
children
|
|
1685
|
+
}
|
|
1686
|
+
)
|
|
1687
|
+
] }) : /* @__PURE__ */ jsx9(
|
|
1688
|
+
DataTableEmptyView,
|
|
1689
|
+
{
|
|
1690
|
+
emptyView: resolvedEmptyView,
|
|
1691
|
+
tableState: graphState,
|
|
1692
|
+
filterCount: filterCount + (!!globalFilter.trim() ? 1 : 0),
|
|
1693
|
+
resetFilters
|
|
1694
|
+
}
|
|
1695
|
+
)
|
|
1696
|
+
] });
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// src/graph/hooks/useGetViewportNodeIds.ts
|
|
1700
|
+
import { useEffect as useEffect3, useMemo as useMemo7, useRef as useRef3, useState as useState4 } from "react";
|
|
1701
|
+
import { useStore, useViewport } from "reactflow";
|
|
1702
|
+
var useGetDebouncedViewport = (debounceDelay = 1e3) => {
|
|
1703
|
+
const timerRef = useRef3(null);
|
|
1704
|
+
const [debouncedValues, setDebouncedValues] = useState4({ x: 0, y: 0, zoom: 1, width: 0, height: 0, nodes: [] });
|
|
1705
|
+
const { x, y, zoom } = useViewport();
|
|
1706
|
+
const { width, height, nodes } = useStore((state) => ({
|
|
1707
|
+
width: state.width,
|
|
1708
|
+
height: state.height,
|
|
1709
|
+
nodes: state.getNodes()
|
|
1710
|
+
}));
|
|
1711
|
+
const nodesHash = nodes.map((n) => n.id).sort().join(",");
|
|
1712
|
+
const memoizedNodes = useMemo7(() => nodes, [nodesHash]);
|
|
1713
|
+
useEffect3(() => {
|
|
1714
|
+
if (timerRef.current) {
|
|
1715
|
+
clearTimeout(timerRef.current);
|
|
1716
|
+
}
|
|
1717
|
+
timerRef.current = setTimeout(() => {
|
|
1718
|
+
setDebouncedValues({ x, y, zoom, width, height, nodes: memoizedNodes });
|
|
1719
|
+
timerRef.current = null;
|
|
1720
|
+
}, debounceDelay);
|
|
1721
|
+
return () => {
|
|
1722
|
+
if (timerRef.current) {
|
|
1723
|
+
clearTimeout(timerRef.current);
|
|
1724
|
+
timerRef.current = null;
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
}, [x, y, zoom, width, height, memoizedNodes, debounceDelay]);
|
|
1728
|
+
return debouncedValues;
|
|
1729
|
+
};
|
|
1730
|
+
var useGetViewportNodeIds = (debounceDelay = 1e3) => {
|
|
1731
|
+
const { x, y, zoom, width, height, nodes } = useGetDebouncedViewport(debounceDelay);
|
|
1732
|
+
return useMemo7(() => {
|
|
1733
|
+
return nodes.filter((node) => {
|
|
1734
|
+
var _a, _b, _c, _d;
|
|
1735
|
+
if (node.hidden) {
|
|
1736
|
+
return false;
|
|
1737
|
+
}
|
|
1738
|
+
const nodeX = ((_b = (_a = node.positionAbsolute) == null ? void 0 : _a.x) != null ? _b : node.position.x) * zoom + x;
|
|
1739
|
+
const nodeY = ((_d = (_c = node.positionAbsolute) == null ? void 0 : _c.y) != null ? _d : node.position.y) * zoom + y;
|
|
1740
|
+
const nodeW = GRAPH_NODE_WIDTH * zoom;
|
|
1741
|
+
const nodeH = GRAPH_NODE_HEIGHT * zoom;
|
|
1742
|
+
return nodeX + nodeW >= 0 && nodeX <= width && nodeY + nodeH >= 0 && nodeY <= height;
|
|
1743
|
+
}).map((node) => node.id).sort((a, b) => a.localeCompare(b));
|
|
1744
|
+
}, [x, y, zoom, width, height, nodes]);
|
|
1745
|
+
};
|
|
1746
|
+
|
|
1747
|
+
// src/graph/utils.ts
|
|
1748
|
+
import { getOutgoers as getOutgoers2 } from "reactflow";
|
|
1749
|
+
var getGraphDescendants = (node, nodes, edges) => {
|
|
1750
|
+
const outgoers = getOutgoers2(node, nodes, edges);
|
|
1751
|
+
return outgoers.reduce(
|
|
1752
|
+
(acc, outgoer) => {
|
|
1753
|
+
const { nodes: outgoerNodes, edges: outgoerEdges } = getGraphDescendants(outgoer, nodes, edges);
|
|
1754
|
+
return {
|
|
1755
|
+
nodes: [...acc.nodes, ...outgoerNodes],
|
|
1756
|
+
edges: [...acc.edges, ...outgoerEdges.filter((edge) => edge.source === outgoer.id)]
|
|
1757
|
+
};
|
|
1758
|
+
},
|
|
1759
|
+
{ nodes: [node], edges: edges.filter((edge) => edge.source === node.id) }
|
|
1760
|
+
);
|
|
1761
|
+
};
|
|
1762
|
+
export {
|
|
1763
|
+
Graph,
|
|
1764
|
+
GraphActionsProvider,
|
|
1765
|
+
GraphEdge,
|
|
1766
|
+
GraphNode,
|
|
1767
|
+
GraphNodeSkeleton,
|
|
1768
|
+
GraphView,
|
|
1769
|
+
defaultGraphEmptyConfig,
|
|
1770
|
+
defaultGraphErrorConfig,
|
|
1771
|
+
defaultGraphNoFilterFoundConfig,
|
|
1772
|
+
getGraphDescendants,
|
|
1773
|
+
isDefaultNode,
|
|
1774
|
+
useAutoLayout,
|
|
1775
|
+
useGetViewportNodeIds,
|
|
1776
|
+
useGraphActions,
|
|
1777
|
+
useGraphActionsContext,
|
|
1778
|
+
useNodeVisibility
|
|
1779
|
+
};
|
|
1780
|
+
//# sourceMappingURL=graph.js.map
|