@ai-matrx/records-ui 0.110.5 → 0.110.7
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 +12 -0
- package/dist/first-page.cjs +229 -41
- package/dist/first-page.cjs.map +1 -1
- package/dist/first-page.d.cts +14 -3
- package/dist/first-page.d.ts +14 -3
- package/dist/first-page.js +230 -43
- package/dist/first-page.js.map +1 -1
- package/dist/index.cjs +34 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +34 -10
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +23 -3
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +23 -3
- package/dist/pickers.js.map +1 -1
- package/package.json +3 -3
package/dist/first-page.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Uuid, RecordFilter, RuleExpression, RecordsConfig, RecordPageSort, Table, Field } from '@ai-matrx/records';
|
|
1
|
+
import { Uuid, RecordFilter, RuleExpression, RecordsConfig, StoreFilter, RecordPageSort, Table, Field } from '@ai-matrx/records';
|
|
2
2
|
import { RecordsClient, RecordsSeed } from '@ai-matrx/records/core';
|
|
3
3
|
import { TableStyle, GroupAggregateSpec, GroupOrder } from '@ai-matrx/design-system/data-table';
|
|
4
4
|
import { FieldFormatConfig } from '@ai-matrx/design-system/field-formats';
|
|
@@ -324,13 +324,18 @@ declare const TABLE_PAGE_SIZE = 100;
|
|
|
324
324
|
declare const EMBEDDED_PAGE_SIZE = 50;
|
|
325
325
|
interface FirstGridPageArgs {
|
|
326
326
|
p_table_id: Uuid;
|
|
327
|
-
p_filter: Record<string, never>;
|
|
327
|
+
p_filter: StoreFilter | Record<string, never>;
|
|
328
328
|
p_search: null;
|
|
329
329
|
p_sort: RecordPageSort[];
|
|
330
330
|
p_view_id: null;
|
|
331
331
|
p_limit: number;
|
|
332
332
|
p_offset: 0;
|
|
333
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* THE VIEW'S OWN QUESTION, as the grid asks it (`TablePage` `shownFilter`): its Rule, else its flat filter,
|
|
336
|
+
* through the one question builder. Undefined when it asks none.
|
|
337
|
+
*/
|
|
338
|
+
declare function viewFilterOf(shown: Pick<SavedViewSpec, "where" | "filters"> | null, fields: readonly Field[]): StoreFilter | undefined;
|
|
334
339
|
/** The request for the plain opening, from what the screen itself reads — or null. Pure. */
|
|
335
340
|
declare function firstGridPageArgs(input: {
|
|
336
341
|
tableId: Uuid;
|
|
@@ -402,6 +407,12 @@ declare function askTablePageSeed(input: {
|
|
|
402
407
|
* page — each asked with the arguments the grid sends, so the browser's own call finds its answer.
|
|
403
408
|
*/
|
|
404
409
|
merged?: boolean;
|
|
410
|
+
/**
|
|
411
|
+
* THE VIEW THE HOST HANDS THE GRID (a Space's own spec: sorts, filters, `where`, grouping), for a table with
|
|
412
|
+
* no saved view behind it. The first page is built from it exactly as the grid reads it; the table's saved
|
|
413
|
+
* views and looks are not asked.
|
|
414
|
+
*/
|
|
415
|
+
view?: SavedViewSpec | undefined;
|
|
405
416
|
}): Promise<RecordsSeed>;
|
|
406
417
|
|
|
407
|
-
export { EMBEDDED_PAGE_SIZE, type FirstGridPageArgs, TABLE_PAGE_SIZE, type TileSpec, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf };
|
|
418
|
+
export { EMBEDDED_PAGE_SIZE, type FirstGridPageArgs, TABLE_PAGE_SIZE, type TileSpec, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf, viewFilterOf };
|
package/dist/first-page.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Uuid, RecordFilter, RuleExpression, RecordsConfig, RecordPageSort, Table, Field } from '@ai-matrx/records';
|
|
1
|
+
import { Uuid, RecordFilter, RuleExpression, RecordsConfig, StoreFilter, RecordPageSort, Table, Field } from '@ai-matrx/records';
|
|
2
2
|
import { RecordsClient, RecordsSeed } from '@ai-matrx/records/core';
|
|
3
3
|
import { TableStyle, GroupAggregateSpec, GroupOrder } from '@ai-matrx/design-system/data-table';
|
|
4
4
|
import { FieldFormatConfig } from '@ai-matrx/design-system/field-formats';
|
|
@@ -324,13 +324,18 @@ declare const TABLE_PAGE_SIZE = 100;
|
|
|
324
324
|
declare const EMBEDDED_PAGE_SIZE = 50;
|
|
325
325
|
interface FirstGridPageArgs {
|
|
326
326
|
p_table_id: Uuid;
|
|
327
|
-
p_filter: Record<string, never>;
|
|
327
|
+
p_filter: StoreFilter | Record<string, never>;
|
|
328
328
|
p_search: null;
|
|
329
329
|
p_sort: RecordPageSort[];
|
|
330
330
|
p_view_id: null;
|
|
331
331
|
p_limit: number;
|
|
332
332
|
p_offset: 0;
|
|
333
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* THE VIEW'S OWN QUESTION, as the grid asks it (`TablePage` `shownFilter`): its Rule, else its flat filter,
|
|
336
|
+
* through the one question builder. Undefined when it asks none.
|
|
337
|
+
*/
|
|
338
|
+
declare function viewFilterOf(shown: Pick<SavedViewSpec, "where" | "filters"> | null, fields: readonly Field[]): StoreFilter | undefined;
|
|
334
339
|
/** The request for the plain opening, from what the screen itself reads — or null. Pure. */
|
|
335
340
|
declare function firstGridPageArgs(input: {
|
|
336
341
|
tableId: Uuid;
|
|
@@ -402,6 +407,12 @@ declare function askTablePageSeed(input: {
|
|
|
402
407
|
* page — each asked with the arguments the grid sends, so the browser's own call finds its answer.
|
|
403
408
|
*/
|
|
404
409
|
merged?: boolean;
|
|
410
|
+
/**
|
|
411
|
+
* THE VIEW THE HOST HANDS THE GRID (a Space's own spec: sorts, filters, `where`, grouping), for a table with
|
|
412
|
+
* no saved view behind it. The first page is built from it exactly as the grid reads it; the table's saved
|
|
413
|
+
* views and looks are not asked.
|
|
414
|
+
*/
|
|
415
|
+
view?: SavedViewSpec | undefined;
|
|
405
416
|
}): Promise<RecordsSeed>;
|
|
406
417
|
|
|
407
|
-
export { EMBEDDED_PAGE_SIZE, type FirstGridPageArgs, TABLE_PAGE_SIZE, type TileSpec, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf };
|
|
418
|
+
export { EMBEDDED_PAGE_SIZE, type FirstGridPageArgs, TABLE_PAGE_SIZE, type TileSpec, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf, viewFilterOf };
|
package/dist/first-page.js
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { recordingDataSource, asRecordsDataSource, createRecordsClient, mapPgError } from '@ai-matrx/records/core';
|
|
2
2
|
import { isUuidShape } from '@ai-matrx/kit/uuid';
|
|
3
|
-
import {
|
|
4
|
-
import '@ai-matrx/
|
|
5
|
-
import '@ai-matrx/design-system/data-table';
|
|
3
|
+
import { humanizeIdentifier } from '@ai-matrx/kit/text-case';
|
|
4
|
+
import { PARITY_FIELD_TYPES, KERNEL_TABLES, isRuleFilter, mintOpId, fieldKindFor } from '@ai-matrx/records';
|
|
5
|
+
import { dateFilterBounds, dateFilterDayBounds, dayInstantWindow } from '@ai-matrx/design-system/data-table';
|
|
6
|
+
import 'react';
|
|
6
7
|
import { parseTableStyle, tableStyleIsEmpty } from '@ai-matrx/design-system/data-table/table-style';
|
|
7
8
|
import '@ai-matrx/design-system/field-formats';
|
|
8
9
|
import { defineChannelNamespace } from '@ai-matrx/realtime';
|
|
9
10
|
|
|
10
11
|
// src/firstPage.ts
|
|
12
|
+
function fieldName(field2) {
|
|
13
|
+
const label = (field2.label ?? "").trim();
|
|
14
|
+
if (label !== "" && label === field2.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return humanizeIdentifier(label);
|
|
15
|
+
if (label !== "") return label;
|
|
16
|
+
const name = (field2.name ?? "").trim();
|
|
17
|
+
if (name !== "") return name;
|
|
18
|
+
return humanizeIdentifier(field2.key);
|
|
19
|
+
}
|
|
11
20
|
var kernelId = (name) => KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
|
|
12
21
|
var KERNEL_FILE_TABLE = kernelId("File");
|
|
13
22
|
var KERNEL_PERSON_TABLE = kernelId("Person");
|
|
@@ -15,8 +24,8 @@ Object.fromEntries(
|
|
|
15
24
|
PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
|
|
16
25
|
);
|
|
17
26
|
var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
|
|
18
|
-
function editorKindFor(
|
|
19
|
-
return fieldKindFor(
|
|
27
|
+
function editorKindFor(field2) {
|
|
28
|
+
return fieldKindFor(field2, KERNEL);
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
// src/gridLook.ts
|
|
@@ -43,8 +52,8 @@ function defaultSortOf(table, fields) {
|
|
|
43
52
|
if (String(entry["direction"] ?? "").toLowerCase() === "desc") direction = "desc";
|
|
44
53
|
}
|
|
45
54
|
if (!name) return null;
|
|
46
|
-
const
|
|
47
|
-
return
|
|
55
|
+
const field2 = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);
|
|
56
|
+
return field2 ? { id: field2.key, direction } : null;
|
|
48
57
|
}
|
|
49
58
|
var MEASURE_LABEL = {
|
|
50
59
|
count: "Count",
|
|
@@ -60,22 +69,169 @@ var MEASURE_LABEL = {
|
|
|
60
69
|
percent_filled: "Percent filled",
|
|
61
70
|
range: "Range"
|
|
62
71
|
};
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
|
|
73
|
+
// src/gridPage.ts
|
|
74
|
+
function filterRefusal(f) {
|
|
75
|
+
if (!computedOnRead(f)) return null;
|
|
76
|
+
return `${fieldName(f)} is worked out each time it is read, so the store keeps no value to filter the whole table by. Filter by a column it is worked out from.`;
|
|
77
|
+
}
|
|
78
|
+
function computedOnRead(f) {
|
|
79
|
+
const kind = String(editorKindFor(f));
|
|
80
|
+
return (f.type === "formula" || kind === "formula" || kind === "lookup" || kind === "rollup") && String(f.compute_on ?? "read") === "read";
|
|
81
|
+
}
|
|
82
|
+
function holdsNumbers(field2) {
|
|
83
|
+
const kind = String(editorKindFor(field2));
|
|
65
84
|
if (kind === "number" || kind === "currency" || kind === "percent") return true;
|
|
66
|
-
return String((
|
|
85
|
+
return String((field2.config ?? {})["system"] ?? "") === "autonumber";
|
|
67
86
|
}
|
|
68
|
-
function sortAsFor(
|
|
69
|
-
const kind = String(editorKindFor(
|
|
70
|
-
if (holdsNumbers(
|
|
87
|
+
function sortAsFor(field2) {
|
|
88
|
+
const kind = String(editorKindFor(field2));
|
|
89
|
+
if (holdsNumbers(field2)) return "number";
|
|
71
90
|
if (kind === "datetime") return "date";
|
|
72
91
|
return "text";
|
|
73
92
|
}
|
|
74
93
|
function pageSortFor(fields, sort) {
|
|
75
94
|
if (!sort) return [];
|
|
76
|
-
const
|
|
77
|
-
if (!
|
|
78
|
-
return [{ field:
|
|
95
|
+
const field2 = fields.find((f) => f.key === sort.id);
|
|
96
|
+
if (!field2) return [];
|
|
97
|
+
return [{ field: field2.key, direction: sort.direction === "desc" ? "desc" : "asc", as: sortAsFor(field2) }];
|
|
98
|
+
}
|
|
99
|
+
function containsPattern(text) {
|
|
100
|
+
return `(?i)${text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`;
|
|
101
|
+
}
|
|
102
|
+
var BLANK_VALUE = "__empty__";
|
|
103
|
+
var field = (f) => ({ field: f.id });
|
|
104
|
+
var constant = (value) => ({ const: value });
|
|
105
|
+
var withinWindow = (f, window) => ({ op: "within", args: [field(f), constant(window)] });
|
|
106
|
+
var op = (name, ...args) => ({ op: name, args });
|
|
107
|
+
function clauseFor(f, value, now = /* @__PURE__ */ new Date()) {
|
|
108
|
+
const name = fieldName(f);
|
|
109
|
+
const refused = filterRefusal(f);
|
|
110
|
+
if (refused) return refused;
|
|
111
|
+
const not = (rule) => op("not", rule);
|
|
112
|
+
switch (value.kind) {
|
|
113
|
+
case "text": {
|
|
114
|
+
const mode = value.mode ?? "contains";
|
|
115
|
+
if (mode === "empty" || mode === "null") return value.negated ? { rule: op("present", field(f)) } : { rule: not(op("present", field(f))) };
|
|
116
|
+
if (mode === "not_empty" || mode === "not_null") return value.negated ? { rule: not(op("present", field(f))) } : { rule: op("present", field(f)) };
|
|
117
|
+
const text = value.value.trim();
|
|
118
|
+
if (text === "") return null;
|
|
119
|
+
const kind = String(editorKindFor(f));
|
|
120
|
+
if (kind === "number" || kind === "currency" || kind === "percent" || kind === "checkbox") {
|
|
121
|
+
return `${name} holds numbers, so it is filtered by a range, not by words.`;
|
|
122
|
+
}
|
|
123
|
+
const matches = op("matches", field(f), constant(containsPattern(text)));
|
|
124
|
+
return { rule: value.negated ? not(matches) : matches };
|
|
125
|
+
}
|
|
126
|
+
case "select": {
|
|
127
|
+
const ticked = value.values ?? (value.value === "" ? [] : [value.value]);
|
|
128
|
+
const blank = ticked.includes(BLANK_VALUE);
|
|
129
|
+
const words = ticked.filter((w) => w !== BLANK_VALUE);
|
|
130
|
+
if (!blank && words.length === 0) return value.negated ? null : { rule: constant(false) };
|
|
131
|
+
const present = op("present", field(f));
|
|
132
|
+
const kind = String(editorKindFor(f));
|
|
133
|
+
if (kind === "datetime" || kind === "date") {
|
|
134
|
+
if (words.length === 0) return { rule: value.negated ? present : not(present) };
|
|
135
|
+
const holdsDays = String((f.config ?? {})["kind"] ?? "") === "date";
|
|
136
|
+
if (words.length === 1 && !blank && !value.negated) return { flat: { [f.key]: dayWindow(words[0], holdsDays) } };
|
|
137
|
+
const leaves = words.map((day) => withinWindow(f, dayWindow(day, holdsDays)));
|
|
138
|
+
const anyDay = leaves.length === 1 ? leaves[0] : op("or", ...leaves);
|
|
139
|
+
if (!value.negated) return { rule: blank ? op("or", not(present), anyDay) : anyDay };
|
|
140
|
+
return { rule: blank ? op("and", present, not(anyDay)) : not(anyDay) };
|
|
141
|
+
}
|
|
142
|
+
const asStored = (w) => holdsNumbers(f) || kind === "rating" ? Number.isFinite(Number(w)) ? Number(w) : w : kind === "checkbox" && (w === "true" || w === "false") ? w === "true" : w;
|
|
143
|
+
const stored = words.map(asStored);
|
|
144
|
+
const anyWord = stored.length === 0 ? null : stored.length === 1 ? op("eq", field(f), constant(stored[0])) : op("or", ...stored.map((w) => op("eq", field(f), constant(w))));
|
|
145
|
+
if (!value.negated) {
|
|
146
|
+
if (!blank && stored.length === 1) return { flat: { [f.key]: stored[0] } };
|
|
147
|
+
if (!blank) return { rule: anyWord };
|
|
148
|
+
return { rule: anyWord ? op("or", not(present), anyWord) : not(present) };
|
|
149
|
+
}
|
|
150
|
+
if (blank) return { rule: anyWord ? op("and", present, not(anyWord)) : present };
|
|
151
|
+
return { rule: op("or", not(present), not(anyWord)) };
|
|
152
|
+
}
|
|
153
|
+
case "boolean":
|
|
154
|
+
return value.negated ? { rule: op("ne", field(f), constant(value.value)) } : { flat: { [f.key]: value.value } };
|
|
155
|
+
case "number": {
|
|
156
|
+
const turned = (rule) => ({ rule: value.negated ? not(rule) : rule });
|
|
157
|
+
if (value.op === "empty") return turned(not(op("present", field(f))));
|
|
158
|
+
if (value.op === "not_empty") return turned(op("present", field(f)));
|
|
159
|
+
if (value.op === "eq" || value.op === "ne") {
|
|
160
|
+
if (typeof value.min !== "number") return null;
|
|
161
|
+
return turned(op(value.op, field(f), constant(value.min)));
|
|
162
|
+
}
|
|
163
|
+
if (value.op === "lt") return typeof value.max === "number" ? turned(op("lt", field(f), constant(value.max))) : null;
|
|
164
|
+
if (value.op === "gt") return typeof value.min === "number" ? turned(op("gt", field(f), constant(value.min))) : null;
|
|
165
|
+
const parts = [];
|
|
166
|
+
if (typeof value.min === "number") parts.push(op("gte", field(f), constant(value.min)));
|
|
167
|
+
if (typeof value.max === "number") parts.push(op("lte", field(f), constant(value.max)));
|
|
168
|
+
if (parts.length === 0) return null;
|
|
169
|
+
const range = parts.length === 1 ? parts[0] : op("and", ...parts);
|
|
170
|
+
return { rule: value.negated ? not(range) : range };
|
|
171
|
+
}
|
|
172
|
+
case "date": {
|
|
173
|
+
const zone2 = void 0;
|
|
174
|
+
const moments = dateFilterBounds(value.preset ? { ...value, anchor: now.toISOString() } : value, now, zone2);
|
|
175
|
+
const holdsDays = String((f.config ?? {})["kind"] ?? "") === "date";
|
|
176
|
+
const bounds = holdsDays ? dateFilterDayBounds(moments, zone2) : moments;
|
|
177
|
+
if (bounds.since === void 0 && bounds.until === void 0) return null;
|
|
178
|
+
return {
|
|
179
|
+
flat: {
|
|
180
|
+
[f.key]: {
|
|
181
|
+
...bounds.since === void 0 ? {} : { from: bounds.since },
|
|
182
|
+
...bounds.until === void 0 ? {} : { to: bounds.until }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
default:
|
|
188
|
+
return `${name} cannot be filtered that way by the store.`;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function dayWindow(day, holdsDays) {
|
|
192
|
+
if (!holdsDays) return dayInstantWindow(day, void 0);
|
|
193
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
194
|
+
const ymd = (t) => `${t.getUTCFullYear()}-${String(t.getUTCMonth() + 1).padStart(2, "0")}-${String(t.getUTCDate()).padStart(2, "0")}`;
|
|
195
|
+
return { from: ymd(new Date(Date.UTC(y, (m ?? 1) - 1, d ?? 1))), to: ymd(new Date(Date.UTC(y, (m ?? 1) - 1, (d ?? 1) + 1))) };
|
|
196
|
+
}
|
|
197
|
+
function storeQuestion(fields, base, columnFilters, now = /* @__PURE__ */ new Date()) {
|
|
198
|
+
const refused = [];
|
|
199
|
+
const flat = {};
|
|
200
|
+
const rules = [];
|
|
201
|
+
if (base && isRuleFilter(base)) rules.push(base);
|
|
202
|
+
else if (base) Object.assign(flat, base);
|
|
203
|
+
for (const [key, value] of Object.entries(columnFilters)) {
|
|
204
|
+
if (!value) continue;
|
|
205
|
+
const f = fields.find((candidate) => candidate.key === key);
|
|
206
|
+
if (!f) continue;
|
|
207
|
+
const clause = clauseFor(f, value, now);
|
|
208
|
+
if (clause === null) continue;
|
|
209
|
+
if (typeof clause === "string") {
|
|
210
|
+
refused.push(clause);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if ("flat" in clause) Object.assign(flat, clause.flat);
|
|
214
|
+
else rules.push(clause.rule);
|
|
215
|
+
}
|
|
216
|
+
const flatKeys = Object.keys(flat);
|
|
217
|
+
if (rules.length === 0) return { filter: flatKeys.length > 0 ? flat : void 0, refused };
|
|
218
|
+
for (const key of flatKeys) {
|
|
219
|
+
const f = fields.find((candidate) => candidate.key === key);
|
|
220
|
+
const value = flat[key];
|
|
221
|
+
if (!f) continue;
|
|
222
|
+
if (value !== null && typeof value === "object") {
|
|
223
|
+
rules.push(withinWindow(f, value));
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
rules.push(value === null ? op("not", op("present", field(f))) : op("eq", field(f), constant(value)));
|
|
227
|
+
}
|
|
228
|
+
return { filter: rules.length === 1 ? rules[0] : op("and", ...rules), refused };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/rowVersion.ts
|
|
232
|
+
function ownVersion(row) {
|
|
233
|
+
const v = row.version;
|
|
234
|
+
return typeof v === "number" ? v : null;
|
|
79
235
|
}
|
|
80
236
|
|
|
81
237
|
// src/groupReads.ts
|
|
@@ -84,18 +240,24 @@ function isPlainGrouping(grouping) {
|
|
|
84
240
|
}
|
|
85
241
|
function groupedPageSort(fields, groupKey, sort) {
|
|
86
242
|
if (!groupKey) return sort;
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
89
|
-
return [{ field: groupKey, direction: "asc", as: sortAsFor(
|
|
243
|
+
const field2 = fields.find((f) => f.key === groupKey);
|
|
244
|
+
if (!field2) return sort;
|
|
245
|
+
return [{ field: groupKey, direction: "asc", as: sortAsFor(field2) }, ...sort.filter((s) => s.field !== groupKey)];
|
|
90
246
|
}
|
|
91
247
|
function chosenEntries(chosen) {
|
|
92
|
-
return JSON.parse(JSON.stringify(Object.entries(chosen).sort())).filter(([,
|
|
248
|
+
return JSON.parse(JSON.stringify(Object.entries(chosen).sort())).filter(([, op2]) => op2 in MEASURE_LABEL);
|
|
93
249
|
}
|
|
94
250
|
function groupFactMeasures(chosen) {
|
|
95
|
-
return [{ op: "count" }, ...chosenEntries(chosen).filter(([,
|
|
251
|
+
return [{ op: "count" }, ...chosenEntries(chosen).filter(([, op2]) => op2 !== "count").map(([key, op2]) => ({ op: op2, key }))];
|
|
96
252
|
}
|
|
97
253
|
function summaryBarMeasures(chosen) {
|
|
98
|
-
return chosenEntries(chosen).map(([key,
|
|
254
|
+
return chosenEntries(chosen).map(([key, op2]) => op2 === "count" ? { op: "count" } : { op: op2, key });
|
|
255
|
+
}
|
|
256
|
+
function choiceFieldKeys(fields) {
|
|
257
|
+
return fields.filter((f) => {
|
|
258
|
+
const kind = String(editorKindFor(f));
|
|
259
|
+
return kind === "select" || kind === "multi_select";
|
|
260
|
+
}).map((f) => f.key);
|
|
99
261
|
}
|
|
100
262
|
var MIN_ROW_HEIGHT = 24;
|
|
101
263
|
var MAX_ROW_HEIGHT = 96;
|
|
@@ -195,8 +357,8 @@ function parseGridPresentation(raw) {
|
|
|
195
357
|
if (footer === "sticky" || footer === "inline") out.footer = footer;
|
|
196
358
|
if (isRecord(value["summaries"])) {
|
|
197
359
|
const summaries = {};
|
|
198
|
-
for (const [key,
|
|
199
|
-
if (typeof
|
|
360
|
+
for (const [key, op2] of Object.entries(value["summaries"])) {
|
|
361
|
+
if (typeof op2 === "string" && op2.trim() !== "") summaries[key] = op2;
|
|
200
362
|
}
|
|
201
363
|
if (Object.keys(summaries).length > 0) out.summaries = summaries;
|
|
202
364
|
}
|
|
@@ -204,9 +366,9 @@ function parseGridPresentation(raw) {
|
|
|
204
366
|
}
|
|
205
367
|
function parseGrouping(raw) {
|
|
206
368
|
if (!isRecord(raw)) return void 0;
|
|
207
|
-
const
|
|
208
|
-
if (typeof
|
|
209
|
-
const out = { field };
|
|
369
|
+
const field2 = raw["field"];
|
|
370
|
+
if (typeof field2 !== "string" || field2.trim() === "") return void 0;
|
|
371
|
+
const out = { field: field2 };
|
|
210
372
|
if (isRecord(raw["aggregates"])) {
|
|
211
373
|
const aggregates = {};
|
|
212
374
|
for (const [key, spec] of Object.entries(raw["aggregates"])) {
|
|
@@ -986,13 +1148,17 @@ async function askTileSeed(input) {
|
|
|
986
1148
|
// src/firstPage.ts
|
|
987
1149
|
var TABLE_PAGE_SIZE = 100;
|
|
988
1150
|
var EMBEDDED_PAGE_SIZE = 50;
|
|
1151
|
+
function viewFilterOf(shown, fields) {
|
|
1152
|
+
if (!shown) return void 0;
|
|
1153
|
+
const base = shown.where ?? (shown.filters && Object.keys(shown.filters).length > 0 ? shown.filters : void 0);
|
|
1154
|
+
return storeQuestion(fields, base, {}).filter;
|
|
1155
|
+
}
|
|
989
1156
|
function firstGridPageArgs(input) {
|
|
990
1157
|
const { shown, fields } = input;
|
|
991
1158
|
if (!shown) return null;
|
|
992
1159
|
if ((shown.layout ?? "grid") !== "grid") return null;
|
|
993
1160
|
if (shown.ruleId) return null;
|
|
994
1161
|
if (shown.order === "manual") return null;
|
|
995
|
-
if (shown.filters && Object.keys(shown.filters).length > 0) return null;
|
|
996
1162
|
const grouping = shown.presentation?.grouping;
|
|
997
1163
|
if (grouping && !(input.merged && isPlainGrouping(grouping))) return null;
|
|
998
1164
|
const first = (shown.sorts ?? [])[0];
|
|
@@ -1000,7 +1166,7 @@ function firstGridPageArgs(input) {
|
|
|
1000
1166
|
const opening = viewSort ?? defaultSortOf(input.table, fields);
|
|
1001
1167
|
return {
|
|
1002
1168
|
p_table_id: input.tableId,
|
|
1003
|
-
p_filter: {},
|
|
1169
|
+
p_filter: viewFilterOf(shown, fields) ?? {},
|
|
1004
1170
|
p_search: null,
|
|
1005
1171
|
p_sort: groupedPageSort(fields, grouping?.field ?? null, pageSortFor(fields, opening)),
|
|
1006
1172
|
p_view_id: null,
|
|
@@ -1011,17 +1177,30 @@ function firstGridPageArgs(input) {
|
|
|
1011
1177
|
async function firstGridPageRequest(client, tableId, options = {}) {
|
|
1012
1178
|
return (await firstGridOpening(client, tableId, options))?.args ?? null;
|
|
1013
1179
|
}
|
|
1180
|
+
function defaultSpec(tableId) {
|
|
1181
|
+
return { name: "All", subject: tableId, layout: "grid", isDefault: true };
|
|
1182
|
+
}
|
|
1183
|
+
function shownFromSpec(spec, tableId) {
|
|
1184
|
+
return { ...spec, subject: spec.subject ?? tableId, id: "", movedFrom: null };
|
|
1185
|
+
}
|
|
1014
1186
|
async function firstGridOpening(client, tableId, options) {
|
|
1187
|
+
const explicit = options.view;
|
|
1015
1188
|
const [table, fields, views, looks] = await Promise.all([
|
|
1016
1189
|
client.tableRead({ table_id: tableId }),
|
|
1017
1190
|
client.fields({ table_id: tableId }),
|
|
1018
|
-
client.views({ table_id: tableId }),
|
|
1019
|
-
readLooks(client, tableId)
|
|
1191
|
+
explicit ? Promise.resolve(null) : client.views({ table_id: tableId }),
|
|
1192
|
+
explicit ? Promise.resolve(null) : readLooks(client, tableId)
|
|
1020
1193
|
]);
|
|
1021
|
-
if (!table.ok || !fields.ok
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1194
|
+
if (!table.ok || !fields.ok) return null;
|
|
1195
|
+
let shown;
|
|
1196
|
+
if (explicit) {
|
|
1197
|
+
shown = shownFromSpec(explicit, tableId);
|
|
1198
|
+
} else {
|
|
1199
|
+
if (!views?.ok || !looks?.ok) return null;
|
|
1200
|
+
const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);
|
|
1201
|
+
const view = defaultViewOf(mine) ?? mine[0] ?? null;
|
|
1202
|
+
shown = view ? applyLook(view, looks.data[view.id]) : shownFromSpec(defaultSpec(tableId), tableId);
|
|
1203
|
+
}
|
|
1025
1204
|
const args = firstGridPageArgs({
|
|
1026
1205
|
tableId,
|
|
1027
1206
|
table: table.data,
|
|
@@ -1052,7 +1231,7 @@ async function askTablePageSeed(input) {
|
|
|
1052
1231
|
).catch(() => null) : null;
|
|
1053
1232
|
try {
|
|
1054
1233
|
const pageSize = input.pageSize ?? (input.embedded ? EMBEDDED_PAGE_SIZE : TABLE_PAGE_SIZE);
|
|
1055
|
-
const opening = await firstGridOpening(client, input.tableId, { pageSize, ...input.merged ? { merged: true } : {} });
|
|
1234
|
+
const opening = await firstGridOpening(client, input.tableId, { pageSize, ...input.merged ? { merged: true } : {}, ...input.view ? { view: input.view } : {} });
|
|
1056
1235
|
const wantRows = typeof input.rows === "function" ? input.rows(recording.seed()) : input.rows !== false;
|
|
1057
1236
|
if (opening && wantRows) {
|
|
1058
1237
|
const { args, shown, fields } = opening;
|
|
@@ -1069,20 +1248,28 @@ async function askTablePageSeed(input) {
|
|
|
1069
1248
|
const chosen = shown?.presentation?.summaries ?? {};
|
|
1070
1249
|
const groupKey = shown?.presentation?.grouping?.field ?? null;
|
|
1071
1250
|
const reads = [];
|
|
1251
|
+
const filter = args.p_filter && Object.keys(args.p_filter).length > 0 ? args.p_filter : void 0;
|
|
1072
1252
|
if (groupKey) {
|
|
1073
|
-
reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1e3 }));
|
|
1253
|
+
reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1e3, ...filter ? { filter } : {} }));
|
|
1074
1254
|
}
|
|
1075
1255
|
const bar = summaryBarMeasures(chosen);
|
|
1076
|
-
if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar }));
|
|
1077
|
-
for (const
|
|
1078
|
-
if (
|
|
1256
|
+
if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar, ...filter ? { filter } : {} }));
|
|
1257
|
+
for (const field2 of fields) {
|
|
1258
|
+
if (field2.type !== "list" && field2.type !== "relation") continue;
|
|
1079
1259
|
const ids = /* @__PURE__ */ new Set();
|
|
1080
1260
|
for (const row of page.data.rows) {
|
|
1081
|
-
const value = row.document?.[
|
|
1261
|
+
const value = row.document?.[field2.key];
|
|
1082
1262
|
for (const id of Array.isArray(value) ? value.map(String) : typeof value === "string" ? [value] : []) if (isUuidShape(id)) ids.add(id);
|
|
1083
1263
|
}
|
|
1084
|
-
if (ids.size > 0) reads.push(client.relationWordsMany({ field_id:
|
|
1264
|
+
if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field2.id, record_ids: [...ids] }));
|
|
1265
|
+
}
|
|
1266
|
+
if (input.merged) {
|
|
1267
|
+
for (const key of choiceFieldKeys(fields)) {
|
|
1268
|
+
reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [key], measures: [{ op: "count" }], limit: 500, ...filter ? { filter } : {} }));
|
|
1269
|
+
}
|
|
1085
1270
|
}
|
|
1271
|
+
const unversioned = page.data.rows.filter((row) => ownVersion(row) === null).map((row) => row.id);
|
|
1272
|
+
if (unversioned.length > 0) reads.push(client.recordHeaders({ ids: unversioned }));
|
|
1086
1273
|
await Promise.all(reads.map((read) => read.catch(() => null)));
|
|
1087
1274
|
}
|
|
1088
1275
|
}
|
|
@@ -1092,6 +1279,6 @@ async function askTablePageSeed(input) {
|
|
|
1092
1279
|
return recording.seed();
|
|
1093
1280
|
}
|
|
1094
1281
|
|
|
1095
|
-
export { EMBEDDED_PAGE_SIZE, TABLE_PAGE_SIZE, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf };
|
|
1282
|
+
export { EMBEDDED_PAGE_SIZE, TABLE_PAGE_SIZE, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf, viewFilterOf };
|
|
1096
1283
|
//# sourceMappingURL=first-page.js.map
|
|
1097
1284
|
//# sourceMappingURL=first-page.js.map
|