@ai-matrx/records-ui 0.115.75 → 0.115.77

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/dist/pickers.cjs CHANGED
@@ -2520,9 +2520,7 @@ var NOT_TYPED_BY_NAME = /* @__PURE__ */ new Set([
2520
2520
  "fx.lte",
2521
2521
  "fx.gt",
2522
2522
  "fx.gte",
2523
- "fx.autonumber",
2524
- "fx.created_time",
2525
- "fx.modified_time"
2523
+ "fx.autonumber"
2526
2524
  ]);
2527
2525
  function formulaFunctionNames(kinds) {
2528
2526
  return [...new Set(kinds.map((row) => row.node))].filter((node) => node.startsWith("fx.") && !NOT_TYPED_BY_NAME.has(node)).map((node) => node.slice(3).toUpperCase()).sort();
@@ -3375,13 +3373,38 @@ var CONTEXT_POLICY_LABEL = {
3375
3373
  exclude: "No, keep it away from assistants"
3376
3374
  };
3377
3375
  var SUBSCRIPTION_CADENCES = exhaustiveValuesOf()(["instant", "hourly", "daily", "weekly"]);
3378
- var ROLLUP_AGG_VALUES = ["count", "sum", "avg", "min", "max"];
3376
+ var ROLLUP_AGG_VALUES = [
3377
+ "count",
3378
+ "sum",
3379
+ "avg",
3380
+ "min",
3381
+ "max",
3382
+ // AIRTABLE PARITY (lane AT-FORMULA): the aggregates that read the linked values as they are.
3383
+ "countall",
3384
+ "counta",
3385
+ "and",
3386
+ "or",
3387
+ "xor",
3388
+ "arrayjoin",
3389
+ "concatenate",
3390
+ "arrayunique",
3391
+ "arraycompact"
3392
+ ];
3379
3393
  var ROLLUP_AGG_LABEL = {
3380
3394
  count: "How many there are",
3381
3395
  sum: "The total of",
3382
3396
  avg: "The average of",
3383
3397
  min: "The smallest",
3384
- max: "The largest"
3398
+ max: "The largest",
3399
+ countall: "How many values, empty ones too",
3400
+ counta: "How many values are filled in",
3401
+ and: "Yes when every value is yes",
3402
+ or: "Yes when any value is yes",
3403
+ xor: "Yes when an odd number are yes",
3404
+ arrayjoin: "All the values, joined with commas",
3405
+ concatenate: "All the values, run together",
3406
+ arrayunique: "Each different value once",
3407
+ arraycompact: "The values that are filled in"
3385
3408
  };
3386
3409
  var FORMULA_OP_VALUES = ["concat", "add", "sub", "mul", "div"];
3387
3410
  var FORMULA_OP_LABEL = {
@@ -3973,20 +3996,19 @@ function rememberHostFileIds(ids) {
3973
3996
  }
3974
3997
  var FILE_CHIP_UNREAD = "File";
3975
3998
  var FILE_CHIP_NO_PAGE = "This screen has no way to open files yet.";
3976
- function AttachmentChip({ id, onRemove }) {
3999
+ function useFileRead(id) {
3977
4000
  const client = (0, import_react32.useOptionalRecordsClient)();
3978
- const host = useRecordsUi();
3979
4001
  const [read, setRead] = (0, import_react31.useState)(null);
3980
4002
  (0, import_react31.useEffect)(() => {
3981
4003
  let cancelled = false;
3982
- if (!client) {
3983
- setRead({ name: null, fileId: null, why: null });
3984
- return;
3985
- }
3986
4004
  if (handedHostFileIds.has(id)) {
3987
4005
  setRead({ name: null, fileId: id, why: null });
3988
4006
  return;
3989
4007
  }
4008
+ if (!client) {
4009
+ setRead({ name: null, fileId: null, why: null });
4010
+ return;
4011
+ }
3990
4012
  void client.recordRead({ record_id: id }).then((answered) => {
3991
4013
  if (cancelled) return;
3992
4014
  if (!answered.ok) {
@@ -4002,11 +4024,30 @@ function AttachmentChip({ id, onRemove }) {
4002
4024
  cancelled = true;
4003
4025
  };
4004
4026
  }, [client, id]);
4027
+ return read;
4028
+ }
4029
+ function AttachmentChip({ id, onRemove, onPreview }) {
4030
+ const host = useRecordsUi();
4031
+ const read = useFileRead(id);
4005
4032
  const words = read?.name ?? (read ? FILE_CHIP_UNREAD : "\u2026");
4006
4033
  const href = read?.fileId ? host.hrefForFile?.({ fileId: read.fileId }) : void 0;
4007
- const title = href ? `Open ${words}` : read?.why ?? FILE_CHIP_NO_PAGE;
4034
+ const title = onPreview ? `Preview ${words}` : href ? `Open ${words}` : read?.why ?? FILE_CHIP_NO_PAGE;
4008
4035
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_design_system19.Badge, { variant: "secondary", className: "max-w-[14rem] gap-1 text-[11px]", "data-records-file-chip": id, title, children: [
4009
- href ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4036
+ onPreview ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4037
+ "button",
4038
+ {
4039
+ type: "button",
4040
+ className: "truncate underline-offset-2 hover:underline",
4041
+ "data-records-file-preview": id,
4042
+ onClick: (event) => {
4043
+ event.stopPropagation();
4044
+ onPreview();
4045
+ },
4046
+ onMouseDown: (event) => event.stopPropagation(),
4047
+ onDoubleClick: (event) => event.stopPropagation(),
4048
+ children: words
4049
+ }
4050
+ ) : href ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4010
4051
  import_outbound_react4.OutboundLink,
4011
4052
  {
4012
4053
  href,
@@ -4033,9 +4074,70 @@ function AttachmentChip({ id, onRemove }) {
4033
4074
  ] });
4034
4075
  }
4035
4076
  function AttachmentChips({ value, onRemove }) {
4077
+ const host = useRecordsUi();
4036
4078
  const ids = attachmentIdsOf(value);
4079
+ const [viewing, setViewing] = (0, import_react31.useState)(null);
4037
4080
  if (ids.length === 0) return null;
4038
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex flex-wrap gap-1", "data-records-file-chips": "", children: ids.map((id) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AttachmentChip, { id, ...onRemove ? { onRemove: () => onRemove(id) } : {} }, id)) });
4081
+ const canPreview = Boolean(host.fileImage);
4082
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "flex flex-wrap gap-1", "data-records-file-chips": "", children: [
4083
+ ids.map((id, index) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4084
+ AttachmentChip,
4085
+ {
4086
+ id,
4087
+ ...onRemove ? { onRemove: () => onRemove(id) } : {},
4088
+ ...canPreview ? { onPreview: () => setViewing(index) } : {}
4089
+ },
4090
+ id
4091
+ )),
4092
+ viewing !== null && canPreview ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(AttachmentViewer, { ids, index: Math.min(viewing, ids.length - 1), onIndex: setViewing, onClose: () => setViewing(null) }) : null
4093
+ ] });
4094
+ }
4095
+ function stepAttachment(index, total, way) {
4096
+ return Math.max(0, Math.min(total - 1, index + way));
4097
+ }
4098
+ function AttachmentViewer({
4099
+ ids,
4100
+ index,
4101
+ onIndex,
4102
+ onClose
4103
+ }) {
4104
+ const host = useRecordsUi();
4105
+ const id = ids[index];
4106
+ const read = useFileRead(id);
4107
+ const name = read?.name ?? (read ? FILE_CHIP_UNREAD : "\u2026");
4108
+ const href = read?.fileId ? host.hrefForFile?.({ fileId: read.fileId }) : void 0;
4109
+ const go = (way) => onIndex(stepAttachment(index, ids.length, way));
4110
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system19.Dialog, { open: true, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4111
+ import_design_system19.DialogContent,
4112
+ {
4113
+ className: "sm:max-w-3xl",
4114
+ "data-records-attachment-viewer": id,
4115
+ onKeyDown: (event) => {
4116
+ if (event.key === "ArrowLeft") {
4117
+ event.preventDefault();
4118
+ go(-1);
4119
+ } else if (event.key === "ArrowRight") {
4120
+ event.preventDefault();
4121
+ go(1);
4122
+ }
4123
+ },
4124
+ onClick: (event) => event.stopPropagation(),
4125
+ children: [
4126
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system19.DialogHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system19.DialogTitle, { className: "truncate text-sm", children: name }) }),
4127
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "relative h-[60vh] w-full overflow-hidden rounded-md bg-muted", "data-records-attachment-stage": "", children: read?.fileId ? host.fileImage?.({ fileId: read.fileId, alt: name }) : null }),
4128
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
4129
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system19.Button, { size: "sm", variant: "outline", "aria-label": "Previous file", disabled: index === 0, onClick: () => go(-1), "data-records-attachment-previous": "", children: "\u2190" }),
4130
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_design_system19.Button, { size: "sm", variant: "outline", "aria-label": "Next file", disabled: index >= ids.length - 1, onClick: () => go(1), "data-records-attachment-next": "", children: "\u2192" }),
4131
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { className: "text-xs tabular-nums text-muted-foreground", "data-records-attachment-position": "", children: [
4132
+ index + 1,
4133
+ " of ",
4134
+ ids.length
4135
+ ] }),
4136
+ href ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_outbound_react4.OutboundLink, { href, className: "ml-auto text-xs underline-offset-2 hover:underline", children: "Open in new tab" }) : null
4137
+ ] })
4138
+ ]
4139
+ }
4140
+ ) });
4039
4141
  }
