@ai-matrx/records-ui 0.110.4 → 0.110.5
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/CHANGELOG.md +6 -0
- package/dist/first-page.cjs +74 -7
- package/dist/first-page.cjs.map +1 -1
- package/dist/first-page.d.cts +13 -0
- package/dist/first-page.d.ts +13 -0
- package/dist/first-page.js +74 -7
- package/dist/first-page.js.map +1 -1
- package/dist/index.cjs +25 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +26 -22
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/first-page.d.cts
CHANGED
|
@@ -339,6 +339,12 @@ declare function firstGridPageArgs(input: {
|
|
|
339
339
|
/** The view as THIS person sees it (the saved view with her look over it), or null for none. */
|
|
340
340
|
shown: SavedView | null;
|
|
341
341
|
pageSize?: number;
|
|
342
|
+
/**
|
|
343
|
+
* The host draws the MERGED grid (`RecordsUiProvider grid="merged"`), which keeps a plain grouping as rows
|
|
344
|
+
* under group headers: the page is then read in group order. The classic grid opens the drill for a
|
|
345
|
+
* grouped view, so a grouped view stays unseeded there.
|
|
346
|
+
*/
|
|
347
|
+
merged?: boolean;
|
|
342
348
|
}): FirstGridPageArgs | null;
|
|
343
349
|
/**
|
|
344
350
|
* Read what the screen reads (through `client`, e.g. one served from `custom.table_page_bundle`)
|
|
@@ -346,6 +352,7 @@ declare function firstGridPageArgs(input: {
|
|
|
346
352
|
*/
|
|
347
353
|
declare function firstGridPageRequest(client: Pick<RecordsClient, "tableRead" | "fields" | "views" | "viewLookRead">, tableId: Uuid, options?: {
|
|
348
354
|
pageSize?: number;
|
|
355
|
+
merged?: boolean;
|
|
349
356
|
}): Promise<FirstGridPageArgs | null>;
|
|
350
357
|
/**
|
|
351
358
|
* A TABLE PAGE'S SEED, ASKED BY THE SERVER AS THE PERSON (lane SSR-ROWS).
|
|
@@ -389,6 +396,12 @@ declare function askTablePageSeed(input: {
|
|
|
389
396
|
/** The table is drawn EMBEDDED (`TablePage presentation="embedded"`, a Space's inline table): its page size. */
|
|
390
397
|
embedded?: boolean;
|
|
391
398
|
pageSize?: number;
|
|
399
|
+
/**
|
|
400
|
+
* The host draws the merged grid. A view grouped by one field (no sub-levels, pivot or drill trail) is then
|
|
401
|
+
* seeded too: the page in group order, the group counts, the summary bar, and the words of the links on the
|
|
402
|
+
* page — each asked with the arguments the grid sends, so the browser's own call finds its answer.
|
|
403
|
+
*/
|
|
404
|
+
merged?: boolean;
|
|
392
405
|
}): Promise<RecordsSeed>;
|
|
393
406
|
|
|
394
407
|
export { EMBEDDED_PAGE_SIZE, type FirstGridPageArgs, TABLE_PAGE_SIZE, type TileSpec, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf };
|
package/dist/first-page.d.ts
CHANGED
|
@@ -339,6 +339,12 @@ declare function firstGridPageArgs(input: {
|
|
|
339
339
|
/** The view as THIS person sees it (the saved view with her look over it), or null for none. */
|
|
340
340
|
shown: SavedView | null;
|
|
341
341
|
pageSize?: number;
|
|
342
|
+
/**
|
|
343
|
+
* The host draws the MERGED grid (`RecordsUiProvider grid="merged"`), which keeps a plain grouping as rows
|
|
344
|
+
* under group headers: the page is then read in group order. The classic grid opens the drill for a
|
|
345
|
+
* grouped view, so a grouped view stays unseeded there.
|
|
346
|
+
*/
|
|
347
|
+
merged?: boolean;
|
|
342
348
|
}): FirstGridPageArgs | null;
|
|
343
349
|
/**
|
|
344
350
|
* Read what the screen reads (through `client`, e.g. one served from `custom.table_page_bundle`)
|
|
@@ -346,6 +352,7 @@ declare function firstGridPageArgs(input: {
|
|
|
346
352
|
*/
|
|
347
353
|
declare function firstGridPageRequest(client: Pick<RecordsClient, "tableRead" | "fields" | "views" | "viewLookRead">, tableId: Uuid, options?: {
|
|
348
354
|
pageSize?: number;
|
|
355
|
+
merged?: boolean;
|
|
349
356
|
}): Promise<FirstGridPageArgs | null>;
|
|
350
357
|
/**
|
|
351
358
|
* A TABLE PAGE'S SEED, ASKED BY THE SERVER AS THE PERSON (lane SSR-ROWS).
|
|
@@ -389,6 +396,12 @@ declare function askTablePageSeed(input: {
|
|
|
389
396
|
/** The table is drawn EMBEDDED (`TablePage presentation="embedded"`, a Space's inline table): its page size. */
|
|
390
397
|
embedded?: boolean;
|
|
391
398
|
pageSize?: number;
|
|
399
|
+
/**
|
|
400
|
+
* The host draws the merged grid. A view grouped by one field (no sub-levels, pivot or drill trail) is then
|
|
401
|
+
* seeded too: the page in group order, the group counts, the summary bar, and the words of the links on the
|
|
402
|
+
* page — each asked with the arguments the grid sends, so the browser's own call finds its answer.
|
|
403
|
+
*/
|
|
404
|
+
merged?: boolean;
|
|
392
405
|
}): Promise<RecordsSeed>;
|
|
393
406
|
|
|
394
407
|
export { EMBEDDED_PAGE_SIZE, type FirstGridPageArgs, TABLE_PAGE_SIZE, type TileSpec, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf };
|
package/dist/first-page.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { recordingDataSource, asRecordsDataSource, createRecordsClient, mapPgError } from '@ai-matrx/records/core';
|
|
2
|
+
import { isUuidShape } from '@ai-matrx/kit/uuid';
|
|
2
3
|
import { PARITY_FIELD_TYPES, KERNEL_TABLES, mintOpId, fieldKindFor } from '@ai-matrx/records';
|
|
3
4
|
import '@ai-matrx/kit/text-case';
|
|
4
5
|
import '@ai-matrx/design-system/data-table';
|
|
5
6
|
import { parseTableStyle, tableStyleIsEmpty } from '@ai-matrx/design-system/data-table/table-style';
|
|
6
7
|
import '@ai-matrx/design-system/field-formats';
|
|
7
|
-
import { isUuidShape } from '@ai-matrx/kit/uuid';
|
|
8
8
|
import { defineChannelNamespace } from '@ai-matrx/realtime';
|
|
9
9
|
|
|
10
10
|
// src/firstPage.ts
|
|
@@ -46,6 +46,20 @@ function defaultSortOf(table, fields) {
|
|
|
46
46
|
const field = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);
|
|
47
47
|
return field ? { id: field.key, direction } : null;
|
|
48
48
|
}
|
|
49
|
+
var MEASURE_LABEL = {
|
|
50
|
+
count: "Count",
|
|
51
|
+
sum: "Sum",
|
|
52
|
+
avg: "Average",
|
|
53
|
+
min: "Smallest",
|
|
54
|
+
max: "Largest",
|
|
55
|
+
median: "Median",
|
|
56
|
+
filled: "Filled",
|
|
57
|
+
empty: "Empty",
|
|
58
|
+
unique: "Unique",
|
|
59
|
+
percent_empty: "Percent empty",
|
|
60
|
+
percent_filled: "Percent filled",
|
|
61
|
+
range: "Range"
|
|
62
|
+
};
|
|
49
63
|
function holdsNumbers(field) {
|
|
50
64
|
const kind = String(editorKindFor(field));
|
|
51
65
|
if (kind === "number" || kind === "currency" || kind === "percent") return true;
|
|
@@ -63,6 +77,26 @@ function pageSortFor(fields, sort) {
|
|
|
63
77
|
if (!field) return [];
|
|
64
78
|
return [{ field: field.key, direction: sort.direction === "desc" ? "desc" : "asc", as: sortAsFor(field) }];
|
|
65
79
|
}
|
|
80
|
+
|
|
81
|
+
// src/groupReads.ts
|
|
82
|
+
function isPlainGrouping(grouping) {
|
|
83
|
+
return Boolean(grouping) && !(grouping.then && grouping.then.length > 0) && !grouping.across && !(grouping.trail && grouping.trail.length > 0);
|
|
84
|
+
}
|
|
85
|
+
function groupedPageSort(fields, groupKey, sort) {
|
|
86
|
+
if (!groupKey) return sort;
|
|
87
|
+
const field = fields.find((f) => f.key === groupKey);
|
|
88
|
+
if (!field) return sort;
|
|
89
|
+
return [{ field: groupKey, direction: "asc", as: sortAsFor(field) }, ...sort.filter((s) => s.field !== groupKey)];
|
|
90
|
+
}
|
|
91
|
+
function chosenEntries(chosen) {
|
|
92
|
+
return JSON.parse(JSON.stringify(Object.entries(chosen).sort())).filter(([, op]) => op in MEASURE_LABEL);
|
|
93
|
+
}
|
|
94
|
+
function groupFactMeasures(chosen) {
|
|
95
|
+
return [{ op: "count" }, ...chosenEntries(chosen).filter(([, op]) => op !== "count").map(([key, op]) => ({ op, key }))];
|
|
96
|
+
}
|
|
97
|
+
function summaryBarMeasures(chosen) {
|
|
98
|
+
return chosenEntries(chosen).map(([key, op]) => op === "count" ? { op: "count" } : { op, key });
|
|
99
|
+
}
|
|
66
100
|
var MIN_ROW_HEIGHT = 24;
|
|
67
101
|
var MAX_ROW_HEIGHT = 96;
|
|
68
102
|
var AGGREGATE_KINDS = [
|
|
@@ -959,7 +993,8 @@ function firstGridPageArgs(input) {
|
|
|
959
993
|
if (shown.ruleId) return null;
|
|
960
994
|
if (shown.order === "manual") return null;
|
|
961
995
|
if (shown.filters && Object.keys(shown.filters).length > 0) return null;
|
|
962
|
-
|
|
996
|
+
const grouping = shown.presentation?.grouping;
|
|
997
|
+
if (grouping && !(input.merged && isPlainGrouping(grouping))) return null;
|
|
963
998
|
const first = (shown.sorts ?? [])[0];
|
|
964
999
|
const viewSort = first && fields.some((f) => f.key === first.field) ? { id: first.field, direction: first.direction } : null;
|
|
965
1000
|
const opening = viewSort ?? defaultSortOf(input.table, fields);
|
|
@@ -967,13 +1002,16 @@ function firstGridPageArgs(input) {
|
|
|
967
1002
|
p_table_id: input.tableId,
|
|
968
1003
|
p_filter: {},
|
|
969
1004
|
p_search: null,
|
|
970
|
-
p_sort: pageSortFor(fields, opening),
|
|
1005
|
+
p_sort: groupedPageSort(fields, grouping?.field ?? null, pageSortFor(fields, opening)),
|
|
971
1006
|
p_view_id: null,
|
|
972
1007
|
p_limit: input.pageSize ?? TABLE_PAGE_SIZE,
|
|
973
1008
|
p_offset: 0
|
|
974
1009
|
};
|
|
975
1010
|
}
|
|
976
1011
|
async function firstGridPageRequest(client, tableId, options = {}) {
|
|
1012
|
+
return (await firstGridOpening(client, tableId, options))?.args ?? null;
|
|
1013
|
+
}
|
|
1014
|
+
async function firstGridOpening(client, tableId, options) {
|
|
977
1015
|
const [table, fields, views, looks] = await Promise.all([
|
|
978
1016
|
client.tableRead({ table_id: tableId }),
|
|
979
1017
|
client.fields({ table_id: tableId }),
|
|
@@ -984,7 +1022,15 @@ async function firstGridPageRequest(client, tableId, options = {}) {
|
|
|
984
1022
|
const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);
|
|
985
1023
|
const view = defaultViewOf(mine) ?? mine[0] ?? null;
|
|
986
1024
|
const shown = view ? applyLook(view, looks.data[view.id]) : null;
|
|
987
|
-
|
|
1025
|
+
const args = firstGridPageArgs({
|
|
1026
|
+
tableId,
|
|
1027
|
+
table: table.data,
|
|
1028
|
+
fields: fields.data,
|
|
1029
|
+
shown,
|
|
1030
|
+
...options.pageSize ? { pageSize: options.pageSize } : {},
|
|
1031
|
+
...options.merged ? { merged: true } : {}
|
|
1032
|
+
});
|
|
1033
|
+
return args ? { args, shown, fields: fields.data } : null;
|
|
988
1034
|
}
|
|
989
1035
|
function serverRowsOf(seed) {
|
|
990
1036
|
const found = seed?.answers.find((a) => a.door === "server_rows");
|
|
@@ -1006,10 +1052,11 @@ async function askTablePageSeed(input) {
|
|
|
1006
1052
|
).catch(() => null) : null;
|
|
1007
1053
|
try {
|
|
1008
1054
|
const pageSize = input.pageSize ?? (input.embedded ? EMBEDDED_PAGE_SIZE : TABLE_PAGE_SIZE);
|
|
1009
|
-
const
|
|
1055
|
+
const opening = await firstGridOpening(client, input.tableId, { pageSize, ...input.merged ? { merged: true } : {} });
|
|
1010
1056
|
const wantRows = typeof input.rows === "function" ? input.rows(recording.seed()) : input.rows !== false;
|
|
1011
|
-
if (
|
|
1012
|
-
|
|
1057
|
+
if (opening && wantRows) {
|
|
1058
|
+
const { args, shown, fields } = opening;
|
|
1059
|
+
const page = await client.listPage({
|
|
1013
1060
|
table_id: args.p_table_id,
|
|
1014
1061
|
filter: args.p_filter,
|
|
1015
1062
|
search: args.p_search,
|
|
@@ -1018,6 +1065,26 @@ async function askTablePageSeed(input) {
|
|
|
1018
1065
|
limit: args.p_limit,
|
|
1019
1066
|
offset: args.p_offset
|
|
1020
1067
|
});
|
|
1068
|
+
if (page.ok) {
|
|
1069
|
+
const chosen = shown?.presentation?.summaries ?? {};
|
|
1070
|
+
const groupKey = shown?.presentation?.grouping?.field ?? null;
|
|
1071
|
+
const reads = [];
|
|
1072
|
+
if (groupKey) {
|
|
1073
|
+
reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1e3 }));
|
|
1074
|
+
}
|
|
1075
|
+
const bar = summaryBarMeasures(chosen);
|
|
1076
|
+
if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar }));
|
|
1077
|
+
for (const field of fields) {
|
|
1078
|
+
if (field.type !== "list" && field.type !== "relation") continue;
|
|
1079
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1080
|
+
for (const row of page.data.rows) {
|
|
1081
|
+
const value = row.document?.[field.key];
|
|
1082
|
+
for (const id of Array.isArray(value) ? value.map(String) : typeof value === "string" ? [value] : []) if (isUuidShape(id)) ids.add(id);
|
|
1083
|
+
}
|
|
1084
|
+
if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field.id, record_ids: [...ids] }));
|
|
1085
|
+
}
|
|
1086
|
+
await Promise.all(reads.map((read) => read.catch(() => null)));
|
|
1087
|
+
}
|
|
1021
1088
|
}
|
|
1022
1089
|
} catch {
|
|
1023
1090
|
}
|