@protobase/ui 0.19.8 → 0.19.10
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/app.js +4 -4
- package/dist/app/create/create-offered.js +9 -0
- package/dist/app/create/create-page.js +4 -4
- package/dist/app/create/create-record-form.js +26 -62
- package/dist/app/create/create-route.js +29 -0
- package/dist/app/create/form.d.ts +30 -0
- package/dist/app/create/form.js +72 -0
- package/dist/app/custom-section.js +1 -1
- package/dist/app/displays/map-display.js +1 -1
- package/dist/app/displays/record-card.js +18 -4
- package/dist/app/displays/record-link.js +1 -1
- package/dist/app/displays/use-map-records.js +2 -2
- package/dist/app/embedded-list/embedded-columns.js +2 -2
- package/dist/app/embedded-list/embedded-list.js +5 -5
- package/dist/app/embedded-list/use-embedded-rows.js +2 -2
- package/dist/app/field-editor.js +21 -5
- package/dist/app/gallery/gallery-section.js +1 -1
- package/dist/app/gallery/photo-details.js +2 -2
- package/dist/app/list-page.js +18 -17
- package/dist/app/pages/blocks/card-row.js +2 -2
- package/dist/app/pages/blocks/field.js +2 -2
- package/dist/app/pages/blocks/link.js +1 -1
- package/dist/app/pages/blocks/modal-form.js +6 -6
- package/dist/app/pages/blocks/progress.js +1 -1
- package/dist/app/pages/blocks/record-card.js +2 -2
- package/dist/app/pages/blocks/record-row.js +1 -1
- package/dist/app/pages/blocks/show.js +2 -2
- package/dist/app/pages/blocks/stat.js +3 -3
- package/dist/app/pages/blocks/table.js +1 -1
- package/dist/app/pages/field-value.js +5 -2
- package/dist/app/pages/record-actions.js +2 -2
- package/dist/app/pages/use-run-action.js +2 -2
- package/dist/app/record-page.js +15 -11
- package/dist/app/record-row/record-row-body.js +2 -2
- package/dist/app/record-row/saved-record-row.js +1 -1
- package/dist/app/related-section.js +1 -1
- package/dist/app/searches/saved-search-rows.js +1 -1
- package/dist/app/searches/saved-search-section.js +2 -2
- package/dist/app/searches/saved-searches.js +2 -2
- package/dist/app/searches/search-controls.js +1 -1
- package/dist/app/searches/search-name-dialog.js +1 -1
- package/dist/app/searches/update-search-dialog.js +2 -2
- package/dist/app/searches/use-filter-chips.js +1 -1
- package/dist/app/searches/use-save-search.js +2 -2
- package/dist/app/searches/use-saved-searches.js +1 -1
- package/dist/app/searches/use-search-row.js +1 -1
- package/dist/app/sensitive-field.js +1 -1
- package/dist/app/use-app-address.js +1 -1
- package/dist/app/value-badge.js +1 -1
- package/dist/auth/sign-in-prompt.js +1 -1
- package/dist/data-grid/cells.js +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +10 -2
- package/dist/live/columns-from-model.js +3 -0
- package/dist/live/grid-loader.js +1 -1
- package/dist/live/use-filter-config.js +1 -1
- package/dist/markdown/markdown-content.js +18 -0
- package/dist/markdown/markdown-editor.js +46 -0
- package/dist/markdown/markdown-excerpt.js +36 -0
- package/dist/markdown/markdown-hint.js +5 -0
- package/dist/markdown/markdown-html.js +59 -0
- package/dist/markdown/markdown.js +15 -0
- package/dist/package.js +1 -1
- package/dist/primitives/choices.d.ts +22 -0
- package/dist/primitives/choices.js +33 -0
- package/dist/primitives/dialog.d.ts +15 -0
- package/dist/primitives/menu-select.d.ts +14 -0
- package/dist/primitives/segmented.d.ts +23 -0
- package/dist/primitives/textarea.d.ts +9 -0
- package/dist/record-view/field.d.ts +27 -0
- package/dist/record-view/section.d.ts +10 -0
- package/dist/shell/assets/app-GKl91FLS.js +69 -0
- package/dist/shell/assets/{index-cMmFPuUQ.js → index-BB2BN-6s.js} +1 -1
- package/dist/shell/assets/index-DxV5Rp_F.css +2 -0
- package/dist/shell/assets/{maplibre-9XYrB9k6.js → maplibre-DHotMteL.js} +2 -2
- package/dist/shell/assets/markdown-content-C4zE1qZp.js +67 -0
- package/dist/shell/assets/{record-map-OBtKCiUB.js → record-map-BBbVY6vn.js} +1 -1
- package/dist/shell/assets/{records-map-C4oMPL5C.js → records-map-ur6HR-I5.js} +1 -1
- package/dist/shell/assets/shared-protobase-ui-D7kkYiTL.js +1 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +6 -4
- package/dist/shell/assets/app-D8i5r_6U.js +0 -68
- package/dist/shell/assets/index-Cuulz26c.css +0 -2
- package/dist/shell/assets/shared-protobase-ui-CN1HmyDa.js +0 -1
package/dist/app/app.js
CHANGED
|
@@ -24,7 +24,8 @@ import { breadcrumbFor } from "./breadcrumb-for.js";
|
|
|
24
24
|
import { sourceStatusOf } from "../data/with-sources.js";
|
|
25
25
|
import { MetaGate, Notice, useAdminMeta } from "./meta-gate.js";
|
|
26
26
|
import { useAppAddress } from "./use-app-address.js";
|
|
27
|
-
import {
|
|
27
|
+
import { ComposedPage } from "./pages/composed-page.js";
|
|
28
|
+
import { CreateRoute } from "./create/create-route.js";
|
|
28
29
|
import { ListPage } from "./list-page.js";
|
|
29
30
|
import { MembersPage } from "../organizations/members-page.js";
|
|
30
31
|
import { InvitationsPage } from "../organizations/invitations-page.js";
|
|
@@ -35,7 +36,6 @@ import { actingUser } from "../roles/acting-user.js";
|
|
|
35
36
|
import { useActiveRole } from "../roles/use-active-role.js";
|
|
36
37
|
import { userMenuFromMeta } from "./user-menu-from-meta.js";
|
|
37
38
|
import { navFromMeta } from "./nav-from-meta.js";
|
|
38
|
-
import { ComposedPage } from "./pages/composed-page.js";
|
|
39
39
|
import { ProjectLogo } from "./project-logo.js";
|
|
40
40
|
import { PluginGate, usePluginsOff } from "../plugins/plugin-gate.js";
|
|
41
41
|
import { PluginNotice } from "../plugins/plugin-notice.js";
|
|
@@ -163,10 +163,10 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
163
163
|
address.page,
|
|
164
164
|
"."
|
|
165
165
|
]
|
|
166
|
-
}) : drawnFrom && drawnFrom.state !== "ready" ? /* @__PURE__ */ jsx(SourceNotice, { status: drawnFrom }) : address.kind === "create" ? /* @__PURE__ */ jsx(
|
|
166
|
+
}) : drawnFrom && drawnFrom.state !== "ready" ? /* @__PURE__ */ jsx(SourceNotice, { status: drawnFrom }) : address.kind === "create" ? /* @__PURE__ */ jsx(CreateRoute, {
|
|
167
167
|
resource: address.resource,
|
|
168
168
|
values: address.values
|
|
169
|
-
},
|
|
169
|
+
}, address.resource) : address.kind === "record" ? /* @__PURE__ */ jsx(RecordPage, {
|
|
170
170
|
resource: address.resource,
|
|
171
171
|
recordKey: address.key
|
|
172
172
|
}, `${address.resource}/${address.key}`) : /* @__PURE__ */ jsx(ListPage, { address }, address.resource);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { can } from "../permissions.js";
|
|
2
|
+
//#region src/app/create/create-offered.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether the list offers New: a view's create page to whoever `/meta` gives that page, whatever the resource's access
|
|
5
|
+
* says; the create form to whoever may create records.
|
|
6
|
+
*/
|
|
7
|
+
const createOffered = (view, pages, permissions) => view?.create ? Object.hasOwn(pages, view.create.page) : can(permissions, "create");
|
|
8
|
+
//#endregion
|
|
9
|
+
export { createOffered };
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { useClient } from "../../data/api-provider.js";
|
|
2
2
|
import { recordKey } from "../../live/model-helpers.js";
|
|
3
3
|
import { humanize } from "../../live/naming.js";
|
|
4
|
-
import { fetchLabels } from "../../live/relation-labels.js";
|
|
5
4
|
import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
5
|
+
import { can } from "../permissions.js";
|
|
6
6
|
import { useAppAddress } from "../use-app-address.js";
|
|
7
|
+
import { fetchLabels } from "../../live/relation-labels.js";
|
|
8
|
+
import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired } from "./create-values.js";
|
|
7
9
|
import { FieldEditor } from "../field-editor.js";
|
|
8
|
-
import { can } from "../permissions.js";
|
|
9
10
|
import { useLiveSave } from "../use-live-save.js";
|
|
10
11
|
import { CreateRecordForm } from "./create-record-form.js";
|
|
11
|
-
import { createBody, createSections, hasDatabaseDefault, initialDraft, isChosen, isRequired } from "./create-values.js";
|
|
12
12
|
import { useEffect, useState } from "react";
|
|
13
13
|
import { isApiError } from "@protobase/client";
|
|
14
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -117,7 +117,7 @@ const LoadedCreate = ({ resource, values }) => {
|
|
|
117
117
|
help: view?.fields[name]?.help,
|
|
118
118
|
required: isRequired(field),
|
|
119
119
|
error: errors[name],
|
|
120
|
-
wide: Boolean(view?.fields[name]?.multiline),
|
|
120
|
+
wide: Boolean(view?.fields[name]?.multiline || view?.fields[name]?.markdown),
|
|
121
121
|
editor: /* @__PURE__ */ jsx(FieldEditor, {
|
|
122
122
|
field,
|
|
123
123
|
hints: view?.fields[name],
|
|
@@ -1,69 +1,33 @@
|
|
|
1
|
-
import { Button } from "../../primitives/button.js";
|
|
2
|
-
import { ErrorBanner } from "../error-banner.js";
|
|
3
1
|
import { Field } from "../../record-view/field.js";
|
|
4
2
|
import { Section } from "../../record-view/section.js";
|
|
5
|
-
import {
|
|
3
|
+
import { Form } from "./form.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
5
|
//#region src/app/create/create-record-form.tsx
|
|
7
6
|
/** The presentation of "New <thing>": the view's sections, each field with its editor, and Create or Cancel. */
|
|
8
|
-
const CreateRecordForm = ({ noun, collection, sections, saving, message, onSubmit, onCancel }) => /* @__PURE__ */ jsx(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
variant: "primary",
|
|
33
|
-
type: "submit",
|
|
34
|
-
className: "max-sm:min-h-11 max-sm:flex-1",
|
|
35
|
-
loading: saving,
|
|
36
|
-
children: ["Create ", noun.toLowerCase()]
|
|
37
|
-
})]
|
|
38
|
-
})]
|
|
39
|
-
}),
|
|
40
|
-
message && /* @__PURE__ */ jsx("div", {
|
|
41
|
-
className: "mt-4",
|
|
42
|
-
children: /* @__PURE__ */ jsx(ErrorBanner, {
|
|
43
|
-
title: "Not created",
|
|
44
|
-
error: new Error(message)
|
|
45
|
-
})
|
|
46
|
-
}),
|
|
47
|
-
/* @__PURE__ */ jsx("div", {
|
|
48
|
-
className: "divide-y divide-border",
|
|
49
|
-
children: sections.map((section) => /* @__PURE__ */ jsx(Section, {
|
|
50
|
-
title: section.title,
|
|
51
|
-
help: section.help,
|
|
52
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
53
|
-
className: "grid grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-2",
|
|
54
|
-
children: section.fields.map((field) => /* @__PURE__ */ jsx(Field, {
|
|
55
|
-
label: field.label,
|
|
56
|
-
required: field.required,
|
|
57
|
-
help: field.help,
|
|
58
|
-
error: field.error,
|
|
59
|
-
wide: field.wide,
|
|
60
|
-
children: field.editor
|
|
61
|
-
}, field.name))
|
|
62
|
-
})
|
|
63
|
-
}, section.title))
|
|
64
|
-
})
|
|
65
|
-
]
|
|
66
|
-
})
|
|
7
|
+
const CreateRecordForm = ({ noun, collection, sections, saving, message, onSubmit, onCancel }) => /* @__PURE__ */ jsx(Form, {
|
|
8
|
+
title: `New ${noun.toLowerCase()}`,
|
|
9
|
+
eyebrow: collection,
|
|
10
|
+
submit: `Create ${noun.toLowerCase()}`,
|
|
11
|
+
saving,
|
|
12
|
+
error: message ? new Error(message) : void 0,
|
|
13
|
+
errorTitle: "Not created",
|
|
14
|
+
onSubmit,
|
|
15
|
+
onCancel,
|
|
16
|
+
children: sections.map((section) => /* @__PURE__ */ jsx(Section, {
|
|
17
|
+
title: section.title,
|
|
18
|
+
help: section.help,
|
|
19
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: "grid grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-2",
|
|
21
|
+
children: section.fields.map((field) => /* @__PURE__ */ jsx(Field, {
|
|
22
|
+
label: field.label,
|
|
23
|
+
required: field.required,
|
|
24
|
+
help: field.help,
|
|
25
|
+
error: field.error,
|
|
26
|
+
wide: field.wide,
|
|
27
|
+
children: field.editor
|
|
28
|
+
}, field.name))
|
|
29
|
+
})
|
|
30
|
+
}, section.title))
|
|
67
31
|
});
|
|
68
32
|
//#endregion
|
|
69
33
|
export { CreateRecordForm };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { humanize } from "../../live/naming.js";
|
|
2
|
+
import { Notice, useAdminMeta } from "../meta-gate.js";
|
|
3
|
+
import { ComposedPage } from "../pages/composed-page.js";
|
|
4
|
+
import { CreatePage } from "./create-page.js";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/app/create/create-route.tsx
|
|
7
|
+
/**
|
|
8
|
+
* `/:resource/new`: the view's create page when it names one, which reads the address's `values` itself and may change
|
|
9
|
+
* them in place, else the create form, started again when the address's `values` change.
|
|
10
|
+
*/
|
|
11
|
+
const CreateRoute = ({ resource, values }) => {
|
|
12
|
+
const meta = useAdminMeta();
|
|
13
|
+
const view = meta.views[resource];
|
|
14
|
+
if (!view?.create) return /* @__PURE__ */ jsx(CreatePage, {
|
|
15
|
+
resource,
|
|
16
|
+
values
|
|
17
|
+
}, new URLSearchParams(values).toString());
|
|
18
|
+
if (!meta.pages[view.create.page]) return /* @__PURE__ */ jsxs(Notice, {
|
|
19
|
+
title: "Not allowed",
|
|
20
|
+
children: [
|
|
21
|
+
"You cannot create ",
|
|
22
|
+
view.names?.plural.toLowerCase() ?? humanize(resource),
|
|
23
|
+
"."
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
return /* @__PURE__ */ jsx(ComposedPage, { name: view.create.page });
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { CreateRoute };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/app/create/form.d.ts
|
|
3
|
+
export type FormProps = {
|
|
4
|
+
/**
|
|
5
|
+
* The heading, such as "New repair", when the form is the whole page, as the create form is: the heading and its
|
|
6
|
+
* buttons on top. Without it, the form sits in a page with a heading of its own, a composed page's, and its buttons
|
|
7
|
+
* follow the sections.
|
|
8
|
+
*/
|
|
9
|
+
title?: string;
|
|
10
|
+
/** The line above the heading, such as the records' name. */
|
|
11
|
+
eyebrow?: string;
|
|
12
|
+
/** The submit button's label, such as "Create repair". */
|
|
13
|
+
submit: string;
|
|
14
|
+
/** The submit button spins and cannot be pressed again. */
|
|
15
|
+
saving?: boolean;
|
|
16
|
+
/** The submit button cannot be pressed, while the answers are not complete. */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/** A failure that belongs to no field, shown above the sections; an API error shows its messages and hints. */
|
|
19
|
+
error?: unknown;
|
|
20
|
+
/** The failure's title. */
|
|
21
|
+
errorTitle?: string;
|
|
22
|
+
onSubmit: () => void;
|
|
23
|
+
/** Shows Cancel beside the submit button. */
|
|
24
|
+
onCancel?: () => void;
|
|
25
|
+
/** The form's sections (`Section`), drawn one under the other with a line between them. */
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
};
|
|
28
|
+
/** The frame of the app's forms: Cancel and the submit button, a failure that belongs to no field, and the sections. */
|
|
29
|
+
export declare const Form: ({ title, eyebrow, submit, saving, disabled, error, errorTitle, onSubmit, onCancel, children }: FormProps) => import("react").JSX.Element;
|
|
30
|
+
//#endregion
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Button } from "../../primitives/button.js";
|
|
2
|
+
import { ErrorBanner } from "../error-banner.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/app/create/form.tsx
|
|
5
|
+
/** The frame of the app's forms: Cancel and the submit button, a failure that belongs to no field, and the sections. */
|
|
6
|
+
const Form = ({ title, eyebrow, submit, saving, disabled, error, errorTitle = "Not saved", onSubmit, onCancel, children }) => {
|
|
7
|
+
const buttons = /* @__PURE__ */ jsxs("div", {
|
|
8
|
+
className: "flex items-center gap-2",
|
|
9
|
+
children: [onCancel && /* @__PURE__ */ jsx(Button, {
|
|
10
|
+
className: "max-sm:min-h-11",
|
|
11
|
+
onClick: onCancel,
|
|
12
|
+
children: "Cancel"
|
|
13
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
14
|
+
variant: "primary",
|
|
15
|
+
type: "submit",
|
|
16
|
+
className: "max-sm:min-h-11 max-sm:flex-1",
|
|
17
|
+
loading: saving,
|
|
18
|
+
disabled,
|
|
19
|
+
children: submit
|
|
20
|
+
})]
|
|
21
|
+
});
|
|
22
|
+
const failure = error !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
23
|
+
className: "mt-4",
|
|
24
|
+
children: /* @__PURE__ */ jsx(ErrorBanner, {
|
|
25
|
+
title: errorTitle,
|
|
26
|
+
error
|
|
27
|
+
})
|
|
28
|
+
});
|
|
29
|
+
const sections = /* @__PURE__ */ jsx("div", {
|
|
30
|
+
className: "divide-y divide-border",
|
|
31
|
+
children
|
|
32
|
+
});
|
|
33
|
+
const send = (event) => {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
onSubmit();
|
|
36
|
+
};
|
|
37
|
+
if (title === void 0) return /* @__PURE__ */ jsxs("form", {
|
|
38
|
+
className: "max-w-[760px]",
|
|
39
|
+
onSubmit: send,
|
|
40
|
+
children: [
|
|
41
|
+
failure,
|
|
42
|
+
sections,
|
|
43
|
+
/* @__PURE__ */ jsx("footer", {
|
|
44
|
+
className: "border-t pt-6 sm:flex sm:justify-end",
|
|
45
|
+
children: buttons
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
return /* @__PURE__ */ jsx("form", {
|
|
50
|
+
className: "flex-1",
|
|
51
|
+
onSubmit: send,
|
|
52
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
53
|
+
className: "mx-auto max-w-[760px] px-4 py-6 md:px-10 md:py-8",
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ jsxs("header", {
|
|
56
|
+
className: "flex flex-col gap-4 border-b pb-6 sm:flex-row sm:items-end sm:justify-between sm:gap-6",
|
|
57
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [eyebrow && /* @__PURE__ */ jsx("p", {
|
|
58
|
+
className: "text-xs text-muted-foreground",
|
|
59
|
+
children: eyebrow
|
|
60
|
+
}), /* @__PURE__ */ jsx("h1", {
|
|
61
|
+
className: "mt-1 text-2xl font-semibold tracking-tight",
|
|
62
|
+
children: title
|
|
63
|
+
})] }), buttons]
|
|
64
|
+
}),
|
|
65
|
+
failure,
|
|
66
|
+
sections
|
|
67
|
+
]
|
|
68
|
+
})
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { Form };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Problem } from "./pages/problem.js";
|
|
2
2
|
import { CustomBoundary } from "./pages/custom-boundary.js";
|
|
3
3
|
import { useProjectUi } from "./pages/project-ui.js";
|
|
4
|
+
import { RecordScope } from "./pages/record-scope.js";
|
|
4
5
|
import { Section } from "../record-view/section.js";
|
|
5
6
|
import { SidebarCard } from "../record-view/record-sidebar.js";
|
|
6
|
-
import { RecordScope } from "./pages/record-scope.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { unmarkFieldRefs } from "@protobase/schema/internal";
|
|
9
9
|
//#region src/app/custom-section.tsx
|
|
@@ -2,8 +2,8 @@ import { cn } from "../../lib/cn.js";
|
|
|
2
2
|
import { Button } from "../../primitives/button.js";
|
|
3
3
|
import { Problem } from "../pages/problem.js";
|
|
4
4
|
import { humanize } from "../../live/naming.js";
|
|
5
|
-
import { mapCards } from "./list-displays.js";
|
|
6
5
|
import { useAdminMeta } from "../meta-gate.js";
|
|
6
|
+
import { mapCards } from "./list-displays.js";
|
|
7
7
|
import { ErrorBanner } from "../error-banner.js";
|
|
8
8
|
import { useDebounced } from "../../lib/use-debounced.js";
|
|
9
9
|
import { RecordCard } from "./record-card.js";
|
|
@@ -8,9 +8,12 @@ import { amountParts, cardFields, filledAmount, filledMeta } from "./list-displa
|
|
|
8
8
|
import { AppLink } from "../use-app-address.js";
|
|
9
9
|
import { shownWidget } from "../field-types/shown-widget.js";
|
|
10
10
|
import { WidgetPart } from "../field-types/widget-part.js";
|
|
11
|
+
import { markdownExcerpt } from "../../markdown/markdown-excerpt.js";
|
|
12
|
+
import { isMarkdown } from "../../markdown/markdown-hint.js";
|
|
13
|
+
import { ValueBadge } from "../value-badge.js";
|
|
14
|
+
import { Markdown } from "../../markdown/markdown.js";
|
|
11
15
|
import { namedIcon } from "../named-icon.js";
|
|
12
16
|
import { FavoriteButton } from "../saved/favorite-button.js";
|
|
13
|
-
import { ValueBadge } from "../value-badge.js";
|
|
14
17
|
import { CardImage } from "./card-image.js";
|
|
15
18
|
import { CardCarousel } from "./card-carousel.js";
|
|
16
19
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -38,7 +41,9 @@ const RecordCard = ({ model, view, display, row, swipe = true, mark }) => {
|
|
|
38
41
|
const text = (name) => {
|
|
39
42
|
const field = model.fields[name];
|
|
40
43
|
if (!field) return "";
|
|
41
|
-
|
|
44
|
+
const value = row.record[name];
|
|
45
|
+
if (row.labels[name] === void 0 && isMarkdown(field, view?.fields[name]) && typeof value === "string" && value !== "") return markdownExcerpt(value);
|
|
46
|
+
return row.labels[name] ?? displayValue(field, value, view?.fields[name]);
|
|
42
47
|
};
|
|
43
48
|
const { fields: fieldTypes, components } = useProjectUi();
|
|
44
49
|
const Card = display.card === void 0 ? void 0 : components?.[display.card];
|
|
@@ -53,6 +58,15 @@ const RecordCard = ({ model, view, display, row, swipe = true, mark }) => {
|
|
|
53
58
|
fallback: text(name)
|
|
54
59
|
}) : text(name);
|
|
55
60
|
};
|
|
61
|
+
const subtitleValue = (name) => {
|
|
62
|
+
const field = model.fields[name];
|
|
63
|
+
const value = row.record[name];
|
|
64
|
+
if (!field || !isMarkdown(field, view?.fields[name]) || shownWidget(fieldTypes, field, view?.fields[name]) || typeof value !== "string" || value === "") return shownValue(name);
|
|
65
|
+
return /* @__PURE__ */ jsx(Markdown, {
|
|
66
|
+
source: value,
|
|
67
|
+
inLink: true
|
|
68
|
+
});
|
|
69
|
+
};
|
|
56
70
|
const metaValue = (name) => {
|
|
57
71
|
const field = model.fields[name];
|
|
58
72
|
const hints = view?.fields[name];
|
|
@@ -99,8 +113,8 @@ const RecordCard = ({ model, view, display, row, swipe = true, mark }) => {
|
|
|
99
113
|
})]
|
|
100
114
|
}),
|
|
101
115
|
subtitle !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
102
|
-
className: view?.fields[subtitle]?.multiline ? "line-clamp-3 whitespace-pre-line break-words text-muted-foreground" : "truncate text-muted-foreground",
|
|
103
|
-
children:
|
|
116
|
+
className: view?.fields[subtitle]?.markdown ? "line-clamp-3 break-words text-muted-foreground" : view?.fields[subtitle]?.multiline ? "line-clamp-3 whitespace-pre-line break-words text-muted-foreground" : "truncate text-muted-foreground",
|
|
117
|
+
children: subtitleValue(subtitle)
|
|
104
118
|
}),
|
|
105
119
|
shownAmount !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
106
120
|
className: "truncate tabular-nums text-muted-foreground",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useLayoutRecord } from "../pages/record-scope.js";
|
|
1
2
|
import { AppLink } from "../use-app-address.js";
|
|
2
3
|
import { useOptionalListDisplay } from "./display-context.js";
|
|
3
|
-
import { useLayoutRecord } from "../pages/record-scope.js";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/app/displays/record-link.tsx
|
|
6
6
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { keys } from "../../data/query-keys.js";
|
|
2
2
|
import { useClient } from "../../data/api-provider.js";
|
|
3
|
-
import {
|
|
3
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
4
4
|
import { listPhotos, withPhotos } from "../../live/row-photos.js";
|
|
5
5
|
import { displayRow } from "./list-displays.js";
|
|
6
|
-
import {
|
|
6
|
+
import { listLabels, withLabels } from "../../live/relation-labels.js";
|
|
7
7
|
import { areaFilter, requestZoom } from "../map/map-area.js";
|
|
8
8
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
9
9
|
//#region src/app/displays/use-map-records.ts
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { humanize } from "../../live/naming.js";
|
|
2
2
|
import { displayRow } from "../displays/list-displays.js";
|
|
3
|
-
import { columnsFromModel } from "../../live/columns-from-model.js";
|
|
4
3
|
import { scopeOf } from "../pages/use-layout-data.js";
|
|
5
|
-
import {
|
|
4
|
+
import { columnsFromModel } from "../../live/columns-from-model.js";
|
|
6
5
|
import { FieldValue } from "../pages/field-value.js";
|
|
7
6
|
import { usePathValue } from "../pages/use-path-value.js";
|
|
7
|
+
import { withFieldTypeCells } from "../field-types/field-type-cells.js";
|
|
8
8
|
import { jsx } from "react/jsx-runtime";
|
|
9
9
|
import { resolveFieldPath } from "@protobase/layout";
|
|
10
10
|
//#region src/app/embedded-list/embedded-columns.tsx
|
|
@@ -3,19 +3,19 @@ import { Skeleton } from "../../primitives/skeleton.js";
|
|
|
3
3
|
import { recordKey } from "../../live/model-helpers.js";
|
|
4
4
|
import { humanize } from "../../live/naming.js";
|
|
5
5
|
import { useProjectUi } from "../pages/project-ui.js";
|
|
6
|
+
import { useAdminMeta } from "../meta-gate.js";
|
|
6
7
|
import { displayRow, loadedFields, shownDisplay } from "../displays/list-displays.js";
|
|
7
8
|
import { defaultOrderBy, parseSort, sortToOrderBy } from "../list-order.js";
|
|
8
|
-
import { useAdminMeta } from "../meta-gate.js";
|
|
9
9
|
import { AppLink, useAppAddress } from "../use-app-address.js";
|
|
10
10
|
import { ErrorBanner } from "../error-banner.js";
|
|
11
|
-
import { DataGrid } from "../../data-grid/data-grid.js";
|
|
12
11
|
import { listColumnNames } from "../../live/columns-from-model.js";
|
|
13
|
-
import {
|
|
12
|
+
import { DataGrid } from "../../data-grid/data-grid.js";
|
|
13
|
+
import { viewAllAddress } from "./embedded-display.js";
|
|
14
|
+
import { CardsDisplay } from "../displays/cards-display.js";
|
|
14
15
|
import { CustomDisplay } from "../displays/custom-display.js";
|
|
16
|
+
import { MapDisplay } from "../displays/map-display.js";
|
|
15
17
|
import { DisplaySwitch } from "../displays/display-controls.js";
|
|
16
|
-
import { CardsDisplay } from "../displays/cards-display.js";
|
|
17
18
|
import { embeddedColumns } from "./embedded-columns.js";
|
|
18
|
-
import { viewAllAddress } from "./embedded-display.js";
|
|
19
19
|
import { useEmbeddedRows } from "./use-embedded-rows.js";
|
|
20
20
|
import { useMemo, useState } from "react";
|
|
21
21
|
import { ArrowRight } from "lucide-react";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { keys } from "../../data/query-keys.js";
|
|
2
2
|
import { useClient } from "../../data/api-provider.js";
|
|
3
3
|
import { recordId } from "../../live/model-helpers.js";
|
|
4
|
-
import { listLabels, withLabels } from "../../live/relation-labels.js";
|
|
5
|
-
import { listPhotos, withPhotos } from "../../live/row-photos.js";
|
|
6
4
|
import { useAdminMeta } from "../meta-gate.js";
|
|
5
|
+
import { listPhotos, withPhotos } from "../../live/row-photos.js";
|
|
6
|
+
import { listLabels, withLabels } from "../../live/relation-labels.js";
|
|
7
7
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
|
8
8
|
//#region src/app/embedded-list/use-embedded-rows.ts
|
|
9
9
|
/**
|
package/dist/app/field-editor.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { Input } from "../primitives/input.js";
|
|
3
|
-
import {
|
|
4
|
-
import { Textarea } from "../primitives/textarea.js";
|
|
3
|
+
import { humanize } from "../live/naming.js";
|
|
5
4
|
import { displayValue } from "../live/display-value.js";
|
|
6
5
|
import { AppLink } from "./use-app-address.js";
|
|
7
|
-
import {
|
|
6
|
+
import { useShownWidget } from "./field-types/shown-widget.js";
|
|
7
|
+
import { WidgetPart } from "./field-types/widget-part.js";
|
|
8
|
+
import { isMarkdown } from "../markdown/markdown-hint.js";
|
|
9
|
+
import { Markdown } from "../markdown/markdown.js";
|
|
8
10
|
import { fromDraft, isEditable, toFieldDraft } from "../live/field-values.js";
|
|
11
|
+
import { Switch } from "../primitives/switch.js";
|
|
12
|
+
import { Textarea } from "../primitives/textarea.js";
|
|
13
|
+
import { RelationPicker } from "./relation-picker.js";
|
|
9
14
|
import { SensitiveField } from "./sensitive-field.js";
|
|
10
15
|
import { FileField } from "./file-field.js";
|
|
11
|
-
import {
|
|
12
|
-
import { WidgetPart } from "./field-types/widget-part.js";
|
|
16
|
+
import { MarkdownEditor } from "../markdown/markdown-editor.js";
|
|
13
17
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
18
|
import { enumLabel } from "@protobase/schema";
|
|
15
19
|
//#region src/app/field-editor.tsx
|
|
@@ -70,6 +74,11 @@ const BuiltInEditor = ({ field, hints, value, stored, relationLabel, relationTo,
|
|
|
70
74
|
});
|
|
71
75
|
}
|
|
72
76
|
const multiline = Boolean(hints?.multiline) && field.type === "text";
|
|
77
|
+
const markdown = isMarkdown(field, hints);
|
|
78
|
+
if ((!isEditable(field) || locked) && markdown && typeof stored === "string" && stored !== "") return /* @__PURE__ */ jsx(Markdown, {
|
|
79
|
+
source: stored,
|
|
80
|
+
className: "py-1.5"
|
|
81
|
+
});
|
|
73
82
|
if (!isEditable(field) || locked) return /* @__PURE__ */ jsx("div", {
|
|
74
83
|
className: cn("py-1.5 tabular-nums", field.type === "decimal" || field.type === "integer" ? "font-medium" : "text-muted-foreground", multiline && "whitespace-pre-line break-words"),
|
|
75
84
|
children: displayValue(field, stored, hints)
|
|
@@ -86,6 +95,13 @@ const BuiltInEditor = ({ field, hints, value, stored, relationLabel, relationTo,
|
|
|
86
95
|
children: enumLabel(option, hints?.valueLabels)
|
|
87
96
|
}, option))]
|
|
88
97
|
});
|
|
98
|
+
if (markdown) return /* @__PURE__ */ jsx(MarkdownEditor, {
|
|
99
|
+
label: hints?.label ?? humanize(field.name),
|
|
100
|
+
invalid,
|
|
101
|
+
placeholder,
|
|
102
|
+
value: String(value),
|
|
103
|
+
onChange
|
|
104
|
+
});
|
|
89
105
|
if (multiline) return /* @__PURE__ */ jsx(Textarea, {
|
|
90
106
|
invalid,
|
|
91
107
|
placeholder,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
3
3
|
import { humanize } from "../../live/naming.js";
|
|
4
|
-
import { shownImage } from "../../live/file-values.js";
|
|
5
4
|
import { useAdminMeta, usePermissions } from "../meta-gate.js";
|
|
5
|
+
import { shownImage } from "../../live/file-values.js";
|
|
6
6
|
import { ErrorBanner } from "../error-banner.js";
|
|
7
7
|
import { FileImage } from "../../live/file-image.js";
|
|
8
8
|
import { Section } from "../../record-view/section.js";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Skeleton } from "../../primitives/skeleton.js";
|
|
2
2
|
import { useRecord } from "../../data/use-record.js";
|
|
3
3
|
import { humanize } from "../../live/naming.js";
|
|
4
|
+
import { fieldAccess } from "../permissions.js";
|
|
4
5
|
import { AppLink } from "../use-app-address.js";
|
|
5
6
|
import { ErrorBanner } from "../error-banner.js";
|
|
6
|
-
import { fieldAccess } from "../permissions.js";
|
|
7
|
-
import { SpecTable } from "../../record-view/spec-table.js";
|
|
8
7
|
import { FieldValue } from "../pages/field-value.js";
|
|
8
|
+
import { SpecTable } from "../../record-view/spec-table.js";
|
|
9
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
//#region src/app/gallery/photo-details.tsx
|
|
11
11
|
/**
|
package/dist/app/list-page.js
CHANGED
|
@@ -2,47 +2,48 @@ import { useIsDesktop } from "../lib/use-media-query.js";
|
|
|
2
2
|
import { usePageScroll } from "../app-shell/page-scroll.js";
|
|
3
3
|
import { PageHeader } from "../app-shell/page-header.js";
|
|
4
4
|
import { useAuth } from "../auth/auth-provider.js";
|
|
5
|
-
import { Button } from "../primitives/button.js";
|
|
5
|
+
import { Button, buttonClass } from "../primitives/button.js";
|
|
6
6
|
import { recordKey, recordTitleField } from "../live/model-helpers.js";
|
|
7
7
|
import { humanize } from "../live/naming.js";
|
|
8
8
|
import { useProjectUi } from "./pages/project-ui.js";
|
|
9
|
+
import { Notice, useAdminMeta, usePermissions } from "./meta-gate.js";
|
|
10
|
+
import { can, rowAllows } from "./permissions.js";
|
|
9
11
|
import { loadedFields, shownDisplay } from "./displays/list-displays.js";
|
|
10
12
|
import { parseSort, sortToOrderBy } from "./list-order.js";
|
|
11
13
|
import { listedResource } from "./current-address.js";
|
|
12
|
-
import {
|
|
13
|
-
import { useAppAddress } from "./use-app-address.js";
|
|
14
|
+
import { AppLink, useAppAddress } from "./use-app-address.js";
|
|
14
15
|
import { ErrorBanner } from "./error-banner.js";
|
|
15
|
-
import {
|
|
16
|
+
import { columnsFromModel } from "../live/columns-from-model.js";
|
|
16
17
|
import { pinnedStorageKey } from "../data-grid/pinned-storage.js";
|
|
17
18
|
import { DataGrid } from "../data-grid/data-grid.js";
|
|
19
|
+
import { useLiveRows } from "../live/use-live-rows.js";
|
|
18
20
|
import { ActiveFilterChips } from "../filter-panel/active-filter-chips.js";
|
|
19
21
|
import { activeChips, emptyFilterState, removeChip } from "../filter-panel/filter-state.js";
|
|
20
22
|
import { FilterSection } from "../filter-panel/filter-section.js";
|
|
21
23
|
import { ToolbarRow } from "../filter-panel/toolbar-row.js";
|
|
22
24
|
import { FilterPanel } from "../filter-panel/filter-panel.js";
|
|
23
|
-
import { columnsFromModel } from "../live/columns-from-model.js";
|
|
24
25
|
import { stateToText, textToState } from "../live/filter-string.js";
|
|
25
26
|
import { widgetFields } from "../live/view-fields.js";
|
|
26
|
-
import {
|
|
27
|
-
import { useLiveRows } from "../live/use-live-rows.js";
|
|
27
|
+
import { facetNames, filterChips } from "./searches/saved-search.js";
|
|
28
28
|
import { Sheet } from "../primitives/sheet.js";
|
|
29
|
+
import { SignInPrompt } from "../auth/sign-in-prompt.js";
|
|
30
|
+
import { CustomDisplay } from "./displays/custom-display.js";
|
|
31
|
+
import { MapDisplay } from "./displays/map-display.js";
|
|
32
|
+
import { DisplaySwitch, SortMenu } from "./displays/display-controls.js";
|
|
33
|
+
import { withFieldTypeCells } from "./field-types/field-type-cells.js";
|
|
34
|
+
import { useFilterConfig } from "../live/use-filter-config.js";
|
|
29
35
|
import { useDeleteFlow } from "./delete/use-delete-flow.js";
|
|
30
36
|
import { useRestore } from "./delete/use-restore.js";
|
|
37
|
+
import { createOffered } from "./create/create-offered.js";
|
|
31
38
|
import { ImportButton } from "./import/import-button.js";
|
|
32
39
|
import { exportOffered, exportsSelection } from "./export/export-options.js";
|
|
33
40
|
import { useExportFlow } from "./export/use-export-flow.js";
|
|
34
|
-
import { SignInPrompt } from "../auth/sign-in-prompt.js";
|
|
35
41
|
import { ListCards } from "./displays/list-cards.js";
|
|
36
|
-
import { MapDisplay } from "./displays/map-display.js";
|
|
37
|
-
import { CustomDisplay } from "./displays/custom-display.js";
|
|
38
|
-
import { DisplaySwitch, SortMenu } from "./displays/display-controls.js";
|
|
39
42
|
import { useDisplayRows } from "./displays/use-display-rows.js";
|
|
40
43
|
import { useListScroll } from "./list-scroll.js";
|
|
41
44
|
import { ListChart } from "./list-chart.js";
|
|
42
45
|
import { ListSearch } from "./list-search.js";
|
|
43
|
-
import { withFieldTypeCells } from "./field-types/field-type-cells.js";
|
|
44
46
|
import { SavedList } from "./saved/saved-list.js";
|
|
45
|
-
import { facetNames, filterChips } from "./searches/saved-search.js";
|
|
46
47
|
import { SaveSearchButton } from "./searches/save-search-button.js";
|
|
47
48
|
import { SavedSearches } from "./searches/saved-searches.js";
|
|
48
49
|
import { useMemo, useState } from "react";
|
|
@@ -254,12 +255,12 @@ const LoadedList = ({ model, address, scope, heading }) => {
|
|
|
254
255
|
})
|
|
255
256
|
]
|
|
256
257
|
});
|
|
257
|
-
const createButton = !scope &&
|
|
258
|
-
|
|
259
|
-
onClick: () => go({
|
|
258
|
+
const createButton = !scope && createOffered(view, meta.pages, permissions) && /* @__PURE__ */ jsxs(AppLink, {
|
|
259
|
+
to: {
|
|
260
260
|
kind: "create",
|
|
261
261
|
resource
|
|
262
|
-
}
|
|
262
|
+
},
|
|
263
|
+
className: buttonClass({ variant: "primary" }),
|
|
263
264
|
children: [
|
|
264
265
|
/* @__PURE__ */ jsx(Plus, { className: "size-3.5" }),
|
|
265
266
|
"New ",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
2
2
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
3
|
-
import { ErrorBanner } from "../../error-banner.js";
|
|
4
|
-
import { scopeOf, useListPage } from "../use-layout-data.js";
|
|
5
3
|
import { RecordScope } from "../record-scope.js";
|
|
4
|
+
import { scopeOf, useListPage } from "../use-layout-data.js";
|
|
6
5
|
import { propsOf } from "./block-props.js";
|
|
7
6
|
import { CardFrame, Quiet } from "./card-frame.js";
|
|
7
|
+
import { ErrorBanner } from "../../error-banner.js";
|
|
8
8
|
import { jsx } from "react/jsx-runtime";
|
|
9
9
|
//#region src/app/pages/blocks/card-row.tsx
|
|
10
10
|
const LoadedCardRow = ({ model, filter, sort, limit = 12, title, description, empty, actions, children }) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Skeleton } from "../../../primitives/skeleton.js";
|
|
2
2
|
import { humanize } from "../../../live/naming.js";
|
|
3
3
|
import { useAdminMeta } from "../../meta-gate.js";
|
|
4
|
-
import { FieldValue } from "../field-value.js";
|
|
5
|
-
import { usePathValue } from "../use-path-value.js";
|
|
6
4
|
import { useLayoutRecord } from "../record-scope.js";
|
|
7
5
|
import { propsOf } from "./block-props.js";
|
|
6
|
+
import { FieldValue } from "../field-value.js";
|
|
7
|
+
import { usePathValue } from "../use-path-value.js";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/app/pages/blocks/field.tsx
|
|
10
10
|
/** The view's label for the field at the end of the path, else the path in words: `companyId.city` is "Company city". */
|