cortena-ui 1.3.0 → 1.3.1
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 +6 -3
- package/dist/index.js +94 -22
- package/dist/index.js.map +1 -1
- package/package.json +4 -11
- package/src/a2ui/catalogue.ts +11 -2
- package/src/components/chart/chart.tsx +41 -8
- package/src/components/chart/nivo-charts.tsx +41 -13
- package/src/components/data-table/export.ts +18 -2
- package/src/components/data-table/use-server-source.ts +17 -4
package/dist/index.d.ts
CHANGED
|
@@ -1108,7 +1108,10 @@ interface ExportSheet {
|
|
|
1108
1108
|
records: ExportCell[][];
|
|
1109
1109
|
}
|
|
1110
1110
|
export declare function toSheet<Row extends RowData>(table: DataTableTable<Row>, rows?: DataTableRow<Row>[]): ExportSheet;
|
|
1111
|
-
/**
|
|
1111
|
+
/**
|
|
1112
|
+
* RFC 4180 text: comma separated, CRLF lines, quotes doubled. A cell that a
|
|
1113
|
+
* spreadsheet would read as a formula is prefixed with `'` so it stays text.
|
|
1114
|
+
*/
|
|
1112
1115
|
export declare function toCsv<Row extends RowData>(table: DataTableTable<Row>, rows?: DataTableRow<Row>[]): string;
|
|
1113
1116
|
export declare function downloadCsv<Row extends RowData>(table: DataTableTable<Row>, fileName?: string, rows?: DataTableRow<Row>[]): void;
|
|
1114
1117
|
/**
|
|
@@ -1351,7 +1354,7 @@ declare function AvatarFallback({ className, ...props }: Avatar$1.Fallback.Props
|
|
|
1351
1354
|
//#endregion
|
|
1352
1355
|
//#region src/components/badge.d.ts
|
|
1353
1356
|
declare const badgeVariants: (props?: ({
|
|
1354
|
-
variant?: "default" | "success" | "warn" | "destructive" | "danger" | "info" | "
|
|
1357
|
+
variant?: "default" | "success" | "warn" | "destructive" | "danger" | "info" | "secondary" | "outline" | "warning" | "neutral" | null | undefined;
|
|
1355
1358
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
1356
1359
|
interface BadgeProps extends React$1.ComponentProps<"span">, VariantProps<typeof badgeVariants> {}
|
|
1357
1360
|
declare function Badge({ className, variant, ...props }: BadgeProps): React$1.JSX.Element;
|
|
@@ -1454,7 +1457,7 @@ declare function BreadcrumbEllipsis({ className, children, ...props }: React$1.C
|
|
|
1454
1457
|
* rendered element, which is Base UI's equivalent.
|
|
1455
1458
|
*/
|
|
1456
1459
|
declare const buttonVariants: (props?: ({
|
|
1457
|
-
variant?: "
|
|
1460
|
+
variant?: "destructive" | "primary" | "secondary" | "outline" | "ghost" | "soft" | "link" | null | undefined;
|
|
1458
1461
|
size?: "sm" | "md" | "lg" | "icon" | "icon-sm" | null | undefined;
|
|
1459
1462
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
1460
1463
|
interface ButtonProps extends React$1.ComponentProps<typeof Button$1>, VariantProps<typeof buttonVariants> {}
|
package/dist/index.js
CHANGED
|
@@ -950,14 +950,28 @@ function useNivoTheme() {
|
|
|
950
950
|
}
|
|
951
951
|
//#endregion
|
|
952
952
|
//#region src/components/chart/nivo-charts.tsx
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
953
|
+
const loaders = {
|
|
954
|
+
"heatmap-svg": async () => (await import("@nivo/heatmap")).ResponsiveHeatMap,
|
|
955
|
+
"heatmap-canvas": async () => (await import("@nivo/heatmap")).ResponsiveHeatMapCanvas,
|
|
956
|
+
"calendar-svg": async () => (await import("@nivo/calendar")).ResponsiveCalendar,
|
|
957
|
+
"calendar-canvas": async () => (await import("@nivo/calendar")).ResponsiveCalendarCanvas,
|
|
958
|
+
"treemap-svg": async () => (await import("@nivo/treemap")).ResponsiveTreeMap,
|
|
959
|
+
"treemap-canvas": async () => (await import("@nivo/treemap")).ResponsiveTreeMapCanvas,
|
|
960
|
+
"sunburst-svg": async () => (await import("@nivo/sunburst")).ResponsiveSunburst
|
|
961
|
+
};
|
|
962
|
+
let chunks = /* @__PURE__ */ new Map();
|
|
963
|
+
function chunk(name) {
|
|
964
|
+
let component = chunks.get(name);
|
|
965
|
+
if (!component) {
|
|
966
|
+
component = React.lazy(async () => ({ default: await loaders[name]() }));
|
|
967
|
+
chunks.set(name, component);
|
|
968
|
+
}
|
|
969
|
+
return component;
|
|
970
|
+
}
|
|
971
|
+
/** Forget every loaded-or-failed Nivo chunk, so the next render loads it again. */
|
|
972
|
+
function resetNivoChunks() {
|
|
973
|
+
chunks = /* @__PURE__ */ new Map();
|
|
974
|
+
}
|
|
961
975
|
/**
|
|
962
976
|
* Resolved series colours for Nivo: config colours are `var()` strings, which
|
|
963
977
|
* Nivo cannot use, so a configured colour is resolved through the container's
|
|
@@ -1007,7 +1021,7 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
|
|
|
1007
1021
|
const labelOn = (d) => onFill(d.color);
|
|
1008
1022
|
switch (type) {
|
|
1009
1023
|
case "heatmap": {
|
|
1010
|
-
const Component = canvas ?
|
|
1024
|
+
const Component = chunk(canvas ? "heatmap-canvas" : "heatmap-svg");
|
|
1011
1025
|
const rows = isMatrix(data) ? matrixToSeries(data) : data;
|
|
1012
1026
|
const cells = rows.reduce((n, r) => n + r.data.length, 0);
|
|
1013
1027
|
const colorConfig = scale === "diverging" ? {
|
|
@@ -1056,7 +1070,7 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
|
|
|
1056
1070
|
});
|
|
1057
1071
|
}
|
|
1058
1072
|
case "calendar": {
|
|
1059
|
-
const Component = canvas ?
|
|
1073
|
+
const Component = chunk(canvas ? "calendar-canvas" : "calendar-svg");
|
|
1060
1074
|
const days = data;
|
|
1061
1075
|
const range = calendarRange(days, from, to);
|
|
1062
1076
|
return /* @__PURE__ */ jsx(Component, {
|
|
@@ -1090,7 +1104,7 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
|
|
|
1090
1104
|
});
|
|
1091
1105
|
}
|
|
1092
1106
|
case "treemap": {
|
|
1093
|
-
const Component = canvas ?
|
|
1107
|
+
const Component = chunk(canvas ? "treemap-canvas" : "treemap-svg");
|
|
1094
1108
|
const tree = data;
|
|
1095
1109
|
return /* @__PURE__ */ jsx(Component, {
|
|
1096
1110
|
data: tree,
|
|
@@ -1117,8 +1131,9 @@ function NivoChart({ type, data, config, renderer, scale, legend, valueFormatter
|
|
|
1117
1131
|
});
|
|
1118
1132
|
}
|
|
1119
1133
|
case "sunburst": {
|
|
1134
|
+
const Component = chunk("sunburst-svg");
|
|
1120
1135
|
const tree = data;
|
|
1121
|
-
return /* @__PURE__ */ jsx(
|
|
1136
|
+
return /* @__PURE__ */ jsx(Component, {
|
|
1122
1137
|
data: tree,
|
|
1123
1138
|
id: treeIdentity(tree),
|
|
1124
1139
|
value: "value",
|
|
@@ -1404,13 +1419,33 @@ function seriesKeys(type, data, indexBy, keys) {
|
|
|
1404
1419
|
case "calendar": return [];
|
|
1405
1420
|
}
|
|
1406
1421
|
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Catches a Nivo chunk that failed to load. Recovering takes two things,
|
|
1424
|
+
* because `React.lazy` memoises a rejection for the life of the page: fresh
|
|
1425
|
+
* lazy components ({@link resetNivoChunks}) and a boundary that leaves its
|
|
1426
|
+
* failed state. `resetKey` does the second on its own when the chart changes,
|
|
1427
|
+
* and the fallback gets a `retry` that does both.
|
|
1428
|
+
*/
|
|
1407
1429
|
var ChunkBoundary = class extends React.Component {
|
|
1408
|
-
state = {
|
|
1430
|
+
state = {
|
|
1431
|
+
failed: false,
|
|
1432
|
+
key: this.props.resetKey
|
|
1433
|
+
};
|
|
1409
1434
|
static getDerivedStateFromError() {
|
|
1410
1435
|
return { failed: true };
|
|
1411
1436
|
}
|
|
1437
|
+
static getDerivedStateFromProps(props, state) {
|
|
1438
|
+
return props.resetKey === state.key ? null : {
|
|
1439
|
+
failed: false,
|
|
1440
|
+
key: props.resetKey
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
retry = () => {
|
|
1444
|
+
resetNivoChunks();
|
|
1445
|
+
this.setState({ failed: false });
|
|
1446
|
+
};
|
|
1412
1447
|
render() {
|
|
1413
|
-
return this.state.failed ? this.props.fallback : this.props.children;
|
|
1448
|
+
return this.state.failed ? this.props.fallback(this.retry) : this.props.children;
|
|
1414
1449
|
}
|
|
1415
1450
|
};
|
|
1416
1451
|
/**
|
|
@@ -1450,7 +1485,18 @@ function Chart({ type, data, config, renderer = "svg", indexBy, keys, stacked, s
|
|
|
1450
1485
|
else if (errorText) body = /* @__PURE__ */ jsx(ChartError, { children: errorText });
|
|
1451
1486
|
else if (empty) body = /* @__PURE__ */ jsx(ChartEmpty, { children: emptyMessage });
|
|
1452
1487
|
else if (engine === "nivo") body = /* @__PURE__ */ jsx(ChunkBoundary, {
|
|
1453
|
-
|
|
1488
|
+
resetKey: `${type}|${renderer}`,
|
|
1489
|
+
fallback: (retry) => /* @__PURE__ */ jsxs(ChartError, { children: [
|
|
1490
|
+
"Chart could not be loaded.",
|
|
1491
|
+
" ",
|
|
1492
|
+
/* @__PURE__ */ jsx("button", {
|
|
1493
|
+
type: "button",
|
|
1494
|
+
"data-slot": "chart-retry",
|
|
1495
|
+
onClick: retry,
|
|
1496
|
+
className: "underline underline-offset-2",
|
|
1497
|
+
children: "Retry"
|
|
1498
|
+
})
|
|
1499
|
+
] }),
|
|
1454
1500
|
children: /* @__PURE__ */ jsx(React.Suspense, {
|
|
1455
1501
|
fallback: /* @__PURE__ */ jsx(ChartLoading, {}),
|
|
1456
1502
|
children: /* @__PURE__ */ jsx(NivoChart, {
|
|
@@ -1904,12 +1950,25 @@ function toSheet(table, rows = exportRows(table)) {
|
|
|
1904
1950
|
}))
|
|
1905
1951
|
};
|
|
1906
1952
|
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Text a spreadsheet would evaluate rather than display. Excel, Sheets and
|
|
1955
|
+
* LibreOffice all treat a leading `=`, `+`, `-` or `@` as the start of a
|
|
1956
|
+
* formula, and a leading tab or CR as whitespace they strip before looking
|
|
1957
|
+
* again — so `\t=cmd|'/c calc'!A1` is a formula too. Only strings are checked:
|
|
1958
|
+
* a number, boolean or Date cell cannot carry one, and prefixing `-5` would
|
|
1959
|
+
* turn a number column into text.
|
|
1960
|
+
*/
|
|
1961
|
+
const FORMULA_START = /^[=+\-@\t\r]/;
|
|
1907
1962
|
function csvField(value) {
|
|
1908
1963
|
if (value == null) return "";
|
|
1909
|
-
|
|
1964
|
+
let text = value instanceof Date ? value.toISOString() : String(value);
|
|
1965
|
+
if (typeof value === "string" && FORMULA_START.test(text)) text = `'${text}`;
|
|
1910
1966
|
return /[",\n\r]/.test(text) ? `"${text.replace(/"/g, "\"\"")}"` : text;
|
|
1911
1967
|
}
|
|
1912
|
-
/**
|
|
1968
|
+
/**
|
|
1969
|
+
* RFC 4180 text: comma separated, CRLF lines, quotes doubled. A cell that a
|
|
1970
|
+
* spreadsheet would read as a formula is prefixed with `'` so it stays text.
|
|
1971
|
+
*/
|
|
1913
1972
|
function toCsv(table, rows) {
|
|
1914
1973
|
const { headers, records } = toSheet(table, rows);
|
|
1915
1974
|
return [headers, ...records].map((line) => line.map(csvField).join(",")).join("\r\n");
|
|
@@ -2596,14 +2655,20 @@ function useLocalStrategy(source, query, mode) {
|
|
|
2596
2655
|
const targetPage = infinite ? loadedForScope.length : query.page;
|
|
2597
2656
|
const needs = infinite ? loadedForScope.length < wantedPages : !(state.scopeKey === scopeKey && state.page === query.page && state.pages.length > 0);
|
|
2598
2657
|
const requestKey = `${scopeKey}|${targetPage}|${tick}`;
|
|
2658
|
+
const fetchRef = React.useRef(fetch);
|
|
2659
|
+
React.useEffect(() => {
|
|
2660
|
+
fetchRef.current = fetch;
|
|
2661
|
+
});
|
|
2662
|
+
const hasFetch = Boolean(fetch);
|
|
2599
2663
|
React.useEffect(() => {
|
|
2600
|
-
|
|
2664
|
+
const run = fetchRef.current;
|
|
2665
|
+
if (!run || !needs) return;
|
|
2601
2666
|
const controller = new AbortController();
|
|
2602
2667
|
setState((s) => ({
|
|
2603
2668
|
...s,
|
|
2604
2669
|
isFetching: true
|
|
2605
2670
|
}));
|
|
2606
|
-
|
|
2671
|
+
run({
|
|
2607
2672
|
...scope,
|
|
2608
2673
|
page: targetPage
|
|
2609
2674
|
}, controller.signal).then((result) => {
|
|
@@ -2625,7 +2690,7 @@ function useLocalStrategy(source, query, mode) {
|
|
|
2625
2690
|
});
|
|
2626
2691
|
return () => controller.abort();
|
|
2627
2692
|
}, [
|
|
2628
|
-
|
|
2693
|
+
hasFetch,
|
|
2629
2694
|
requestKey,
|
|
2630
2695
|
needs
|
|
2631
2696
|
]);
|
|
@@ -5127,14 +5192,21 @@ const buttonSize = z.enum([
|
|
|
5127
5192
|
"lg"
|
|
5128
5193
|
]).describe("Control size.");
|
|
5129
5194
|
/**
|
|
5195
|
+
* Root-relative means one slash and then a path. `//evil.example/x` is
|
|
5196
|
+
* protocol-relative — the browser resolves it against the page's scheme and
|
|
5197
|
+
* goes off-origin — and `/\evil.example` is the same trick, because browsers
|
|
5198
|
+
* treat a backslash after the leading slash as a second slash.
|
|
5199
|
+
*/
|
|
5200
|
+
const rootRelative = (value) => value.startsWith("/") && value[1] !== "/" && value[1] !== "\\";
|
|
5201
|
+
/**
|
|
5130
5202
|
* A URL an agent may point at.
|
|
5131
5203
|
*
|
|
5132
5204
|
* Restricted by scheme rather than sanitised after the fact: `javascript:` and
|
|
5133
5205
|
* friends never reach the DOM because they never pass validation, so the
|
|
5134
5206
|
* component gets an error card instead.
|
|
5135
5207
|
*/
|
|
5136
|
-
const httpUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || value
|
|
5137
|
-
const imageUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || /^data:image\//i.test(value) || value
|
|
5208
|
+
const httpUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || rootRelative(value) || value.startsWith("#"), "Must be an http(s), root-relative or fragment URL.").describe("Destination URL (http, https, root-relative or fragment).");
|
|
5209
|
+
const imageUrl = z.string().refine((value) => /^https?:\/\//i.test(value) || /^data:image\//i.test(value) || rootRelative(value), "Must be an http(s), root-relative or data:image URL.").describe("Image URL (http, https, root-relative or data:image).");
|
|
5138
5210
|
/** Every chart type the Chart composite draws, Recharts and Nivo alike. */
|
|
5139
5211
|
const chartType = z.enum([
|
|
5140
5212
|
"bar",
|