@protobase/ui 0.12.22 → 0.13.3

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.
Files changed (63) hide show
  1. package/dist/app/create/create-page.js +1 -0
  2. package/dist/app/current-address.d.ts +3 -2
  3. package/dist/app/current-address.js +2 -1
  4. package/dist/app/delete/use-delete-flow.js +2 -1
  5. package/dist/app/displays/card-carousel.js +4 -1
  6. package/dist/app/displays/card-layout.js +19 -0
  7. package/dist/app/displays/card-photos.js +6 -3
  8. package/dist/app/displays/cards-display.js +16 -11
  9. package/dist/app/displays/list-cards.js +50 -0
  10. package/dist/app/displays/list-displays.js +4 -3
  11. package/dist/app/displays/map-display.js +83 -23
  12. package/dist/app/displays/map-groups.js +1 -8
  13. package/dist/app/displays/record-card.js +8 -6
  14. package/dist/app/displays/use-map-records.js +44 -0
  15. package/dist/app/displays/virtual-cards.js +138 -0
  16. package/dist/app/embedded-list/embedded-columns.js +52 -0
  17. package/dist/app/embedded-list/embedded-display.js +26 -0
  18. package/dist/app/embedded-list/embedded-list.js +157 -0
  19. package/dist/app/embedded-list/use-embedded-display.js +23 -0
  20. package/dist/app/embedded-list/use-embedded-rows.js +33 -0
  21. package/dist/app/file-field.js +16 -13
  22. package/dist/app/list-page.js +19 -6
  23. package/dist/app/list-scroll.js +25 -0
  24. package/dist/app/map/map-area.js +53 -0
  25. package/dist/app/map/records-map.js +91 -38
  26. package/dist/app/pages/blocks/table.js +21 -41
  27. package/dist/app/pages/field-value.js +22 -9
  28. package/dist/app/record-page.js +2 -1
  29. package/dist/app/related-section.js +30 -52
  30. package/dist/app/router.js +1 -0
  31. package/dist/app/use-record-editor.js +2 -1
  32. package/dist/data/api-provider.d.ts +2 -0
  33. package/dist/data/query-keys.js +9 -0
  34. package/dist/data/use-update-record.js +1 -0
  35. package/dist/data-grid/cells.js +8 -5
  36. package/dist/data-grid/data-grid.js +19 -39
  37. package/dist/data-grid/geometry.js +7 -9
  38. package/dist/data-grid/use-grid-scroll.js +8 -1
  39. package/dist/data-grid/use-virtual-rows.js +31 -0
  40. package/dist/live/columns-from-model.js +6 -4
  41. package/dist/live/file-image.js +35 -0
  42. package/dist/live/file-values.js +17 -1
  43. package/dist/live/grid-loader.js +26 -10
  44. package/dist/package.js +1 -1
  45. package/dist/record-view/section.js +24 -17
  46. package/dist/shell/assets/app-CfuMqqAY.js +66 -0
  47. package/dist/shell/assets/index-CYWfU_c1.css +2 -0
  48. package/dist/shell/assets/{index-B8MhIaLr.js → index-DCTyUhH-.js} +3 -3
  49. package/dist/shell/assets/maplibre-gl-DP9hPAtI.js +1 -0
  50. package/dist/shell/assets/record-map-Dgn4pFlE.js +814 -0
  51. package/dist/shell/assets/records-map-3O6u05bx.js +1 -0
  52. package/dist/shell/assets/shared-protobase-ui-C84DU2o6.js +1 -0
  53. package/dist/shell/index.html +5 -5
  54. package/dist/shell/protobase.shell.json +1 -1
  55. package/package.json +5 -5
  56. package/dist/app/pages/record-table.js +0 -114
  57. package/dist/app/pages/use-relation-labels.js +0 -30
  58. package/dist/shell/assets/app-BYw-8qpE.js +0 -66
  59. package/dist/shell/assets/index-CdSIk0BQ.css +0 -2
  60. package/dist/shell/assets/navigation-control-CGS0pamh.js +0 -814
  61. package/dist/shell/assets/record-map-9Y3IL_dg.js +0 -1
  62. package/dist/shell/assets/records-map-DXIWowJ3.js +0 -1
  63. package/dist/shell/assets/shared-protobase-ui-Bol6wL_S.js +0 -1
