@ai-matrx/records-ui 0.84.7 → 0.84.8

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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog — @ai-matrx/records-ui
2
2
 
3
+ ## 0.84.8
4
+
5
+ **A host's own layout sits beside Grid, Kanban, Calendar and Gallery on the one table page.**
6
+ (lane GRID-PORT `5cb9bcbd99`, published by PUBLISH-SWEEP-4.) `RecordsUiHost.layouts?: HostLayout[]`
7
+ (`{ id, label, render({ tableId, filter }) }`) draws as a button in `ViewSwitcher`'s layout row;
8
+ `?view=<id>` opens it (`openingHostLayout`), and `onViewChanged` gets the id. It is page-level and is
9
+ never written onto the view record. A host id that is one of `PAGE_VIEWS`, or a spelling of one, is
10
+ ignored. **Consumer action:** matrx-frontend passes its ported /data grid as the Sheet layout.
11
+
12
+ ## Unreleased (source only — lane GRID-PORT; the chair's publisher owns the slot)
13
+
14
+ **A host can draw a layout beside the package's own four, on the one table page.**
15
+ `RecordsUiHost.layouts?: HostLayout[]` — `{ id, label, render({ tableId, filter }) }`. Each
16
+ appears as a button in `ViewSwitcher`'s layout row after Grid / Kanban / Calendar / Gallery, and
17
+ `?view=<id>` opens it (`openingHostLayout`); `TablePage.onViewChanged` is called with the id.
18
+ Page-level, never written onto the view record (the store's layout words stay the four). A host
19
+ id equal to one of `PAGE_VIEWS` (or a spelling of one) is ignored, so `?view=grid` is always the
20
+ package's Grid. Why: the owner's ruling (2026-09-23) — AI Matrx's ported /data grid is the
21
+ "Sheet" layout of `/data-v2/<table>`, not a second page for the same table.
22
+
23
+ Consumer action: `onViewChanged` now receives `PageView | string`; widen a handler typed
24
+ `(view: PageView) => void`.
25
+
3
26
  ## 0.84.7 (unpublished — needs @ai-matrx/records 0.57.3)
4
27
 
5
28
  * **The suite is green again (386/386), and the class is gone rather than patched.** 0.84.5's
package/dist/index.cjs CHANGED
@@ -8653,15 +8653,20 @@ function ViewSwitcher({
8653
8653
  onAskWhoChanged,
8654
8654
  pageSize = 200,
8655
8655
  filter,
8656
+ hostLayout,
8657
+ onHostLayoutChange,
8656
8658
  className
8657
8659
  }) {
8658
8660
  const [layout, setLayout] = (0, import_react49.useState)(view.layout);
8661
+ const hostLayouts = useRecordsUi().layouts ?? [];
8662
+ const drawnByHost = hostLayout ? hostLayouts.find((h) => h.id === hostLayout) ?? null : null;
8659
8663
  const [local, setLocal] = (0, import_react49.useState)({});
8660
8664
  (0, import_react49.useEffect)(() => {
8661
8665
  setLayout(view.layout);
8662
8666
  setLocal({});
8663
8667
  }, [view.layout, view.name, view.subject]);
8664
8668
  const pick = (next) => {
8669
+ onHostLayoutChange?.(null);
8665
8670
  setLayout(next);
8666
8671
  onLayoutChange?.(next);
8667
8672
  onViewChange?.({ layout: next });
@@ -8673,19 +8678,32 @@ function ViewSwitcher({
8673
8678
  return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: (0, import_design_system25.cn)("flex min-h-0 flex-col gap-2", className), children: [
8674
8679
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2", children: [
8675
8680
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate text-sm font-medium", children: view.name }),
8676
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "ml-auto flex items-center gap-0.5", role: "group", "aria-label": "Layout", children: VIEW_LAYOUTS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
8677
- import_design_system25.Button,
8678
- {
8679
- size: "sm",
8680
- variant: option === layout ? "secondary" : "ghost",
8681
- "aria-pressed": option === layout,
8682
- onClick: () => pick(option),
8683
- children: LAYOUT_LABEL[option]
8684
- },
8685
- option
8686
- )) })
8681
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "ml-auto flex items-center gap-0.5", role: "group", "aria-label": "Layout", children: [
8682
+ VIEW_LAYOUTS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
8683
+ import_design_system25.Button,
8684
+ {
8685
+ size: "sm",
8686
+ variant: !drawnByHost && option === layout ? "secondary" : "ghost",
8687
+ "aria-pressed": !drawnByHost && option === layout,
8688
+ onClick: () => pick(option),
8689
+ children: LAYOUT_LABEL[option]
8690
+ },
8691
+ option
8692
+ )),
8693
+ hostLayouts.map((option) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
8694
+ import_design_system25.Button,
8695
+ {
8696
+ size: "sm",
8697
+ variant: drawnByHost?.id === option.id ? "secondary" : "ghost",
8698
+ "aria-pressed": drawnByHost?.id === option.id,
8699
+ onClick: () => onHostLayoutChange?.(option.id),
8700
+ children: option.label
8701
+ },
8702
+ `host:${option.id}`
8703
+ ))
8704
+ ] })
8687
8705
  ] }),
