@protobase/ui 0.58.3 → 0.58.5
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/list-actions/use-list-actions.js +14 -30
- package/dist/app/pages/action-link.js +25 -0
- package/dist/app/pages/record-actions.js +22 -9
- package/dist/data-grid/row-menu.js +2 -2
- package/dist/package.js +1 -1
- package/dist/primitives/route-link.js +17 -10
- package/dist/shell/assets/{app-BaOz1-n8.js → app-C5XgjDF7.js} +5 -5
- package/dist/shell/assets/{index-Ct-G-yK-.js → index-DW9UwGu8.js} +2 -2
- package/dist/shell/assets/{records-map-CrHOQg_6.js → records-map-Bfd6VQ7g.js} +2 -2
- package/dist/shell/assets/{shared-protobase-ui-BKdOx2jH.js → shared-protobase-ui-W0TKg6UW.js} +2 -2
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +2 -2
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-C5XgjDF7.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-Ct-G-yK-.js.map → index-DW9UwGu8.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-CrHOQg_6.js.map → records-map-Bfd6VQ7g.js.map} +1 -1
- package/dist/shell-source-maps/assets/{shared-protobase-ui-BKdOx2jH.js.map → shared-protobase-ui-W0TKg6UW.js.map} +1 -1
- package/package.json +4 -4
- package/dist/shell-source-maps/assets/app-BaOz1-n8.js.map +0 -1
|
@@ -2,25 +2,24 @@ import { useAdminMeta } from "../meta-gate.js";
|
|
|
2
2
|
import { useAppAddress } from "../use-app-address.js";
|
|
3
3
|
import { ConfirmAction } from "../pages/confirm-action.js";
|
|
4
4
|
import { isDanger } from "../pages/action-prompt.js";
|
|
5
|
-
import {
|
|
6
|
-
import { actionFor, linkTarget, openLink } from "../pages/action-href.js";
|
|
5
|
+
import { actionFor } from "../pages/action-href.js";
|
|
7
6
|
import { useActionRunner } from "../pages/use-run-action.js";
|
|
8
7
|
import { text } from "../list-messages.js";
|
|
8
|
+
import { actionLink } from "../pages/action-link.js";
|
|
9
9
|
import { rowRecord } from "./row-record.js";
|
|
10
10
|
import { useState } from "react";
|
|
11
|
-
import { fillAddress } from "@protobase/schema/internal";
|
|
12
11
|
import { jsx } from "react/jsx-runtime";
|
|
13
12
|
//#region src/app/list-actions/use-list-actions.tsx
|
|
14
13
|
/**
|
|
15
14
|
* The named actions of `model`'s view for its list, those without inputs (which ask in a dialog of their own): each
|
|
16
15
|
* in the row menu of the rows it shows for, disabled with its reason where `when` disables it, and in red when it is a
|
|
17
|
-
* delete or a danger action; a link is a link there. Updates and handlers marked `bulk` are offered for a selection
|
|
16
|
+
* delete or a danger action; a link is a link there, and a disabled one a disabled item without one. Updates and handlers marked `bulk` are offered for a selection
|
|
18
17
|
* while one selected row applies, and run on those that do. `prompt` is the question one asks before it runs.
|
|
19
18
|
*/
|
|
20
19
|
const useListActions = (model) => {
|
|
21
20
|
const { views } = useAdminMeta();
|
|
22
21
|
const runner = useActionRunner(model.name, void 0);
|
|
23
|
-
const
|
|
22
|
+
const app = useAppAddress();
|
|
24
23
|
const [asking, setAsking] = useState();
|
|
25
24
|
const listed = (views[model.name]?.actions ?? []).filter((action) => !action.inputs?.length);
|
|
26
25
|
const perform = (run) => "row" in run ? runner.run(run.action, run.row) : runner.runEach(run.action, run.selection, run.skipped);
|
|
@@ -36,33 +35,18 @@ const useListActions = (model) => {
|
|
|
36
35
|
destructive: isDanger(action)
|
|
37
36
|
};
|
|
38
37
|
const run = action.run;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
href: href(address(row)),
|
|
48
|
-
onFollow: () => go(address(row))
|
|
49
|
-
})
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
const target = (row) => linkTarget(run.href, rowRecord(model, row).record) ?? "";
|
|
53
|
-
return {
|
|
54
|
-
...base,
|
|
55
|
-
available,
|
|
56
|
-
link: (row) => ({
|
|
57
|
-
href: target(row),
|
|
58
|
-
onFollow: () => openLink(target(row))
|
|
59
|
-
})
|
|
60
|
-
};
|
|
61
|
-
}
|
|
38
|
+
const blocked = (row) => runner.blockedOn(action, rowRecord(model, row));
|
|
39
|
+
const available = (row) => shown(row) !== void 0;
|
|
40
|
+
if (run?.kind === "link") return {
|
|
41
|
+
...base,
|
|
42
|
+
available,
|
|
43
|
+
blocked,
|
|
44
|
+
link: (row) => actionLink(run, rowRecord(model, row).record, app)
|
|
45
|
+
};
|
|
62
46
|
return {
|
|
63
47
|
...base,
|
|
64
|
-
available
|
|
65
|
-
blocked
|
|
48
|
+
available,
|
|
49
|
+
blocked,
|
|
66
50
|
onSelect: (row) => start({
|
|
67
51
|
action,
|
|
68
52
|
row: rowRecord(model, row)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { linkTarget } from "./action-href.js";
|
|
2
|
+
import { fillAddress } from "@protobase/schema/internal";
|
|
3
|
+
//#region src/app/pages/action-link.ts
|
|
4
|
+
/**
|
|
5
|
+
* A link action on `record` as a link: an address in the app is followed in place, a page outside it, an `https:` or
|
|
6
|
+
* `http:` one in a new tab, is left to the browser. A link whose `href` has no target on the record is never shown
|
|
7
|
+
* (`actionFor` leaves it out), so asking for it is a mistake.
|
|
8
|
+
*/
|
|
9
|
+
const actionLink = (run, record, { href, go }) => {
|
|
10
|
+
if ("to" in run) {
|
|
11
|
+
const to = fillAddress(run.to, record);
|
|
12
|
+
return {
|
|
13
|
+
href: href(to),
|
|
14
|
+
onFollow: () => go(to)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
const target = linkTarget(run.href, record);
|
|
18
|
+
if (target === void 0) throw new Error("A link action without a target on the record is not shown");
|
|
19
|
+
return {
|
|
20
|
+
href: target,
|
|
21
|
+
newTab: /^https?:/i.test(target)
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { actionLink };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ActionRail } from "../../record-view/action-rail.js";
|
|
2
|
+
import { RouteLink } from "../../primitives/route-link.js";
|
|
2
3
|
import { AppLink, useAppAddress } from "../use-app-address.js";
|
|
3
4
|
import { ActionMenu } from "../../record-view/action-menu.js";
|
|
4
5
|
import { ActionPrompt, asksFirst, isDanger } from "./action-prompt.js";
|
|
@@ -6,6 +7,7 @@ import { useActionRunner } from "./use-run-action.js";
|
|
|
6
7
|
import { useTopBarSlot } from "../../app-shell/top-bar-slot.js";
|
|
7
8
|
import "../../app-shell/top-bar.js";
|
|
8
9
|
import { text } from "../record-messages.js";
|
|
10
|
+
import { actionLink } from "./action-link.js";
|
|
9
11
|
import { actionIcon } from "../action-icon.js";
|
|
10
12
|
import { useState } from "react";
|
|
11
13
|
import { Pencil, Trash2 } from "lucide-react";
|
|
@@ -19,7 +21,8 @@ import { createPortal } from "react-dom";
|
|
|
19
21
|
* however long the record is, when the shell has a top bar.
|
|
20
22
|
*/
|
|
21
23
|
const RecordActions = ({ model, stored, recordKey, edit, onDelete }) => {
|
|
22
|
-
const
|
|
24
|
+
const app = useAppAddress();
|
|
25
|
+
const { go } = app;
|
|
23
26
|
const slot = useTopBarSlot();
|
|
24
27
|
const scope = {
|
|
25
28
|
model,
|
|
@@ -33,14 +36,24 @@ const RecordActions = ({ model, stored, recordKey, edit, onDelete }) => {
|
|
|
33
36
|
resource: model.name
|
|
34
37
|
}, { replace: true }));
|
|
35
38
|
const [asking, setAsking] = useState();
|
|
36
|
-
const named = runner.actions.map((action) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const named = runner.actions.map((action) => {
|
|
40
|
+
const blocked = runner.blocked(action);
|
|
41
|
+
const link = action.run?.kind === "link" && !blocked ? actionLink(action.run, stored.record, app) : void 0;
|
|
42
|
+
return {
|
|
43
|
+
name: action.label,
|
|
44
|
+
icon: actionIcon(action.icon),
|
|
45
|
+
blocked,
|
|
46
|
+
running: runner.running === action.name,
|
|
47
|
+
...isDanger(action) && { tone: "danger" },
|
|
48
|
+
onRun: () => asksFirst(action) ? setAsking(action) : void runner.run(action),
|
|
49
|
+
...link && { render: ({ className, children, role }) => /* @__PURE__ */ jsx(RouteLink, {
|
|
50
|
+
to: link,
|
|
51
|
+
role,
|
|
52
|
+
className,
|
|
53
|
+
children
|
|
54
|
+
}) }
|
|
55
|
+
};
|
|
56
|
+
});
|
|
44
57
|
const deletes = runner.actions.some((action) => action.run?.kind === "delete");
|
|
45
58
|
const actions = [
|
|
46
59
|
...edit ? [{
|
|
@@ -40,7 +40,7 @@ const RowMenu = ({ row, actions }) => {
|
|
|
40
40
|
const itemClass = cn("block w-full px-3 py-1.5 text-left hover:bg-muted disabled:cursor-default disabled:opacity-60 disabled:hover:bg-transparent", action.destructive && "text-danger-text");
|
|
41
41
|
return /* @__PURE__ */ jsx("li", {
|
|
42
42
|
role: "none",
|
|
43
|
-
children: action.link ? /* @__PURE__ */ jsx(RouteLink, {
|
|
43
|
+
children: action.link && blocked === void 0 ? /* @__PURE__ */ jsx(RouteLink, {
|
|
44
44
|
role: "menuitem",
|
|
45
45
|
to: action.link(row),
|
|
46
46
|
onClick: () => setOpen(false),
|
|
@@ -54,7 +54,7 @@ const RowMenu = ({ row, actions }) => {
|
|
|
54
54
|
className: itemClass,
|
|
55
55
|
onClick: () => {
|
|
56
56
|
setOpen(false);
|
|
57
|
-
action.onSelect(row);
|
|
57
|
+
action.onSelect?.(row);
|
|
58
58
|
},
|
|
59
59
|
children: label
|
|
60
60
|
})
|
package/dist/package.js
CHANGED
|
@@ -6,15 +6,22 @@ import { jsx } from "react/jsx-runtime";
|
|
|
6
6
|
* with Ctrl, Cmd, Shift or Alt opens it in a new tab or window, or saves it, as on any link. Every navigation a click
|
|
7
7
|
* starts is one of these, never a button that moves the page, so the browser shows where it goes and can open it.
|
|
8
8
|
*/
|
|
9
|
-
const RouteLink = ({ to, onClick, ...rest }) =>
|
|
10
|
-
href
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
const RouteLink = ({ to, onClick, ...rest }) => {
|
|
10
|
+
const { href, onFollow, newTab } = to;
|
|
11
|
+
return /* @__PURE__ */ jsx("a", {
|
|
12
|
+
href,
|
|
13
|
+
...newTab && {
|
|
14
|
+
target: "_blank",
|
|
15
|
+
rel: "noreferrer"
|
|
16
|
+
},
|
|
17
|
+
onClick: (event) => {
|
|
18
|
+
onClick?.(event);
|
|
19
|
+
if (!onFollow || event.defaultPrevented || !inApp(event)) return;
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
onFollow();
|
|
22
|
+
},
|
|
23
|
+
...rest
|
|
24
|
+
});
|
|
25
|
+
};
|
|
19
26
|
//#endregion
|
|
20
27
|
export { RouteLink };
|