@ai-matrx/records-ui 0.115.78 → 0.115.80
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 +29 -1
- package/dist/index.cjs +220 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +578 -428
- package/dist/index.js.map +1 -1
- package/dist/pickers.cjs +129 -49
- package/dist/pickers.cjs.map +1 -1
- package/dist/pickers.js +162 -82
- package/dist/pickers.js.map +1 -1
- package/package.json +5 -5
package/dist/pickers.cjs
CHANGED
|
@@ -4032,46 +4032,63 @@ function AttachmentChip({ id, onRemove, onPreview }) {
|
|
|
4032
4032
|
const words = read?.name ?? (read ? FILE_CHIP_UNREAD : "\u2026");
|
|
4033
4033
|
const href = read?.fileId ? host.hrefForFile?.({ fileId: read.fileId }) : void 0;
|
|
4034
4034
|
const title = onPreview ? `Preview ${words}` : href ? `Open ${words}` : read?.why ?? FILE_CHIP_NO_PAGE;
|
|
4035
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4035
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
4036
|
+
import_design_system19.Badge,
|
|
4037
|
+
{
|
|
4038
|
+
variant: "secondary",
|
|
4039
|
+
className: (0, import_design_system19.cn)("max-w-[14rem] gap-1 text-[11px]", onPreview && "cursor-pointer"),
|
|
4040
|
+
"data-records-file-chip": id,
|
|
4041
|
+
title,
|
|
4042
|
+
...onPreview ? {
|
|
4042
4043
|
onClick: (event) => {
|
|
4043
4044
|
event.stopPropagation();
|
|
4044
4045
|
onPreview();
|
|
4045
4046
|
},
|
|
4046
4047
|
onMouseDown: (event) => event.stopPropagation(),
|
|
4047
|
-
onDoubleClick: (event) => event.stopPropagation()
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4048
|
+
onDoubleClick: (event) => event.stopPropagation()
|
|
4049
|
+
} : {},
|
|
4050
|
+
children: [
|
|
4051
|
+
onPreview ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4052
|
+
"button",
|
|
4053
|
+
{
|
|
4054
|
+
type: "button",
|
|
4055
|
+
className: "truncate underline-offset-2 hover:underline",
|
|
4056
|
+
"data-records-file-preview": id,
|
|
4057
|
+
onClick: (event) => {
|
|
4058
|
+
event.stopPropagation();
|
|
4059
|
+
onPreview();
|
|
4060
|
+
},
|
|
4061
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
4062
|
+
onDoubleClick: (event) => event.stopPropagation(),
|
|
4063
|
+
children: words
|
|
4064
|
+
}
|
|
4065
|
+
) : href ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4066
|
+
import_outbound_react4.OutboundLink,
|
|
4067
|
+
{
|
|
4068
|
+
href,
|
|
4069
|
+
className: "truncate underline-offset-2 hover:underline",
|
|
4070
|
+
"data-records-file-link": read?.fileId ?? "",
|
|
4071
|
+
onClick: (event) => event.stopPropagation(),
|
|
4072
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
4073
|
+
children: words
|
|
4074
|
+
}
|
|
4075
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "truncate", children: words }),
|
|
4076
|
+
onRemove ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
4077
|
+
"button",
|
|
4078
|
+
{
|
|
4079
|
+
type: "button",
|
|
4080
|
+
"aria-label": `Remove ${words}`,
|
|
4081
|
+
className: "shrink-0 rounded px-0.5 text-muted-foreground hover:text-foreground",
|
|
4082
|
+
onClick: (event) => {
|
|
4083
|
+
event.stopPropagation();
|
|
4084
|
+
onRemove();
|
|
4085
|
+
},
|
|
4086
|
+
children: "\xD7"
|
|
4087
|
+
}
|
|
4088
|
+
) : null
|
|
4089
|
+
]
|
|
4090
|
+
}
|
|
4091
|
+
);
|
|
4075
4092
|
}
|
|
4076
4093
|
function AttachmentChips({ value, onRemove }) {
|
|
4077
4094
|
const host = useRecordsUi();
|
|
@@ -4112,6 +4129,18 @@ function AttachmentViewer({
|
|
|
4112
4129
|
{
|
|
4113
4130
|
className: "sm:max-w-3xl",
|
|
4114
4131
|
"data-records-attachment-viewer": id,
|
|
4132
|
+
tabIndex: -1,
|
|
4133
|
+
onOpenAutoFocus: (event) => {
|
|
4134
|
+
event.preventDefault();
|
|
4135
|
+
event.currentTarget.focus();
|
|
4136
|
+
},
|
|
4137
|
+
onCloseAutoFocus: (event) => {
|
|
4138
|
+
const chip = document.querySelector(`[data-records-file-preview="${ids[index]}"]`);
|
|
4139
|
+
if (chip) {
|
|
4140
|
+
event.preventDefault();
|
|
4141
|
+
chip.focus();
|
|
4142
|
+
}
|
|
4143
|
+
},
|
|
4115
4144
|
onKeyDown: (event) => {
|
|
4116
4145
|
if (event.key === "ArrowLeft") {
|
|
4117
4146
|
event.preventDefault();
|
|
@@ -4749,6 +4778,33 @@ var CONDITION_OPS = [
|
|
|
4749
4778
|
{ op: "gte", label: "is at least" },
|
|
4750
4779
|
{ op: "lte", label: "is at most" }
|
|
4751
4780
|
];
|
|
4781
|
+
var LIST_TERMS = [
|
|
4782
|
+
{ op: "contains", label: "contains" },
|
|
4783
|
+
{ op: "not_contains", label: "does not contain" },
|
|
4784
|
+
{ op: "is_empty", label: "is empty" },
|
|
4785
|
+
{ op: "is_not_empty", label: "is not empty" }
|
|
4786
|
+
];
|
|
4787
|
+
var VALUELESS_OPS = /* @__PURE__ */ new Set(["present", "is_empty", "is_not_empty"]);
|
|
4788
|
+
var LINK_SEP = ">";
|
|
4789
|
+
function linkedNode(viaId, innerId, op, value) {
|
|
4790
|
+
return {
|
|
4791
|
+
op: "linked",
|
|
4792
|
+
match: op === "eq" ? "any" : "none",
|
|
4793
|
+
args: [{ field: viaId }, { op: "eq", args: [{ field: innerId }, { const: value }] }]
|
|
4794
|
+
};
|
|
4795
|
+
}
|
|
4796
|
+
function linkedOf(expr) {
|
|
4797
|
+
const node = asNode(expr);
|
|
4798
|
+
if (!node || node["op"] !== "linked") return null;
|
|
4799
|
+
const [rel, cond] = argsOf(node);
|
|
4800
|
+
const innerNode = asNode(cond);
|
|
4801
|
+
const [a, b] = innerNode ? argsOf(innerNode) : [];
|
|
4802
|
+
const match = node["match"] ?? "any";
|
|
4803
|
+
if (!rel || typeof rel["field"] !== "string" || !innerNode || innerNode["op"] !== "eq") return null;
|
|
4804
|
+
if (!a || typeof a["field"] !== "string" || !b || !("const" in b)) return null;
|
|
4805
|
+
if (match !== "any" && match !== "none") return null;
|
|
4806
|
+
return { via: rel["field"], inner: a["field"], op: match === "any" ? "eq" : "ne", value: b["const"] };
|
|
4807
|
+
}
|
|
4752
4808
|
var FilterTermsContext = (0, import_react36.createContext)(false);
|
|
4753
4809
|
var RELATIVE_DAY_TERMS = [
|
|
4754
4810
|
{ kind: "today", label: "is today" },
|
|
@@ -4861,13 +4917,15 @@ function ChoiceValue({ field, value, onChange }) {
|
|
|
4861
4917
|
}
|
|
4862
4918
|
function conditionIsSimple(expr) {
|
|
4863
4919
|
if (expr === null || expr === void 0) return true;
|
|
4864
|
-
if (relativeDayOf(expr) !== null || meOf(expr) !== null) return true;
|
|
4920
|
+
if (relativeDayOf(expr) !== null || meOf(expr) !== null || linkedOf(expr) !== null) return true;
|
|
4865
4921
|
const node = expr;
|
|
4866
|
-
if (typeof node.op !== "string" || !CONDITION_OPS.some((c) => c.op === node.op))
|
|
4922
|
+
if (typeof node.op !== "string" || !(CONDITION_OPS.some((c) => c.op === node.op) || LIST_TERMS.some((c) => c.op === node.op))) {
|
|
4923
|
+
return false;
|
|
4924
|
+
}
|
|
4867
4925
|
if (!Array.isArray(node.args)) return false;
|
|
4868
4926
|
const first = node.args[0];
|
|
4869
4927
|
if (!first || typeof first.field !== "string") return false;
|
|
4870
|
-
if (node.op
|
|
4928
|
+
if (VALUELESS_OPS.has(node.op)) return node.args.length === 1;
|
|
4871
4929
|
const second = node.args[1];
|
|
4872
4930
|
return node.args.length === 2 && !!second && "const" in second;
|
|
4873
4931
|
}
|
|
@@ -4885,7 +4943,13 @@ function conditionInWords(expr, fields) {
|
|
|
4885
4943
|
lte: "is at most",
|
|
4886
4944
|
and: "and",
|
|
4887
4945
|
or: "or",
|
|
4888
|
-
matches: "matches"
|
|
4946
|
+
matches: "matches",
|
|
4947
|
+
contains: "contains",
|
|
4948
|
+
not_contains: "does not contain",
|
|
4949
|
+
has_any: "has any of",
|
|
4950
|
+
has_all: "has all of",
|
|
4951
|
+
has_none: "has none of",
|
|
4952
|
+
is_exactly: "is exactly"
|
|
4889
4953
|
};
|
|
4890
4954
|
const walk = (node) => {
|
|
4891
4955
|
if (node === null || node === void 0) return "nothing";
|
|
@@ -4906,6 +4970,12 @@ function conditionInWords(expr, fields) {
|
|
|
4906
4970
|
const args = Array.isArray(n["args"]) ? n["args"] : [];
|
|
4907
4971
|
if (op === "not") return `not ${walk(args[0])}`;
|
|
4908
4972
|
if (op === "present") return `${walk(args[0])} has been answered`;
|
|
4973
|
+
if (op === "is_empty") return `${walk(args[0])} is empty`;
|
|
4974
|
+
if (op === "is_not_empty") return `${walk(args[0])} is not empty`;
|
|
4975
|
+
if (op === "linked") {
|
|
4976
|
+
const how = n["match"] === "every" ? "every" : n["match"] === "none" ? "none of" : "any";
|
|
4977
|
+
return `${how} linked record in ${walk(args[0])}: ${walk(args[1])}`;
|
|
4978
|
+
}
|
|
4909
4979
|
const dayRange = relativeDayOf(n);
|
|
4910
4980
|
if (dayRange) {
|
|
4911
4981
|
const term = RELATIVE_DAY_TERMS.find((t) => t.kind === dayRange.kind);
|
|
@@ -4938,12 +5008,14 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
4938
5008
|
const filterTerms = (0, import_react36.useContext)(FilterTermsContext);
|
|
4939
5009
|
const relative = relativeDayOf(expr);
|
|
4940
5010
|
const reader = meOf(expr);
|
|
4941
|
-
const
|
|
4942
|
-
const
|
|
4943
|
-
const
|
|
5011
|
+
const linked = linkedOf(expr);
|
|
5012
|
+
const op = relative ? `rel:${relative.kind}` : reader ? reader.which : linked ? linked.op : node?.op ?? "";
|
|
5013
|
+
const onField = linked ? `${linked.via}${LINK_SEP}${linked.inner}` : node?.args?.[0]?.field ?? "";
|
|
5014
|
+
const value = relative || reader ? "" : linked ? linked.value : node?.args?.[1]?.const ?? "";
|
|
4944
5015
|
const chosen = fields.find((f) => f.id === onField);
|
|
4945
5016
|
const chosenKind = chosen?.field ? String(editorKindFor(chosen.field)) : "";
|
|
4946
|
-
const extraTerms = !filterTerms ? [] : chosenKind === "datetime" ? RELATIVE_DAY_TERMS.map((t) => ({ op: `rel:${t.kind}`, label: t.label })) : chosenKind === "member" ? [{ op: "me", label: "is me" }, { op: "not_me", label: "is not me" }] : [];
|
|
5017
|
+
const extraTerms = !filterTerms || chosen?.via ? [] : chosenKind === "datetime" ? RELATIVE_DAY_TERMS.map((t) => ({ op: `rel:${t.kind}`, label: t.label })) : chosenKind === "member" ? [{ op: "me", label: "is me" }, { op: "not_me", label: "is not me" }] : chosenKind === "multi_select" ? LIST_TERMS : [];
|
|
5018
|
+
const baseTerms = chosen?.via ? CONDITION_OPS.filter((c) => c.op === "eq" || c.op === "ne") : CONDITION_OPS;
|
|
4947
5019
|
const relativeTerm = relative ? RELATIVE_DAY_TERMS.find((t) => t.kind === relative.kind) : void 0;
|
|
4948
5020
|
function write(next) {
|
|
4949
5021
|
const picked = next.op;
|
|
@@ -4956,8 +5028,11 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
4956
5028
|
onChange(relativeDayNode(fieldNow, relative.kind, next.days));
|
|
4957
5029
|
return;
|
|
4958
5030
|
}
|
|
4959
|
-
if (next.field !== void 0 && (relative || reader)) {
|
|
4960
|
-
|
|
5031
|
+
if (next.field !== void 0 && (relative || reader || linked)) {
|
|
5032
|
+
const target2 = fields.find((f) => f.id === next.field);
|
|
5033
|
+
onChange(
|
|
5034
|
+
!next.field ? null : target2?.via ? linkedNode(target2.via.id, target2.via.fieldId, "eq", "") : { op: CONDITION_OPS[0].op, args: [{ field: next.field }, { const: "" }] }
|
|
5035
|
+
);
|
|
4961
5036
|
return;
|
|
4962
5037
|
}
|
|
4963
5038
|
const chosenOp = next.op ?? (op || (next.field ? CONDITION_OPS[0].op : ""));
|
|
@@ -4967,8 +5042,13 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
4967
5042
|
onChange(null);
|
|
4968
5043
|
return;
|
|
4969
5044
|
}
|
|
5045
|
+
const target = fields.find((f) => f.id === chosenField);
|
|
5046
|
+
if (target?.via) {
|
|
5047
|
+
onChange(linkedNode(target.via.id, target.via.fieldId, chosenOp === "ne" ? "ne" : "eq", chosenValue));
|
|
5048
|
+
return;
|
|
5049
|
+
}
|
|
4970
5050
|
onChange(
|
|
4971
|
-
chosenOp
|
|
5051
|
+
VALUELESS_OPS.has(chosenOp) ? { op: chosenOp, args: [{ field: chosenField }] } : { op: chosenOp, args: [{ field: chosenField }, { const: chosenValue }] }
|
|
4972
5052
|
);
|
|
4973
5053
|
}
|
|
4974
5054
|
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: className ?? "flex flex-wrap items-center gap-1 text-xs", children: [
|
|
@@ -5001,7 +5081,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5001
5081
|
className: "h-8 w-auto min-w-[6rem] text-xs",
|
|
5002
5082
|
value: op,
|
|
5003
5083
|
onChange: (next) => write({ op: next }),
|
|
5004
|
-
options: [...
|
|
5084
|
+
options: [...baseTerms, ...extraTerms].map((c) => ({ value: c.op, label: c.label })),
|
|
5005
5085
|
attrs: { "data-condition": "comparison" }
|
|
5006
5086
|
}
|
|
5007
5087
|
),
|
|
@@ -5015,7 +5095,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5015
5095
|
value: String(relative.days ?? 7),
|
|
5016
5096
|
onChange: (e) => write({ days: Math.max(0, Math.floor(Number(e.target.value) || 0)) })
|
|
5017
5097
|
}
|
|
5018
|
-
) : null : reader || op
|
|
5098
|
+
) : null : reader || VALUELESS_OPS.has(op) ? null : pointsAtARecord(chosen) ? (
|
|
5019
5099
|
// A RECORD IS PICKED BY ITS WORDS, NEVER TYPED AS AN ID. The same picker the
|
|
5020
5100
|
// cell uses, on the same `relation_words_many` the cell reads, so a filter and
|
|
5021
5101
|
// the column it filters can never disagree about what a record is called. It
|
|
@@ -5033,7 +5113,7 @@ function ConditionRow({ lead, expr, fields, onChange, emptyLabel = "always", cla
|
|
|
5033
5113
|
allowCreate: false
|
|
5034
5114
|
}
|
|
5035
5115
|
)
|
|
5036
|
-
) : isChoiceColumn(chosen) && (op === "eq" || op === "ne") ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ChoiceValue, { field: chosen.field, value: String(value ?? ""), onChange: (word) => write({ value: word }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5116
|
+
) : isChoiceColumn(chosen) && (op === "eq" || op === "ne" || op === "contains" || op === "not_contains") ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ChoiceValue, { field: chosen.field, value: String(value ?? ""), onChange: (word) => write({ value: word }) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
5037
5117
|
import_design_system23.BasicInput,
|
|
5038
5118
|
{
|
|
5039
5119
|
className: "h-8 w-28 text-xs",
|