@ai-matrx/records-ui 0.110.5 → 0.110.6

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.
@@ -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 };
@@ -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 };
@@ -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 { PARITY_FIELD_TYPES, KERNEL_TABLES, mintOpId, fieldKindFor } from '@ai-matrx/records';
4
- import '@ai-matrx/kit/text-case';
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(field) {
19
- return fieldKindFor(field, KERNEL);
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 field = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);
47
- return field ? { id: field.key, direction } : null;
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
- function holdsNumbers(field) {
64
- const kind = String(editorKindFor(field));
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((field.config ?? {})["system"] ?? "") === "autonumber";
85
+ return String((field2.config ?? {})["system"] ?? "") === "autonumber";
67
86
  }
68
- function sortAsFor(field) {
69
- const kind = String(editorKindFor(field));
70
- if (holdsNumbers(field)) return "number";
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 field = fields.find((f) => f.key === sort.id);
77
- if (!field) return [];
78
- return [{ field: field.key, direction: sort.direction === "desc" ? "desc" : "asc", as: sortAsFor(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 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)];
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(([, op]) => op in MEASURE_LABEL);
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(([, op]) => op !== "count").map(([key, op]) => ({ op, key }))];
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, op]) => op === "count" ? { op: "count" } : { op, 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, op] of Object.entries(value["summaries"])) {
199
- if (typeof op === "string" && op.trim() !== "") summaries[key] = op;
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 field = raw["field"];
208
- if (typeof field !== "string" || field.trim() === "") return void 0;
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 || !views.ok || !looks.ok) return null;
1022
- const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);
1023
- const view = defaultViewOf(mine) ?? mine[0] ?? null;
1024
- const shown = view ? applyLook(view, looks.data[view.id]) : null;
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 field of fields) {
1078
- if (field.type !== "list" && field.type !== "relation") continue;
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?.[field.key];
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: field.id, record_ids: [...ids] }));
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