@protobase/ui 0.25.4 → 0.25.6

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.
@@ -0,0 +1,85 @@
1
+ import { useIsXs } from "../../lib/use-media-query.js";
2
+ import { Skeleton } from "../../primitives/skeleton.js";
3
+ import { humanize } from "../../live/naming.js";
4
+ import { defaultOrderBy } from "../list-order.js";
5
+ import { AppLink } from "../use-app-address.js";
6
+ import { ErrorBanner } from "../error-banner.js";
7
+ import { scopeOf, useCount, useListPage } from "../pages/use-layout-data.js";
8
+ import { embeddedDisplay, viewAllAddress } from "../embedded-list/embedded-display.js";
9
+ import { text } from "../embedded-list/messages.js";
10
+ import { cutOffNote } from "../embedded-list/cut-off-note.js";
11
+ import { text as text$1 } from "./messages.js";
12
+ import { RelatedCard } from "./related-card.js";
13
+ import { ArrowRight } from "lucide-react";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ //#region src/app/related-cards/related-cards.tsx
16
+ /**
17
+ * The first `limit` records of a list as cards stacked full width, each with its fields and its actions' buttons, and a
18
+ * link to the whole list beside a note such as "The first 10 of 37." when there are more.
19
+ */
20
+ const RelatedCards = ({ model, view, filter, sort, fields, actions, limit, display, empty }) => {
21
+ const xs = useIsXs();
22
+ const orderBy = sort || defaultOrderBy(model, view);
23
+ const page = useListPage(model, {
24
+ filter,
25
+ sort: orderBy,
26
+ pageSize: limit
27
+ });
28
+ const count = useCount(model, filter);
29
+ if (page.isPending) return /* @__PURE__ */ jsx("div", {
30
+ className: "flex flex-col gap-3",
31
+ children: Array.from({ length: 2 }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full" }, index))
32
+ });
33
+ if (page.error) return /* @__PURE__ */ jsx(ErrorBanner, {
34
+ title: text$1("couldNotLoad"),
35
+ error: page.error
36
+ });
37
+ const rows = page.data.items;
38
+ if (rows.length === 0) return /* @__PURE__ */ jsx("p", {
39
+ className: "text-muted-foreground",
40
+ children: empty ?? text$1("nothingYet")
41
+ });
42
+ const note = count.data === void 0 ? void 0 : cutOffNote(rows.length, count.data, false);
43
+ return /* @__PURE__ */ jsxs("div", {
44
+ className: "flex flex-col gap-3",
45
+ children: [/* @__PURE__ */ jsx("ul", {
46
+ className: "flex flex-col gap-3",
47
+ children: rows.map((row) => {
48
+ const scope = scopeOf(model, row);
49
+ return /* @__PURE__ */ jsx(RelatedCard, {
50
+ scope,
51
+ view,
52
+ fields,
53
+ actions
54
+ }, scope.key);
55
+ })
56
+ }), /* @__PURE__ */ jsxs("div", {
57
+ className: "flex flex-wrap items-center justify-between gap-2",
58
+ children: [/* @__PURE__ */ jsx("p", {
59
+ className: "text-xs text-muted-foreground",
60
+ children: note
61
+ }), /* @__PURE__ */ jsxs(AppLink, {
62
+ to: viewAllAddress(model, view, xs, {
63
+ filter,
64
+ orderBy,
65
+ display: embeddedDisplay(view, xs, { preferred: display })
66
+ }),
67
+ className: "flex items-center gap-1 font-medium text-primary-text hover:underline",
68
+ children: [
69
+ text("viewAll"),
70
+ " ",
71
+ /* @__PURE__ */ jsx("span", {
72
+ className: "sr-only",
73
+ children: (view?.names?.plural ?? humanize(model.name)).toLowerCase()
74
+ }),
75
+ /* @__PURE__ */ jsx(ArrowRight, {
76
+ className: "size-3.5",
77
+ "aria-hidden": true
78
+ })
79
+ ]
80
+ })]
81
+ })]
82
+ });
83
+ };
84
+ //#endregion
85
+ export { RelatedCards };
@@ -14,6 +14,7 @@ import { useEmbeddedDisplay } from "./embedded-list/use-embedded-display.js";
14
14
  import { text } from "./record-messages.js";
15
15
  import { HandSortedList } from "./hand-order/hand-sorted-list.js";
16
16
  import { ReorderButton } from "./hand-order/reorder-button.js";
17
+ import { RelatedCards } from "./related-cards/related-cards.js";
17
18
  import { useState } from "react";
18
19
  import { jsx, jsxs } from "react/jsx-runtime";
19
20
  import { useQuery } from "@tanstack/react-query";
@@ -78,7 +79,7 @@ const Empty = ({ empty }) => /* @__PURE__ */ jsx("p", {
78
79
  });
79
80
  /**
80
81
  * A record page section listing the first records of another resource that point at this record, directly or through
81
- * a join resource, in the list page's table or display, with a link to them all in the list. When they are ordered by
82
+ * a join resource, in the list page's table or display, or as cards with their actions, with a link to them all in the list. When they are ordered by
82
83
  * hand within this record, **Reorder** shows them all in their order, to be moved by their drag handles.
83
84
  */
84
85
  const RelatedSection = ({ item, value, owner }) => {
@@ -100,6 +101,25 @@ const RelatedSection = ({ item, value, owner }) => {
100
101
  if (item.hideEmpty && (match.isPending || match.filter === null || counting && (count.isPending || count.data === 0))) return null;
101
102
  const opens = relatedOpens(item, model, resources);
102
103
  const columns = item.columns ?? listColumnNames(model, view).filter((name) => name !== item.field);
104
+ const listed = (list) => match.isPending ? /* @__PURE__ */ jsx(Loading, {}) : match.error ? /* @__PURE__ */ jsx(ErrorBanner, {
105
+ title: text("couldNotLoadRelated"),
106
+ error: match.error
107
+ }) : match.filter === null || match.filter === void 0 ? /* @__PURE__ */ jsx(Empty, { empty: item.empty }) : list(match.filter);
108
+ if (item.presentation === "cards") return /* @__PURE__ */ jsx(Section, {
109
+ title: item.title,
110
+ help: item.help,
111
+ children: listed((filter) => /* @__PURE__ */ jsx(RelatedCards, {
112
+ model,
113
+ view,
114
+ filter,
115
+ sort: item.sort ?? "",
116
+ fields: columns,
117
+ actions: item.actions ?? [],
118
+ limit: item.limit ?? 10,
119
+ display: item.display,
120
+ empty: item.empty
121
+ }))
122
+ });
103
123
  const handOrder = relatedHandOrder(item, model, permissions);
104
124
  const displaySwitch = /* @__PURE__ */ jsx(EmbeddedDisplaySwitch, {
105
125
  view,
@@ -130,20 +150,17 @@ const RelatedSection = ({ item, value, owner }) => {
130
150
  title: item.title,
131
151
  help: item.help,
132
152
  actions,
133
- children: match.isPending ? /* @__PURE__ */ jsx(Loading, {}) : match.error ? /* @__PURE__ */ jsx(ErrorBanner, {
134
- title: text("couldNotLoadRelated"),
135
- error: match.error
136
- }) : match.filter === null || match.filter === void 0 ? /* @__PURE__ */ jsx(Empty, { empty: item.empty }) : /* @__PURE__ */ jsx(EmbeddedList, {
153
+ children: listed((filter) => /* @__PURE__ */ jsx(EmbeddedList, {
137
154
  model,
138
155
  view,
139
- filter: match.filter,
156
+ filter,
140
157
  sort: item.sort ?? "",
141
158
  columns,
142
159
  limit: item.limit ?? 10,
143
160
  display,
144
161
  empty: item.empty,
145
162
  opens
146
- }, match.filter)
163
+ }, filter))
147
164
  });
148
165
  };
149
166
  //#endregion
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.25.4";
2
+ var version = "0.25.6";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };