@ocelescope/resources 0.10.2 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +19 -15
- package/dist/index.js +488 -175
- package/package.json +14 -13
package/dist/index.d.ts
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { MultiSelectProps, SelectProps } from "@mantine/core";
|
|
2
|
+
import { resourceVisualization } from "@ocelescope/api-base";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
type Controlled<P extends SelectProps | MultiSelectProps> = Omit<P, "defaultValue"> & Required<Pick<P, "value" | "onChange">>;
|
|
5
|
+
type ActionSelectProps = {
|
|
6
|
+
actions?: ReactNode;
|
|
7
|
+
} & ((Controlled<SelectProps> & {
|
|
8
|
+
multiple?: false;
|
|
9
|
+
}) | (Controlled<MultiSelectProps> & {
|
|
10
|
+
multiple: true;
|
|
11
|
+
}));
|
|
12
|
+
export declare const ActionSelect: React.FC<ActionSelectProps>;
|
|
5
13
|
export declare const ResourceModal: React.FC<{
|
|
6
14
|
id: string;
|
|
7
15
|
onClose: () => void;
|
|
8
16
|
}>;
|
|
9
|
-
|
|
10
|
-
//#region src/components/ResourceSelect/ResourceSelect.d.ts
|
|
11
|
-
export declare const ResourceSelect: React.FC<Omit<ComponentProps<typeof Select<string>>, "data"> & {
|
|
17
|
+
type ResourceSelectProps = ActionSelectProps & {
|
|
12
18
|
type?: string;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
withUpload?: boolean;
|
|
20
|
+
withView?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare const ResourceSelect: React.FC<ResourceSelectProps>;
|
|
16
23
|
type RequireType<T> = T extends {
|
|
17
24
|
type?: infer L;
|
|
18
25
|
} ? Omit<T, "type"> & {
|
|
19
26
|
type: L;
|
|
20
27
|
} : T;
|
|
21
|
-
type VisualizationsType = RequireType<NonNullable<
|
|
28
|
+
type VisualizationsType = RequireType<NonNullable<Awaited<ReturnType<typeof resourceVisualization>>>>;
|
|
22
29
|
type VisualizationsTypes = NonNullable<VisualizationsType["type"]>;
|
|
23
30
|
type VisualizationByType<T extends VisualizationsTypes> = Extract<VisualizationsType, {
|
|
24
31
|
type: T;
|
|
25
32
|
}>;
|
|
26
|
-
//#endregion
|
|
27
|
-
//#region src/components/ResourceView/index.d.ts
|
|
28
33
|
type ExtraMenuItem = {
|
|
29
34
|
icon: React.ReactNode;
|
|
30
35
|
label: string;
|
|
@@ -40,5 +45,4 @@ export declare const ResourceViewer: React.FC<{
|
|
|
40
45
|
id: string;
|
|
41
46
|
isPreview?: boolean;
|
|
42
47
|
}>;
|
|
43
|
-
|
|
44
|
-
export type { VisualizationsType };
|
|
48
|
+
export type { ActionSelectProps, ResourceSelectProps, VisualizationsType };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { ActionIcon, Alert, Badge, Box, Button, ButtonGroup, Code, Group, LoadingOverlay, Menu, Modal, Popover, ScrollArea, Select, Stack,
|
|
2
|
-
import { useResource, useResources } from "@ocelescope/api-base";
|
|
3
|
-
import { DownloadIcon, EyeOffIcon, Maximize2Icon, MaximizeIcon, MinusIcon, PlusIcon, XIcon } from "lucide-react";
|
|
4
|
-
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
-
import dynamic from "next/dynamic.js";
|
|
1
|
+
import { ActionIcon, Alert, Autocomplete, Badge, Box, Button, ButtonGroup, Center, Code, FileButton, Group, LoadingOverlay, Menu, Modal, MultiSelect, Popover, ScrollArea, Scroller, Select, Stack, Tabs, Text, Title, Tooltip } from "@mantine/core";
|
|
6
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useResource, useResourceTableColumnValues, useResourceTableRows, useResourceTables, useResourceVisualization, useResources, useUpload } from "@ocelescope/api-base";
|
|
4
|
+
import { DownloadIcon, EyeIcon, EyeOffIcon, Maximize2Icon, MaximizeIcon, MinusIcon, PlusIcon, SearchIcon, UploadIcon, XIcon } from "lucide-react";
|
|
5
|
+
import { useDebouncedValue, useDisclosure } from "@mantine/hooks";
|
|
6
|
+
import { DataTable } from "mantine-datatable";
|
|
7
|
+
import { memo, useCallback, useEffect, useEffectEvent, useMemo, useRef, useState } from "react";
|
|
8
|
+
import dayjs from "dayjs";
|
|
9
|
+
import isBetween from "dayjs/plugin/isBetween.js";
|
|
10
|
+
import dynamic from "next/dynamic.js";
|
|
7
11
|
import { Graphviz } from "@hpcc-js/wasm-graphviz";
|
|
8
12
|
import FileSaver from "file-saver";
|
|
9
13
|
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
|
|
@@ -11,15 +15,302 @@ import { Background, BaseEdge, ControlButton, Controls, EdgeLabelRenderer, Handl
|
|
|
11
15
|
import { toPng, toSvg } from "html-to-image";
|
|
12
16
|
import { jsPDF } from "jspdf";
|
|
13
17
|
import ELK from "elkjs/lib/elk.bundled.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
// src/components/ActionSelect/ActionSelect.tsx
|
|
19
|
+
const attachedStyles = { input: {
|
|
20
|
+
borderTopRightRadius: 0,
|
|
21
|
+
borderBottomRightRadius: 0
|
|
22
|
+
} };
|
|
23
|
+
const renderSelect = (props) => {
|
|
24
|
+
const styles = props.actions ? attachedStyles : void 0;
|
|
25
|
+
if (props.multiple) {
|
|
26
|
+
const { multiple: _, actions: __, ...rest } = props;
|
|
27
|
+
return /* @__PURE__ */ jsx(MultiSelect, {
|
|
28
|
+
flex: 1,
|
|
29
|
+
miw: 0,
|
|
30
|
+
styles,
|
|
31
|
+
...rest
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const { multiple: _, actions: __, ...rest } = props;
|
|
35
|
+
return /* @__PURE__ */ jsx(Select, {
|
|
36
|
+
flex: 1,
|
|
37
|
+
miw: 0,
|
|
38
|
+
styles,
|
|
39
|
+
...rest
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const ActionSelect = (props) => /* @__PURE__ */ jsxs(Group, {
|
|
43
|
+
wrap: "nowrap",
|
|
44
|
+
gap: 0,
|
|
45
|
+
align: "flex-end",
|
|
46
|
+
children: [renderSelect(props), props.actions && /* @__PURE__ */ jsxs(ActionIcon.Group, { children: [/* @__PURE__ */ jsx(ActionIcon.GroupSection, { hidden: true }), props.actions] })]
|
|
47
|
+
});
|
|
48
|
+
// src/util/dayjs.ts
|
|
49
|
+
dayjs.extend(isBetween);
|
|
50
|
+
var dayjs_default = dayjs;
|
|
51
|
+
const formatDateTime = (dateString) => dayjs(dateString).format("YYYY-MM-DD HH:mm");
|
|
52
|
+
const formatDate = (dateString) => dayjs(dateString).format("YYYY-MM-DD");
|
|
53
|
+
// src/components/ResourceView/Viewers/Table.tsx
|
|
54
|
+
const formatCell = (value, dataType) => {
|
|
55
|
+
if (value == null) return "";
|
|
56
|
+
switch (dataType) {
|
|
57
|
+
case "boolean": return value ? "✅" : "❌";
|
|
58
|
+
case "date": return dayjs_default(value).isValid() ? formatDate(value) : String(value);
|
|
59
|
+
case "datetime": return dayjs_default(value).isValid() ? formatDateTime(value) : String(value);
|
|
60
|
+
default: return String(value);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const PAGE_SIZES$1 = [
|
|
64
|
+
20,
|
|
65
|
+
50,
|
|
66
|
+
75
|
|
67
|
+
];
|
|
68
|
+
const TableView = ({ visualization }) => {
|
|
69
|
+
const [pageSize, setPageSize] = useState(PAGE_SIZES$1[0]);
|
|
70
|
+
const columns = useMemo(() => visualization.columns.map((col) => ({
|
|
71
|
+
accessor: col.id,
|
|
72
|
+
title: col.label ?? col.id,
|
|
73
|
+
render: (record) => formatCell(record[col.id], col.data_type)
|
|
74
|
+
})), [visualization.columns]);
|
|
75
|
+
const [page, setPage] = useState(1);
|
|
76
|
+
const [records, setRecords] = useState(visualization.rows.slice(0, pageSize));
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const from = (page - 1) * pageSize;
|
|
79
|
+
const to = from + pageSize;
|
|
80
|
+
setRecords(visualization.rows.slice(from, to));
|
|
81
|
+
}, [
|
|
82
|
+
page,
|
|
83
|
+
pageSize,
|
|
84
|
+
visualization
|
|
85
|
+
]);
|
|
86
|
+
const paginationProps = visualization.rows.length > PAGE_SIZES$1[0] ? {
|
|
87
|
+
totalRecords: visualization.rows.length,
|
|
88
|
+
recordsPerPage: pageSize,
|
|
89
|
+
onPageChange: (p) => setPage(p),
|
|
90
|
+
page,
|
|
91
|
+
recordsPerPageOptions: [...PAGE_SIZES$1],
|
|
92
|
+
onRecordsPerPageChange: (newPageSize) => {
|
|
93
|
+
setPageSize(newPageSize);
|
|
94
|
+
setPage(1);
|
|
95
|
+
}
|
|
96
|
+
} : {};
|
|
97
|
+
return /* @__PURE__ */ jsx(DataTable, {
|
|
98
|
+
records,
|
|
99
|
+
columns,
|
|
100
|
+
withTableBorder: true,
|
|
101
|
+
withColumnBorders: true,
|
|
102
|
+
withRowBorders: true,
|
|
103
|
+
...paginationProps
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
// src/components/ResourceView/TabularView.tsx
|
|
107
|
+
const PAGE_SIZES = [
|
|
108
|
+
20,
|
|
109
|
+
50,
|
|
110
|
+
100
|
|
111
|
+
];
|
|
112
|
+
const UNSORTED = {
|
|
113
|
+
columnAccessor: "",
|
|
114
|
+
direction: "asc"
|
|
115
|
+
};
|
|
116
|
+
const VALUE_LIMIT = 50;
|
|
117
|
+
const ColumnFilter = ({ resourceId, tableName, columnId, selected, onChange }) => {
|
|
118
|
+
const [search, setSearch] = useState("");
|
|
119
|
+
const [debouncedSearch] = useDebouncedValue(search, 250);
|
|
120
|
+
const { data, isFetching } = useResourceTableColumnValues(resourceId, tableName, columnId, {
|
|
121
|
+
size: VALUE_LIMIT,
|
|
122
|
+
...debouncedSearch && { search: debouncedSearch }
|
|
123
|
+
}, { query: { placeholderData: (previous) => previous } });
|
|
124
|
+
const options = useMemo(() => [.../* @__PURE__ */ new Set([...selected, ...data?.response ?? []])], [selected, data?.response]);
|
|
125
|
+
const total = data?.total_items ?? 0;
|
|
126
|
+
return /* @__PURE__ */ jsx(MultiSelect, {
|
|
127
|
+
w: 280,
|
|
128
|
+
searchable: true,
|
|
129
|
+
clearable: true,
|
|
130
|
+
data: options,
|
|
131
|
+
value: selected,
|
|
132
|
+
onChange,
|
|
133
|
+
searchValue: search,
|
|
134
|
+
onSearchChange: setSearch,
|
|
135
|
+
filter: ({ options: unfiltered }) => unfiltered,
|
|
136
|
+
placeholder: `Search ${columnId}`,
|
|
137
|
+
nothingFoundMessage: isFetching ? "Loading..." : "No values found",
|
|
138
|
+
description: total > VALUE_LIMIT ? `Showing ${VALUE_LIMIT} of ${total} values, type to narrow down` : void 0,
|
|
139
|
+
comboboxProps: { withinPortal: false }
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
const TableRows = ({ resourceId, table }) => {
|
|
143
|
+
const [page, setPage] = useState(1);
|
|
144
|
+
const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);
|
|
145
|
+
const [sortStatus, setSortStatus] = useState(UNSORTED);
|
|
146
|
+
const [filters, setFilters] = useState({});
|
|
147
|
+
const hasFilters = Object.values(filters).some((values) => values.length > 0);
|
|
148
|
+
const { data, isError, isFetching } = useResourceTableRows(resourceId, table.name, {
|
|
149
|
+
page,
|
|
150
|
+
size: pageSize,
|
|
151
|
+
...sortStatus.columnAccessor && {
|
|
152
|
+
sort_by: sortStatus.columnAccessor,
|
|
153
|
+
descending: sortStatus.direction === "desc"
|
|
154
|
+
},
|
|
155
|
+
...hasFilters && { filters: JSON.stringify(filters) }
|
|
156
|
+
}, { query: { placeholderData: (previous) => previous } });
|
|
157
|
+
const columns = useMemo(() => table.columns.map((column) => ({
|
|
158
|
+
accessor: column.id,
|
|
159
|
+
title: column.id,
|
|
160
|
+
sortable: true,
|
|
161
|
+
render: (row) => formatCell(row[column.id], column.data_type),
|
|
162
|
+
filtering: (filters[column.id] ?? []).length > 0,
|
|
163
|
+
filter: /* @__PURE__ */ jsx(ColumnFilter, {
|
|
164
|
+
resourceId,
|
|
165
|
+
tableName: table.name,
|
|
166
|
+
columnId: column.id,
|
|
167
|
+
selected: filters[column.id] ?? [],
|
|
168
|
+
onChange: (values) => {
|
|
169
|
+
setFilters((current) => ({
|
|
170
|
+
...current,
|
|
171
|
+
[column.id]: values
|
|
172
|
+
}));
|
|
173
|
+
setPage(1);
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
})), [
|
|
177
|
+
resourceId,
|
|
178
|
+
table.name,
|
|
179
|
+
table.columns,
|
|
180
|
+
filters
|
|
181
|
+
]);
|
|
182
|
+
const records = useMemo(() => (data?.response ?? []).map((row, index) => ({
|
|
183
|
+
...row,
|
|
184
|
+
rowKey: (page - 1) * pageSize + index
|
|
185
|
+
})), [
|
|
186
|
+
data?.response,
|
|
187
|
+
page,
|
|
188
|
+
pageSize
|
|
189
|
+
]);
|
|
190
|
+
if (isError) return /* @__PURE__ */ jsx(Center, {
|
|
191
|
+
h: "100%",
|
|
192
|
+
p: "md",
|
|
193
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
194
|
+
c: "dimmed",
|
|
195
|
+
children: "Could not load the table"
|
|
196
|
+
})
|
|
197
|
+
});
|
|
198
|
+
return /* @__PURE__ */ jsx(DataTable, {
|
|
199
|
+
height: "100%",
|
|
200
|
+
idAccessor: "rowKey",
|
|
201
|
+
records,
|
|
202
|
+
columns,
|
|
203
|
+
fetching: isFetching,
|
|
204
|
+
withTableBorder: true,
|
|
205
|
+
withColumnBorders: true,
|
|
206
|
+
sortStatus,
|
|
207
|
+
onSortStatusChange: (status) => {
|
|
208
|
+
setSortStatus(status);
|
|
209
|
+
setPage(1);
|
|
210
|
+
},
|
|
211
|
+
totalRecords: data?.total_items ?? 0,
|
|
212
|
+
recordsPerPage: pageSize,
|
|
213
|
+
page,
|
|
214
|
+
onPageChange: setPage,
|
|
215
|
+
recordsPerPageOptions: PAGE_SIZES,
|
|
216
|
+
onRecordsPerPageChange: (size) => {
|
|
217
|
+
setPageSize(size);
|
|
218
|
+
setPage(1);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
const TableTabs = ({ resourceId, tables }) => {
|
|
223
|
+
const [activeTable, setActiveTable] = useState(tables[0]?.name ?? null);
|
|
224
|
+
const [searchOpened, { toggle: toggleSearch, close: closeSearch }] = useDisclosure(false);
|
|
225
|
+
return /* @__PURE__ */ jsxs(Tabs, {
|
|
226
|
+
keepMounted: false,
|
|
227
|
+
h: "100%",
|
|
228
|
+
style: {
|
|
229
|
+
display: "flex",
|
|
230
|
+
flexDirection: "column"
|
|
231
|
+
},
|
|
232
|
+
value: activeTable,
|
|
233
|
+
onChange: setActiveTable,
|
|
234
|
+
children: [tables.length > 1 && /* @__PURE__ */ jsxs(Group, {
|
|
235
|
+
wrap: "nowrap",
|
|
236
|
+
gap: "0",
|
|
237
|
+
style: { flexShrink: 0 },
|
|
238
|
+
children: [/* @__PURE__ */ jsxs(Popover, {
|
|
239
|
+
width: 300,
|
|
240
|
+
position: "bottom-start",
|
|
241
|
+
withArrow: true,
|
|
242
|
+
shadow: "md",
|
|
243
|
+
opened: searchOpened,
|
|
244
|
+
onChange: closeSearch,
|
|
245
|
+
trapFocus: true,
|
|
246
|
+
returnFocus: true,
|
|
247
|
+
children: [/* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
248
|
+
size: "lg",
|
|
249
|
+
h: "100%",
|
|
250
|
+
onClick: toggleSearch,
|
|
251
|
+
"aria-label": "Search tables",
|
|
252
|
+
bdrs: 0,
|
|
253
|
+
children: /* @__PURE__ */ jsx(SearchIcon, { size: 16 })
|
|
254
|
+
}) }), /* @__PURE__ */ jsx(Popover.Dropdown, { children: /* @__PURE__ */ jsx(Autocomplete, {
|
|
255
|
+
data: tables.map(({ name }) => name),
|
|
256
|
+
"aria-label": "Table",
|
|
257
|
+
placeholder: "Search tables",
|
|
258
|
+
selectFirstOptionOnChange: true,
|
|
259
|
+
comboboxProps: { withinPortal: false },
|
|
260
|
+
onOptionSubmit: (name) => {
|
|
261
|
+
setActiveTable(name);
|
|
262
|
+
closeSearch();
|
|
263
|
+
}
|
|
264
|
+
}) })]
|
|
265
|
+
}), /* @__PURE__ */ jsx(Tabs.List, {
|
|
266
|
+
flex: 1,
|
|
267
|
+
miw: 0,
|
|
268
|
+
children: /* @__PURE__ */ jsx(Scroller, { children: tables.map(({ name }) => /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
269
|
+
value: name,
|
|
270
|
+
children: name
|
|
271
|
+
}, name)) })
|
|
272
|
+
})]
|
|
273
|
+
}), tables.map((table) => /* @__PURE__ */ jsx(Tabs.Panel, {
|
|
274
|
+
value: table.name,
|
|
275
|
+
flex: 1,
|
|
276
|
+
mih: 0,
|
|
277
|
+
children: /* @__PURE__ */ jsx(TableRows, {
|
|
278
|
+
resourceId,
|
|
279
|
+
table
|
|
280
|
+
})
|
|
281
|
+
}, table.name))]
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
const TabularView = ({ resourceId }) => {
|
|
285
|
+
const { data: tables, isError, isPending } = useResourceTables(resourceId);
|
|
286
|
+
if (isError) return /* @__PURE__ */ jsx(Center, {
|
|
287
|
+
h: "100%",
|
|
288
|
+
p: "md",
|
|
289
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
290
|
+
c: "dimmed",
|
|
291
|
+
children: "Could not load the tables"
|
|
292
|
+
})
|
|
293
|
+
});
|
|
294
|
+
if (isPending) return /* @__PURE__ */ jsx(LoadingOverlay, { visible: true });
|
|
295
|
+
if (tables.length === 0) return /* @__PURE__ */ jsx(Center, {
|
|
296
|
+
h: "100%",
|
|
297
|
+
p: "md",
|
|
298
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
299
|
+
c: "dimmed",
|
|
300
|
+
children: "This resource has no tables"
|
|
301
|
+
})
|
|
302
|
+
});
|
|
303
|
+
return /* @__PURE__ */ jsx(TableTabs, {
|
|
304
|
+
resourceId,
|
|
305
|
+
tables
|
|
306
|
+
});
|
|
307
|
+
};
|
|
308
|
+
// src/components/ResourceView/Viewers/R4pm.tsx
|
|
18
309
|
const r4pmViewer = (pick) => dynamic(async () => {
|
|
19
|
-
const [components, {
|
|
310
|
+
const [components, { wasmViz }] = await Promise.all([import("@r4pm/components"), import("@r4pm/components/viz/wasm")]);
|
|
20
311
|
const { ViewerConfigProvider } = components;
|
|
21
312
|
const Viewer = pick(components);
|
|
22
|
-
const viewerConfig = {
|
|
313
|
+
const viewerConfig = { viz: wasmViz };
|
|
23
314
|
return (props) => /* @__PURE__ */ jsx(ViewerConfigProvider, {
|
|
24
315
|
value: viewerConfig,
|
|
25
316
|
children: /* @__PURE__ */ jsx(Viewer, { ...props })
|
|
@@ -37,8 +328,7 @@ const R4pmViewerContainer = ({ filename, children }) => /* @__PURE__ */ jsx(Box,
|
|
|
37
328
|
children
|
|
38
329
|
})
|
|
39
330
|
});
|
|
40
|
-
|
|
41
|
-
//#region src/components/ResourceView/Viewers/DFG.tsx
|
|
331
|
+
// src/components/ResourceView/Viewers/DFG.tsx
|
|
42
332
|
const R4pmDfg = r4pmViewer((c) => c.DFGViewer);
|
|
43
333
|
const R4pmOcdfg = r4pmViewer((c) => c.OCDFGViewer);
|
|
44
334
|
const toR4pmDfg = ({ activities, directly_follows_relations, start_activities, end_activities }) => ({
|
|
@@ -58,20 +348,17 @@ const DFGViewer = ({ visualization }) => {
|
|
|
58
348
|
});
|
|
59
349
|
};
|
|
60
350
|
const OCDFGViewer = ({ visualization }) => {
|
|
61
|
-
const ocdfg = useMemo(() => ({
|
|
62
|
-
object_type_to_dfg: Object.fromEntries(Object.entries(visualization.object_type_to_dfg ?? {}).map(([objectType, dfg]) => [objectType, toR4pmDfg(dfg)])),
|
|
63
|
-
object_counts: visualization.object_counts ?? {}
|
|
64
|
-
}), [visualization]);
|
|
351
|
+
const ocdfg = useMemo(() => ({ object_type_to_dfg: Object.fromEntries(Object.entries(visualization.object_type_to_dfg ?? {}).map(([objectType, dfg]) => [objectType, toR4pmDfg(dfg)])) }), [visualization]);
|
|
65
352
|
return /* @__PURE__ */ jsx(R4pmViewerContainer, {
|
|
66
353
|
filename: "oc-dfg",
|
|
67
354
|
children: /* @__PURE__ */ jsx(R4pmOcdfg, {
|
|
68
355
|
actions: [],
|
|
69
|
-
data: ocdfg
|
|
356
|
+
data: ocdfg,
|
|
357
|
+
objectCounts: visualization.object_counts
|
|
70
358
|
})
|
|
71
359
|
});
|
|
72
360
|
};
|
|
73
|
-
|
|
74
|
-
//#region src/components/ResourceView/Viewers/SVG.tsx
|
|
361
|
+
// src/components/ResourceView/Viewers/SVG.tsx
|
|
75
362
|
const { saveAs: saveAs$2 } = FileSaver;
|
|
76
363
|
const SvgPanWrapper = ({ children, onDownload }) => {
|
|
77
364
|
return /* @__PURE__ */ jsx(Box, {
|
|
@@ -143,8 +430,7 @@ const SvgViewer = ({ visualization, isPreview }) => {
|
|
|
143
430
|
children: content
|
|
144
431
|
});
|
|
145
432
|
};
|
|
146
|
-
|
|
147
|
-
//#region src/components/ResourceView/Viewers/Dot.tsx
|
|
433
|
+
// src/components/ResourceView/Viewers/Dot.tsx
|
|
148
434
|
const { saveAs: saveAs$1 } = FileSaver;
|
|
149
435
|
const DotToSvgViewer = ({ isPreview, visualization }) => {
|
|
150
436
|
const [svg, setSvg] = useState(null);
|
|
@@ -216,8 +502,7 @@ const DotToSvgViewer = ({ isPreview, visualization }) => {
|
|
|
216
502
|
children: content
|
|
217
503
|
});
|
|
218
504
|
};
|
|
219
|
-
|
|
220
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/GraphControls.tsx
|
|
505
|
+
// src/components/ResourceView/Viewers/GraphFlow/GraphControls.tsx
|
|
221
506
|
const GraphControls = ({ onFitView, onDownloadPng, onDownloadSvg, onDownloadPdf, menuItems, isPreview }) => {
|
|
222
507
|
if (isPreview) return null;
|
|
223
508
|
return /* @__PURE__ */ jsxs(Controls, {
|
|
@@ -261,8 +546,7 @@ const GraphControls = ({ onFitView, onDownloadPng, onDownloadSvg, onDownloadPdf,
|
|
|
261
546
|
]
|
|
262
547
|
});
|
|
263
548
|
};
|
|
264
|
-
|
|
265
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/utils/bounds.ts
|
|
549
|
+
// src/components/ResourceView/Viewers/GraphFlow/utils/bounds.ts
|
|
266
550
|
function nodeBoundsFromData(nodes) {
|
|
267
551
|
if (nodes.length === 0) return {
|
|
268
552
|
x: 0,
|
|
@@ -387,8 +671,7 @@ function getContentBounds(nodes, viewportEl, getNodesBounds) {
|
|
|
387
671
|
}
|
|
388
672
|
return combined;
|
|
389
673
|
}
|
|
390
|
-
|
|
391
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/utils/download.ts
|
|
674
|
+
// src/components/ResourceView/Viewers/GraphFlow/utils/download.ts
|
|
392
675
|
const { saveAs } = FileSaver;
|
|
393
676
|
const MAX_PX = 2048;
|
|
394
677
|
const EXPORT_PADDING = .05;
|
|
@@ -452,8 +735,7 @@ async function downloadPdf(container, nodes, getNodesBounds) {
|
|
|
452
735
|
pdf.addImage(dataUrl, "PNG", 0, 0, imageWidth, imageHeight);
|
|
453
736
|
pdf.save("graph.pdf");
|
|
454
737
|
}
|
|
455
|
-
|
|
456
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/hooks/useGraphExport.ts
|
|
738
|
+
// src/components/ResourceView/Viewers/GraphFlow/hooks/useGraphExport.ts
|
|
457
739
|
const useGraphExport = (wrapperRef) => {
|
|
458
740
|
const { getNodes, getNodesBounds } = useReactFlow();
|
|
459
741
|
return {
|
|
@@ -483,8 +765,7 @@ const useGraphExport = (wrapperRef) => {
|
|
|
483
765
|
])
|
|
484
766
|
};
|
|
485
767
|
};
|
|
486
|
-
|
|
487
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/model/types.ts
|
|
768
|
+
// src/components/ResourceView/Viewers/GraphFlow/model/types.ts
|
|
488
769
|
var GraphError = class extends Error {
|
|
489
770
|
details;
|
|
490
771
|
constructor(message, details) {
|
|
@@ -502,8 +783,7 @@ const DEFAULT_COLORS = {
|
|
|
502
783
|
text: "#1a1a1a"
|
|
503
784
|
};
|
|
504
785
|
const FIT_VIEW_PADDING = .15;
|
|
505
|
-
|
|
506
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/layout/elk.ts
|
|
786
|
+
// src/components/ResourceView/Viewers/GraphFlow/layout/elk.ts
|
|
507
787
|
const EDGE_ROUTINGS = /* @__PURE__ */ new Set([
|
|
508
788
|
"SPLINES",
|
|
509
789
|
"ORTHOGONAL",
|
|
@@ -653,8 +933,7 @@ var ElkEngine = class ElkEngine {
|
|
|
653
933
|
return routes;
|
|
654
934
|
}
|
|
655
935
|
};
|
|
656
|
-
|
|
657
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/layout/graphviz.ts
|
|
936
|
+
// src/components/ResourceView/Viewers/GraphFlow/layout/graphviz.ts
|
|
658
937
|
const GRAPHVIZ_DPI = 72;
|
|
659
938
|
const parsePoint = (value) => {
|
|
660
939
|
if (!value) return null;
|
|
@@ -822,8 +1101,7 @@ var GraphvizEngine = class GraphvizEngine {
|
|
|
822
1101
|
return Object.entries(ranks).map(([rank, ids]) => `{ rank=${["source", "sink"].includes(rank) ? rank : "same"}; ${(ids ?? []).join(" ")} }`);
|
|
823
1102
|
}
|
|
824
1103
|
};
|
|
825
|
-
|
|
826
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/layout/engine.ts
|
|
1104
|
+
// src/components/ResourceView/Viewers/GraphFlow/layout/engine.ts
|
|
827
1105
|
const selectEngine = (visualization) => {
|
|
828
1106
|
const config = visualization.layout_config;
|
|
829
1107
|
switch (config?.type) {
|
|
@@ -839,8 +1117,7 @@ const selectEngine = (visualization) => {
|
|
|
839
1117
|
default: throw new GraphError("Invalid graph layout config.", ["Expected an ELK or Graphviz layout config from the backend."]);
|
|
840
1118
|
}
|
|
841
1119
|
};
|
|
842
|
-
|
|
843
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/model/buildModel.ts
|
|
1120
|
+
// src/components/ResourceView/Viewers/GraphFlow/model/buildModel.ts
|
|
844
1121
|
const requireNodeId = (node, index) => {
|
|
845
1122
|
if (!node.id) throw new GraphError("Invalid graph node.", [`Node at index ${index} is missing an id.`]);
|
|
846
1123
|
return {
|
|
@@ -859,8 +1136,7 @@ const buildGraphModel = (visualization) => ({
|
|
|
859
1136
|
nodes: (visualization.nodes ?? []).map(requireNodeId),
|
|
860
1137
|
edges: (visualization.edges ?? []).map(requireEdgeId)
|
|
861
1138
|
});
|
|
862
|
-
|
|
863
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/routeToPath.ts
|
|
1139
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/routeToPath.ts
|
|
864
1140
|
const buildPolylinePath = (points) => {
|
|
865
1141
|
if (points.length < 2) return null;
|
|
866
1142
|
return points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x},${point.y}`).join(" ");
|
|
@@ -905,8 +1181,7 @@ const straightEdgeLayout = (source, target) => ({
|
|
|
905
1181
|
endPoint: target,
|
|
906
1182
|
labelPosition: midpoint(source, target)
|
|
907
1183
|
});
|
|
908
|
-
|
|
909
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/project.ts
|
|
1184
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/project.ts
|
|
910
1185
|
const projectToRenderModel = (model, layout) => {
|
|
911
1186
|
const centers = nodeCenters(model.nodes, layout.positions);
|
|
912
1187
|
return {
|
|
@@ -966,8 +1241,7 @@ const routeToEdgeLayout = (route) => {
|
|
|
966
1241
|
labelPosition: route.labelPosition
|
|
967
1242
|
};
|
|
968
1243
|
};
|
|
969
|
-
|
|
970
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/hooks/useGraphLayout.ts
|
|
1244
|
+
// src/components/ResourceView/Viewers/GraphFlow/hooks/useGraphLayout.ts
|
|
971
1245
|
const prepare = (visualization) => {
|
|
972
1246
|
try {
|
|
973
1247
|
return {
|
|
@@ -1029,8 +1303,7 @@ const useGraphLayout = (visualization) => {
|
|
|
1029
1303
|
onNodesChange
|
|
1030
1304
|
};
|
|
1031
1305
|
};
|
|
1032
|
-
|
|
1033
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/edgeArrows.tsx
|
|
1306
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/edgeArrows.tsx
|
|
1034
1307
|
const colorToId = (color) => color.replace(/[^a-zA-Z0-9]/g, "");
|
|
1035
1308
|
const ArrowMarker = ({ id, type, color, isStart }) => {
|
|
1036
1309
|
if (!type) return null;
|
|
@@ -1197,8 +1470,7 @@ const getMarkerIds = ({ color, startArrow, endArrow }) => {
|
|
|
1197
1470
|
endMarkerId: endArrow ? `arrow-end-${endArrow}-${colorKey}` : null
|
|
1198
1471
|
};
|
|
1199
1472
|
};
|
|
1200
|
-
|
|
1201
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/annotation.tsx
|
|
1473
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/annotation.tsx
|
|
1202
1474
|
const AnnotationBadge = () => /* @__PURE__ */ jsx(Badge, {
|
|
1203
1475
|
size: "xs",
|
|
1204
1476
|
variant: "filled",
|
|
@@ -1258,8 +1530,7 @@ const NodeAnnotation = ({ annotation }) => {
|
|
|
1258
1530
|
})]
|
|
1259
1531
|
});
|
|
1260
1532
|
};
|
|
1261
|
-
|
|
1262
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/edgeLabels.tsx
|
|
1533
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/edgeLabels.tsx
|
|
1263
1534
|
const EdgeLabel = ({ color, label, hasAnnotation, x, y }) => /* @__PURE__ */ jsx(EdgeLabelRenderer, { children: /* @__PURE__ */ jsxs("div", {
|
|
1264
1535
|
style: {
|
|
1265
1536
|
position: "absolute",
|
|
@@ -1313,8 +1584,7 @@ const endLabelPositions = (start, end) => {
|
|
|
1313
1584
|
}
|
|
1314
1585
|
};
|
|
1315
1586
|
};
|
|
1316
|
-
|
|
1317
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/Edge.tsx
|
|
1587
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/Edge.tsx
|
|
1318
1588
|
const centerOf = ({ sourceX, sourceY, targetX, targetY }) => ({
|
|
1319
1589
|
x: (sourceX + targetX) / 2,
|
|
1320
1590
|
y: (sourceY + targetY) / 2
|
|
@@ -1428,8 +1698,7 @@ const GraphFlowEdge = memo((props) => {
|
|
|
1428
1698
|
] });
|
|
1429
1699
|
});
|
|
1430
1700
|
GraphFlowEdge.displayName = "GraphFlowEdge";
|
|
1431
|
-
|
|
1432
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/nodeShapes.tsx
|
|
1701
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/nodeShapes.tsx
|
|
1433
1702
|
const InnerSymbol = ({ symbol, color }) => {
|
|
1434
1703
|
if (symbol === "triangle") return /* @__PURE__ */ jsx("div", { style: {
|
|
1435
1704
|
width: 0,
|
|
@@ -1559,8 +1828,7 @@ const BoxShape = ({ shape, color, borderColor, doubleBorder, innerSymbol, width,
|
|
|
1559
1828
|
} })]
|
|
1560
1829
|
});
|
|
1561
1830
|
};
|
|
1562
|
-
|
|
1563
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/render/Node.tsx
|
|
1831
|
+
// src/components/ResourceView/Viewers/GraphFlow/render/Node.tsx
|
|
1564
1832
|
const HIDDEN_HANDLE_STYLE = {
|
|
1565
1833
|
opacity: 0,
|
|
1566
1834
|
pointerEvents: "none"
|
|
@@ -1656,8 +1924,7 @@ const GraphFlowNode = memo(({ data }) => {
|
|
|
1656
1924
|
});
|
|
1657
1925
|
});
|
|
1658
1926
|
GraphFlowNode.displayName = "GraphFlowNode";
|
|
1659
|
-
|
|
1660
|
-
//#region src/components/ResourceView/Viewers/GraphFlow/index.tsx
|
|
1927
|
+
// src/components/ResourceView/Viewers/GraphFlow/index.tsx
|
|
1661
1928
|
const nodeTypes = { node: GraphFlowNode };
|
|
1662
1929
|
const edgeTypes = { graphflow: GraphFlowEdge };
|
|
1663
1930
|
const GraphFlowCanvas = ({ visualization, isPreview, menuItems }) => {
|
|
@@ -1735,8 +2002,7 @@ const GraphFlowCanvas = ({ visualization, isPreview, menuItems }) => {
|
|
|
1735
2002
|
})] });
|
|
1736
2003
|
};
|
|
1737
2004
|
const GraphFlow = (props) => /* @__PURE__ */ jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsx(GraphFlowCanvas, { ...props }) });
|
|
1738
|
-
|
|
1739
|
-
//#region src/components/ResourceView/Viewers/Graph.tsx
|
|
2005
|
+
// src/components/ResourceView/Viewers/Graph.tsx
|
|
1740
2006
|
const GraphViewer = (props) => /* @__PURE__ */ jsx(Box, {
|
|
1741
2007
|
h: "100%",
|
|
1742
2008
|
w: "100%",
|
|
@@ -1744,8 +2010,7 @@ const GraphViewer = (props) => /* @__PURE__ */ jsx(Box, {
|
|
|
1744
2010
|
style: { overflow: "hidden" },
|
|
1745
2011
|
children: /* @__PURE__ */ jsx(GraphFlow, { ...props })
|
|
1746
2012
|
});
|
|
1747
|
-
|
|
1748
|
-
//#region src/components/ResourceView/Viewers/PetriNet.tsx
|
|
2013
|
+
// src/components/ResourceView/Viewers/PetriNet.tsx
|
|
1749
2014
|
const R4pmOcpn = r4pmViewer((c) => c.ObjectCentricPetriNetViewer);
|
|
1750
2015
|
const R4pmPetriNet = r4pmViewer((c) => c.PetriNetViewer);
|
|
1751
2016
|
const toR4pmPetriNet = (visualization) => ({
|
|
@@ -1759,22 +2024,24 @@ const toR4pmPetriNet = (visualization) => ({
|
|
|
1759
2024
|
final_marking: visualization.final_marking ?? null
|
|
1760
2025
|
});
|
|
1761
2026
|
const OCPetriNetViewer = ({ visualization }) => {
|
|
1762
|
-
const ocpnData = useMemo(() => {
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
}
|
|
1777
|
-
|
|
2027
|
+
const ocpnData = useMemo(() => ({
|
|
2028
|
+
places: (visualization.places ?? []).map(({ id, object_type }) => ({
|
|
2029
|
+
name: id,
|
|
2030
|
+
object_type
|
|
2031
|
+
})),
|
|
2032
|
+
transitions: (visualization.transitions ?? []).map(({ id, label }) => ({
|
|
2033
|
+
name: id,
|
|
2034
|
+
label
|
|
2035
|
+
})),
|
|
2036
|
+
arcs: visualization.arcs.map(({ source, target, weight, variable }) => ({
|
|
2037
|
+
source,
|
|
2038
|
+
target,
|
|
2039
|
+
type: variable ? "variable" : "normal",
|
|
2040
|
+
...weight != null && { weight }
|
|
2041
|
+
})),
|
|
2042
|
+
initial_marking: visualization.initial_marking ?? {},
|
|
2043
|
+
final_marking: visualization.final_marking ?? {}
|
|
2044
|
+
}), [visualization]);
|
|
1778
2045
|
return /* @__PURE__ */ jsx(R4pmViewerContainer, {
|
|
1779
2046
|
filename: "oc-petri-net",
|
|
1780
2047
|
children: /* @__PURE__ */ jsx(R4pmOcpn, { data: ocpnData })
|
|
@@ -1787,8 +2054,7 @@ const PetriNetViewer = ({ visualization }) => {
|
|
|
1787
2054
|
children: /* @__PURE__ */ jsx(R4pmPetriNet, { data: petriNet })
|
|
1788
2055
|
});
|
|
1789
2056
|
};
|
|
1790
|
-
|
|
1791
|
-
//#region src/components/ResourceView/Viewers/Plotly.tsx
|
|
2057
|
+
// src/components/ResourceView/Viewers/Plotly.tsx
|
|
1792
2058
|
const Plot = dynamic(() => import("react-plotly.js"), { ssr: false });
|
|
1793
2059
|
const PlotlyViewer = ({ visualization, isPreview }) => {
|
|
1794
2060
|
const { data, layout } = visualization.data;
|
|
@@ -1813,68 +2079,7 @@ const PlotlyViewer = ({ visualization, isPreview }) => {
|
|
|
1813
2079
|
}
|
|
1814
2080
|
});
|
|
1815
2081
|
};
|
|
1816
|
-
|
|
1817
|
-
//#region src/util/dayjs.ts
|
|
1818
|
-
dayjs.extend(isBetween);
|
|
1819
|
-
var dayjs_default = dayjs;
|
|
1820
|
-
const formatDateTime = (dateString) => dayjs(dateString).format("YYYY-MM-DD HH:mm");
|
|
1821
|
-
const formatDate = (dateString) => dayjs(dateString).format("YYYY-MM-DD");
|
|
1822
|
-
//#endregion
|
|
1823
|
-
//#region src/components/ResourceView/Viewers/Table.tsx
|
|
1824
|
-
const formatCell = (value, dataType) => {
|
|
1825
|
-
if (value == null) return "";
|
|
1826
|
-
switch (dataType) {
|
|
1827
|
-
case "boolean": return value ? "✅" : "❌";
|
|
1828
|
-
case "date": return dayjs_default(value).isValid() ? formatDate(value) : String(value);
|
|
1829
|
-
case "datetime": return dayjs_default(value).isValid() ? formatDateTime(value) : String(value);
|
|
1830
|
-
default: return String(value);
|
|
1831
|
-
}
|
|
1832
|
-
};
|
|
1833
|
-
const PAGE_SIZES = [
|
|
1834
|
-
20,
|
|
1835
|
-
50,
|
|
1836
|
-
75
|
|
1837
|
-
];
|
|
1838
|
-
const TableView = ({ visualization }) => {
|
|
1839
|
-
const [pageSize, setPageSize] = useState(PAGE_SIZES[0]);
|
|
1840
|
-
const columns = useMemo(() => visualization.columns.map((col) => ({
|
|
1841
|
-
accessor: col.id,
|
|
1842
|
-
title: col.label ?? col.id,
|
|
1843
|
-
render: (record) => formatCell(record[col.id], col.data_type)
|
|
1844
|
-
})), [visualization.columns]);
|
|
1845
|
-
const [page, setPage] = useState(1);
|
|
1846
|
-
const [records, setRecords] = useState(visualization.rows.slice(0, pageSize));
|
|
1847
|
-
useEffect(() => {
|
|
1848
|
-
const from = (page - 1) * pageSize;
|
|
1849
|
-
const to = from + pageSize;
|
|
1850
|
-
setRecords(visualization.rows.slice(from, to));
|
|
1851
|
-
}, [
|
|
1852
|
-
page,
|
|
1853
|
-
pageSize,
|
|
1854
|
-
visualization
|
|
1855
|
-
]);
|
|
1856
|
-
const paginationProps = visualization.rows.length > PAGE_SIZES[0] ? {
|
|
1857
|
-
totalRecords: visualization.rows.length,
|
|
1858
|
-
recordsPerPage: pageSize,
|
|
1859
|
-
onPageChange: (p) => setPage(p),
|
|
1860
|
-
page,
|
|
1861
|
-
recordsPerPageOptions: [...PAGE_SIZES],
|
|
1862
|
-
onRecordsPerPageChange: (newPageSize) => {
|
|
1863
|
-
setPageSize(newPageSize);
|
|
1864
|
-
setPage(1);
|
|
1865
|
-
}
|
|
1866
|
-
} : {};
|
|
1867
|
-
return /* @__PURE__ */ jsx(DataTable, {
|
|
1868
|
-
records,
|
|
1869
|
-
columns,
|
|
1870
|
-
withTableBorder: true,
|
|
1871
|
-
withColumnBorders: true,
|
|
1872
|
-
withRowBorders: true,
|
|
1873
|
-
...paginationProps
|
|
1874
|
-
});
|
|
1875
|
-
};
|
|
1876
|
-
//#endregion
|
|
1877
|
-
//#region src/components/ResourceView/index.tsx
|
|
2082
|
+
// src/components/ResourceView/index.tsx
|
|
1878
2083
|
const visualizationMap = {
|
|
1879
2084
|
table: TableView,
|
|
1880
2085
|
svg: SvgViewer,
|
|
@@ -1894,34 +2099,72 @@ const Visualization = ({ visualization, isPreview = false, menuItems }) => {
|
|
|
1894
2099
|
...menuItems && { menuItems }
|
|
1895
2100
|
});
|
|
1896
2101
|
};
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
2102
|
+
const LoadError = () => /* @__PURE__ */ jsx(Center, {
|
|
2103
|
+
h: "100%",
|
|
2104
|
+
p: "md",
|
|
2105
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2106
|
+
c: "dimmed",
|
|
2107
|
+
children: "Could not load the visualization"
|
|
2108
|
+
})
|
|
2109
|
+
});
|
|
2110
|
+
const NoVisualization = ({ resourceTypeLabel }) => /* @__PURE__ */ jsx(Center, {
|
|
2111
|
+
h: "100%",
|
|
2112
|
+
p: "md",
|
|
2113
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1902
2114
|
align: "center",
|
|
1903
|
-
|
|
1904
|
-
children:
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
2115
|
+
gap: "xs",
|
|
2116
|
+
children: [
|
|
2117
|
+
/* @__PURE__ */ jsx(EyeOffIcon, {
|
|
2118
|
+
size: 64,
|
|
2119
|
+
strokeWidth: 1.5,
|
|
2120
|
+
color: "var(--mantine-color-dimmed)"
|
|
2121
|
+
}),
|
|
2122
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2123
|
+
fw: 500,
|
|
2124
|
+
children: "No visualization available"
|
|
2125
|
+
}),
|
|
2126
|
+
/* @__PURE__ */ jsxs(Group, {
|
|
2127
|
+
gap: 6,
|
|
2128
|
+
justify: "center",
|
|
2129
|
+
children: [/* @__PURE__ */ jsx(Badge, {
|
|
2130
|
+
size: "sm",
|
|
2131
|
+
color: "gray",
|
|
2132
|
+
children: resourceTypeLabel
|
|
2133
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2134
|
+
size: "sm",
|
|
2135
|
+
c: "dimmed",
|
|
2136
|
+
children: "does not provide a visualization"
|
|
2137
|
+
})]
|
|
1912
2138
|
})
|
|
1913
|
-
|
|
1914
|
-
})
|
|
2139
|
+
]
|
|
2140
|
+
})
|
|
2141
|
+
});
|
|
2142
|
+
const ResourceVisualization = ({ id, resourceTypeLabel, isPreview }) => {
|
|
2143
|
+
const { data, isError, isPending } = useResourceVisualization(id);
|
|
2144
|
+
if (isError) return /* @__PURE__ */ jsx(LoadError, {});
|
|
2145
|
+
if (isPending) return /* @__PURE__ */ jsx(LoadingOverlay, { visible: true });
|
|
2146
|
+
if (!data) return /* @__PURE__ */ jsx(NoVisualization, { resourceTypeLabel });
|
|
1915
2147
|
return /* @__PURE__ */ jsx(Visualization, {
|
|
1916
|
-
visualization: data
|
|
2148
|
+
visualization: data,
|
|
1917
2149
|
isPreview
|
|
1918
2150
|
});
|
|
1919
2151
|
};
|
|
1920
|
-
|
|
1921
|
-
|
|
2152
|
+
const ResourceViewer = ({ id, isPreview = false }) => {
|
|
2153
|
+
const { data: resource, isError, isPending } = useResource(id);
|
|
2154
|
+
if (isError) return /* @__PURE__ */ jsx(LoadError, {});
|
|
2155
|
+
if (isPending) return /* @__PURE__ */ jsx(LoadingOverlay, { visible: true });
|
|
2156
|
+
if (resource.has_visualization) return /* @__PURE__ */ jsx(ResourceVisualization, {
|
|
2157
|
+
id,
|
|
2158
|
+
resourceTypeLabel: resource.resource_type_label,
|
|
2159
|
+
isPreview
|
|
2160
|
+
});
|
|
2161
|
+
if (resource.kind === "tabular") return /* @__PURE__ */ jsx(TabularView, { resourceId: id });
|
|
2162
|
+
return /* @__PURE__ */ jsx(NoVisualization, { resourceTypeLabel: resource.resource_type_label });
|
|
2163
|
+
};
|
|
2164
|
+
// src/components/ResourceModal/ResourceModal.tsx
|
|
1922
2165
|
const ResourceModal = ({ id, onClose }) => {
|
|
1923
2166
|
const { data } = useResource(id, { query: { enabled: !!id } });
|
|
1924
|
-
return data?.
|
|
2167
|
+
return data?.id && /* @__PURE__ */ jsx(Modal, {
|
|
1925
2168
|
opened: !!data,
|
|
1926
2169
|
onClose,
|
|
1927
2170
|
fullScreen: true,
|
|
@@ -1942,7 +2185,7 @@ const ResourceModal = ({ id, onClose }) => {
|
|
|
1942
2185
|
justify: "space-between",
|
|
1943
2186
|
children: [/* @__PURE__ */ jsx(Title, {
|
|
1944
2187
|
size: "h5",
|
|
1945
|
-
children: data.
|
|
2188
|
+
children: data.name
|
|
1946
2189
|
}), /* @__PURE__ */ jsx(ActionIcon, {
|
|
1947
2190
|
variant: "subtle",
|
|
1948
2191
|
color: "red",
|
|
@@ -1954,22 +2197,92 @@ const ResourceModal = ({ id, onClose }) => {
|
|
|
1954
2197
|
h: "100%",
|
|
1955
2198
|
flex: 1,
|
|
1956
2199
|
mih: 0,
|
|
1957
|
-
children: /* @__PURE__ */ jsx(ResourceViewer, { id: data.
|
|
2200
|
+
children: /* @__PURE__ */ jsx(ResourceViewer, { id: data.id })
|
|
1958
2201
|
})]
|
|
1959
2202
|
})
|
|
1960
2203
|
});
|
|
1961
2204
|
};
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
const ResourceSelect = ({ type, value, ...props }) => {
|
|
2205
|
+
// src/components/ResourceSelect/ResourceSelect.tsx
|
|
2206
|
+
const useResourceOptions = (type, onUploaded) => {
|
|
1965
2207
|
const { data: resources = [] } = useResources({ schema_hash: type ?? null });
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
2208
|
+
const [pendingTaskIds, setPendingTaskIds] = useState([]);
|
|
2209
|
+
const options = useMemo(() => resources.map(({ name, id }) => ({
|
|
2210
|
+
value: id,
|
|
2211
|
+
label: name
|
|
2212
|
+
})), [resources]);
|
|
2213
|
+
const handleUploaded = useEffectEvent(onUploaded);
|
|
2214
|
+
useEffect(() => {
|
|
2215
|
+
const uploaded = resources.filter(({ source_id }) => source_id && pendingTaskIds.includes(source_id));
|
|
2216
|
+
if (!uploaded.length) return;
|
|
2217
|
+
setPendingTaskIds((taskIds) => taskIds.filter((taskId) => !uploaded.some(({ source_id }) => source_id === taskId)));
|
|
2218
|
+
handleUploaded(uploaded.map(({ id, name }) => ({
|
|
1969
2219
|
value: id,
|
|
1970
2220
|
label: name
|
|
1971
|
-
}))
|
|
2221
|
+
})));
|
|
2222
|
+
}, [resources, pendingTaskIds]);
|
|
2223
|
+
const trackUpload = (taskIds) => setPendingTaskIds((pending) => [...pending, ...taskIds]);
|
|
2224
|
+
return {
|
|
2225
|
+
options,
|
|
2226
|
+
trackUpload
|
|
2227
|
+
};
|
|
2228
|
+
};
|
|
2229
|
+
const UploadButton = ({ disabled, onUpload }) => {
|
|
2230
|
+
const { mutate: upload, isPending } = useUpload({ mutation: { onSuccess: onUpload } });
|
|
2231
|
+
return /* @__PURE__ */ jsx(FileButton, {
|
|
2232
|
+
accept: ".ocelescope",
|
|
2233
|
+
multiple: true,
|
|
2234
|
+
onChange: (files) => {
|
|
2235
|
+
if (files.length) upload({ data: { files } });
|
|
2236
|
+
},
|
|
2237
|
+
children: (props) => /* @__PURE__ */ jsx(Tooltip, {
|
|
2238
|
+
label: "Upload resource",
|
|
2239
|
+
withArrow: true,
|
|
2240
|
+
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
2241
|
+
...props,
|
|
2242
|
+
size: "input-sm",
|
|
2243
|
+
loading: isPending,
|
|
2244
|
+
disabled,
|
|
2245
|
+
"aria-label": "Upload resource",
|
|
2246
|
+
children: /* @__PURE__ */ jsx(UploadIcon, { size: 16 })
|
|
2247
|
+
})
|
|
2248
|
+
})
|
|
2249
|
+
});
|
|
2250
|
+
};
|
|
2251
|
+
const ViewButton = ({ resourceId, onView }) => /* @__PURE__ */ jsx(Tooltip, {
|
|
2252
|
+
label: resourceId ? "View resource" : "Select a resource to view it",
|
|
2253
|
+
withArrow: true,
|
|
2254
|
+
children: /* @__PURE__ */ jsx(ActionIcon, {
|
|
2255
|
+
variant: "default",
|
|
2256
|
+
size: "input-sm",
|
|
2257
|
+
"data-disabled": !resourceId || void 0,
|
|
2258
|
+
"aria-disabled": !resourceId,
|
|
2259
|
+
onClick: resourceId ? () => onView(resourceId) : void 0,
|
|
2260
|
+
"aria-label": "View resource",
|
|
2261
|
+
children: /* @__PURE__ */ jsx(EyeIcon, { size: 16 })
|
|
2262
|
+
})
|
|
2263
|
+
});
|
|
2264
|
+
const ResourceSelect = (props) => {
|
|
2265
|
+
const { type, withUpload = true, withView = true, ...selectProps } = props;
|
|
2266
|
+
const [viewedId, setViewedId] = useState();
|
|
2267
|
+
const { options, trackUpload } = useResourceOptions(type, (uploaded) => {
|
|
2268
|
+
if (props.multiple) props.onChange([.../* @__PURE__ */ new Set([...props.value, ...uploaded.map(({ value }) => value)])]);
|
|
2269
|
+
else if (uploaded[0]) props.onChange(uploaded[0].value, uploaded[0]);
|
|
1972
2270
|
});
|
|
2271
|
+
const viewId = props.multiple ? null : props.value;
|
|
2272
|
+
const showView = withView && !props.multiple;
|
|
2273
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ActionSelect, {
|
|
2274
|
+
...selectProps,
|
|
2275
|
+
data: options,
|
|
2276
|
+
actions: (showView || withUpload) && /* @__PURE__ */ jsxs(Fragment, { children: [showView && /* @__PURE__ */ jsx(ViewButton, {
|
|
2277
|
+
resourceId: viewId,
|
|
2278
|
+
onView: setViewedId
|
|
2279
|
+
}), withUpload && /* @__PURE__ */ jsx(UploadButton, {
|
|
2280
|
+
disabled: props.disabled,
|
|
2281
|
+
onUpload: trackUpload
|
|
2282
|
+
})] })
|
|
2283
|
+
}), viewedId && /* @__PURE__ */ jsx(ResourceModal, {
|
|
2284
|
+
id: viewedId,
|
|
2285
|
+
onClose: () => setViewedId(void 0)
|
|
2286
|
+
})] });
|
|
1973
2287
|
};
|
|
1974
|
-
|
|
1975
|
-
export { ResourceModal, ResourceSelect, ResourceViewer, Visualization };
|
|
2288
|
+
export { ActionSelect, ResourceModal, ResourceSelect, ResourceViewer, Visualization };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ocelescope/resources",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"description": "Ocelescope resources: helpers and UI components for rendering and exporting data.",
|
|
6
6
|
"author": "Görkem-Emre Öztürk <goerkem.oeztuerk@rwth-aachen.de>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -28,29 +28,30 @@
|
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@floating-ui/react": "^0.27.18",
|
|
31
|
-
"@hpcc-js/wasm-graphviz": "^1.
|
|
32
|
-
"@mantine/core": "^9.7.
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
31
|
+
"@hpcc-js/wasm-graphviz": "^1.30.0",
|
|
32
|
+
"@mantine/core": "^9.7.4",
|
|
33
|
+
"@mantine/hooks": "^9.7.4",
|
|
34
|
+
"@ocelescope/api-base": "^0.12.0",
|
|
35
|
+
"@r4pm/components": "^0.4.0",
|
|
36
|
+
"@xyflow/react": "^12.12.0",
|
|
36
37
|
"dayjs": "^1.11.23",
|
|
37
38
|
"elkjs": "^0.12.0",
|
|
38
39
|
"file-saver": "^2.0.5",
|
|
39
|
-
"lucide-react": "^1.
|
|
40
|
+
"lucide-react": "^1.63.0",
|
|
40
41
|
"mantine-datatable": "^9.4.0",
|
|
41
|
-
"next": "16.4.
|
|
42
|
-
"react": "^19.3.
|
|
43
|
-
"react-dom": "^19.3.
|
|
42
|
+
"next": "16.4.3",
|
|
43
|
+
"react": "^19.3.1",
|
|
44
|
+
"react-dom": "^19.3.1",
|
|
44
45
|
"react-plotly.js": "^4.0.0",
|
|
45
46
|
"react-zoom-pan-pinch": "^4.0.0"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
|
-
"@tsdown/css": "^0.23.
|
|
49
|
+
"@tsdown/css": "^0.23.3",
|
|
49
50
|
"@types/file-saver": "^2.0.7",
|
|
50
|
-
"@types/node": "^26.6.
|
|
51
|
+
"@types/node": "^26.6.5",
|
|
51
52
|
"@types/react": "^19.3.0",
|
|
52
53
|
"@types/react-dom": "^19.3.0",
|
|
53
|
-
"tsdown": "^0.23.
|
|
54
|
+
"tsdown": "^0.23.3",
|
|
54
55
|
"typescript": "^6.0.3"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|