@protobase/ui 0.19.2 → 0.19.4
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/dist/app/displays/map-display.js +3 -1
- package/dist/app/gallery/detail-fields.js +12 -0
- package/dist/app/gallery/gallery-layout.js +3 -1
- package/dist/app/gallery/gallery-section.js +7 -4
- package/dist/app/gallery/lightbox.js +38 -44
- package/dist/app/gallery/photo-details.js +21 -28
- package/dist/app/map/map-layer-choice.js +11 -0
- package/dist/app/map/map-layer-switch.js +55 -0
- package/dist/app/map/map-section.js +3 -1
- package/dist/app/map/position-areas.js +4 -2
- package/dist/app/map/record-map.js +16 -11
- package/dist/app/map/records-map.js +7 -2
- package/dist/app/pages/record-actions.js +32 -6
- package/dist/app/record-mode/record-mode.js +12 -7
- package/dist/app/record-page.js +27 -38
- package/dist/data/meta-data.d.ts +3 -2
- package/dist/format/date.js +2 -15
- package/dist/package.js +1 -1
- package/dist/record-view/action-rail.js +22 -11
- package/dist/record-view/record-header.js +5 -5
- package/dist/shell/assets/app-BYFiTXK6.js +66 -0
- package/dist/shell/assets/{index-Crd6_WsY.js → index-CEvfMgFJ.js} +1 -1
- package/dist/shell/assets/index-CiGP6kle.css +2 -0
- package/dist/shell/assets/maplibre-D6zfLfTV.js +814 -0
- package/dist/shell/assets/record-map-B6_rbCcG.js +1 -0
- package/dist/shell/assets/records-map-bQbuIc13.js +1 -0
- package/dist/shell/assets/{shared-protobase-ui-DvvdY2OO.js → shared-protobase-ui-8pJgwjfc.js} +1 -1
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-Rbouc_gR.js +0 -66
- package/dist/shell/assets/index-DaFzUWmg.css +0 -2
- package/dist/shell/assets/maplibre-Gu5hHDDK.js +0 -814
- package/dist/shell/assets/record-map-DQ0oyZff.js +0 -1
- package/dist/shell/assets/records-map-Dp97bL3p.js +0 -1
package/dist/app/record-page.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { inApp } from "../app-shell/breadcrumb.js";
|
|
3
3
|
import { useAuth } from "../auth/auth-provider.js";
|
|
4
|
-
import { formatRelativeTime } from "../format/date.js";
|
|
5
4
|
import { Button, buttonClass } from "../primitives/button.js";
|
|
6
5
|
import { keys } from "../data/query-keys.js";
|
|
7
6
|
import { useClient } from "../data/api-provider.js";
|
|
@@ -42,7 +41,7 @@ import { useRecordEditor } from "./use-record-editor.js";
|
|
|
42
41
|
import { useRelatedRecords } from "./use-related-records.js";
|
|
43
42
|
import { RelatedSection } from "./related-section.js";
|
|
44
43
|
import { Fragment, useMemo } from "react";
|
|
45
|
-
import { Check
|
|
44
|
+
import { Check } from "lucide-react";
|
|
46
45
|
import { isApiError } from "@protobase/client";
|
|
47
46
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
48
47
|
import { useQueryClient } from "@tanstack/react-query";
|
|
@@ -50,9 +49,8 @@ import { enumLabel } from "@protobase/schema";
|
|
|
50
49
|
//#region src/app/record-page.tsx
|
|
51
50
|
const hidden = (model, name) => name === model.tenant || model.primaryKey.includes(name);
|
|
52
51
|
/**
|
|
53
|
-
* The view's layout, or one section with the editable fields when the view has none.
|
|
54
|
-
*
|
|
55
|
-
* view, which shows the record rather than what can be changed.
|
|
52
|
+
* The view's layout, or one section with the editable fields when the view has none. The read view, which is all a
|
|
53
|
+
* user who may only read the resource gets, shows every field: the record rather than what can be changed.
|
|
56
54
|
*/
|
|
57
55
|
const layoutFor = (model, view, read) => {
|
|
58
56
|
if (view && view.layout.length > 0) return view.layout;
|
|
@@ -148,9 +146,10 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
|
|
|
148
146
|
const { fields: fieldTypes } = useProjectUi();
|
|
149
147
|
const layout = layoutFor(model, view, shown === "read");
|
|
150
148
|
const sidebarFields = layout.flatMap((item) => item.kind === "sidebar" ? item.fields : []);
|
|
151
|
-
const
|
|
149
|
+
const hasStatus = model.fields.status?.type === "enum";
|
|
150
|
+
const statusValue = stored.record.status;
|
|
151
|
+
const status = hasStatus && statusValue !== null && statusValue !== void 0 && statusValue !== "" ? String(statusValue) : void 0;
|
|
152
152
|
const updatedAt = auditField(model, "updated");
|
|
153
|
-
const updated = updatedAt && stored.record[updatedAt] ? Math.max(0, Math.round((Date.now() - Date.parse(String(stored.record[updatedAt]))) / 1e3)) : void 0;
|
|
154
153
|
const hint = (name) => view?.fields[name];
|
|
155
154
|
const visible = (name) => fieldAccess(stored.permissions, name) !== "hidden";
|
|
156
155
|
const fieldView = (name) => {
|
|
@@ -251,30 +250,18 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
|
|
|
251
250
|
const save = async () => {
|
|
252
251
|
if (await editor.save() && leavesForm) go(readAddress, { replace: true });
|
|
253
252
|
};
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
snapshot: stored.record
|
|
262
|
-
}),
|
|
263
|
-
children: "Delete"
|
|
264
|
-
});
|
|
265
|
-
const saveControls = offersEdit ? /* @__PURE__ */ jsxs(Fragment$1, { children: [deleteButton, /* @__PURE__ */ jsxs(AppLink, {
|
|
266
|
-
to: editAddress,
|
|
267
|
-
className: cn(buttonClass({ variant: "primary" }), "max-md:min-h-11 max-md:flex-1"),
|
|
268
|
-
children: [/* @__PURE__ */ jsx(Pencil, {
|
|
269
|
-
className: "size-3.5",
|
|
270
|
-
"aria-hidden": true
|
|
271
|
-
}), "Edit"]
|
|
272
|
-
})] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
253
|
+
const startDelete = canDelete ? () => void remove.start({
|
|
254
|
+
key: recordKey,
|
|
255
|
+
title,
|
|
256
|
+
etag: stored.etag,
|
|
257
|
+
snapshot: stored.record
|
|
258
|
+
}) : void 0;
|
|
259
|
+
const formControls = shown === "form" && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
273
260
|
editor.dirty && /* @__PURE__ */ jsx("span", {
|
|
274
261
|
className: "text-xs text-muted-foreground",
|
|
275
262
|
children: "Unsaved changes"
|
|
276
263
|
}),
|
|
277
|
-
editor.dirty &&
|
|
264
|
+
editor.dirty && !leavesForm && /* @__PURE__ */ jsx(Button, {
|
|
278
265
|
className: "max-md:min-h-11",
|
|
279
266
|
disabled: editor.saving,
|
|
280
267
|
onClick: editor.discard,
|
|
@@ -286,8 +273,7 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
|
|
|
286
273
|
onClick: (event) => inApp(event) && editor.discard(),
|
|
287
274
|
children: "Cancel"
|
|
288
275
|
}),
|
|
289
|
-
|
|
290
|
-
canUpdate && /* @__PURE__ */ jsxs(Button, {
|
|
276
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
291
277
|
variant: "primary",
|
|
292
278
|
className: "max-md:min-h-11 max-md:flex-1",
|
|
293
279
|
loading: editor.saving,
|
|
@@ -306,16 +292,17 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
|
|
|
306
292
|
className: "mx-auto max-w-[1100px] px-4 pb-8 pt-6 md:px-10 md:py-8",
|
|
307
293
|
children: [
|
|
308
294
|
/* @__PURE__ */ jsx(RecordHeader, {
|
|
309
|
-
collection: view?.names?.plural ?? humanize(model.name),
|
|
310
295
|
title,
|
|
311
|
-
status: {
|
|
312
|
-
label:
|
|
313
|
-
tone:
|
|
296
|
+
status: status ? {
|
|
297
|
+
label: enumLabel(status, hint("status")?.valueLabels),
|
|
298
|
+
tone: toneFor(status)
|
|
299
|
+
} : hasStatus ? void 0 : {
|
|
300
|
+
label: singular,
|
|
301
|
+
tone: "neutral"
|
|
314
302
|
},
|
|
315
|
-
lastEditedBy: { ago: updated === void 0 ? "recently" : formatRelativeTime(updated) },
|
|
316
303
|
actions: /* @__PURE__ */ jsx("div", {
|
|
317
304
|
className: "hidden items-center gap-4 md:flex",
|
|
318
|
-
children:
|
|
305
|
+
children: formControls
|
|
319
306
|
})
|
|
320
307
|
}),
|
|
321
308
|
editor.message && /* @__PURE__ */ jsx("div", {
|
|
@@ -411,7 +398,9 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
|
|
|
411
398
|
/* @__PURE__ */ jsx(RecordActions, {
|
|
412
399
|
model,
|
|
413
400
|
stored,
|
|
414
|
-
recordKey
|
|
401
|
+
recordKey,
|
|
402
|
+
edit: offersEdit ? editAddress : void 0,
|
|
403
|
+
onDelete: startDelete
|
|
415
404
|
}),
|
|
416
405
|
firstSidebar === -1 && summary,
|
|
417
406
|
layout.map((item, index) => {
|
|
@@ -448,9 +437,9 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
|
|
|
448
437
|
]
|
|
449
438
|
})
|
|
450
439
|
}),
|
|
451
|
-
|
|
440
|
+
formControls && /* @__PURE__ */ jsx("div", {
|
|
452
441
|
className: "sticky bottom-[var(--pb-sticky-bottom,0px)] z-20 flex shrink-0 items-center gap-2 border-t bg-background p-3 md:hidden",
|
|
453
|
-
children:
|
|
442
|
+
children: formControls
|
|
454
443
|
})
|
|
455
444
|
]
|
|
456
445
|
});
|
package/dist/data/meta-data.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PluginRef, ResourcePermissions, SourceRef } from "@protobase/client";
|
|
1
|
+
import { MapLayerModel, PluginRef, ResourcePermissions, SourceRef } from "@protobase/client";
|
|
2
2
|
import { BottomBarModel, ResourceModel, SignInViewModel, UserMenuModel, ViewModel } from "@protobase/schema";
|
|
3
3
|
import { PageModel } from "@protobase/layout";
|
|
4
4
|
//#region src/data/meta-data.d.ts
|
|
@@ -27,9 +27,10 @@ export type MetaData = {
|
|
|
27
27
|
dsn: string;
|
|
28
28
|
environment?: string;
|
|
29
29
|
};
|
|
30
|
-
/** The map style the
|
|
30
|
+
/** The map style the app's maps are drawn with, and the layers they may switch to, when the app has one (see `MapSection`). */
|
|
31
31
|
maps?: {
|
|
32
32
|
style: string;
|
|
33
|
+
layers?: MapLayerModel[];
|
|
33
34
|
};
|
|
34
35
|
/** The hosts besides the app's own whose images are drawn with the page's referrer (see `FileImage`). */
|
|
35
36
|
files?: {
|
package/dist/format/date.js
CHANGED
|
@@ -19,19 +19,6 @@ const time = new Intl.DateTimeFormat("en-GB", {
|
|
|
19
19
|
const formatDate = (ms) => day.format(ms);
|
|
20
20
|
const formatDayShort = (ms) => dayShort.format(ms);
|
|
21
21
|
const formatDateTime = (ms) => `${day.format(ms)}, ${time.format(ms)}`;
|
|
22
|
-
|
|
23
|
-
const units = [
|
|
24
|
-
["year", 31536e3],
|
|
25
|
-
["month", 2592e3],
|
|
26
|
-
["day", 86400],
|
|
27
|
-
["hour", 3600],
|
|
28
|
-
["minute", 60]
|
|
29
|
-
];
|
|
30
|
-
/** "5 minutes ago", "yesterday", "3 years ago"; `seconds` is how long ago it was. */
|
|
31
|
-
const formatRelativeTime = (seconds) => {
|
|
32
|
-
const [unit, size] = units.find(([, size]) => seconds >= size) ?? ["second", 1];
|
|
33
|
-
if (unit === "second" && seconds < 45) return "just now";
|
|
34
|
-
return relative.format(-Math.floor(seconds / size), unit);
|
|
35
|
-
};
|
|
22
|
+
new Intl.RelativeTimeFormat("en", { numeric: "auto" });
|
|
36
23
|
//#endregion
|
|
37
|
-
export { formatDate, formatDateTime, formatDayShort
|
|
24
|
+
export { formatDate, formatDateTime, formatDayShort };
|
package/dist/package.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
3
|
//#region src/record-view/action-rail.tsx
|
|
4
|
+
const itemClass = "flex min-h-11 items-center gap-2 rounded-md border border-border-strong px-3 text-left hover:bg-surface disabled:cursor-default disabled:opacity-60 md:min-h-0 md:w-full md:items-start md:gap-3 md:rounded-none md:border-0 md:px-4 md:py-3";
|
|
3
5
|
/** Named actions with one-line descriptions; on phones a row of buttons, from `md` a list. */
|
|
4
6
|
const ActionRail = ({ actions }) => /* @__PURE__ */ jsxs("div", {
|
|
5
7
|
className: "overflow-hidden rounded-lg border border-border bg-background",
|
|
@@ -8,20 +10,29 @@ const ActionRail = ({ actions }) => /* @__PURE__ */ jsxs("div", {
|
|
|
8
10
|
children: "Actions"
|
|
9
11
|
}), /* @__PURE__ */ jsx("ul", {
|
|
10
12
|
className: "flex flex-wrap gap-2 p-2 md:block md:divide-y md:divide-border md:p-0",
|
|
11
|
-
children: actions.map(({ name, description, icon: Icon, onRun, blocked, running }) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
children: [/* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground md:mt-0.5" }), /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
18
|
-
className: "block text-[13px] font-medium text-foreground",
|
|
13
|
+
children: actions.map(({ name, description, icon: Icon, onRun, blocked, running, tone, render }) => {
|
|
14
|
+
const children = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Icon, {
|
|
15
|
+
className: cn("size-4 shrink-0 md:mt-0.5", tone === "danger" ? "text-danger-text" : "text-muted-foreground"),
|
|
16
|
+
"aria-hidden": true
|
|
17
|
+
}), /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
18
|
+
className: cn("block text-[13px] font-medium", tone === "danger" ? "text-danger-text" : "text-foreground"),
|
|
19
19
|
children: name
|
|
20
20
|
}), description && /* @__PURE__ */ jsx("span", {
|
|
21
21
|
className: "mt-0.5 hidden text-xs leading-snug text-muted-foreground md:block",
|
|
22
22
|
children: description
|
|
23
|
-
})] })]
|
|
24
|
-
|
|
23
|
+
})] })] });
|
|
24
|
+
return /* @__PURE__ */ jsx("li", { children: render ? render({
|
|
25
|
+
className: itemClass,
|
|
26
|
+
children
|
|
27
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
28
|
+
type: "button",
|
|
29
|
+
onClick: onRun,
|
|
30
|
+
disabled: Boolean(blocked) || running,
|
|
31
|
+
title: blocked,
|
|
32
|
+
className: itemClass,
|
|
33
|
+
children
|
|
34
|
+
}) }, name);
|
|
35
|
+
})
|
|
25
36
|
})]
|
|
26
37
|
});
|
|
27
38
|
//#endregion
|
|
@@ -5,21 +5,21 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
|
5
5
|
const RecordHeader = ({ collection, title, status, lastEditedBy, presence = [], actions }) => /* @__PURE__ */ jsxs("header", {
|
|
6
6
|
className: "flex flex-col gap-4 border-b border-border pb-6 md:flex-row md:items-end md:justify-between md:gap-6",
|
|
7
7
|
children: [/* @__PURE__ */ jsxs("div", { children: [
|
|
8
|
-
/* @__PURE__ */ jsx("p", {
|
|
9
|
-
className: "text-xs text-muted-foreground",
|
|
8
|
+
collection && /* @__PURE__ */ jsx("p", {
|
|
9
|
+
className: "mb-1 text-xs text-muted-foreground",
|
|
10
10
|
children: collection
|
|
11
11
|
}),
|
|
12
12
|
/* @__PURE__ */ jsxs("div", {
|
|
13
|
-
className: "
|
|
13
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1",
|
|
14
14
|
children: [/* @__PURE__ */ jsx("h1", {
|
|
15
15
|
className: "text-xl font-semibold tracking-tight text-foreground md:text-2xl",
|
|
16
16
|
children: title
|
|
17
|
-
}), /* @__PURE__ */ jsx(Badge, {
|
|
17
|
+
}), status && /* @__PURE__ */ jsx(Badge, {
|
|
18
18
|
tone: status.tone,
|
|
19
19
|
children: status.label
|
|
20
20
|
})]
|
|
21
21
|
}),
|
|
22
|
-
/* @__PURE__ */ jsx("p", {
|
|
22
|
+
lastEditedBy && /* @__PURE__ */ jsx("p", {
|
|
23
23
|
className: "mt-1.5 text-xs text-muted-foreground",
|
|
24
24
|
children: lastEditedBy.name ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
25
|
"Last edited by ",
|