@ocelescope/core 0.7.0 → 0.8.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 +61 -14
- package/dist/ActivityPicker-DbrGeiC_.js +18 -0
- package/dist/AreaChart-DoD3LM50.js +36 -0
- package/dist/BarChart-Cixo5Okd.js +69 -0
- package/dist/EventAttributePicker-Cf2vVKv5.js +27 -0
- package/dist/LineChart-CVz-dkJI.js +64 -0
- package/dist/NamePicker-Be-qmr1x.js +43 -0
- package/dist/ObjectAttributePicker-CL43EsYt.js +27 -0
- package/dist/ObjectTypePicker-CKmszS0y.js +18 -0
- package/dist/OcelChart-DvGTjJXo.js +41 -0
- package/dist/PieChart-DBiASngC.js +62 -0
- package/dist/Plot-CxHHyXot.js +75 -0
- package/dist/ScatterChart-jIQOjQes.js +36 -0
- package/dist/SqlChart-bwDArK2l.js +101 -0
- package/dist/SunburstChart-D6FhYBHI.js +73 -0
- package/dist/cartesian-5TPNWjl_.js +41 -0
- package/dist/index.d.ts +478 -39
- package/dist/index.js +182 -85
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/series-BBXVjHHY.js +32 -0
- package/dist/style.css +46576 -1
- package/dist/useCurrentOCEL-BeBa7K0g.js +34 -0
- package/dist/useOcelId-BSU0-Srk.js +6 -0
- package/dist/useOcelQuery-D1QM-Zgv.js +34 -0
- package/package.json +21 -17
package/README.md
CHANGED
|
@@ -27,14 +27,6 @@ for a complete setup.
|
|
|
27
27
|
```tsx
|
|
28
28
|
// pages/_app.tsx
|
|
29
29
|
|
|
30
|
-
// 1. Third-party global styles core relies on. Import these FIRST, in this
|
|
31
|
-
// order (@mantine/core must come before its extensions).
|
|
32
|
-
import "@mantine/core/styles.css";
|
|
33
|
-
import "@mantine/dropzone/styles.css";
|
|
34
|
-
import "@mantine/notifications/styles.css";
|
|
35
|
-
import "mantine-datatable/styles.css";
|
|
36
|
-
|
|
37
|
-
// 2. core's own (scoped) styles — must come AFTER the above so its overrides win.
|
|
38
30
|
import "@ocelescope/core/styles.css";
|
|
39
31
|
|
|
40
32
|
import { OcelescopeApp } from "@ocelescope/core";
|
|
@@ -43,12 +35,13 @@ import config from "../ocelescope.config";
|
|
|
43
35
|
export default OcelescopeApp(config);
|
|
44
36
|
```
|
|
45
37
|
|
|
46
|
-
> **Styles.** `@ocelescope/core`
|
|
47
|
-
>
|
|
48
|
-
>
|
|
49
|
-
>
|
|
50
|
-
> core's
|
|
51
|
-
> stylesheets it
|
|
38
|
+
> **Styles.** `@ocelescope/core/styles.css` is the only stylesheet an app needs.
|
|
39
|
+
> It inlines the third-party global CSS core and the Ocelescope modules rely on
|
|
40
|
+
> (`@mantine/core` and its `dates` / `charts` / `dropzone` / `notifications`
|
|
41
|
+
> extensions, `mantine-datatable`, `@xyflow/react`, `@r4pm/components`) ahead of
|
|
42
|
+
> core's own component styles, in cascade order — so do not import those
|
|
43
|
+
> stylesheets again. A module that ships its own stylesheet documents it; import
|
|
44
|
+
> those after core's.
|
|
52
45
|
|
|
53
46
|
```tsx
|
|
54
47
|
// pages/_document.tsx
|
|
@@ -105,6 +98,60 @@ export default defineModule({
|
|
|
105
98
|
});
|
|
106
99
|
```
|
|
107
100
|
|
|
101
|
+
### Charts
|
|
102
|
+
|
|
103
|
+
`OcelChart` runs DuckDB SQL against the selected OCEL and draws the result. The
|
|
104
|
+
query is usually the whole configuration: the first non-numeric column goes
|
|
105
|
+
across, the numeric ones go up.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import { OcelChart } from "@ocelescope/core";
|
|
109
|
+
|
|
110
|
+
export const EventsByActivity = () => (
|
|
111
|
+
<OcelChart
|
|
112
|
+
title="Events per activity"
|
|
113
|
+
colorScope="activity"
|
|
114
|
+
sql={`SELECT "ocel:activity" AS activity, count(*) AS events
|
|
115
|
+
FROM events GROUP BY 1 ORDER BY 2 DESC`}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Bar, line, area, scatter and pie; `series` unfolds a column into one series per
|
|
121
|
+
value, `stacked` and `horizontal` change the shape, `types` offers the reader a
|
|
122
|
+
switch between chart types, and `x`/`y` override the columns when the defaults
|
|
123
|
+
guess wrong. Only a single `SELECT` runs, over the OCEL's stored tables
|
|
124
|
+
(`events`, `objects`, `e2o`, `o2o`, `object_changes`); bind values through
|
|
125
|
+
`parameters` rather than building SQL by hand. `SqlChart` is the same chart for
|
|
126
|
+
a result you already have.
|
|
127
|
+
|
|
128
|
+
Line charts can share a scale or overlay an independent scale per line. The
|
|
129
|
+
second form is useful for measures with different units or very different
|
|
130
|
+
ranges:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<OcelChart
|
|
134
|
+
type="line"
|
|
135
|
+
x="time"
|
|
136
|
+
y="value"
|
|
137
|
+
series="measure"
|
|
138
|
+
yAxes="independent"
|
|
139
|
+
sql={measurements}
|
|
140
|
+
/>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Charts are r4pm viewers. Colours come from the ambient `ViewerConfig` when a
|
|
144
|
+
`colorScope` is given, so a category keeps the colour the graph viewers give it;
|
|
145
|
+
clicks report a `ViewerTarget` to `onSelect`; and a chart inside a
|
|
146
|
+
`<ViewerExportFrame>` exports with everything else in the frame:
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<ViewerExportFrame filename="activities">
|
|
150
|
+
<OcelChart title="Events per activity" sql={events} />
|
|
151
|
+
<OcelChart title="Objects per type" sql={objects} series="type" stacked />
|
|
152
|
+
</ViewerExportFrame>
|
|
153
|
+
```
|
|
154
|
+
|
|
108
155
|
## About
|
|
109
156
|
|
|
110
157
|
Part of [Ocelescope](https://github.com/promi4s/ocelescope), a framework for
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { t as useOcelId } from "./useOcelId-BSU0-Srk.js";
|
|
2
|
+
import { NamePicker } from "./NamePicker-Be-qmr1x.js";
|
|
3
|
+
import { useEventCounts } from "@ocelescope/api-base";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/pickers/ActivityPicker.tsx
|
|
6
|
+
/** The log's activities, by how often they occur. */
|
|
7
|
+
const ActivityPicker = ({ ocelId, ocelVersion = "filtered", ...picker }) => {
|
|
8
|
+
const id = useOcelId(ocelId);
|
|
9
|
+
const { data, isPending } = useEventCounts(id, { ocel_version: ocelVersion }, { query: { enabled: id != null } });
|
|
10
|
+
return /* @__PURE__ */ jsx(NamePicker, {
|
|
11
|
+
items: data ?? {},
|
|
12
|
+
scope: "activity",
|
|
13
|
+
loading: isPending,
|
|
14
|
+
...picker
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ActivityPicker };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { i as measureOf, n as axis, t as Plot } from "./Plot-CxHHyXot.js";
|
|
3
|
+
import { n as traceCount, t as cartesian } from "./cartesian-5TPNWjl_.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/charts/AreaChart.tsx
|
|
6
|
+
var AreaChart_exports = /* @__PURE__ */ __exportAll({ AreaChart: () => AreaChart });
|
|
7
|
+
/** A line with the ground beneath it filled in, for totals over time. */
|
|
8
|
+
const AreaChart = ({ xRange, ...props }) => {
|
|
9
|
+
const measure = measureOf(props.y);
|
|
10
|
+
return /* @__PURE__ */ jsx(Plot, {
|
|
11
|
+
traces: cartesian(props, {
|
|
12
|
+
type: "scatter",
|
|
13
|
+
mode: "lines",
|
|
14
|
+
fill: "tozeroy",
|
|
15
|
+
line: {
|
|
16
|
+
width: 2,
|
|
17
|
+
shape: "spline",
|
|
18
|
+
smoothing: .35
|
|
19
|
+
},
|
|
20
|
+
opacity: .72,
|
|
21
|
+
connectgaps: false,
|
|
22
|
+
hovertemplate: "<b>%{fullData.name}</b><br>%{x}<br>%{y}<extra></extra>"
|
|
23
|
+
}, false, false),
|
|
24
|
+
legend: traceCount(props) > 1,
|
|
25
|
+
onSelect: props.onSelect,
|
|
26
|
+
layout: {
|
|
27
|
+
hovermode: "x unified",
|
|
28
|
+
hoverdistance: 40,
|
|
29
|
+
spikedistance: -1,
|
|
30
|
+
xaxis: axis(props.x, xRange),
|
|
31
|
+
yaxis: axis(measure)
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { AreaChart_exports as n, AreaChart as t };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { i as measureOf, n as axis, t as Plot } from "./Plot-CxHHyXot.js";
|
|
3
|
+
import { n as traceCount, t as cartesian } from "./cartesian-5TPNWjl_.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/charts/BarChart.tsx
|
|
6
|
+
var BarChart_exports = /* @__PURE__ */ __exportAll({ BarChart: () => BarChart });
|
|
7
|
+
/** Counts as bars, one per category. */
|
|
8
|
+
const BarChart = ({ stacked = false, horizontal = false, xRange, ...props }) => {
|
|
9
|
+
const measure = measureOf(props.y);
|
|
10
|
+
const count = props.rows.length;
|
|
11
|
+
const traces = cartesian(props, {
|
|
12
|
+
type: "bar",
|
|
13
|
+
marker: {
|
|
14
|
+
line: { width: 0 },
|
|
15
|
+
cornerradius: 4
|
|
16
|
+
},
|
|
17
|
+
...count <= 24 && {
|
|
18
|
+
texttemplate: horizontal ? "%{x:~s}" : "%{y:~s}",
|
|
19
|
+
textposition: "auto"
|
|
20
|
+
},
|
|
21
|
+
hovertemplate: horizontal ? `<b>%{y}</b><br>${measure}: %{x}<extra>%{fullData.name}</extra>` : `<b>%{x}</b><br>${measure}: %{y}<extra>%{fullData.name}</extra>`,
|
|
22
|
+
cliponaxis: false
|
|
23
|
+
}, horizontal);
|
|
24
|
+
return /* @__PURE__ */ jsx(Plot, {
|
|
25
|
+
traces: stacked ? withStackShare(traces, horizontal, measure) : traces,
|
|
26
|
+
legend: traceCount(props) > 1,
|
|
27
|
+
readName: (point) => String((horizontal ? point.y : point.x) ?? point.label ?? ""),
|
|
28
|
+
onSelect: props.onSelect,
|
|
29
|
+
layout: {
|
|
30
|
+
barmode: stacked ? "stack" : "group",
|
|
31
|
+
bargap: .22,
|
|
32
|
+
bargroupgap: .08,
|
|
33
|
+
hovermode: "closest",
|
|
34
|
+
uniformtext: {
|
|
35
|
+
mode: "hide",
|
|
36
|
+
minsize: 10
|
|
37
|
+
},
|
|
38
|
+
xaxis: axis(horizontal ? measure : props.x, xRange),
|
|
39
|
+
yaxis: axis(horizontal ? props.x : measure)
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
/** Add each segment's share of its complete bar to stacked-bar hover. */
|
|
44
|
+
const withStackShare = (traces, horizontal, measure) => {
|
|
45
|
+
const totals = /* @__PURE__ */ new Map();
|
|
46
|
+
for (const trace of traces) {
|
|
47
|
+
const labels = horizontal ? trace.y : trace.x;
|
|
48
|
+
const values = horizontal ? trace.x : trace.y;
|
|
49
|
+
labels.forEach((label, index) => {
|
|
50
|
+
const key = String(label);
|
|
51
|
+
totals.set(key, (totals.get(key) ?? 0) + Number(values[index] ?? 0));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return traces.map((trace) => {
|
|
55
|
+
const labels = horizontal ? trace.y : trace.x;
|
|
56
|
+
const values = horizontal ? trace.x : trace.y;
|
|
57
|
+
const shares = labels.map((label, index) => {
|
|
58
|
+
const total = totals.get(String(label)) ?? 0;
|
|
59
|
+
return total > 0 ? Number(values[index] ?? 0) / total : 0;
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
...trace,
|
|
63
|
+
customdata: shares,
|
|
64
|
+
hovertemplate: horizontal ? `<b>%{y}</b><br>${measure}: %{x}<br>Share: %{customdata:.1%}<extra>%{fullData.name}</extra>` : `<b>%{x}</b><br>${measure}: %{y}<br>Share: %{customdata:.1%}<extra>%{fullData.name}</extra>`
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
69
|
+
export { BarChart_exports as n, BarChart as t };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { t as useOcelId } from "./useOcelId-BSU0-Srk.js";
|
|
2
|
+
import { NamePicker } from "./NamePicker-Be-qmr1x.js";
|
|
3
|
+
import { useEventAttributes } from "@ocelescope/api-base";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/pickers/EventAttributePicker.tsx
|
|
6
|
+
/** The attributes events carry, counted by how many values each takes. */
|
|
7
|
+
const EventAttributePicker = ({ activity, ocelId, ocelVersion = "filtered", ...picker }) => {
|
|
8
|
+
const id = useOcelId(ocelId);
|
|
9
|
+
const { data, isPending } = useEventAttributes(id, {
|
|
10
|
+
ocel_version: ocelVersion,
|
|
11
|
+
...activity ? { names: [activity] } : {}
|
|
12
|
+
}, { query: { enabled: id != null } });
|
|
13
|
+
return /* @__PURE__ */ jsx(NamePicker, {
|
|
14
|
+
items: (data ?? []).map((attribute) => ({
|
|
15
|
+
key: attribute.name,
|
|
16
|
+
count: attribute.distinct_values
|
|
17
|
+
})),
|
|
18
|
+
scope: "attribute",
|
|
19
|
+
loading: isPending,
|
|
20
|
+
bars: false,
|
|
21
|
+
cutoff: false,
|
|
22
|
+
sort: "name",
|
|
23
|
+
...picker
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { EventAttributePicker };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { i as measureOf, n as axis, t as Plot } from "./Plot-CxHHyXot.js";
|
|
3
|
+
import { n as toSeries, t as paint } from "./series-BBXVjHHY.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/charts/LineChart.tsx
|
|
6
|
+
var LineChart_exports = /* @__PURE__ */ __exportAll({ LineChart: () => LineChart });
|
|
7
|
+
/** Lines through ordered values, optionally with one scale per line. */
|
|
8
|
+
const LineChart = ({ step = false, yAxes = "shared", xRange, ...props }) => {
|
|
9
|
+
const groups = toSeries(props);
|
|
10
|
+
const independent = yAxes === "independent" && groups.length > 1;
|
|
11
|
+
const colors = groups.map((group) => {
|
|
12
|
+
const color = paint(group, props.colorOf, false);
|
|
13
|
+
return typeof color === "string" ? color : void 0;
|
|
14
|
+
});
|
|
15
|
+
const traces = groups.map((group, index) => ({
|
|
16
|
+
type: "scatter",
|
|
17
|
+
mode: step || group.values.length <= 40 ? "lines+markers" : "lines",
|
|
18
|
+
name: group.name,
|
|
19
|
+
x: group.labels,
|
|
20
|
+
y: group.values,
|
|
21
|
+
...independent && { yaxis: index === 0 ? "y" : `y${index + 1}` },
|
|
22
|
+
line: {
|
|
23
|
+
width: 2,
|
|
24
|
+
...step && { shape: "hv" },
|
|
25
|
+
...colors[index] && { color: colors[index] }
|
|
26
|
+
},
|
|
27
|
+
marker: {
|
|
28
|
+
size: 5,
|
|
29
|
+
...colors[index] && { color: colors[index] }
|
|
30
|
+
},
|
|
31
|
+
connectgaps: false,
|
|
32
|
+
hovertemplate: "<b>%{fullData.name}</b><br>%{x}<br>%{y}<extra></extra>"
|
|
33
|
+
}));
|
|
34
|
+
const yLayout = independent ? Object.fromEntries(groups.map((group, index) => [index === 0 ? "yaxis" : `yaxis${index + 1}`, {
|
|
35
|
+
...axis(group.name),
|
|
36
|
+
...index > 0 && {
|
|
37
|
+
overlaying: "y",
|
|
38
|
+
side: index % 2 === 0 ? "left" : "right",
|
|
39
|
+
anchor: "free",
|
|
40
|
+
autoshift: true,
|
|
41
|
+
showgrid: false,
|
|
42
|
+
zeroline: false
|
|
43
|
+
},
|
|
44
|
+
...colors[index] && {
|
|
45
|
+
tickfont: { color: colors[index] },
|
|
46
|
+
title: {
|
|
47
|
+
text: group.name,
|
|
48
|
+
font: { color: colors[index] }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}])) : { yaxis: axis(measureOf(props.y)) };
|
|
52
|
+
return /* @__PURE__ */ jsx(Plot, {
|
|
53
|
+
traces,
|
|
54
|
+
legend: groups.length > 1,
|
|
55
|
+
onSelect: props.onSelect,
|
|
56
|
+
layout: {
|
|
57
|
+
hovermode: "x unified",
|
|
58
|
+
xaxis: axis(props.x, xRange),
|
|
59
|
+
...yLayout
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { LineChart_exports as n, LineChart as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Stack, Text } from "@mantine/core";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { FrequencyPicker } from "@r4pm/components";
|
|
4
|
+
//#region src/components/pickers/types.ts
|
|
5
|
+
/** The names as a set, which is how the pickers themselves hold them. */
|
|
6
|
+
const chosen = (selection) => new Set(selection.multiple ? selection.value : selection.value == null ? [] : [selection.value]);
|
|
7
|
+
/** Report a set back as the one name, or the list, the caller asked for. */
|
|
8
|
+
const report = (selection, names) => {
|
|
9
|
+
const list = [...names];
|
|
10
|
+
if (selection.multiple) selection.onChange(list);
|
|
11
|
+
else selection.onChange(list[0]);
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/components/pickers/NamePicker.tsx
|
|
15
|
+
/**
|
|
16
|
+
* Picking names out of a list, with what is known about how often each occurs.
|
|
17
|
+
*
|
|
18
|
+
* Presentational: it is handed the names and reports back what was picked.
|
|
19
|
+
* `ActivityPicker` and the others wrap it around an endpoint.
|
|
20
|
+
*/
|
|
21
|
+
const NamePicker = ({ items, label, searchable = true, bars = true, counts = true, cutoff = false, sort = "count", scope = "name", loading = false, emptyText, disabled = false, autoFocus, ...selection }) => /* @__PURE__ */ jsxs(Stack, {
|
|
22
|
+
gap: 4,
|
|
23
|
+
children: [label && /* @__PURE__ */ jsx(Text, {
|
|
24
|
+
size: "xs",
|
|
25
|
+
c: "dimmed",
|
|
26
|
+
children: label
|
|
27
|
+
}), /* @__PURE__ */ jsx(FrequencyPicker, {
|
|
28
|
+
items,
|
|
29
|
+
mode: selection.multiple ? "multi" : "single",
|
|
30
|
+
value: chosen(selection),
|
|
31
|
+
onChange: (next) => !disabled && report(selection, next),
|
|
32
|
+
searchable,
|
|
33
|
+
showBars: bars,
|
|
34
|
+
showCounts: counts,
|
|
35
|
+
showCutoff: cutoff,
|
|
36
|
+
sort,
|
|
37
|
+
scope,
|
|
38
|
+
autoFocus,
|
|
39
|
+
emptyText: emptyText ?? (loading ? "Reading the OCEL…" : "Nothing here")
|
|
40
|
+
})]
|
|
41
|
+
});
|
|
42
|
+
//#endregion
|
|
43
|
+
export { NamePicker };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { t as useOcelId } from "./useOcelId-BSU0-Srk.js";
|
|
2
|
+
import { NamePicker } from "./NamePicker-Be-qmr1x.js";
|
|
3
|
+
import { useObjectAttributes } from "@ocelescope/api-base";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/pickers/ObjectAttributePicker.tsx
|
|
6
|
+
/** The attributes objects carry, counted by how many values each takes. */
|
|
7
|
+
const ObjectAttributePicker = ({ objectType, ocelId, ocelVersion = "filtered", ...picker }) => {
|
|
8
|
+
const id = useOcelId(ocelId);
|
|
9
|
+
const { data, isPending } = useObjectAttributes(id, {
|
|
10
|
+
ocel_version: ocelVersion,
|
|
11
|
+
...objectType ? { names: [objectType] } : {}
|
|
12
|
+
}, { query: { enabled: id != null } });
|
|
13
|
+
return /* @__PURE__ */ jsx(NamePicker, {
|
|
14
|
+
items: (data ?? []).map((attribute) => ({
|
|
15
|
+
key: attribute.name,
|
|
16
|
+
count: attribute.distinct_values
|
|
17
|
+
})),
|
|
18
|
+
scope: "attribute",
|
|
19
|
+
loading: isPending,
|
|
20
|
+
bars: false,
|
|
21
|
+
cutoff: false,
|
|
22
|
+
sort: "name",
|
|
23
|
+
...picker
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { ObjectAttributePicker };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { t as useOcelId } from "./useOcelId-BSU0-Srk.js";
|
|
2
|
+
import { NamePicker } from "./NamePicker-Be-qmr1x.js";
|
|
3
|
+
import { useObjectCounts } from "@ocelescope/api-base";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/pickers/ObjectTypePicker.tsx
|
|
6
|
+
/** The log's object types, by how often they occur. */
|
|
7
|
+
const ObjectTypePicker = ({ ocelId, ocelVersion = "filtered", ...picker }) => {
|
|
8
|
+
const id = useOcelId(ocelId);
|
|
9
|
+
const { data, isPending } = useObjectCounts(id, { ocel_version: ocelVersion }, { query: { enabled: id != null } });
|
|
10
|
+
return /* @__PURE__ */ jsx(NamePicker, {
|
|
11
|
+
items: data ?? {},
|
|
12
|
+
scope: "objectType",
|
|
13
|
+
loading: isPending,
|
|
14
|
+
...picker
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ObjectTypePicker };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { t as useOcelQuery } from "./useOcelQuery-D1QM-Zgv.js";
|
|
2
|
+
import { SqlChart } from "./SqlChart-bwDArK2l.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { AsyncBoundary, EmptyState } from "@r4pm/components";
|
|
5
|
+
//#region src/components/charts/OcelChart.tsx
|
|
6
|
+
/**
|
|
7
|
+
* A chart of the current OCEL: give it SQL, get a chart.
|
|
8
|
+
*
|
|
9
|
+
* Loading, failure and emptiness are handled here, so the charts never have to
|
|
10
|
+
* think about them; the failure shows the query's own error text, because that
|
|
11
|
+
* is the part someone writing SQL can act on.
|
|
12
|
+
*/
|
|
13
|
+
const OcelChart = ({ sql, parameters, ocelVersion = "filtered", enabled = true, height = 320, ...chart }) => {
|
|
14
|
+
const query = useOcelQuery({
|
|
15
|
+
sql,
|
|
16
|
+
parameters,
|
|
17
|
+
ocelVersion,
|
|
18
|
+
enabled
|
|
19
|
+
});
|
|
20
|
+
return /* @__PURE__ */ jsx("div", {
|
|
21
|
+
style: {
|
|
22
|
+
height,
|
|
23
|
+
width: "100%"
|
|
24
|
+
},
|
|
25
|
+
children: query.ocelId ? /* @__PURE__ */ jsx(AsyncBoundary, {
|
|
26
|
+
status: query,
|
|
27
|
+
loadingLabel: "Querying the OCEL…",
|
|
28
|
+
errorTitle: "The query failed",
|
|
29
|
+
onRetry: () => void query.refetch(),
|
|
30
|
+
children: (data) => /* @__PURE__ */ jsx(SqlChart, {
|
|
31
|
+
...chart,
|
|
32
|
+
data
|
|
33
|
+
})
|
|
34
|
+
}) : /* @__PURE__ */ jsx(EmptyState, {
|
|
35
|
+
title: "No OCEL selected",
|
|
36
|
+
description: "Import or select an OCEL to run this chart's query."
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { OcelChart };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { t as Plot } from "./Plot-CxHHyXot.js";
|
|
3
|
+
import { n as toSeries, t as paint } from "./series-BBXVjHHY.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/charts/PieChart.tsx
|
|
6
|
+
var PieChart_exports = /* @__PURE__ */ __exportAll({ PieChart: () => PieChart });
|
|
7
|
+
/** Shares of a whole, one slice per category. */
|
|
8
|
+
const PieChart = ({ hole = .5, ...props }) => {
|
|
9
|
+
const [slices] = toSeries(props);
|
|
10
|
+
const colors = slices && paint(slices, props.colorOf, true);
|
|
11
|
+
const total = slices?.values.reduce((sum, value) => sum + (typeof value === "number" ? value : 0), 0);
|
|
12
|
+
const compact = new Intl.NumberFormat(void 0, {
|
|
13
|
+
notation: "compact",
|
|
14
|
+
maximumFractionDigits: 1
|
|
15
|
+
});
|
|
16
|
+
return /* @__PURE__ */ jsx(Plot, {
|
|
17
|
+
traces: slices ? [{
|
|
18
|
+
type: "pie",
|
|
19
|
+
labels: slices.labels,
|
|
20
|
+
values: slices.values,
|
|
21
|
+
hole,
|
|
22
|
+
sort: false,
|
|
23
|
+
direction: "clockwise",
|
|
24
|
+
rotation: -90,
|
|
25
|
+
textinfo: slices.labels.length <= 8 ? "label+percent" : "percent",
|
|
26
|
+
textposition: slices.labels.length <= 5 ? "inside" : "auto",
|
|
27
|
+
insidetextorientation: "horizontal",
|
|
28
|
+
hovertemplate: "<b>%{label}</b><br>%{value} · %{percent}<extra></extra>",
|
|
29
|
+
marker: {
|
|
30
|
+
...Array.isArray(colors) && { colors },
|
|
31
|
+
line: {
|
|
32
|
+
color: "rgba(128,128,128,0.28)",
|
|
33
|
+
width: 1.5
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}] : [],
|
|
37
|
+
legend: true,
|
|
38
|
+
onSelect: props.onSelect,
|
|
39
|
+
layout: {
|
|
40
|
+
margin: {
|
|
41
|
+
t: 44,
|
|
42
|
+
r: 12,
|
|
43
|
+
b: 20,
|
|
44
|
+
l: 12
|
|
45
|
+
},
|
|
46
|
+
uniformtext: {
|
|
47
|
+
mode: "hide",
|
|
48
|
+
minsize: 11
|
|
49
|
+
},
|
|
50
|
+
...hole > 0 && total != null && { annotations: [{
|
|
51
|
+
text: `<b>${compact.format(total)}</b><br><span style="font-size:11px">total</span>`,
|
|
52
|
+
showarrow: false,
|
|
53
|
+
x: .5,
|
|
54
|
+
y: .5,
|
|
55
|
+
xanchor: "center",
|
|
56
|
+
yanchor: "middle"
|
|
57
|
+
}] }
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { PieChart_exports as n, PieChart as t };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ThemedPlot } from "@r4pm/components/charts";
|
|
3
|
+
//#region src/components/charts/types.ts
|
|
4
|
+
/** How a cell reads once it is a label. */
|
|
5
|
+
const label = (value) => value == null ? "∅" : String(value);
|
|
6
|
+
/** A cell as the chart plots it: a number where it is one, else its text. */
|
|
7
|
+
const plotted = (value) => {
|
|
8
|
+
if (typeof value === "number") return value;
|
|
9
|
+
if (value == null || value === "") return 0;
|
|
10
|
+
const number = Number(value);
|
|
11
|
+
return Number.isFinite(number) ? number : String(value);
|
|
12
|
+
};
|
|
13
|
+
/** The first, or only, column holding numbers. */
|
|
14
|
+
const measureOf = (y) => (Array.isArray(y) ? y[0] : y) ?? "";
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/components/charts/internal/Plot.tsx
|
|
17
|
+
/**
|
|
18
|
+
* The plot every chart draws on: r4pm's themed Plotly, with the parts that
|
|
19
|
+
* should look the same everywhere - margins, legend, no mode bar - already set.
|
|
20
|
+
*/
|
|
21
|
+
const Plot = ({ traces, layout, legend = false, readName = (point) => String(point?.label ?? point?.x ?? ""), onSelect }) => /* @__PURE__ */ jsx(ThemedPlot, {
|
|
22
|
+
data: traces,
|
|
23
|
+
layout: {
|
|
24
|
+
autosize: true,
|
|
25
|
+
margin: {
|
|
26
|
+
t: legend ? 44 : 12,
|
|
27
|
+
r: 16,
|
|
28
|
+
b: 48,
|
|
29
|
+
l: 56,
|
|
30
|
+
pad: 2
|
|
31
|
+
},
|
|
32
|
+
showlegend: legend,
|
|
33
|
+
clickmode: onSelect ? "event+select" : "event",
|
|
34
|
+
legend: {
|
|
35
|
+
orientation: "h",
|
|
36
|
+
y: 1.02,
|
|
37
|
+
yref: "paper",
|
|
38
|
+
yanchor: "bottom",
|
|
39
|
+
x: 0,
|
|
40
|
+
xanchor: "left",
|
|
41
|
+
bgcolor: "rgba(0,0,0,0)"
|
|
42
|
+
},
|
|
43
|
+
hoverlabel: { namelength: -1 },
|
|
44
|
+
...layout
|
|
45
|
+
},
|
|
46
|
+
config: {
|
|
47
|
+
displaylogo: false,
|
|
48
|
+
displayModeBar: false,
|
|
49
|
+
responsive: true,
|
|
50
|
+
scrollZoom: false,
|
|
51
|
+
doubleClick: "reset"
|
|
52
|
+
},
|
|
53
|
+
style: {
|
|
54
|
+
width: "100%",
|
|
55
|
+
height: "100%"
|
|
56
|
+
},
|
|
57
|
+
useResizeHandler: true,
|
|
58
|
+
onClick: ({ points }) => points[0] && onSelect?.(readName(points[0]), points[0])
|
|
59
|
+
});
|
|
60
|
+
/** An axis that keeps its labels on screen, named after its column. */
|
|
61
|
+
const axis = (title, range) => ({
|
|
62
|
+
automargin: true,
|
|
63
|
+
ticks: "outside",
|
|
64
|
+
ticklen: 4,
|
|
65
|
+
tickfont: { size: 11 },
|
|
66
|
+
zeroline: false,
|
|
67
|
+
...title && { title: {
|
|
68
|
+
text: title,
|
|
69
|
+
font: { size: 12 },
|
|
70
|
+
standoff: 10
|
|
71
|
+
} },
|
|
72
|
+
...range && { range: [...range] }
|
|
73
|
+
});
|
|
74
|
+
//#endregion
|
|
75
|
+
export { plotted as a, measureOf as i, axis as n, label as r, Plot as t };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
2
|
+
import { i as measureOf, n as axis, t as Plot } from "./Plot-CxHHyXot.js";
|
|
3
|
+
import { n as traceCount, t as cartesian } from "./cartesian-5TPNWjl_.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/charts/ScatterChart.tsx
|
|
6
|
+
var ScatterChart_exports = /* @__PURE__ */ __exportAll({ ScatterChart: () => ScatterChart });
|
|
7
|
+
/** A point per row, for values that have no order to follow. */
|
|
8
|
+
const ScatterChart = ({ xRange, ...props }) => {
|
|
9
|
+
const measure = measureOf(props.y);
|
|
10
|
+
return /* @__PURE__ */ jsx(Plot, {
|
|
11
|
+
traces: cartesian(props, {
|
|
12
|
+
type: "scatter",
|
|
13
|
+
mode: "markers",
|
|
14
|
+
marker: {
|
|
15
|
+
size: props.rows.length > 500 ? 6 : 9,
|
|
16
|
+
opacity: props.rows.length > 500 ? .55 : .78,
|
|
17
|
+
line: {
|
|
18
|
+
color: "rgba(128,128,128,0.35)",
|
|
19
|
+
width: .75
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
hovertemplate: "<b>%{fullData.name}</b><br>%{x}<br>%{y}<extra></extra>"
|
|
23
|
+
}),
|
|
24
|
+
legend: traceCount(props) > 1,
|
|
25
|
+
onSelect: props.onSelect,
|
|
26
|
+
layout: {
|
|
27
|
+
hovermode: "closest",
|
|
28
|
+
hoverdistance: 24,
|
|
29
|
+
dragmode: "zoom",
|
|
30
|
+
xaxis: axis(props.x, xRange),
|
|
31
|
+
yaxis: axis(measure)
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { ScatterChart_exports as n, ScatterChart as t };
|