@ai-matrx/records-ui 0.113.30 → 0.113.32

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,265 +1,12 @@
1
- import { recordingDataSource, asRecordsDataSource, createRecordsClient } from '@ai-matrx/records/core';
2
- import { isUuidShape } from '@ai-matrx/kit/uuid';
3
- import { humanizeIdentifier } from '@ai-matrx/kit/text-case';
4
- import { PARITY_FIELD_TYPES, KERNEL_TABLES, isRuleFilter, fieldKindFor } from '@ai-matrx/records';
5
- import { dateFilterBounds, dateFilterDayBounds, dayInstantWindow } from '@ai-matrx/design-system/data-table';
6
- import 'react';
7
- import { parseTableStyle, tableStyleIsEmpty } from '@ai-matrx/design-system/data-table/table-style';
1
+ import { bindFirstPageCodec } from '@ai-matrx/records/first-page';
2
+ export { EMBEDDED_PAGE_SIZE, TABLE_PAGE_SIZE, askChartSeed, askEntityBlockSeed, askTileSeed, firstGridPageArgs, mergeSeeds, serverRowsOf, viewFilterOf } from '@ai-matrx/records/first-page';
3
+ import * as base from '@ai-matrx/records/views';
4
+ import { tableStyleIsEmpty, parseTableStyle } from '@ai-matrx/design-system/data-table/table-style';
5
+ import '@ai-matrx/design-system/data-table';
8
6
  import '@ai-matrx/design-system/field-formats';
9
- import { createSeedPort } from '@ai-matrx/entity-data/port';
7
+ import '@ai-matrx/records';
10
8
 
11
9
  // 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
- }
20
- var kernelId = (name) => KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
21
- var KERNEL_FILE_TABLE = kernelId("File");
22
- var KERNEL_PERSON_TABLE = kernelId("Person");
23
- Object.fromEntries(
24
- PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
25
- );
26
- var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
27
- function editorKindFor(field2) {
28
- return fieldKindFor(field2, KERNEL);
29
- }
30
-
31
- // src/gridLook.ts
32
- function defaultSortOf(table, fields) {
33
- const raw = table?.default_sort ?? null;
34
- const first = Array.isArray(raw) ? raw[0] : raw;
35
- let name = null;
36
- let direction = "asc";
37
- if (typeof first === "string") {
38
- const trimmed = first.trim();
39
- if (trimmed === "") return null;
40
- if (trimmed.startsWith("-")) {
41
- name = trimmed.slice(1).trim();
42
- direction = "desc";
43
- } else {
44
- const [head, tail] = trimmed.split(/\s+/);
45
- name = head ?? null;
46
- if (tail && tail.toLowerCase() === "desc") direction = "desc";
47
- }
48
- } else if (first && typeof first === "object") {
49
- const entry = first;
50
- const named = entry["field"] ?? entry["key"] ?? entry["id"];
51
- name = typeof named === "string" ? named : null;
52
- if (String(entry["direction"] ?? "").toLowerCase() === "desc") direction = "desc";
53
- }
54
- if (!name) return null;
55
- const field2 = fields.find((f) => f.key === name) ?? fields.find((f) => f.id === name);
56
- return field2 ? { id: field2.key, direction } : null;
57
- }
58
- var MEASURE_LABEL = {
59
- count: "Count",
60
- sum: "Sum",
61
- avg: "Average",
62
- min: "Smallest",
63
- max: "Largest",
64
- median: "Median",
65
- filled: "Filled",
66
- empty: "Empty",
67
- unique: "Unique",
68
- percent_empty: "Percent empty",
69
- percent_filled: "Percent filled",
70
- range: "Range"
71
- };
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));
84
- if (kind === "number" || kind === "currency" || kind === "percent") return true;
85
- return String((field2.config ?? {})["system"] ?? "") === "autonumber";
86
- }
87
- function sortAsFor(field2) {
88
- const kind = String(editorKindFor(field2));
89
- if (holdsNumbers(field2)) return "number";
90
- if (kind === "datetime") return "date";
91
- return "text";
92
- }
93
- function pageSortFor(fields, sort) {
94
- if (!sort) return [];
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;
235
- }
236
-
237
- // src/groupReads.ts
238
- function isPlainGrouping(grouping) {
239
- return Boolean(grouping) && !(grouping.then && grouping.then.length > 0) && !grouping.across && !(grouping.trail && grouping.trail.length > 0);
240
- }
241
- function groupedPageSort(fields, groupKey, sort) {
242
- if (!groupKey) return sort;
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)];
246
- }
247
- function chosenEntries(chosen) {
248
- return JSON.parse(JSON.stringify(Object.entries(chosen).sort())).filter(([, op2]) => op2 in MEASURE_LABEL);
249
- }
250
- function groupFactMeasures(chosen) {
251
- return [{ op: "count" }, ...chosenEntries(chosen).filter(([, op2]) => op2 !== "count").map(([key, op2]) => ({ op: op2, key }))];
252
- }
253
- function summaryBarMeasures(chosen) {
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);
261
- }
262
- var REVERSE_CELL_CHIPS = 3;
263
10
  var MIN_ROW_HEIGHT = 24;