@@ -0,0 +1 @@
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{t}from"./react-Cvdyeg_0.js";import{W as n,g as r}from"./app-CfuMqqAY.js";import{t as i}from"./jsx-runtime-NZYk81nU.js";import{Ft as a,it as o,n as s,r as c,t as l}from"./record-map-Dgn4pFlE.js";var u=e(t(),1),d=e(i(),1),f=28,p={padding:48,maxZoom:15,duration:0},m={padding:48,maxZoom:16,duration:300},h=32,g=new Intl.NumberFormat(`en-IE`,{notation:`compact`,maximumFractionDigits:1}),_=e=>[[e.west,e.south],[e.east,e.north]],v=({style:e,initial:t,points:i,buckets:v,fit:y,onFitted:b,onMove:x,selected:S,covered:C,onSelect:w,onClear:T,markerLabel:E,bucketLabel:D})=>{let O=(0,u.useRef)(null),[k,A]=(0,u.useState)([]),j=new globalThis.Map(i.map(e=>[e.key,e])),M=i.map(e=>e.key).join(`,`),N=y&&JSON.stringify(y),P=()=>{let e=O.current?.getMap();e&&A(r(i.map(t=>({key:t.key,...e.project([t.longitude,t.latitude])})),f))},F=()=>{let e=O.current?.getMap();if(!e)return;let t=e.getBounds(),n=e.getCenter();x({bounds:{south:t.getSouth(),west:t.getWest(),north:t.getNorth(),east:t.getEast()},zoom:e.getZoom()},{latitude:n.lat,longitude:n.lng,zoom:e.getZoom()})},I=()=>{let e=O.current?.getMap();e&&y&&(e.fitBounds(_(y),p),b(),F())};(0,u.useEffect)(P,[M]),(0,u.useEffect)(I,[N]);let L=k.find(e=>S!==void 0&&e.keys.some(e=>S.includes(e)))?.at;return(0,u.useEffect)(()=>{let e=O.current?.getMap(),t=L===void 0?void 0:j.get(L);if(!e||!t||C===0)return;let{y:n}=e.project([t.longitude,t.latitude]),r=e.getContainer().clientHeight-C;n>h&&n<r||e.easeTo({center:[t.longitude,t.latitude],offset:[0,(h-C)/2],duration:300})},[L,C]),(0,d.jsxs)(c,{ref:O,mapLib:o,mapStyle:e,initialViewState:t??{latitude:0,longitude:0,zoom:1},style:{width:`100%`,height:`100%`},onLoad:()=>{P(),y?I():F()},onZoomEnd:P,onMoveEnd:F,onClick:T,children:[(0,d.jsx)(l,{showCompass:!1}),v.map(e=>(0,d.jsx)(s,{latitude:e.latitude,longitude:e.longitude,anchor:`center`,children:(0,d.jsx)(`button`,{type:`button`,"aria-label":D(e.count),className:`flex size-10 cursor-pointer items-center justify-center rounded-full border-2 border-background bg-primary text-xs font-semibold text-primary-foreground tabular-nums shadow`,onClick:t=>{t.stopPropagation(),O.current?.getMap().fitBounds(_(e.bounds),m)},children:g.format(e.count)})},`${e.latitude},${e.longitude}`)),k.map(e=>{let t=j.get(e.at);if(!t)return null;let r=S!==void 0&&e.keys.some(e=>S.includes(e));return(0,d.jsx)(s,{latitude:t.latitude,longitude:t.longitude,anchor:`bottom`,style:{zIndex:+!!r},children:(0,d.jsxs)(`button`,{type:`button`,"aria-label":E(e.keys),"aria-pressed":r,className:`relative block cursor-pointer`,onClick:t=>{t.stopPropagation(),w(e.keys)},children:[(0,d.jsx)(a,{"aria-hidden":!0,className:n(`size-8 drop-shadow`,r?`fill-foreground text-background`:`fill-primary text-primary-foreground`),strokeWidth:1.5}),e.keys.length>1&&(0,d.jsx)(`span`,{className:`absolute -right-2 -top-1 min-w-5 rounded-full border border-background bg-foreground px-1 text-center text-[11px] font-medium leading-4 text-background tabular-nums`,children:e.keys.length})]})},e.at)})]})};export{v as RecordsMap};
@@ -0,0 +1 @@
1
+ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{A as t,B as n,C as r,E as i,F as a,H as o,I as s,L as c,M as l,N as u,O as d,P as f,R as p,S as m,T as h,U as g,V as _,_ as v,a as y,b,c as x,d as S,f as C,h as w,i as T,j as E,k as D,l as O,m as k,n as A,o as j,p as M,r as N,s as P,t as F,u as I,v as L,w as R,x as z,y as B,z as V}from"./app-CfuMqqAY.js";import{t as H}from"./jsx-runtime-NZYk81nU.js";var U=e(H(),1),W=({row:e,resource:t,recordKey:n,...r})=>{let i=w(),a=I(),o=t??i?.resource??a?.model.name;if(o===void 0)throw Error(`RecordLink needs a resource outside a list display or a record`);return(0,U.jsx)(v,{to:{kind:`record`,resource:o,key:e?e.key:n},...r})};export{f as ActionCard,_ as ApiProvider,F as App,v as AppLink,m as AssistantDock,p as ChatMessage,c as ChatThread,l as CompactTable,s as Composer,D as DiffView,u as FieldList,R as PartView,W as RecordLink,a as SidePanel,d as StepList,h as definePlugin,i as defineUi,t as diffStats,A as isConflict,x as mergeUi,E as parseDiff,y as pluginApi,j as protobaseVersion,O as uiFromModule,n as useApiToken,L as useAppAddress,z as useAssistant,b as useAssistantClient,r as useBackendData,o as useClient,S as useFacets,C as useHistogram,I as useLayoutRecord,T as useList,k as useListDisplay,B as useMeta,P as usePlugin,V as useRecord,g as useResourceClient,M as useSeries,N as useUpdateRecord};
@@ -6,15 +6,15 @@
6
6
  <meta charset="utf-8" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