8688
- layout === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
8706
+ drawnByHost ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex min-h-0 flex-1 flex-col", "data-host-layout": drawnByHost.id, children: drawnByHost.render({ tableId: view.subject, ...filter ? { filter } : {} }) }) : layout === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
8689
8707
  Grid,
8690
8708
  {
8691
8709
  tableId: view.subject,
@@ -17225,6 +17243,11 @@ function openingView(activeView, activeDashboardId) {
17225
17243
  if (named === "archived") return { main: "archived", layout: null, unknown: null };
17226
17244
  return { main: "records", layout: named, unknown: null };
17227
17245
  }
17246
+ function openingHostLayout(activeView, hostLayouts) {
17247
+ const word = typeof activeView === "string" ? activeView.trim().toLowerCase() : "";
17248
+ if (word === "" || pageViewFromParam(word) !== null) return null;
17249
+ return hostLayouts.some((h) => h.id === word) ? word : null;
17250
+ }
17228
17251
  function TablePage({
17229
17252
  tableId,
17230
17253
  pageSize = 100,
@@ -17250,7 +17273,12 @@ function TablePage({
17250
17273
  const organizationId = (0, import_react119.useRecordsClient)().config.organizationId;
17251
17274
  const [view, setView] = (0, import_react118.useState)(null);
17252
17275
  const opening = openingRail(activeRecordId, activeRail);
17253
- const opened = openingView(activeView, activeDashboardId);
17276
+ const hostLayouts = useRecordsUi().layouts ?? [];
17277
+ const hostLayoutFor = (asked) => openingHostLayout(asked, hostLayouts);
17278
+ const hostFromLink = hostLayoutFor(activeView);
17279
+ const openedByPackage = openingView(hostFromLink ? null : activeView, activeDashboardId);
17280
+ const opened = hostFromLink ? { ...openedByPackage, main: "records" } : openedByPackage;
17281
+ const [hostLayout, setHostLayout] = (0, import_react118.useState)(hostFromLink);
17254
17282
  const railAsked = typeof activeRail === "string" ? activeRail.trim() : "";
17255
17283
  const railUnknown = railAsked !== "" && railFromParam(railAsked) === null ? railAsked : null;
17256
17284
  const shareFromLink = railFromParam(activeRail) === "share";
@@ -17288,8 +17316,15 @@ function TablePage({
17288
17316
  setSurface((now) => ({ ...now, rail: named }));
17289
17317
  }, [activeRail, activeRecordId]);
17290
17318
  (0, import_react118.useEffect)(() => {
17319
+ const host = hostLayoutFor(activeView);
17320
+ if (host) {
17321
+ setHostLayout(host);
17322
+ setSurface((now) => ({ ...now, main: "records" }));
17323
+ return;
17324
+ }
17291
17325
  const named = pageViewFromParam(activeView);
17292
17326
  if (named === null) return;
17327
+ setHostLayout(null);
17293
17328
  const isLayout = named !== "dashboards" && named !== "archived";
17294
17329
  setLayoutFromLink(isLayout ? named : null);
17295
17330
  setSurface((now) => ({ ...now, main: isLayout ? "records" : named }));
@@ -17521,6 +17556,11 @@ function TablePage({
17521
17556
  setLayoutFromLink(layout);
17522
17557
  onViewChanged?.(layout);
17523
17558
  },
17559
+ hostLayout,
17560
+ onHostLayoutChange: (id) => {
17561
+ setHostLayout(id);
17562
+ if (id) onViewChanged?.(id);
17563
+ },
17524
17564
  ...view ? { onViewChange: (patch) => void patchView(patch) } : {},
17525
17565
  ...rights.write ? { onNewRecordForm: () => setRail("new-record") } : {},
17526
17566
  onOpenRecord: (recordId) => {