264
11
  var MAX_ROW_HEIGHT = 96;
265
12
  var AGGREGATE_KINDS = [
@@ -358,8 +105,8 @@ function parseGridPresentation(raw) {
358
105
  if (footer === "sticky" || footer === "inline") out.footer = footer;
359
106
  if (isRecord(value["summaries"])) {
360
107
  const summaries = {};
361
- for (const [key, op2] of Object.entries(value["summaries"])) {
362
- if (typeof op2 === "string" && op2.trim() !== "") summaries[key] = op2;
108
+ for (const [key, op] of Object.entries(value["summaries"])) {
109
+ if (typeof op === "string" && op.trim() !== "") summaries[key] = op;
363
110
  }
364
111
  if (Object.keys(summaries).length > 0) out.summaries = summaries;
365
112
  }
@@ -367,9 +114,9 @@ function parseGridPresentation(raw) {
367
114
  }
368
115
  function parseGrouping(raw) {
369
116
  if (!isRecord(raw)) return void 0;
370
- const field2 = raw["field"];
371
- if (typeof field2 !== "string" || field2.trim() === "") return void 0;
372
- const out = { field: field2 };
117
+ const field = raw["field"];
118
+ if (typeof field !== "string" || field.trim() === "") return void 0;
119
+ const out = { field };
373
120
  if (isRecord(raw["aggregates"])) {
374
121
  const aggregates = {};
375
122
  for (const [key, spec] of Object.entries(raw["aggregates"])) {
@@ -427,328 +174,23 @@ function parseGrouping(raw) {
427
174
  }
428
175
  return out;
429
176
  }
430
- var STORE_KEPT_LAYOUTS = ["grid", "kanban", "calendar", "gallery", ...["timeline"] , "list"];
431
- var KINDS = STORE_KEPT_LAYOUTS;
432
- function viewSettings(definition, rowFilters) {
433
- const data = definition ?? {};
434
- const text = (key) => {
435
- const v = data[key];
436
- return typeof v === "string" && v !== "" ? v : null;
437
- };
438
- const layout = String(data["layout"] ?? "grid");
439
- const flat = rowFilters ?? (isObject(data["filters"]) ? data["filters"] : null);
440
- const moved = data["moved_from"];
441
- return {
442
- layout: KINDS.includes(layout) ? layout : "grid",
443
- filters: flat && Object.keys(flat).length > 0 ? flat : null,
444
- where: isRule(data["where"]) ? data["where"] : null,
445
- groupField: text("group_field"),
446
- swimlaneField: text("swimlane_field"),
447
- collapsedColumns: Array.isArray(data["collapsed_columns"]) ? data["collapsed_columns"].filter((v) => ["string", "number", "boolean"].includes(typeof v)).map(String) : [],
448
- measure: text("measure"),
449
- dateField: text("date_field"),
450
- startField: text("start_field"),
451
- endField: text("end_field"),
452
- imageField: text("image_field"),
453
- sorts: parseSorts(data["sorts"]),
454
- ruleId: text("rule_id"),
455
- isDefault: data["is_default"] === true || data["is_default"] === "true",
456
- presentation: parseGridPresentation(data["presentation"]),
457
- movedFrom: isObject(moved) ? moved : null
458
- };
177
+ function presentationIsEmpty(p) {
178
+ return (p.style === void 0 || tableStyleIsEmpty(p.style)) && Object.keys(p.formats ?? {}).length === 0 && (p.frozen?.length ?? 0) === 0 && p.grouping === void 0 && Object.keys(p.widths ?? {}).length === 0 && p.rowHeight === void 0 && (p.hiddenFields?.length ?? 0) === 0 && p.cardHidden === void 0 && (p.columnOrder?.length ?? 0) === 0 && p.gallerySize === void 0 && p.wrap === void 0 && p.fit === void 0 && p.freezeFirst === void 0 && p.footer === void 0 && Object.keys(p.summaries ?? {}).length === 0;
459
179
  }
460
- function isObject(raw) {
461
- return Boolean(raw) && typeof raw === "object" && !Array.isArray(raw);
462
- }
463
- function isRule(raw) {
464
- if (!isObject(raw)) return false;
465
- const node = raw;
466
- return typeof node.op === "string" && Array.isArray(node.args);
467
- }
468
- function parseSorts(raw) {
469
- let list = raw;
470
- if (typeof raw === "string") {
471
- try {
472
- list = raw.trim() === "" ? [] : JSON.parse(raw);
473
- } catch {
474
- return [];
475
- }
476
- }
477
- if (!Array.isArray(list)) return [];
478
- return list.filter((s) => isObject(s) && typeof s.field === "string").map((s) => ({ field: s.field, direction: s.direction === "desc" ? "desc" : "asc" }));
479
- }
480
-
481
- // src/views.ts
482
- var VIEW_LAYOUTS = ["grid", "kanban", "calendar", "gallery", "timeline", "list"];
483
- function viewOrderOf(definition) {
484
- const raw = definition?.["order"];
485
- return raw === "manual" ? "manual" : raw === "sorted" ? "sorted" : null;
486
- }
487
- function defaultViewOf(views) {
488
- return views.find((v) => v.isDefault);
489
- }
490
- function viewFromRow(row) {
491
- const data = row.definition ?? {};
492
- const k = viewSettings(data, row.filters && Object.keys(row.filters).length > 0 ? row.filters : null);
493
- const tableFromDefinition = typeof data["table_id"] === "string" && data["table_id"] !== "" ? data["table_id"] : null;
494
- return {
495
- id: row.view_id,
496
- name: row.name || "Untitled view",
497
- subject: row.table_id ?? tableFromDefinition ?? "",
498
- layout: k.layout,
499
- groupField: k.groupField,
500
- swimlaneField: k.swimlaneField,
501
- collapsedColumns: k.collapsedColumns,
502
- measure: k.measure,
503
- dateField: k.dateField,
504
- startField: k.startField,
505
- endField: k.endField,
506
- imageField: k.imageField,
507
- sorts: k.sorts,
508
- order: viewOrderOf(data),
509
- ruleId: k.ruleId,
510
- ...k.filters ? { filters: k.filters } : {},
511
- where: k.where,
512
- // The older table's own default view, carried in by the mover, IS this table's default —
513
- // the mover wrote the row's column, which `custom.views` does not return. Read as anything
514
- // else, the page would seed a second "All records" beside it and open on that.
515
- isDefault: k.isDefault || k.movedFrom?.["kind"] === "default_view",
516
- presentation: k.presentation,
517
- movedFrom: k.movedFrom
518
- };
519
- }
520
-
521
- // src/viewLook.ts
522
- function lookFromDocument(doc) {
523
- const data = doc ?? {};
524
- const read = viewSettings(data);
525
- const out = {};
526
- const has = (key) => Object.prototype.hasOwnProperty.call(data, key);
527
- if (has("layout") && VIEW_LAYOUTS.includes(String(data["layout"]))) {
528
- out.layout = data["layout"];
529
- }
530
- if (has("group_field")) out.groupField = read.groupField;
531
- if (has("swimlane_field")) out.swimlaneField = read.swimlaneField;
532
- if (has("collapsed_columns")) out.collapsedColumns = read.collapsedColumns;
533
- if (has("measure")) out.measure = read.measure;
534
- if (has("date_field")) out.dateField = read.dateField;
535
- if (has("start_field")) out.startField = read.startField;
536
- if (has("end_field")) out.endField = read.endField;
537
- if (has("image_field")) out.imageField = read.imageField;
538
- if (has("sorts")) out.sorts = read.sorts;
539
- if (has("where")) out.where = read.where;
540
- if (has("presentation")) out.presentation = parseGridPresentation(data["presentation"]);
541
- return out;
542
- }
543
- function applyLook(view, look) {
544
- if (!look) return view;
545
- return { ...view, ...look };
546
- }
547
- async function readLooks(client, tableId) {
548
- const answered = await client.viewLookRead({ table_id: tableId });
549
- if (!answered.ok) return answered;
550
- const out = {};
551
- for (const [viewId, row] of Object.entries(answered.data ?? {})) {
552
- out[viewId] = lookFromDocument(row?.look ?? null);
553
- }
554
- return { ok: true, data: out };
555
- }
556
-
557
- // src/entityQuery.ts
558
- function entityTableQuery(options = {}) {
559
- const search = (options.search ?? "").trim();
560
- return { ...options.pageSize ? { pageSize: options.pageSize } : {}, ...search ? { search } : {} };
561
- }
562
- function entityChartQuestion(input) {
563
- const m = input.measure ?? { op: "count" };
564
- return {
565
- ...input.by ? { by: [input.by] } : {},
566
- show: [m.op === "count" ? "count" : { op: m.op, of: m.of }],
567
- ...input.where ? { where: input.where } : {},
568
- ...input.organization ? { organization: input.organization } : {}
569
- };
570
- }
571
-
572
- // src/firstPageEntity.ts
573
- function recordedClient(input) {
574
- const recording = recordingDataSource(asRecordsDataSource(input.dataSource));
575
- const client = createRecordsClient({ dataSource: recording.source, organizationId: input.organizationId, actor: input.actor, onError: () => {
576
- } });
577
- return { recording, client };
578
- }
579
- function mergeSeeds(...seeds) {
580
- const held = seeds.filter((s) => Boolean(s));
581
- const answers = /* @__PURE__ */ new Map();
582
- for (const seed of held) for (const a of seed.answers) answers.set(`${a.door}|${JSON.stringify(a.args)}`, a);
583
- return { at: held.length ? Math.min(...held.map((s) => s.at)) : Date.now(), answers: [...answers.values()] };
584
- }
585
- async function askEntityBlockSeed(input) {
586
- const recording = recordingDataSource(asRecordsDataSource(input.dataSource));
587
- const port = createSeedPort(recording.source);
588
- try {
589
- await Promise.all([port.describe(input.token), port.list(input.token, entityTableQuery(input.question ?? {}))]);
590
- } catch {
591
- }
592
- return recording.seed();
593
- }
594
- async function askChartSeed(input) {
595
- const { recording, client } = recordedClient(input);
596
- try {
597
- await client.drillAsk({ source: input.source, question: entityChartQuestion(input.question ?? {}) });
598
- } catch {
599
- }
600
- return recording.seed();
601
- }
602
- async function askTileSeed(input) {
603
- const { recording, client } = recordedClient(input);
604
- const specs = Array.isArray(input.spec) ? input.spec : [input.spec];
605
- await Promise.all(specs.map((spec) => client.recordAggregate({ table_id: input.tableId, ...spec }).catch(() => null)));
606
- return recording.seed();
180
+ function presentationDocument(p) {
181
+ return presentationIsEmpty(p) ? "" : JSON.stringify(p);
607
182
  }
183
+ var gridPresentationCodec = {
184
+ parse: parseGridPresentation,
185
+ isEmpty: presentationIsEmpty,
186
+ toDocument: (presentation) => JSON.parse(presentationDocument(presentation))
187
+ };
188
+ var boundViews = base.bindViewCodec(gridPresentationCodec);
189
+ var { viewSettings, viewSettingsPatch } = boundViews;
608
190
 
609
191
  // src/firstPage.ts
610
- var TABLE_PAGE_SIZE = 100;
611
- var EMBEDDED_PAGE_SIZE = 50;
612
- function viewFilterOf(shown, fields) {
613
- if (!shown) return void 0;
614
- const base = shown.where ?? (shown.filters && Object.keys(shown.filters).length > 0 ? shown.filters : void 0);
615
- return storeQuestion(fields, base, {}).filter;
616
- }
617
- function firstGridPageArgs(input) {
618
- const { shown, fields } = input;
619
- if (!shown) return null;
620
- if ((shown.layout ?? "grid") !== "grid") return null;
621
- if (shown.ruleId) return null;
622
- if (shown.order === "manual") return null;
623
- const grouping = shown.presentation?.grouping;
624
- if (grouping && !(input.merged && isPlainGrouping(grouping))) return null;
625
- const first = (shown.sorts ?? [])[0];
626
- const viewSort = first && fields.some((f) => f.key === first.field) ? { id: first.field, direction: first.direction } : null;
627
- const opening = viewSort ?? defaultSortOf(input.table, fields);
628
- return {
629
- p_table_id: input.tableId,
630
- p_filter: viewFilterOf(shown, fields) ?? {},
631
- p_search: null,
632
- p_sort: groupedPageSort(fields, grouping?.field ?? null, pageSortFor(fields, opening)),
633
- p_view_id: null,
634
- p_limit: input.pageSize ?? TABLE_PAGE_SIZE,
635
- p_offset: 0
636
- };
637
- }
638
- async function firstGridPageRequest(client, tableId, options = {}) {
639
- return (await firstGridOpening(client, tableId, options))?.args ?? null;
640
- }
641
- function defaultSpec(tableId) {
642
- return { name: "All", subject: tableId, layout: "grid", isDefault: true };
643
- }
644
- function shownFromSpec(spec, tableId) {
645
- return { ...spec, subject: spec.subject ?? tableId, id: "", movedFrom: null };
646
- }
647
- async function firstGridOpening(client, tableId, options) {
648
- const explicit = options.view;
649
- const [table, fields, views, looks] = await Promise.all([
650
- client.tableRead({ table_id: tableId }),
651
- client.fields({ table_id: tableId }),
652
- explicit ? Promise.resolve(null) : client.views({ table_id: tableId }),
653
- explicit ? Promise.resolve(null) : readLooks(client, tableId)
654
- ]);
655
- if (!table.ok || !fields.ok) return null;
656
- let shown;
657
- if (explicit) {
658
- shown = shownFromSpec(explicit, tableId);
659
- } else {
660
- if (!views?.ok || !looks?.ok) return null;
661
- const mine = views.data.map(viewFromRow).filter((v) => v.subject === tableId);
662
- const view = defaultViewOf(mine) ?? mine[0] ?? null;
663
- shown = view ? applyLook(view, looks.data[view.id]) : shownFromSpec(defaultSpec(tableId), tableId);
664
- }
665
- const args = firstGridPageArgs({
666
- tableId,
667
- table: table.data,
668
- fields: fields.data,
669
- shown,
670
- ...options.pageSize ? { pageSize: options.pageSize } : {},
671
- ...options.merged ? { merged: true } : {}
672
- });
673
- return args ? { args, shown, fields: fields.data } : null;
674
- }
675
- function serverRowsOf(seed) {
676
- const found = seed?.answers.find((a) => a.door === "server_rows");
677
- const data = found?.data;
678
- if (!data || typeof data !== "object") return null;
679
- return { on: data.on === true, capMs: typeof data.cap_ms === "number" && data.cap_ms > 0 ? data.cap_ms : null };
680
- }
681
- async function askTablePageSeed(input) {
682
- const recording = recordingDataSource(asRecordsDataSource(input.dataSource));
683
- const client = createRecordsClient({
684
- dataSource: recording.source,
685
- organizationId: input.organizationId,
686
- actor: input.actor,
687
- onError: () => {
688
- }
689
- });
690
- const record = input.recordId ? Promise.resolve(
691
- recording.source.rpc("record_page_bundle", { p_organization_id: input.organizationId, p_record_id: input.recordId }, { schema: "custom" })
692
- ).catch(() => null) : null;
693
- try {
694
- const pageSize = input.pageSize ?? (input.embedded ? EMBEDDED_PAGE_SIZE : TABLE_PAGE_SIZE);
695
- const opening = await firstGridOpening(client, input.tableId, { pageSize, ...input.merged ? { merged: true } : {}, ...input.view ? { view: input.view } : {} });
696
- const wantRows = typeof input.rows === "function" ? input.rows(recording.seed()) : input.rows !== false;
697
- if (opening && wantRows) {
698
- const { args, shown, fields } = opening;
699
- const page = await client.listPage({
700
- table_id: args.p_table_id,
701
- filter: args.p_filter,
702
- search: args.p_search,
703
- sort: args.p_sort,
704
- view_id: args.p_view_id,
705
- limit: args.p_limit,
706
- offset: args.p_offset
707
- });
708
- if (page.ok) {
709
- const chosen = shown?.presentation?.summaries ?? {};
710
- const groupKey = shown?.presentation?.grouping?.field ?? null;
711
- const reads = [];
712
- const filter = args.p_filter && Object.keys(args.p_filter).length > 0 ? args.p_filter : void 0;
713
- if (groupKey) {
714
- reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [groupKey], measures: groupFactMeasures(chosen), limit: 1e3, ...filter ? { filter } : {} }));
715
- }
716
- const bar = summaryBarMeasures(chosen);
717
- if (bar.length > 0) reads.push(client.recordAggregate({ table_id: input.tableId, measures: bar, ...filter ? { filter } : {} }));
718
- for (const field2 of fields) {
719
- if (field2.type !== "list" && field2.type !== "relation") continue;
720
- const ids = /* @__PURE__ */ new Set();
721
- for (const row of page.data.rows) {
722
- const value = row.document?.[field2.key];
723
- for (const id of Array.isArray(value) ? value.map(String) : typeof value === "string" ? [value] : []) if (isUuidShape(id)) ids.add(id);
724
- }
725
- if (ids.size > 0) reads.push(client.relationWordsMany({ field_id: field2.id, record_ids: [...ids] }));
726
- }
727
- if (input.merged) {
728
- for (const key of choiceFieldKeys(fields)) {
729
- reads.push(client.recordAggregate({ table_id: input.tableId, groupBy: [key], measures: [{ op: "count" }], limit: 500, ...filter ? { filter } : {} }));
730
- }
731
- }
732
- const unversioned = page.data.rows.filter((row) => ownVersion(row) === null).map((row) => row.id);
733
- if (unversioned.length > 0) reads.push(client.recordHeaders({ ids: unversioned }));
734
- reads.push(
735
- client.reverseColumns({ table_id: input.tableId }).then(async (columns) => {
736
- if (!columns.ok || columns.data.length === 0) return null;
737
- const record_ids = page.data.rows.map((row) => row.id);
738
- const all = await client.reverseLinksFields({ table_id: input.tableId, source_field_ids: columns.data.map((c) => c.source_field_id), record_ids, limit: REVERSE_CELL_CHIPS });
739
- if (all.ok || all.error.code !== "door_absent") return null;
740
- return Promise.all(columns.data.map((c) => client.reverseLinksMany({ table_id: input.tableId, source_field_id: c.source_field_id, record_ids, limit: REVERSE_CELL_CHIPS })));
741
- })
742
- );
743
- await Promise.all(reads.map((read) => read.catch(() => null)));
744
- }
745
- }
746
- } catch {
747
- }
748
- if (record) await record;
749
- return recording.seed();
750
- }
192
+ var { firstGridPageRequest, askTablePageSeed } = bindFirstPageCodec(gridPresentationCodec);
751
193
 
752
- export { EMBEDDED_PAGE_SIZE, TABLE_PAGE_SIZE, askChartSeed, askEntityBlockSeed, askTablePageSeed, askTileSeed, firstGridPageArgs, firstGridPageRequest, mergeSeeds, serverRowsOf, viewFilterOf };
194
+ export { askTablePageSeed, firstGridPageRequest };
753
195
  //# sourceMappingURL=first-page.js.map
754
196
  //# sourceMappingURL=first-page.js.map