8
8
  <title>Protobase</title>
9
- <script type="importmap">{"imports":{"react":"/assets/shared-react-DhkZ2M4l.js","react/jsx-runtime":"/assets/shared-react-jsx-runtime-xPQrWk0g.js","react-dom":"/assets/shared-react-dom-D508a1cq.js","@protobase/ui":"/assets/shared-protobase-ui-Bol6wL_S.js"}}</script>
10
- <script type="module" crossorigin src="/assets/index-B8MhIaLr.js"></script>
9
+ <script type="importmap">{"imports":{"react":"/assets/shared-react-DhkZ2M4l.js","react/jsx-runtime":"/assets/shared-react-jsx-runtime-xPQrWk0g.js","react-dom":"/assets/shared-react-dom-D508a1cq.js","@protobase/ui":"/assets/shared-protobase-ui-C84DU2o6.js"}}</script>
10
+ <script type="module" crossorigin src="/assets/index-DCTyUhH-.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-hePW80VL.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/react-Cvdyeg_0.js">
13
- <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-NZYk81nU.js">
14
13
  <link rel="modulepreload" crossorigin href="/assets/react-dom-B-VxWsjA.js">
15
- <link rel="modulepreload" crossorigin href="/assets/app-BYw-8qpE.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-NZYk81nU.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/app-CfuMqqAY.js">
16
16
  <link rel="stylesheet" crossorigin href="/assets/app-GUjJKojE.css">
17
- <link rel="stylesheet" crossorigin href="/assets/index-CdSIk0BQ.css">
17
+ <link rel="stylesheet" crossorigin href="/assets/index-CYWfU_c1.css">
18
18
  <link rel="stylesheet" href="/protobase.theme.css">
19
19
  </head>
20
20
  <body>
