@ai-matrx/records-ui 0.93.83 → 0.93.85
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 +11 -0
- package/dist/index.cjs +123 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +123 -113
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog — @ai-matrx/records-ui
|
|
2
2
|
|
|
3
|
+
## 0.93.85
|
|
4
|
+
### Unreleased
|
|
5
|
+
|
|
6
|
+
- **The toolbar row's last buttons are never cut: Share folds into the table's menu** (merged-grid leftovers, 2026-09-30): in a table window at its smallest size the row needed 3 px more than it had even with everything folded, and the table menu's button was cut. `toolbarFit` gains fold 4 — the page's own Share moves into the table's menu (where a phone already has it) and comes back when the row has room. A host that draws the page's actions in its own header is unchanged. Proof: `toolbarFit.test.tsx`, red on 0.93.84 (2 of 3). Consumer action: none.
|
|
7
|
+
|
|
8
|
+
## 0.93.84
|
|
9
|
+
|
|
10
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
11
|
+
`git diff npm/records-ui/v0.93.83..npm/records-ui/v0.93.84 -- apps/shared/records-ui`).
|
|
12
|
+
No source changes intended and no consumer action required.
|
|
13
|
+
|
|
3
14
|
## 0.93.83
|
|
4
15
|
|
|
5
16
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
package/dist/index.cjs
CHANGED
|
@@ -1600,111 +1600,114 @@ function RelationPicker({ field: field2, value, onChange, disabled, id, classNam
|
|
|
1600
1600
|
if (q === "") return true;
|
|
1601
1601
|
return `${titleOf(row.id)} ${apart.get(row.id) ?? ""}`.toLowerCase().includes(q);
|
|
1602
1602
|
});
|
|
1603
|
-
return
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
...disabled ? {} : { onRemove: () => toggle(recordId) }
|
|
1609
|
-
},
|
|
1610
|
-
recordId
|
|
1611
|
-
)),
|
|
1612
|
-
disabled ? null : (
|
|
1613
|
-
// THE ONE "PICK OR ADD" BUILDER (PickOrAdd.tsx) — the choice editor draws the same one.
|
|
1614
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1615
|
-
PickOrAdd,
|
|
1603
|
+
return (
|
|
1604
|
+
// Never wider than where it is drawn (a grid cell beside a sticky column: B3-07) — chips truncate, the button wraps.
|
|
1605
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: (0, import_design_system4.cn)("flex min-w-0 max-w-full flex-wrap items-center gap-1", className), children: [
|
|
1606
|
+
picked.map((recordId) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1607
|
+
RecordChip,
|
|
1616
1608
|
{
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
"
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1609
|
+
title: titleOf(recordId),
|
|
1610
|
+
...disabled ? {} : { onRemove: () => toggle(recordId) }
|
|
1611
|
+
},
|
|
1612
|
+
recordId
|
|
1613
|
+
)),
|
|
1614
|
+
disabled ? null : (
|
|
1615
|
+
// THE ONE "PICK OR ADD" BUILDER (PickOrAdd.tsx) — the choice editor draws the same one.
|
|
1616
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1617
|
+
PickOrAdd,
|
|
1618
|
+
{
|
|
1619
|
+
triggerId: id,
|
|
1620
|
+
triggerLabel: `Pick for ${fieldName(field2)}`,
|
|
1621
|
+
triggerText: picked.length === 0 ? "Pick" : "Change",
|
|
1622
|
+
open,
|
|
1623
|
+
onOpenChange: setOpen,
|
|
1624
|
+
search,
|
|
1625
|
+
onSearch: setSearch,
|
|
1626
|
+
items: visible.map((row) => ({
|
|
1627
|
+
id: row.id,
|
|
1628
|
+
label: titleOf(row.id),
|
|
1629
|
+
detail: apart.get(row.id) ?? null,
|
|
1630
|
+
picked: picked.includes(row.id),
|
|
1631
|
+
title: apart.has(row.id) ? `${titleOf(row.id)} \u2014 ${apart.get(row.id)}` : titleOf(row.id),
|
|
1632
|
+
attrs: { "data-relation-candidate": row.id }
|
|
1633
|
+
})),
|
|
1634
|
+
onPick: toggle,
|
|
1635
|
+
notice: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
1636
|
+
refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefusalLine, { error: refusal2, className: "p-2" }) : null,
|
|
1637
|
+
!rows && !refusal2 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading\u2026" }) : null,
|
|
1638
|
+
full ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { className: "p-2 text-xs text-muted-foreground", children: [
|
|
1639
|
+
fieldName(field2),
|
|
1640
|
+
" can point at ",
|
|
1641
|
+
field2.relation_max,
|
|
1642
|
+
" at most. Remove one to add another."
|
|
1643
|
+
] }) : null
|
|
1644
|
+
] }),
|
|
1645
|
+
empty: rows ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Nothing in that table matches." }) : null,
|
|
1646
|
+
add: allowCreate && !full && !targetRefusal && target && !creating ? {
|
|
1647
|
+
label: search.trim() !== "" ? `Create \u201C${search.trim()}\u201D` : `New ${targetWord}`,
|
|
1648
|
+
disabled: busy,
|
|
1649
|
+
// TYPED SOMETHING THAT IS NOT THERE? That IS the name. The champions
|
|
1650
|
+
// (Airtable, Notion) make the search text the new record's title in one
|
|
1651
|
+
// press; an empty box asks for one.
|
|
1652
|
+
onAdd: () => {
|
|
1653
|
+
if (search.trim() !== "") void create(search);
|
|
1654
|
+
else {
|
|
1655
|
+
setNewTitle("");
|
|
1656
|
+
setCreating(true);
|
|
1657
|
+
}
|
|
1655
1658
|
}
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1659
|
+
} : null,
|
|
1660
|
+
foot: !allowCreate || full ? void 0 : targetRefusal ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RefusalLine, { error: targetRefusal, className: "p-2" }) : !target ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "p-2 text-xs text-muted-foreground", children: "Reading that table\u2026" }) : creating ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1 p-1", children: [
|
|
1661
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1662
|
+
import_design_system4.BasicInput,
|
|
1663
|
+
{
|
|
1664
|
+
autoFocus: true,
|
|
1665
|
+
value: newTitle,
|
|
1666
|
+
onChange: (e) => setNewTitle(e.target.value),
|
|
1667
|
+
onKeyDown: (e) => {
|
|
1668
|
+
if (e.key === "Enter") {
|
|
1669
|
+
e.preventDefault();
|
|
1670
|
+
void create(newTitle);
|
|
1671
|
+
}
|
|
1672
|
+
},
|
|
1673
|
+
placeholder: `New ${targetWord}`,
|
|
1674
|
+
className: "h-7 flex-1 text-xs",
|
|
1675
|
+
"aria-label": `Name the new ${targetWord}`
|
|
1676
|
+
}
|
|
1677
|
+
),
|
|
1678
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1679
|
+
import_design_system4.Button,
|
|
1680
|
+
{
|
|
1681
|
+
type: "button",
|
|
1682
|
+
size: "sm",
|
|
1683
|
+
variant: "default",
|
|
1684
|
+
disabled: busy || newTitle.trim() === "",
|
|
1685
|
+
onClick: () => void create(newTitle),
|
|
1686
|
+
children: busy ? "Creating\u2026" : "Create"
|
|
1687
|
+
}
|
|
1688
|
+
)
|
|
1689
|
+
] }) : void 0,
|
|
1690
|
+
footNote: allowCreate && !full && createRefusal ? (
|
|
1691
|
+
// The typed title is still in the search box: the notice owns the
|
|
1692
|
+
// two doors out (lane REFUSAL-SWEEP).
|
|
1693
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1694
|
+
RefusalNotice,
|
|
1695
|
+
{
|
|
1696
|
+
error: createRefusal,
|
|
1697
|
+
className: "mt-1 max-w-72",
|
|
1698
|
+
onKeepEditing: () => setCreateRefusal(null),
|
|
1699
|
+
onDiscard: () => {
|
|
1700
|
+
setCreateRefusal(null);
|
|
1701
|
+
setSearch("");
|
|
1669
1702
|
}
|
|
1670
|
-
},
|
|
1671
|
-
placeholder: `New ${targetWord}`,
|
|
1672
|
-
className: "h-7 flex-1 text-xs",
|
|
1673
|
-
"aria-label": `Name the new ${targetWord}`
|
|
1674
|
-
}
|
|
1675
|
-
),
|
|
1676
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1677
|
-
import_design_system4.Button,
|
|
1678
|
-
{
|
|
1679
|
-
type: "button",
|
|
1680
|
-
size: "sm",
|
|
1681
|
-
variant: "default",
|
|
1682
|
-
disabled: busy || newTitle.trim() === "",
|
|
1683
|
-
onClick: () => void create(newTitle),
|
|
1684
|
-
children: busy ? "Creating\u2026" : "Create"
|
|
1685
|
-
}
|
|
1686
|
-
)
|
|
1687
|
-
] }) : void 0,
|
|
1688
|
-
footNote: allowCreate && !full && createRefusal ? (
|
|
1689
|
-
// The typed title is still in the search box: the notice owns the
|
|
1690
|
-
// two doors out (lane REFUSAL-SWEEP).
|
|
1691
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1692
|
-
RefusalNotice,
|
|
1693
|
-
{
|
|
1694
|
-
error: createRefusal,
|
|
1695
|
-
className: "mt-1 max-w-72",
|
|
1696
|
-
onKeepEditing: () => setCreateRefusal(null),
|
|
1697
|
-
onDiscard: () => {
|
|
1698
|
-
setCreateRefusal(null);
|
|
1699
|
-
setSearch("");
|
|
1700
1703
|
}
|
|
1701
|
-
|
|
1702
|
-
)
|
|
1703
|
-
|
|
1704
|
-
|
|
1704
|
+
)
|
|
1705
|
+
) : null
|
|
1706
|
+
}
|
|
1707
|
+
)
|
|
1705
1708
|
)
|
|
1706
|
-
)
|
|
1707
|
-
|
|
1709
|
+
] })
|
|
1710
|
+
);
|
|
1708
1711
|
}
|
|
1709
1712
|
|
|
1710
1713
|
// src/gridAttachments.tsx
|
|
@@ -5591,9 +5594,10 @@ function OpenCell({
|
|
|
5591
5594
|
{
|
|
5592
5595
|
ref: host,
|
|
5593
5596
|
"data-matrx-cell-control": "",
|
|
5597
|
+
"data-matrx-cell-fill": "",
|
|
5594
5598
|
"data-matrx-cell-editor": "",
|
|
5595
5599
|
tabIndex: -1,
|
|
5596
|
-
className: "relative z-[25] flex w-full min-w-
|
|
5600
|
+
className: "relative z-[25] flex w-full min-w-0 max-w-full flex-col gap-1 outline-none",
|
|
5597
5601
|
onDoubleClick: (event) => event.stopPropagation(),
|
|
5598
5602
|
onKeyDown: (event) => {
|
|
5599
5603
|
if (event.target?.closest?.("[data-refusal-notice], [data-records-choice-ask]")) return;
|
|
@@ -31575,13 +31579,14 @@ function ViewFilter({ tableId, view, onApply, className }) {
|
|
|
31575
31579
|
|
|
31576
31580
|
// src/toolbarFit.ts
|
|
31577
31581
|
var import_react188 = require("react");
|
|
31578
|
-
var FOLDED_GUESS = { look: 104, chooser: 96, tools: 120 };
|
|
31582
|
+
var FOLDED_GUESS = { look: 104, chooser: 96, tools: 120, share: 0 };
|
|
31579
31583
|
function toolbarFold(input) {
|
|
31580
31584
|
const { available, needsNow, gap, folded } = input;
|
|
31581
31585
|
const pieces = [
|
|
31582
31586
|
{ piece: input.look, stage: 1, guess: FOLDED_GUESS.look },
|
|
31583
31587
|
{ piece: input.chooser, stage: 2, guess: FOLDED_GUESS.chooser },
|
|
31584
|
-
...input.tools ? [{ piece: input.tools, stage: 3, guess: FOLDED_GUESS.tools }] : []
|
|
31588
|
+
...input.tools ? [{ piece: input.tools, stage: 3, guess: FOLDED_GUESS.tools }] : [],
|
|
31589
|
+
...input.tools && input.share ? [{ piece: input.share, stage: 4, guess: FOLDED_GUESS.share }] : []
|
|
31585
31590
|
];
|
|
31586
31591
|
const widthAt = ({ piece, stage, guess }, fold) => {
|
|
31587
31592
|
if (piece.now === 0 && piece.full === null) return 0;
|
|
@@ -31590,7 +31595,7 @@ function toolbarFold(input) {
|
|
|
31590
31595
|
};
|
|
31591
31596
|
const withGap = (width) => width > 0 ? width + gap : 0;
|
|
31592
31597
|
const base = pieces.reduce((rest, p) => rest - withGap(p.piece.now), needsNow);
|
|
31593
|
-
const stages = input.tools ? [0, 1, 2, 3] : [0, 1, 2];
|
|
31598
|
+
const stages = input.tools ? input.share ? [0, 1, 2, 3, 4] : [0, 1, 2, 3] : [0, 1, 2];
|
|
31594
31599
|
const needs = stages.map((fold) => pieces.reduce((sum, p) => sum + withGap(widthAt(p, fold)), base));
|
|
31595
31600
|
const fits = needs.findIndex((need) => need <= available + 0.5);
|
|
31596
31601
|
if (fits >= 0) return stages[fits];
|
|
@@ -31631,9 +31636,10 @@ function useToolbarFit(row) {
|
|
|
31631
31636
|
const widths = {
|
|
31632
31637
|
look: { full: null, folded: null },
|
|
31633
31638
|
chooser: { full: null, folded: null },
|
|
31634
|
-
tools: { full: null, folded: null }
|
|
31639
|
+
tools: { full: null, folded: null },
|
|
31640
|
+
share: { full: null, folded: null }
|
|
31635
31641
|
};
|
|
31636
|
-
const stageOf = { look: 1, chooser: 2, tools: 3 };
|
|
31642
|
+
const stageOf = { look: 1, chooser: 2, tools: 3, share: 4 };
|
|
31637
31643
|
let current = 0;
|
|
31638
31644
|
let pending = 0;
|
|
31639
31645
|
let stopped = false;
|
|
@@ -31643,7 +31649,7 @@ function useToolbarFit(row) {
|
|
|
31643
31649
|
const available = row.clientWidth;
|
|
31644
31650
|
if (available <= 0) return;
|
|
31645
31651
|
const read = (name) => {
|
|
31646
|
-
const el = name === "tools" ? row.querySelector(":scope > [data-toolbar-fit-slot]") : row.querySelector(`:scope > [data-toolbar-fit="${name}"]`);
|
|
31652
|
+
const el = name === "tools" ? row.querySelector(":scope > [data-toolbar-fit-slot]") : name === "share" ? row.querySelector('[data-toolbar-fit="share"]') : row.querySelector(`:scope > [data-toolbar-fit="${name}"]`);
|
|
31647
31653
|
const now = !el ? 0 : name === "tools" ? slotNeeds(el) : naturalWidth(el);
|
|
31648
31654
|
const w = widths[name];
|
|
31649
31655
|
if (el && now > 0) {
|
|
@@ -31656,8 +31662,11 @@ function useToolbarFit(row) {
|
|
|
31656
31662
|
const look = read("look");
|
|
31657
31663
|
const chooser = read("chooser");
|
|
31658
31664
|
const tools = read("tools");
|
|
31665
|
+
const shareEl = row.querySelector('[data-toolbar-fit="share"]');
|
|
31666
|
+
const shareRead = shareEl ? read("share") : null;
|
|
31667
|
+
const share = shareRead && (shareRead.now > 0 || shareRead.full !== null) ? shareRead : void 0;
|
|
31659
31668
|
const { needs, gap } = rowNeeds(row);
|
|
31660
|
-
const next = toolbarFold({ available, needsNow: needs, gap, folded: current, look, chooser, tools });
|
|
31669
|
+
const next = toolbarFold({ available, needsNow: needs, gap, folded: current, look, chooser, tools, share });
|
|
31661
31670
|
if (next !== current) {
|
|
31662
31671
|
current = next;
|
|
31663
31672
|
setFold(next);
|
|
@@ -32074,6 +32083,7 @@ function TablePage({
|
|
|
32074
32083
|
const chooserInline = !designated && !phone && toolbarFold2 < 2;
|
|
32075
32084
|
const chooserCompact = !designated && !phone && toolbarFold2 >= 2;
|
|
32076
32085
|
const controlsInSheet = phone && !designated;
|
|
32086
|
+
const shareInMenu = phone || toolbarFold2 >= 4;
|
|
32077
32087
|
(0, import_react189.useEffect)(() => {
|
|
32078
32088
|
if (face.kind !== "designated") return;
|
|
32079
32089
|
if (hostFromLink || opened.layout || layoutChosen) return;
|
|
@@ -32298,7 +32308,7 @@ function TablePage({
|
|
|
32298
32308
|
rights.share && canShare ? /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_design_system96.Button, { size: "sm", variant: "ghost", className: "h-8", onClick: () => setShareAsked((n) => n + 1), "data-table-sheet-share": "", children: "Share\u2026" }) : null
|
|
32299
32309
|
] }) : void 0;
|
|
32300
32310
|
const pageActions = /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(import_jsx_runtime105.Fragment, { children: [
|
|
32301
|
-
organizationId ? /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
|
|
32311
|
+
organizationId ? /* @__PURE__ */ (0, import_jsx_runtime105.jsx)("span", { className: shareInMenu ? "hidden" : "flex shrink-0 items-center", "data-toolbar-fit": "share", children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(
|
|
32302
32312
|
ShareControl,
|
|
32303
32313
|
{
|
|
32304
32314
|
kind: "table",
|
|
@@ -32308,10 +32318,10 @@ function TablePage({
|
|
|
32308
32318
|
may: rights.share,
|
|
32309
32319
|
whyNot: rights.read ? rights.why("share") : void 0,
|
|
32310
32320
|
initiallyOpen: shareFromLink || shareAsked > 0,
|
|
32311
|
-
...
|
|
32321
|
+
...shareInMenu ? { className: "hidden" } : {}
|
|
32312
32322
|
},
|
|
32313
|
-
|
|
32314
|
-
) : null,
|
|
32323
|
+
shareInMenu ? `share-${shareAsked}` : "share"
|
|
32324
|
+
) }, "share") : null,
|
|
32315
32325
|
/* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(import_design_system96.DropdownMenu, { children: [
|
|
32316
32326
|
/* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_design_system96.DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_design_system96.Button, { size: "sm", variant: "ghost", "aria-label": "Table menu", title: "Table menu", "data-table-menu": "", children: /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)("svg", { viewBox: "0 0 24 24", className: "h-4 w-4", fill: "currentColor", "aria-hidden": "true", children: [
|
|
32317
32327
|
/* @__PURE__ */ (0, import_jsx_runtime105.jsx)("circle", { cx: "5", cy: "12", r: "1.75" }),
|
|
@@ -32334,7 +32344,7 @@ function TablePage({
|
|
|
32334
32344
|
},
|
|
32335
32345
|
entry.key
|
|
32336
32346
|
)),
|
|
32337
|
-
|
|
32347
|
+
shareInMenu && rights.share && canShare ? /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(import_jsx_runtime105.Fragment, { children: [
|
|
32338
32348
|
/* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_design_system96.DropdownMenuSeparator, {}),
|
|
32339
32349
|
/* @__PURE__ */ (0, import_jsx_runtime105.jsx)(import_design_system96.DropdownMenuItem, { onSelect: () => setShareAsked((n) => n + 1), "data-table-menu-share": "", children: "Share\u2026" })
|
|
32340
32350
|
] }) : null,
|