4040
4142
  function hostTakesFiles(host) {
4041
4143
  return Boolean(host.pickFiles || host.upload);
@@ -6229,6 +6331,8 @@ function FieldEditor(props) {
6229
6331
  const [statusGroups, setStatusGroups] = (0, import_react44.useState)({});
6230
6332
  const [statusKeys, setStatusKeys] = (0, import_react44.useState)({});
6231
6333
  const [symbology, setSymbology] = (0, import_react44.useState)(String(start?.config?.["symbology"] ?? ""));
6334
+ const descriptionStart = String(start?.config?.["description"] ?? "");
6335
+ const [description, setDescription] = (0, import_react44.useState)(descriptionStart);
6232
6336
  const buttonStart = (0, import_react44.useMemo)(() => (0, import_records24.buttonOf)(start) ?? { label: "" }, [start]);
6233
6337
  const [buttonSettings, setButtonSettings] = (0, import_react44.useState)(buttonStart);
6234
6338
  const prefixStart = (0, import_records24.autonumberPrefixOf)(start);
@@ -6378,6 +6482,7 @@ function FieldEditor(props) {
6378
6482
  const defaultChanged = takesADefault(field) && JSON.stringify(defaultNow) !== JSON.stringify(defaultToKeep(field.default));
6379
6483
  const patch = {
6380
6484
  ...defaultChanged ? { default: defaultNow } : {},
6485
+ ...description.trim() !== descriptionStart.trim() ? { description: description.trim() === "" ? null : description.trim() } : {},
6381
6486
  // The column's look travels in the same settings patch (`display_format`), so a save is
6382
6487
  // still one store call. `FieldPatch` does not declare the key yet (store gap, reported);
6383
6488
  // the door has carried it since the Sheet's seam started writing it.
@@ -6584,6 +6689,19 @@ function FieldEditor(props) {
6584
6689
  }
6585
6690
  ) : null
6586
6691
  ] }),
6692
+ field ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("label", { className: "flex flex-col gap-1 sm:col-span-2", children: [
6693
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_design_system29.Label, { className: "text-xs", children: "Help text" }),
6694
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
6695
+ import_design_system29.BasicInput,
6696
+ {
6697
+ value: description,
6698
+ maxLength: 300,
6699
+ "aria-label": "Help text",
6700
+ placeholder: "Shown when the column name is hovered",
6701
+ onChange: (e) => setDescription(e.target.value)
6702
+ }
6703
+ )
6704
+ ] }) : null,
6587
6705
  /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-1", children: [
6588
6706
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_design_system29.Label, { className: "text-xs", children: "What it holds" }),
6589
6707
  /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(