@@ -1,5 +1,5 @@
1
1
  {
2
- "protobase": "0.12.22",
2
+ "protobase": "0.13.3",
3
3
  "pluginApi": 1,
4
4
  "paths": [
5
5
  "/index.html",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/ui",
3
- "version": "0.12.22",
3
+ "version": "0.13.3",
4
4
  "description": "The Protobase admin app and the React hooks and components for custom UI.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -38,13 +38,12 @@
38
38
  "dependencies": {
39
39
  "@dnd-kit/core": "^6.3.1",
40
40
  "@dnd-kit/sortable": "^10.0.0",
41
- "@protobase/client": "0.12.22",
42
- "@protobase/layout": "0.12.22",
43
- "@protobase/schema": "0.12.22",
41
+ "@protobase/client": "0.13.3",
42
+ "@protobase/layout": "0.13.3",
43
+ "@protobase/schema": "0.13.3",
44
44
  "@sentry/browser": "^11.7.0",
45
45
  "@tanstack/react-query": "^5.104.1",
46
46
  "@tanstack/react-table": "^9.2.6",
47
- "@tanstack/react-virtual": "^3.14.13",
48
47
  "lucide-react": "^1.52.0",
49
48
  "maplibre-gl": "^6.14.0",
50
49
  "pmtiles": "^4.5.0",
@@ -53,6 +52,7 @@
53
52
  "react-map-gl": "^8.1.3",
54
53
  "recharts": "^3.10.1",
55
54
  "tailwindcss": "^4.3.3",
55
+ "thumbhash": "^0.1.1",
56
56
  "uqr": "^0.1.3"
57
57
  }
58
58
  }
@@ -1,114 +0,0 @@
1
- import { Button } from "../../primitives/button.js";
2
- import { humanize } from "../../live/naming.js";
3
- import { useAdminMeta } from "../meta-gate.js";
4
- import { AppLink } from "../use-app-address.js";
5
- import { scopeOf } from "./use-layout-data.js";
6
- import { FieldValue } from "./field-value.js";
7
- import { usePathValue } from "./use-path-value.js";
8
- import { useRelationLabels } from "./use-relation-labels.js";
9
- import { ChevronLeft, ChevronRight } from "lucide-react";
10
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
- import { resolveFieldPath } from "@protobase/layout";
12
- //#region src/app/pages/record-table.tsx
13
- const isPath = (column) => column.includes(".");
14
- /** The header of a column: the label of the field it ends at, from that field's view. */
15
- const useHeader = (model) => {
16
- const { resources, views } = useAdminMeta();
17
- return (column) => {
18
- const resolved = resolveFieldPath(resources, model.name, column);
19
- const last = resolved.ok ? resolved.steps.at(-1) : void 0;
20
- const name = last?.field.name ?? column;
21
- return views[last?.model.name ?? model.name]?.fields[name]?.label ?? humanize(name);
22
- };
23
- };
24
- /** A value through relations (`unitId.propertyId`), fetched like the record page fetches it and cached. */
25
- const PathCell = ({ model, row, column }) => {
26
- const value = usePathValue(scopeOf(model, row), column);
27
- if (value.state === "loading") return /* @__PURE__ */ jsx("span", {
28
- className: "text-muted-foreground",
29
- children: "…"
30
- });
31
- if (value.state === "missing") return /* @__PURE__ */ jsx("span", {
32
- className: "text-muted-foreground",
33
- children: "—"
34
- });
35
- return /* @__PURE__ */ jsx(FieldValue, {
36
- model: value.model,
37
- field: value.field,
38
- value: value.value
39
- });
40
- };
41
- /**
42
- * Records as table rows; the first column opens the record, unless it is a relation, which links to the record it
43
- * names instead (a join record's first column is usually what it joins). Columns may follow relations (`unitId.propertyId`).
44
- */
45
- const RecordTable = ({ model, columns, rows, label, pager }) => {
46
- const { resources } = useAdminMeta();
47
- const header = useHeader(model);
48
- const first = columns[0] === void 0 ? void 0 : resolveFieldPath(resources, model.name, columns[0]);
49
- const opensRecord = !first?.ok || first.steps.at(-1).field.type !== "relation";
50
- const labels = useRelationLabels(model, columns.filter((column) => !isPath(column)), rows);
51
- const cell = (row, column) => isPath(column) ? /* @__PURE__ */ jsx(PathCell, {
52
- model,
53
- row,
54
- column
55
- }) : /* @__PURE__ */ jsx(FieldValue, {
56
- model,
57
- field: model.fields[column],
58
- value: row[column],
59
- relationLabel: labels.get(column)?.get(String(row[column]))
60
- });
61
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
62
- className: "-mx-4 overflow-x-auto",
63
- children: /* @__PURE__ */ jsxs("table", {
64
- className: "w-full min-w-max text-left text-[13px]",
65
- children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", {
66
- className: "border-b text-xs text-muted-foreground",
67
- children: columns.map((column) => /* @__PURE__ */ jsx("th", {
68
- scope: "col",
69
- className: "px-4 py-2 font-medium",
70
- children: header(column)
71
- }, column))
72
- }) }), /* @__PURE__ */ jsx("tbody", { children: rows.map((row) => {
73
- const scope = scopeOf(model, row);
74
- return /* @__PURE__ */ jsx("tr", {
75
- className: "border-b last:border-0 hover:bg-surface",
76
- children: columns.map((column, index) => /* @__PURE__ */ jsx("td", {
77
- className: "px-4 py-2",
78
- children: index === 0 && opensRecord ? /* @__PURE__ */ jsx(AppLink, {
79
- to: {
80
- kind: "record",
81
- resource: model.name,
82
- key: scope.key
83
- },
84
- className: "font-medium text-primary-text hover:underline",
85
- children: cell(row, column)
86
- }) : cell(row, column)
87
- }, column))
88
- }, scope.key);
89
- }) })]
90
- })
91
- }), pager && (pager.hasPrevious || pager.hasNext) && /* @__PURE__ */ jsxs("nav", {
92
- "aria-label": `${label} pages`,
93
- className: "mt-3 flex items-center justify-end gap-2 text-xs text-muted-foreground",
94
- children: [
95
- /* @__PURE__ */ jsxs("span", { children: ["Page ", pager.page] }),
96
- /* @__PURE__ */ jsx(Button, {
97
- size: "sm",
98
- "aria-label": "Previous page",
99
- disabled: !pager.hasPrevious || pager.busy,
100
- onClick: pager.previous,
101
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-3.5" })
102
- }),
103
- /* @__PURE__ */ jsx(Button, {
104
- size: "sm",
105
- "aria-label": "Next page",
106
- disabled: !pager.hasNext || pager.busy,
107
- onClick: pager.next,
108
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-3.5" })
109
- })
110
- ]
111
- })] });
112
- };
113
- //#endregion
114
- export { RecordTable };
@@ -1,30 +0,0 @@
1
- import { keys } from "../../data/query-keys.js";
2
- import { useClient } from "../../data/api-provider.js";
3
- import { fetchLabels } from "../../live/relation-labels.js";
4
- import { useAdminMeta } from "../meta-gate.js";
5
- import { useQueries, useQueryClient } from "@tanstack/react-query";
6
- //#region src/app/pages/use-relation-labels.ts
7
- /** Names for the relation columns of the rows on screen, one list call per column, by column then key. */
8
- const useRelationLabels = (model, columns, rows) => {
9
- const { resources, views } = useAdminMeta();
10
- const client = useClient();
11
- const queryClient = useQueryClient();
12
- const relations = columns.flatMap((name) => {
13
- const field = model.fields[name];
14
- const target = field?.type === "relation" && field.relation ? resources[field.relation.resource] : void 0;
15
- if (!target || target.primaryKey.length !== 1) return [];
16
- const ids = [...new Set(rows.flatMap((row) => row[name] === null || row[name] === void 0 ? [] : [String(row[name])]))].sort();
17
- return ids.length > 0 ? [{
18
- name,
19
- target,
20
- ids
21
- }] : [];
22
- });
23
- const results = useQueries({ queries: relations.map(({ target, ids }) => ({
24
- queryKey: keys.labelMap(target.name, ids.join(",")),
25
- queryFn: () => fetchLabels(queryClient, client, target, ids, views[target.name])
26
- })) });
27
- return new Map(relations.flatMap(({ name }, index) => results[index]?.data ? [[name, results[index].data]] : []));
28
- };
29
- //#endregion
30
- export { useRelationLabels };