@ai-matrx/records-ui 0.115.70 → 0.115.71
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 +5 -0
- package/dist/first-page.cjs +0 -1
- package/dist/first-page.cjs.map +1 -1
- package/dist/first-page.js +0 -1
- package/dist/first-page.js.map +1 -1
- package/dist/helpers.cjs +618 -0
- package/dist/helpers.cjs.map +1 -0
- package/dist/helpers.d.cts +1258 -0
- package/dist/helpers.d.ts +1258 -0
- package/dist/helpers.js +559 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.cjs +309 -285
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +182 -164
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +94 -72
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +69 -53
- package/dist/pickers.js.map +1 -1
- package/package.json +15 -5
package/dist/helpers.cjs
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var globalSlot = require('@ai-matrx/kit/global-slot');
|
|
4
|
+
var tableStyle = require('@ai-matrx/design-system/data-table/table-style');
|
|
5
|
+
var records = require('@ai-matrx/records');
|
|
6
|
+
var uuid = require('@ai-matrx/kit/uuid');
|
|
7
|
+
var textCase = require('@ai-matrx/kit/text-case');
|
|
8
|
+
var format = require('@ai-matrx/kit/format');
|
|
9
|
+
var core = require('@ai-matrx/records/core');
|
|
10
|
+
require('@ai-matrx/design-system/field-formats');
|
|
11
|
+
|
|
12
|
+
// src/dataSource.ts
|
|
13
|
+
function personActor(userId) {
|
|
14
|
+
return userId ? { actor: "user", user_id: userId } : { actor: "user" };
|
|
15
|
+
}
|
|
16
|
+
var dataSources = globalSlot.globalSlot("ai-matrx.records-ui.data-sources", () => /* @__PURE__ */ new WeakMap());
|
|
17
|
+
function recordsDataSource(client, fallbackSchema = "custom") {
|
|
18
|
+
let bySchema = dataSources.get(client);
|
|
19
|
+
if (!bySchema) {
|
|
20
|
+
bySchema = /* @__PURE__ */ new Map();
|
|
21
|
+
dataSources.set(client, bySchema);
|
|
22
|
+
}
|
|
23
|
+
const held = bySchema.get(fallbackSchema);
|
|
24
|
+
if (held) return held;
|
|
25
|
+
const made = makeRecordsDataSource(client, fallbackSchema);
|
|
26
|
+
bySchema.set(fallbackSchema, made);
|
|
27
|
+
return made;
|
|
28
|
+
}
|
|
29
|
+
function makeRecordsDataSource(client, fallbackSchema) {
|
|
30
|
+
const supabase = client;
|
|
31
|
+
return {
|
|
32
|
+
// PAGE-BUNDLE: the real adapter opens a table page with one `custom.table_page_bundle` call.
|
|
33
|
+
opensInOneCall: true,
|
|
34
|
+
async rpc(fn, args, options) {
|
|
35
|
+
const schema = options?.schema ?? fallbackSchema;
|
|
36
|
+
if (schema === "custom" && supabase.auth?.getSession && !await hasSession(supabase.auth.getSession)) {
|
|
37
|
+
return { data: null, error: SIGNED_OUT };
|
|
38
|
+
}
|
|
39
|
+
const on = supabase.schema(schema);
|
|
40
|
+
return await on.rpc(fn, args);
|
|
41
|
+
},
|
|
42
|
+
schema(name) {
|
|
43
|
+
return supabase.schema(name);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var SIGNED_OUT = {
|
|
48
|
+
code: "42501",
|
|
49
|
+
message: "Nobody is signed in, so there is nothing to read.",
|
|
50
|
+
hint: "Sign in, then try again.",
|
|
51
|
+
details: null
|
|
52
|
+
};
|
|
53
|
+
async function hasSession(getSession) {
|
|
54
|
+
try {
|
|
55
|
+
const answer = await getSession();
|
|
56
|
+
return Boolean(answer?.data?.session);
|
|
57
|
+
} catch {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
var state = globalSlot.globalSlot("ai-matrx.records-ui.person-time-zone", () => ({
|
|
62
|
+
zone: null,
|
|
63
|
+
listeners: /* @__PURE__ */ new Set()
|
|
64
|
+
}));
|
|
65
|
+
function valid(z) {
|
|
66
|
+
if (typeof z !== "string" || !z.trim()) return false;
|
|
67
|
+
try {
|
|
68
|
+
new Intl.DateTimeFormat("en-US", { timeZone: z });
|
|
69
|
+
return true;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function setPersonTimeZone(next) {
|
|
75
|
+
const clean = valid(next) ? next : null;
|
|
76
|
+
if (clean === state.zone) return;
|
|
77
|
+
state.zone = clean;
|
|
78
|
+
tableStyle.setColorRuleTimeZone(clean);
|
|
79
|
+
state.listeners.forEach((l) => l());
|
|
80
|
+
}
|
|
81
|
+
function getPersonTimeZone() {
|
|
82
|
+
return state.zone;
|
|
83
|
+
}
|
|
84
|
+
function subscribePersonTimeZone(listener) {
|
|
85
|
+
state.listeners.add(listener);
|
|
86
|
+
return () => {
|
|
87
|
+
state.listeners.delete(listener);
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function personWallNow(at = /* @__PURE__ */ new Date()) {
|
|
91
|
+
const zone = state.zone;
|
|
92
|
+
if (!zone) return at;
|
|
93
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
94
|
+
timeZone: zone,
|
|
95
|
+
year: "numeric",
|
|
96
|
+
month: "2-digit",
|
|
97
|
+
day: "2-digit",
|
|
98
|
+
hour: "2-digit",
|
|
99
|
+
minute: "2-digit",
|
|
100
|
+
second: "2-digit",
|
|
101
|
+
hourCycle: "h23"
|
|
102
|
+
}).formatToParts(at);
|
|
103
|
+
const n = (t) => Number(parts.find((p) => p.type === t)?.value ?? 0);
|
|
104
|
+
return new Date(n("year"), n("month") - 1, n("day"), n("hour"), n("minute"), n("second"));
|
|
105
|
+
}
|
|
106
|
+
var LEVEL_WORD = {
|
|
107
|
+
viewer: "Viewer",
|
|
108
|
+
commenter: "Commenter",
|
|
109
|
+
edit_content: "Content editor",
|
|
110
|
+
editor: "Editor",
|
|
111
|
+
admin: "Admin"
|
|
112
|
+
};
|
|
113
|
+
var NEEDS = {
|
|
114
|
+
read: "viewer",
|
|
115
|
+
// custom.io_comment_write
|
|
116
|
+
comment: "commenter",
|
|
117
|
+
// custom.record_update / custom.record_write — `edit_content` changes rows and page content;
|
|
118
|
+
// columns, views and settings (structure) stay with admin below, and sharing with admin.
|
|
119
|
+
write: "edit_content",
|
|
120
|
+
// custom.view_designate / view_declare / migrate_merge / record_restore — all ask `editor`: more than
|
|
121
|
+
// changing content (`edit_content`), less than changing columns (`admin`).
|
|
122
|
+
organise: "editor",
|
|
123
|
+
// custom.record_delete
|
|
124
|
+
remove: "editor",
|
|
125
|
+
// custom.share_grant — ONE-LADDER (2026-10-10): Edit may pass View / Comment / Edit on inside the
|
|
126
|
+
// organization (iam.may_grant_as, knobs access.sharing/*). Which levels, and to whom, is the share
|
|
127
|
+
// dialog's own question (iam.my_grantable_levels); the Share button only has to open for Edit.
|
|
128
|
+
share: "editor",
|
|
129
|
+
// custom.field_declare / field_update / field_retire / table settings
|
|
130
|
+
structure: "admin"
|
|
131
|
+
};
|
|
132
|
+
var NOT_YET = "Still asking the store what you may do here. Nothing is offered until it has answered.";
|
|
133
|
+
var NOTHING_AT_ALL = "You have not been given this. Ask somebody who administers it to share it with you.";
|
|
134
|
+
function sentenceFor(what, level) {
|
|
135
|
+
if (level === null) return NOTHING_AT_ALL;
|
|
136
|
+
const need = LEVEL_WORD[NEEDS[what]];
|
|
137
|
+
const have = LEVEL_WORD[level];
|
|
138
|
+
const doing = {
|
|
139
|
+
read: "open this",
|
|
140
|
+
comment: "leave a comment",
|
|
141
|
+
write: "change anything here",
|
|
142
|
+
organise: "organise this",
|
|
143
|
+
remove: "delete this",
|
|
144
|
+
share: "pass this on to somebody else",
|
|
145
|
+
structure: "change the columns of this table"
|
|
146
|
+
};
|
|
147
|
+
return `You are ${have} here, and it takes ${need} to ${doing[what]}. Ask somebody who is Admin on it to move you up.`;
|
|
148
|
+
}
|
|
149
|
+
function whatYouMayDo(level, known = true) {
|
|
150
|
+
const may = (what) => known && records.atLeast(level, NEEDS[what]);
|
|
151
|
+
return {
|
|
152
|
+
level: known ? level : null,
|
|
153
|
+
known,
|
|
154
|
+
read: may("read"),
|
|
155
|
+
comment: may("comment"),
|
|
156
|
+
write: may("write"),
|
|
157
|
+
organise: may("organise"),
|
|
158
|
+
remove: may("remove"),
|
|
159
|
+
share: may("share"),
|
|
160
|
+
structure: may("structure"),
|
|
161
|
+
why: (what) => known ? sentenceFor(what, level) : NOT_YET
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
var NOT_ANSWERED_YET = whatYouMayDo(null, false);
|
|
165
|
+
|
|
166
|
+
// src/tableRights.ts
|
|
167
|
+
function tableRights(may, overallReason) {
|
|
168
|
+
return {
|
|
169
|
+
...may,
|
|
170
|
+
admin: may.structure,
|
|
171
|
+
reason: (may.structure ? "" : may.why("structure"))
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
var NO_RIGHTS = tableRights(NOT_ANSWERED_YET);
|
|
175
|
+
function tableRightsAt(level) {
|
|
176
|
+
return tableRights(whatYouMayDo(level, true));
|
|
177
|
+
}
|
|
178
|
+
function fieldName(field) {
|
|
179
|
+
const label = (field.label ?? "").trim();
|
|
180
|
+
if (label !== "" && label === field.key && /^[a-z0-9]+(?:[_.-][a-z0-9]+)+$/.test(label)) return textCase.humanizeIdentifier(label);
|
|
181
|
+
if (label !== "") return label;
|
|
182
|
+
const name = (field.name ?? "").trim();
|
|
183
|
+
if (name !== "") return name;
|
|
184
|
+
return textCase.humanizeIdentifier(field.key);
|
|
185
|
+
}
|
|
186
|
+
function tableName(table) {
|
|
187
|
+
const name = (table.name ?? "").trim();
|
|
188
|
+
if (name !== "") return name;
|
|
189
|
+
const singular = (table.label_singular ?? "").trim();
|
|
190
|
+
if (singular !== "") return singular;
|
|
191
|
+
return textCase.humanizeIdentifier(table.slug ?? "");
|
|
192
|
+
}
|
|
193
|
+
function recordName(document, titleKey, fallback = "Untitled") {
|
|
194
|
+
const data = document ?? {};
|
|
195
|
+
const keys = [titleKey, "title", "name", "label", "full_name", "subject", "summary"].filter(
|
|
196
|
+
(key) => typeof key === "string" && key !== ""
|
|
197
|
+
);
|
|
198
|
+
for (const key of keys) {
|
|
199
|
+
const value = data[key];
|
|
200
|
+
if (typeof value === "string" && value.trim() !== "" && !looksLikeId(value.trim())) {
|
|
201
|
+
return value.trim();
|
|
202
|
+
}
|
|
203
|
+
if (typeof value === "number") return String(value);
|
|
204
|
+
}
|
|
205
|
+
return fallback;
|
|
206
|
+
}
|
|
207
|
+
function looksLikeId(value) {
|
|
208
|
+
return typeof value === "string" && uuid.isUuidShape(value.trim());
|
|
209
|
+
}
|
|
210
|
+
function rowName(row, titleKey, fallback = "Untitled") {
|
|
211
|
+
return recordName(row?.document, titleKey, fallback);
|
|
212
|
+
}
|
|
213
|
+
function recordNameIn(table, document, fallback = "Untitled") {
|
|
214
|
+
return recordName(document, table?.title_field ?? null, fallback);
|
|
215
|
+
}
|
|
216
|
+
function rowNameIn(table, row, fallback = "Untitled") {
|
|
217
|
+
return recordName(row?.document, table?.title_field ?? null, fallback);
|
|
218
|
+
}
|
|
219
|
+
var WITHHELD_RECORD_LABEL = "A record you have not been given access to";
|
|
220
|
+
var UNREAD_RECORD_LABEL = "Couldn't load this name";
|
|
221
|
+
function singularNoun(table, fallback = "record") {
|
|
222
|
+
const word = (table?.label_singular ?? "").trim() || (table?.name ?? "").trim() || fallback;
|
|
223
|
+
return word.split(/\s+/).map((w) => /^[A-Z][a-z'’-]*$/.test(w) ? w.toLowerCase() : w).join(" ");
|
|
224
|
+
}
|
|
225
|
+
var CONVENTIONAL_TITLE_KEYS = ["title", "name", "label", "full_name", "subject", "summary"];
|
|
226
|
+
function canTitle(field, excluded) {
|
|
227
|
+
if (excluded.has(field.key)) return false;
|
|
228
|
+
if (field.type === "boolean") return false;
|
|
229
|
+
if (field.type === "formula" && (field.config ?? {})["system"]) return false;
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
function cardTitleField(fields, titleKey, exclude = []) {
|
|
233
|
+
const excluded = new Set(exclude.filter((k) => typeof k === "string" && k !== ""));
|
|
234
|
+
const ordered = [...fields].sort((a, b) => (a.sort ?? 100) - (b.sort ?? 100) || a.key.localeCompare(b.key));
|
|
235
|
+
const byKey = (key) => ordered.find((f) => f.key === key && canTitle(f, excluded));
|
|
236
|
+
if (titleKey) {
|
|
237
|
+
const declared = byKey(titleKey);
|
|
238
|
+
if (declared) return declared;
|
|
239
|
+
}
|
|
240
|
+
for (const key of CONVENTIONAL_TITLE_KEYS) {
|
|
241
|
+
const found = byKey(key);
|
|
242
|
+
if (found) return found;
|
|
243
|
+
}
|
|
244
|
+
return ordered.find((f) => f.type === "text" && canTitle(f, excluded)) ?? ordered.find((f) => canTitle(f, excluded));
|
|
245
|
+
}
|
|
246
|
+
var SOMEBODY = "somebody in this organization";
|
|
247
|
+
var THE_SYSTEM = "the system";
|
|
248
|
+
function actorWords(actor, resolve) {
|
|
249
|
+
if (actor.kind === "system") return THE_SYSTEM;
|
|
250
|
+
const named = actor.name?.trim() || (actor.user_id && resolve ? resolve(actor.user_id)?.trim() : null) || null;
|
|
251
|
+
if (actor.kind === "agent") {
|
|
252
|
+
const on = actor.on_behalf_of;
|
|
253
|
+
const person = on?.name?.trim() || (on?.user_id && resolve ? resolve(on.user_id)?.trim() : null) || SOMEBODY;
|
|
254
|
+
const approver = actor.approved_by ? actor.approved_by.name?.trim() || (resolve ? resolve(actor.approved_by.user_id)?.trim() : null) || SOMEBODY : null;
|
|
255
|
+
return `${named ?? "an assistant"}, for ${person}${approver ? `, approved by ${approver}` : ""}`;
|
|
256
|
+
}
|
|
257
|
+
return named ?? SOMEBODY;
|
|
258
|
+
}
|
|
259
|
+
function whenWords(at, now = /* @__PURE__ */ new Date()) {
|
|
260
|
+
const then = new Date(at);
|
|
261
|
+
const age = now.getTime() - then.getTime();
|
|
262
|
+
if (!Number.isFinite(age)) return "at an unknown time";
|
|
263
|
+
if (age < 30 * 864e5) return format.formatRelativeTime(then, { style: "intl", now: now.getTime() });
|
|
264
|
+
return then.toLocaleDateString();
|
|
265
|
+
}
|
|
266
|
+
var OPERATION_WORDS = {
|
|
267
|
+
created: "Created",
|
|
268
|
+
edited: "Edited",
|
|
269
|
+
deleted: "Archived",
|
|
270
|
+
archived: "Archived",
|
|
271
|
+
restored: "Restored",
|
|
272
|
+
retype: "Column type changed",
|
|
273
|
+
delete: "Removed",
|
|
274
|
+
soft_delete: "Archived",
|
|
275
|
+
insert: "Created",
|
|
276
|
+
update: "Edited",
|
|
277
|
+
restore: "Restored"
|
|
278
|
+
};
|
|
279
|
+
function operationWords(label) {
|
|
280
|
+
const raw = (label ?? "").trim();
|
|
281
|
+
if (raw === "") return "Changed";
|
|
282
|
+
const known = OPERATION_WORDS[raw.toLowerCase()];
|
|
283
|
+
if (known) return known;
|
|
284
|
+
const words = textCase.humanizeIdentifier(raw).toLowerCase();
|
|
285
|
+
return words.charAt(0).toUpperCase() + words.slice(1);
|
|
286
|
+
}
|
|
287
|
+
function lastChangeSentence(fieldLabel, entry, resolve) {
|
|
288
|
+
if (!entry) {
|
|
289
|
+
return `Nothing is recorded against ${fieldLabel} on this record yet. Press H for the whole history of this column.`;
|
|
290
|
+
}
|
|
291
|
+
const verb = operationWords(entry.operation_label).toLowerCase();
|
|
292
|
+
return `${fieldLabel} \u2014 ${verb} by ${actorWords(entry.actor, resolve)}, ${whenWords(entry.occurred_at)}. Press H for the whole history.`;
|
|
293
|
+
}
|
|
294
|
+
function actorBadge(actor, resolve) {
|
|
295
|
+
if (actor.kind === "system") return THE_SYSTEM;
|
|
296
|
+
const words = actorWords(actor, resolve);
|
|
297
|
+
if (words === SOMEBODY) return "somebody";
|
|
298
|
+
const first = words.split(",")[0].trim();
|
|
299
|
+
const at = first.indexOf("@");
|
|
300
|
+
return at > 0 ? first.slice(0, at) : first;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/withheldNotice.ts
|
|
304
|
+
var WITHHELD_WORD = "Withheld";
|
|
305
|
+
function withheldNoticeOf(hidden, key) {
|
|
306
|
+
const notice = hidden?.[key];
|
|
307
|
+
return notice && typeof notice === "object" ? notice : null;
|
|
308
|
+
}
|
|
309
|
+
function withheldSentence(field, notice) {
|
|
310
|
+
const extra = notice;
|
|
311
|
+
if (typeof extra.says === "string" && extra.says.trim() !== "") return extra.says.trim();
|
|
312
|
+
const name = fieldName(field);
|
|
313
|
+
if (typeof extra.part === "string" && extra.part.trim() !== "" && typeof extra.who === "string" && extra.who.trim() !== "") {
|
|
314
|
+
return `${name} is in ${extra.part.trim()} \u2014 ${extra.who.trim()} see it.`;
|
|
315
|
+
}
|
|
316
|
+
const reason = notice.reason ? ` It is ${notice.reason}` : "";
|
|
317
|
+
const needs = notice.needs ? `${reason ? "," : " It"} and reading it needs ${notice.needs}` : "";
|
|
318
|
+
const or = notice.or ? `${needs || reason ? ", or" : " It needs"} ${notice.or}` : "";
|
|
319
|
+
return `${name} is withheld from you.${reason}${needs}${or}${reason || needs || or ? "." : ""}`;
|
|
320
|
+
}
|
|
321
|
+
function displayFormatOf(field) {
|
|
322
|
+
const raw = field.display_format;
|
|
323
|
+
if (!raw || typeof raw !== "object") return null;
|
|
324
|
+
const id = raw.id;
|
|
325
|
+
if (typeof id !== "string" || id === "") return null;
|
|
326
|
+
const options = raw.options;
|
|
327
|
+
return { id, options: options && typeof options === "object" ? options : {} };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/docs.ts
|
|
331
|
+
function isSignatureField(field) {
|
|
332
|
+
return field.type === "text" && (field.format ?? "").toLowerCase() === "signature";
|
|
333
|
+
}
|
|
334
|
+
var LOOK_KINDS = {
|
|
335
|
+
rating: { plain: "number", look: ({ ratingMax }) => ({ id: "rating", options: { ratingMax } }) },
|
|
336
|
+
time: { plain: "text", look: () => ({ id: "time" }) }
|
|
337
|
+
};
|
|
338
|
+
function storePublishesKind(kind) {
|
|
339
|
+
return records.DECLARABLE_FIELD_KINDS.some((row) => row.kind === kind);
|
|
340
|
+
}
|
|
341
|
+
records.DECLARABLE_FIELD_KINDS.some(
|
|
342
|
+
(row) => row.kind === "entity_reference"
|
|
343
|
+
);
|
|
344
|
+
var SYSTEM_FIELD_KINDS = ["autonumber", "created_time", "modified_time"];
|
|
345
|
+
function isSystemFieldKind(id) {
|
|
346
|
+
return SYSTEM_FIELD_KINDS.includes(id);
|
|
347
|
+
}
|
|
348
|
+
var FIELD_TYPE_CATALOGUE = [
|
|
349
|
+
{ id: "text", group: "Words", label: "Text", explanation: "A short line of writing \u2014 a name, a reference, a title." },
|
|
350
|
+
{ id: "long_text", group: "Words", label: "Long text", explanation: "Several lines \u2014 a description, notes, an address." },
|
|
351
|
+
{ id: "rich_text", group: "Words", label: "Formatted text", explanation: "Notes with bold, lists and links." },
|
|
352
|
+
{ id: "email", group: "Words", label: "Email address", explanation: "An email address, checked as you type and ready to write to." },
|
|
353
|
+
{ id: "phone", group: "Words", label: "Phone number", explanation: "A phone number, checked as you type." },
|
|
354
|
+
{ id: "url", group: "Words", label: "Link", explanation: "A web address that opens in a new tab when you click it." },
|
|
355
|
+
{ id: "address", group: "Words", label: "Address", explanation: "A street address in parts, opened in maps." },
|
|
356
|
+
{ id: "barcode", group: "Words", label: "Barcode", explanation: "A code a scanner reads, like a wristband or a box." },
|
|
357
|
+
{ id: "button", group: "Words", label: "Button", explanation: "A button on each record that opens a link or runs an action." },
|
|
358
|
+
{ id: "number", group: "Numbers and dates", label: "Number", explanation: "A plain number you can total up and sort by." },
|
|
359
|
+
{ id: "currency", group: "Numbers and dates", label: "Money", explanation: "An amount of money, shown with its currency wherever it appears." },
|
|
360
|
+
{ id: "percent", group: "Numbers and dates", label: "Percentage", explanation: "A number out of a hundred, kept between 0 and 100." },
|
|
361
|
+
{ id: "rating", group: "Numbers and dates", label: "Rating", explanation: "A score in stars, set with one click." },
|
|
362
|
+
{ id: "duration", group: "Numbers and dates", label: "Duration", explanation: "A length of time, typed as 45 min or 1:30." },
|
|
363
|
+
{ id: "time", group: "Numbers and dates", label: "Time", explanation: "A time of day with no date \u2014 a shift start, opening hours." },
|
|
364
|
+
{ id: "datetime", group: "Numbers and dates", label: "Date", explanation: "A day, or a day and a time. Dates are what the calendar view reads." },
|
|
365
|
+
{ id: "select", group: "Choices and people", label: "Choice", explanation: "One answer from a list you write here. Choices are what the board view groups by." },
|
|
366
|
+
{ id: "multi_select", group: "Choices and people", label: "Several choices", explanation: "Any number of answers from a list you write here." },
|
|
367
|
+
{ id: "status", group: "Choices and people", label: "Status", explanation: "Choices grouped into To do, In progress and Done." },
|
|
368
|
+
{ id: "checkbox", group: "Choices and people", label: "Yes / no", explanation: "A box you tick. Left alone it stays unanswered, which is not the same as a no." },
|
|
369
|
+
{ id: "member", group: "Choices and people", label: "Person", explanation: "Somebody in this organization, picked from the people who are in it." },
|
|
370
|
+
{ id: "attachment", group: "Choices and people", label: "File", explanation: "A file or a picture kept with the record." },
|
|
371
|
+
{ id: "signature", group: "Choices and people", label: "Signature", explanation: "Somebody's sign-off on a document made from this record. Nobody types in it \u2014 it fills in when they sign." },
|
|
372
|
+
{ id: "relation", group: "Choices and people", label: "Points at another record", explanation: "A link to records in another of your tables \u2014 the customer a job is for, the lines on an invoice." },
|
|
373
|
+
{
|
|
374
|
+
id: "entity_reference",
|
|
375
|
+
group: "Choices and people",
|
|
376
|
+
label: "Points at something else on the platform",
|
|
377
|
+
explanation: "A note, a web site, an agent, a workbook \u2014 anything you keep here that is not one of your tables."
|
|
378
|
+
},
|
|
379
|
+
{ id: "lookup", group: "Worked out", label: "Borrowed value", explanation: "A value read from a record this one points at, kept in step with it." },
|
|
380
|
+
{ id: "rollup", group: "Worked out", label: "Total of linked records", explanation: "A count or a total worked out from the records this one points at." },
|
|
381
|
+
{ id: "count", group: "Worked out", label: "Count of linked records", explanation: "How many records this one points at." },
|
|
382
|
+
{ id: "formula", group: "Worked out", label: "Worked out from other columns", explanation: "A value this record works out for itself from its own other columns." },
|
|
383
|
+
{ id: "autonumber", group: "Worked out", label: "Record number", explanation: "A number the table gives each record, one after another, never reused. Records already here are numbered in the order they were made." },
|
|
384
|
+
{ id: "created_time", group: "Worked out", label: "Created time", explanation: "When each record was made. Nobody types it." },
|
|
385
|
+
{ id: "modified_time", group: "Worked out", label: "Last changed time", explanation: "When each record last changed. Nobody types it." },
|
|
386
|
+
{ id: "created_by", group: "Worked out", label: "Created by", explanation: "Who made each record. Nobody types it." },
|
|
387
|
+
{ id: "modified_by", group: "Worked out", label: "Last changed by", explanation: "Who last changed each record. Nobody types it." }
|
|
388
|
+
];
|
|
389
|
+
var GATED_KINDS = /* @__PURE__ */ new Set(["entity_reference", ...records.NEWER_FIELD_KINDS]);
|
|
390
|
+
FIELD_TYPE_CATALOGUE.filter(
|
|
391
|
+
(c) => !GATED_KINDS.has(c.id) || storePublishesKind(c.id) || c.id in LOOK_KINDS
|
|
392
|
+
);
|
|
393
|
+
var BY_ID = new Map(FIELD_TYPE_CATALOGUE.map((c) => [c.id, c]));
|
|
394
|
+
function fieldTypeChoice(id) {
|
|
395
|
+
return BY_ID.get(id);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/parity.ts
|
|
399
|
+
var kernelId = (name) => records.KERNEL_TABLES.find((t) => t.name === name)?.id ?? "";
|
|
400
|
+
var KERNEL_FILE_TABLE = kernelId("File");
|
|
401
|
+
var KERNEL_PERSON_TABLE = kernelId("Person");
|
|
402
|
+
var PARITY_LABEL = {
|
|
403
|
+
attachment: "Attachment",
|
|
404
|
+
checkbox: "Yes / no",
|
|
405
|
+
currency: "Currency",
|
|
406
|
+
datetime: "Date",
|
|
407
|
+
email: "Email",
|
|
408
|
+
formula: "Formula",
|
|
409
|
+
lookup: "Lookup",
|
|
410
|
+
member: "Person",
|
|
411
|
+
multi_select: "Multi-select",
|
|
412
|
+
percent: "Percent",
|
|
413
|
+
phone: "Phone",
|
|
414
|
+
rollup: "Rollup",
|
|
415
|
+
select: "Select",
|
|
416
|
+
url: "Link"
|
|
417
|
+
};
|
|
418
|
+
var PARITY_MADE_OF = Object.fromEntries(
|
|
419
|
+
records.PARITY_FIELD_TYPES.map((p) => [p.parity_type, p.made_of])
|
|
420
|
+
);
|
|
421
|
+
var KERNEL = { file: KERNEL_FILE_TABLE, person: KERNEL_PERSON_TABLE };
|
|
422
|
+
function editorKindFor(field) {
|
|
423
|
+
return records.fieldKindFor(field, KERNEL);
|
|
424
|
+
}
|
|
425
|
+
function pickableTypeOf(field) {
|
|
426
|
+
if (isSignatureField(field)) return "signature";
|
|
427
|
+
if (records.isButtonField(field)) return "button";
|
|
428
|
+
const newer = records.newerKindOf(field);
|
|
429
|
+
if (newer) return newer;
|
|
430
|
+
const look = displayFormatOf(field)?.id;
|
|
431
|
+
if (look === "rating" && field.type === "range") return "rating";
|
|
432
|
+
if (look === "time" && field.type === "text") return "time";
|
|
433
|
+
const system = field.config?.["system"];
|
|
434
|
+
if (field.type === "formula" && typeof system === "string" && isSystemFieldKind(system)) return system;
|
|
435
|
+
const kind = editorKindFor(field);
|
|
436
|
+
return fieldTypeChoice(kind) ? kind : void 0;
|
|
437
|
+
}
|
|
438
|
+
function fieldTypeLabel(field) {
|
|
439
|
+
const kind = pickableTypeOf(field) ?? editorKindFor(field);
|
|
440
|
+
const known = fieldTypeChoice(kind)?.label;
|
|
441
|
+
if (known) return field.multi && kind !== "multi_select" ? `${known} (many)` : known;
|
|
442
|
+
const plain = {
|
|
443
|
+
// `boolean` is the BEHAVIOUR word; `checkbox` is the type a person picks. Both are
|
|
444
|
+
// written the way a person writes them, never as a raw token.
|
|
445
|
+
boolean: "Yes / no",
|
|
446
|
+
date: "Date",
|
|
447
|
+
json: "Structured data",
|
|
448
|
+
relation: "Link to a record"
|
|
449
|
+
};
|
|
450
|
+
return plain[kind] ?? textCase.humanizeIdentifier(kind);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// src/tablePickerEntries.ts
|
|
454
|
+
var PLATFORM_TABLE_PREFIX = "records_ui_";
|
|
455
|
+
function keptByPlatform(table) {
|
|
456
|
+
return table.platform_owned === true || table.kept_by_the_app === true;
|
|
457
|
+
}
|
|
458
|
+
function isValueSet(table) {
|
|
459
|
+
if (table.kept_for === "choices") return true;
|
|
460
|
+
return table.kept_for == null && table.kind === "list" && keptByPlatform(table);
|
|
461
|
+
}
|
|
462
|
+
function isKeptTable(table) {
|
|
463
|
+
return keptByPlatform(table) || isValueSet(table) || Boolean(table.work_kind) || (table.slug ?? "").startsWith(PLATFORM_TABLE_PREFIX);
|
|
464
|
+
}
|
|
465
|
+
var CHOICES_SUFFIX = / choices$/i;
|
|
466
|
+
function tablePickerEntries(tables, options = {}) {
|
|
467
|
+
const byId = new Map(tables.map((t) => [t.id, t]));
|
|
468
|
+
const lists = tables.filter(isValueSet);
|
|
469
|
+
const listLabel = (t) => {
|
|
470
|
+
const column = t.name.replace(CHOICES_SUFFIX, "");
|
|
471
|
+
if (!t.used_in_table_id || t.used_in_table_id === t.id) {
|
|
472
|
+
const made = madeOn(t.created_at);
|
|
473
|
+
return made ? `${column} \u2014 no column \xB7 ${made}` : `${column} \u2014 no column`;
|
|
474
|
+
}
|
|
475
|
+
const owner = byId.get(t.used_in_table_id);
|
|
476
|
+
if (!owner) return t.name;
|
|
477
|
+
return `${column} \u2014 ${owner.name}`;
|
|
478
|
+
};
|
|
479
|
+
const plain = tables.filter((t) => !isKeptTable(t) || t.id === options.keep && !isValueSet(t)).map((t) => ({ table: t, id: t.id, label: t.name, isList: false })).sort((a, b) => a.label.localeCompare(b.label));
|
|
480
|
+
const shownLists = lists.filter((t) => options.showLists || t.id === options.keep).map((t) => ({ table: t, id: t.id, label: listLabel(t), isList: true })).sort((a, b) => a.label.localeCompare(b.label));
|
|
481
|
+
const entries = [...plain, ...shownLists];
|
|
482
|
+
const seen = /* @__PURE__ */ new Map();
|
|
483
|
+
for (const entry of entries) {
|
|
484
|
+
const key = entry.label.toLowerCase();
|
|
485
|
+
const n = (seen.get(key) ?? 0) + 1;
|
|
486
|
+
seen.set(key, n);
|
|
487
|
+
if (n > 1) entry.label = `${entry.label} (${n})`;
|
|
488
|
+
}
|
|
489
|
+
return { entries, listCount: lists.length };
|
|
490
|
+
}
|
|
491
|
+
function madeOn(stamp) {
|
|
492
|
+
if (!stamp) return null;
|
|
493
|
+
const at = new Date(stamp);
|
|
494
|
+
if (Number.isNaN(at.getTime())) return null;
|
|
495
|
+
return new Intl.DateTimeFormat(void 0, { month: "short", day: "numeric", year: "numeric" }).format(at);
|
|
496
|
+
}
|
|
497
|
+
function styleFromDecorations(decorations, fields) {
|
|
498
|
+
if (!decorations) return { version: 1 };
|
|
499
|
+
const keyOf = (id) => fields.find((f) => f.id === id)?.key;
|
|
500
|
+
const style = { version: 1 };
|
|
501
|
+
if (decorations.color_by?.field) {
|
|
502
|
+
const field = keyOf(decorations.color_by.field);
|
|
503
|
+
if (field) style.colorBy = { field, target: decorations.color_by.target };
|
|
504
|
+
}
|
|
505
|
+
if (Array.isArray(decorations.rules) && decorations.rules.length > 0) {
|
|
506
|
+
const rules = [];
|
|
507
|
+
decorations.rules.forEach((rule, index) => {
|
|
508
|
+
const field = keyOf(rule.field);
|
|
509
|
+
if (!field) return;
|
|
510
|
+
rules.push({
|
|
511
|
+
id: rule.id ?? `decoration:${index}`,
|
|
512
|
+
field,
|
|
513
|
+
op: rule.op,
|
|
514
|
+
color: rule.color,
|
|
515
|
+
target: rule.target,
|
|
516
|
+
...rule.value === void 0 || rule.value === null ? {} : { value: String(rule.value) }
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
if (rules.length > 0) style.rules = rules;
|
|
520
|
+
}
|
|
521
|
+
if (decorations.rows && Object.keys(decorations.rows).length > 0) {
|
|
522
|
+
style.rows = { ...decorations.rows };
|
|
523
|
+
}
|
|
524
|
+
const columns = {};
|
|
525
|
+
for (const [id, color] of Object.entries(decorations.columns ?? {})) {
|
|
526
|
+
const key = keyOf(id);
|
|
527
|
+
if (key) columns[key] = color;
|
|
528
|
+
}
|
|
529
|
+
if (Object.keys(columns).length > 0) style.columns = columns;
|
|
530
|
+
const cells = {};
|
|
531
|
+
for (const [rowId, byField] of Object.entries(decorations.cells ?? {})) {
|
|
532
|
+
for (const [id, color] of Object.entries(byField ?? {})) {
|
|
533
|
+
const key = keyOf(id);
|
|
534
|
+
if (!key) continue;
|
|
535
|
+
(cells[rowId] ??= {})[key] = color;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (Object.keys(cells).length > 0) style.cells = cells;
|
|
539
|
+
return style;
|
|
540
|
+
}
|
|
541
|
+
function resolveTableStyle(decorations, fields, viewStyle) {
|
|
542
|
+
const base = styleFromDecorations(decorations, fields);
|
|
543
|
+
if (!viewStyle) return base;
|
|
544
|
+
const colorBy = viewStyle.colorBy ?? base.colorBy;
|
|
545
|
+
const rules = viewStyle.rules ?? base.rules;
|
|
546
|
+
const rows = viewStyle.rows ?? base.rows;
|
|
547
|
+
const columns = viewStyle.columns ?? base.columns;
|
|
548
|
+
const cells = viewStyle.cells ?? base.cells;
|
|
549
|
+
return {
|
|
550
|
+
version: 1,
|
|
551
|
+
...colorBy !== void 0 ? { colorBy } : {},
|
|
552
|
+
...rules !== void 0 ? { rules } : {},
|
|
553
|
+
...rows !== void 0 ? { rows } : {},
|
|
554
|
+
...columns !== void 0 ? { columns } : {},
|
|
555
|
+
...cells !== void 0 ? { cells } : {}
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
Object.defineProperty(exports, "DEFAULT_FIELDS", {
|
|
560
|
+
enumerable: true,
|
|
561
|
+
get: function () { return core.DEFAULT_FIELDS; }
|
|
562
|
+
});
|
|
563
|
+
Object.defineProperty(exports, "addFields", {
|
|
564
|
+
enumerable: true,
|
|
565
|
+
get: function () { return core.addFields; }
|
|
566
|
+
});
|
|
567
|
+
Object.defineProperty(exports, "declareTable", {
|
|
568
|
+
enumerable: true,
|
|
569
|
+
get: function () { return core.declareTable; }
|
|
570
|
+
});
|
|
571
|
+
Object.defineProperty(exports, "fieldDeclarationFor", {
|
|
572
|
+
enumerable: true,
|
|
573
|
+
get: function () { return core.fieldDeclarationFor; }
|
|
574
|
+
});
|
|
575
|
+
Object.defineProperty(exports, "tokenFor", {
|
|
576
|
+
enumerable: true,
|
|
577
|
+
get: function () { return core.tokenFor; }
|
|
578
|
+
});
|
|
579
|
+
exports.NO_RIGHTS = NO_RIGHTS;
|
|
580
|
+
exports.PARITY_LABEL = PARITY_LABEL;
|
|
581
|
+
exports.PARITY_MADE_OF = PARITY_MADE_OF;
|
|
582
|
+
exports.SOMEBODY = SOMEBODY;
|
|
583
|
+
exports.THE_SYSTEM = THE_SYSTEM;
|
|
584
|
+
exports.UNREAD_RECORD_LABEL = UNREAD_RECORD_LABEL;
|
|
585
|
+
exports.WITHHELD_RECORD_LABEL = WITHHELD_RECORD_LABEL;
|
|
586
|
+
exports.WITHHELD_WORD = WITHHELD_WORD;
|
|
587
|
+
exports.actorBadge = actorBadge;
|
|
588
|
+
exports.actorWords = actorWords;
|
|
589
|
+
exports.cardTitleField = cardTitleField;
|
|
590
|
+
exports.editorKindFor = editorKindFor;
|
|
591
|
+
exports.fieldName = fieldName;
|
|
592
|
+
exports.fieldTypeLabel = fieldTypeLabel;
|
|
593
|
+
exports.getPersonTimeZone = getPersonTimeZone;
|
|
594
|
+
exports.isKeptTable = isKeptTable;
|
|
595
|
+
exports.isValueSet = isValueSet;
|
|
596
|
+
exports.lastChangeSentence = lastChangeSentence;
|
|
597
|
+
exports.looksLikeId = looksLikeId;
|
|
598
|
+
exports.personActor = personActor;
|
|
599
|
+
exports.personWallNow = personWallNow;
|
|
600
|
+
exports.pickableTypeOf = pickableTypeOf;
|
|
601
|
+
exports.recordName = recordName;
|
|
602
|
+
exports.recordNameIn = recordNameIn;
|
|
603
|
+
exports.recordsDataSource = recordsDataSource;
|
|
604
|
+
exports.resolveTableStyle = resolveTableStyle;
|
|
605
|
+
exports.rowName = rowName;
|
|
606
|
+
exports.rowNameIn = rowNameIn;
|
|
607
|
+
exports.setPersonTimeZone = setPersonTimeZone;
|
|
608
|
+
exports.singularNoun = singularNoun;
|
|
609
|
+
exports.styleFromDecorations = styleFromDecorations;
|
|
610
|
+
exports.subscribePersonTimeZone = subscribePersonTimeZone;
|
|
611
|
+
exports.tableName = tableName;
|
|
612
|
+
exports.tablePickerEntries = tablePickerEntries;
|
|
613
|
+
exports.tableRightsAt = tableRightsAt;
|
|
614
|
+
exports.whenWords = whenWords;
|
|
615
|
+
exports.withheldNoticeOf = withheldNoticeOf;
|
|
616
|
+
exports.withheldSentence = withheldSentence;
|
|
617
|
+
//# sourceMappingURL=helpers.cjs.map
|
|
618
|
+
//# sourceMappingURL=helpers.cjs.map
|