@protobase/ui 0.24.1 → 0.25.0
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/board-display.js +35 -8
- package/dist/app/displays/board-drop.d.ts +26 -0
- package/dist/app/displays/board-drop.js +5 -0
- package/dist/app/displays/messages.js +2 -0
- package/dist/app/displays/use-board-move.js +46 -10
- package/dist/app/list-page.js +2 -9
- package/dist/app/pages/merge-ui.d.ts +1 -1
- package/dist/app/pages/merge-ui.js +3 -1
- package/dist/app/pages/project-ui.d.ts +5 -2
- package/dist/app-shell/page-header.js +15 -18
- package/dist/index.d.ts +2 -1
- package/dist/package.js +1 -1
- package/dist/shell/assets/app-BLztI1ER.js +69 -0
- package/dist/shell/assets/{index-Ihcp7yPR.css → index-BRkDA5KD.css} +1 -1
- package/dist/shell/assets/{index-Ce3tnjxv.js → index-RtKzQhf2.js} +1 -1
- package/dist/shell/assets/{maplibre-CjYd33uD.js → maplibre-GfWN95dC.js} +1 -1
- package/dist/shell/assets/{markdown-content-Cvb4nqTn.js → markdown-content-CKeJWqba.js} +1 -1
- package/dist/shell/assets/{record-map-3K382XcL.js → record-map-5bbrMFLz.js} +1 -1
- package/dist/shell/assets/{records-map-Dolr6LVy.js → records-map-Cw1Nvo2x.js} +1 -1
- package/dist/shell/assets/{shared-protobase-ui-Cv8dHbzX.js → shared-protobase-ui-BGS08-p6.js} +1 -1
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/package.json +4 -4
- package/dist/shell/assets/app-DH7x4kWy.js +0 -69
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { Problem } from "../pages/problem.js";
|
|
1
2
|
import { boardCard, boardColumns, boardRank } from "./board-columns.js";
|
|
2
3
|
import { usePermissions } from "../meta-gate.js";
|
|
4
|
+
import { useProjectUi } from "../pages/project-ui.js";
|
|
3
5
|
import { can } from "../permissions.js";
|
|
4
6
|
import { RecordCard } from "./record-card.js";
|
|
5
7
|
import { listHandOrder } from "../hand-order/hand-order.js";
|
|
6
8
|
import { cardCoordinates, collision, columnId } from "./board-dnd.js";
|
|
7
9
|
import { BoardColumnView } from "./board-column.js";
|
|
8
10
|
import { columnsAllowed } from "./board-allowed.js";
|
|
11
|
+
import { dropWrite } from "./board-drop.js";
|
|
9
12
|
import { placedRows } from "./board-place.js";
|
|
10
13
|
import { useBoardMove } from "./use-board-move.js";
|
|
11
14
|
import { useRef, useState } from "react";
|
|
@@ -30,20 +33,33 @@ const placeOver = (over, row) => {
|
|
|
30
33
|
/**
|
|
31
34
|
* A column per value of the board's enum field, each loading its own records through the list API, with their count.
|
|
32
35
|
* When the list's `drag.board` is on, a card dragged, or moved with the keyboard, to another column saves the record
|
|
33
|
-
* with that column's value
|
|
36
|
+
* with that column's value; when it names a board drop of the UI config, only between the columns it names, each
|
|
37
|
+
* running its own write. When the resource has a position, its records ordered as one, the columns are in its order,
|
|
34
38
|
* and when `drag.rank` is on a card is dropped at a place: the up and down arrow keys move it within its column, and
|
|
35
39
|
* the drop renumbers the cards before it. Each change says so with **Undo**.
|
|
36
40
|
*/
|
|
37
41
|
const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit }) => {
|
|
38
|
-
const
|
|
42
|
+
const every = boardColumns(model, view, display);
|
|
43
|
+
const columns = columnsAllowed(filter, display.by, every);
|
|
39
44
|
const card = boardCard(model, view, display);
|
|
40
45
|
const permissions = usePermissions(model.name);
|
|
46
|
+
const { boardDrops } = useProjectUi();
|
|
41
47
|
const drag = view?.list?.drag;
|
|
42
|
-
const
|
|
48
|
+
const dropName = typeof drag?.board === "string" ? drag.board : void 0;
|
|
49
|
+
const drop = dropName === void 0 ? void 0 : boardDrops?.[dropName];
|
|
50
|
+
const takes = (value) => can(permissions, "update") && (drag?.board === true ? !model.fields[display.by]?.readOnly : drop !== void 0 && dropWrite(drop, value) !== void 0);
|
|
51
|
+
const movable = columns.some((column) => takes(column.value));
|
|
43
52
|
const rank = boardRank(model);
|
|
44
53
|
const placeable = drag?.rank === true && rank !== void 0 && listHandOrder(model, permissions) !== void 0;
|
|
45
54
|
const columnOrder = rank ? `${rank} asc` : orderBy;
|
|
46
|
-
const
|
|
55
|
+
const labelOf = (value) => every.find((column) => column.value === value)?.label ?? String(value);
|
|
56
|
+
const { moves, move } = useBoardMove({
|
|
57
|
+
model,
|
|
58
|
+
by: display.by,
|
|
59
|
+
rank,
|
|
60
|
+
drop,
|
|
61
|
+
labelOf
|
|
62
|
+
});
|
|
47
63
|
const [active, setActive] = useState();
|
|
48
64
|
const dragged = useRef(false);
|
|
49
65
|
const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 4 } }), useSensor(TouchSensor, { activationConstraint: {
|
|
@@ -57,9 +73,15 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
|
|
|
57
73
|
},
|
|
58
74
|
coordinateGetter: cardCoordinates
|
|
59
75
|
}));
|
|
76
|
+
if (dropName !== void 0 && !drop) return /* @__PURE__ */ jsxs(Problem, { children: [
|
|
77
|
+
"No board drop is registered as ",
|
|
78
|
+
dropName,
|
|
79
|
+
". Give it in boardDrops in protobase.ui.tsx, or load the plugin that provides it."
|
|
80
|
+
] });
|
|
60
81
|
const title = (row) => String(row.labels[card.title] ?? row.record[card.title] ?? row.key);
|
|
61
82
|
const columnOf = (id) => columns.find((column) => columnId(column.value) === id);
|
|
62
83
|
const valueOf = (row) => row.record[display.by] ?? null;
|
|
84
|
+
const crosses = (row, to) => takes(valueOf(row)) && takes(to.value);
|
|
63
85
|
const announced = (row, over) => {
|
|
64
86
|
if (!over) return { over: "no column" };
|
|
65
87
|
const place = placeable ? placeOver(over, row) : void 0;
|
|
@@ -70,12 +92,16 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
|
|
|
70
92
|
place: `${place.column.label}, place ${place.index + 1}`
|
|
71
93
|
};
|
|
72
94
|
};
|
|
95
|
+
const refused = (row, over) => {
|
|
96
|
+
const target = (placeable ? placeOver(over, row)?.column : void 0) ?? columnOf(over.id);
|
|
97
|
+
return target !== void 0 && target.value !== valueOf(row) && !crosses(row, target);
|
|
98
|
+
};
|
|
73
99
|
const announcements = {
|
|
74
100
|
onDragStart: ({ active: picked }) => `Picked up ${title(picked.data.current.row)}.`,
|
|
75
101
|
onDragOver: ({ active: picked, over }) => `${title(picked.data.current.row)} is over ${announced(picked.data.current.row, over).over}.`,
|
|
76
102
|
onDragEnd: ({ active: picked, over }) => {
|
|
77
103
|
const row = picked.data.current.row;
|
|
78
|
-
if (!over) return `${title(row)} was put back.`;
|
|
104
|
+
if (!over || refused(row, over)) return `${title(row)} was put back.`;
|
|
79
105
|
const said = announced(row, over);
|
|
80
106
|
return `${title(row)} was moved to ${said.place ?? said.over}.`;
|
|
81
107
|
},
|
|
@@ -93,11 +119,12 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
|
|
|
93
119
|
const place = placeable ? placeOver(over, row) : void 0;
|
|
94
120
|
if (!place) {
|
|
95
121
|
const target = columnOf(over.id);
|
|
96
|
-
if (target && target.value !== valueOf(row) &&
|
|
122
|
+
if (target && target.value !== valueOf(row) && crosses(row, target)) move(row, target);
|
|
97
123
|
return;
|
|
98
124
|
}
|
|
99
125
|
const from = place.rows.findIndex((entry) => entry.key === row.key);
|
|
100
|
-
if (from === -1 && !
|
|
126
|
+
if (from === -1 && !crosses(row, place.column)) return;
|
|
127
|
+
if (from === -1 && drop) return move(row, place.column);
|
|
101
128
|
if (from === place.index) return;
|
|
102
129
|
const order = placedRows(place.rows, row, place.index);
|
|
103
130
|
move(row, place.column, {
|
|
@@ -133,7 +160,7 @@ const BoardDisplay = ({ model, view, display, fields, filter, orderBy, limit })
|
|
|
133
160
|
orderBy: columnOrder,
|
|
134
161
|
limit,
|
|
135
162
|
moves,
|
|
136
|
-
movable,
|
|
163
|
+
movable: takes(column.value),
|
|
137
164
|
placeable,
|
|
138
165
|
dragged: () => dragged.current
|
|
139
166
|
}, columnId(column.value)))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Client } from "@protobase/client";
|
|
2
|
+
import { KeyValue } from "@protobase/schema";
|
|
3
|
+
//#region src/app/displays/board-drop.d.ts
|
|
4
|
+
/** What a board drop's write gets: the card's record as listed, its key and ETag, the column it was dropped on, and the API. */
|
|
5
|
+
export type BoardDropContext = {
|
|
6
|
+
resource: string;
|
|
7
|
+
record: Record<string, unknown>;
|
|
8
|
+
/** The record's key as the API takes it. */
|
|
9
|
+
recordKey: KeyValue | readonly KeyValue[];
|
|
10
|
+
etag: string;
|
|
11
|
+
/** The value of the column the card was dropped on. */
|
|
12
|
+
to: string;
|
|
13
|
+
client: Client;
|
|
14
|
+
};
|
|
15
|
+
/** What undoes a board drop's write, such as deleting the record it created. */
|
|
16
|
+
export type BoardDropUndo = () => Promise<void>;
|
|
17
|
+
/** A board drop's write for one column: what it saves in place of the record's `by`, and what undoes it, when it can be undone. */
|
|
18
|
+
export type BoardDropWrite = (context: BoardDropContext) => Promise<BoardDropUndo | void>;
|
|
19
|
+
/**
|
|
20
|
+
* How cards move between a board's columns, a view's `drag.board` naming it: by the value of each column that takes a
|
|
21
|
+
* card, what a drop there writes, such as creating a record of a related resource when `by` is computed from them. A
|
|
22
|
+
* card in or dropped on any other column stays in its column, as does one dropped on the column of records without a
|
|
23
|
+
* value.
|
|
24
|
+
*/
|
|
25
|
+
export type BoardDrop = Record<string, BoardDropWrite>;
|
|
26
|
+
//#endregion
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
//#region src/app/displays/board-drop.ts
|
|
2
|
+
/** The write a drop on the column of `value` performs, if that column takes cards. */
|
|
3
|
+
const dropWrite = (drop, value) => value !== null && Object.hasOwn(drop, value) ? drop[value] : void 0;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { dropWrite };
|
|
@@ -9,6 +9,7 @@ const text = defineMessages({
|
|
|
9
9
|
descending: "descending",
|
|
10
10
|
movedTo: (column) => `Moved to ${column}`,
|
|
11
11
|
orderChanged: "Order changed",
|
|
12
|
+
stayedIn: (column) => `Saved, still in ${column}`,
|
|
12
13
|
undo: "Undo",
|
|
13
14
|
couldNotUndo: "Could not undo"
|
|
14
15
|
},
|
|
@@ -20,6 +21,7 @@ const text = defineMessages({
|
|
|
20
21
|
descending: "aflopend",
|
|
21
22
|
movedTo: (column) => `Verplaatst naar ${column}`,
|
|
22
23
|
orderChanged: "Volgorde gewijzigd",
|
|
24
|
+
stayedIn: (column) => `Opgeslagen, nog in ${column}`,
|
|
23
25
|
undo: "Ongedaan maken",
|
|
24
26
|
couldNotUndo: "Kon niet ongedaan maken"
|
|
25
27
|
}
|
|
@@ -4,6 +4,7 @@ import { recordKey } from "../../live/model-helpers.js";
|
|
|
4
4
|
import { refusalReason } from "./board-columns.js";
|
|
5
5
|
import { text } from "./messages.js";
|
|
6
6
|
import { useRefreshResource } from "../pages/use-layout-data.js";
|
|
7
|
+
import { dropWrite } from "./board-drop.js";
|
|
7
8
|
import { boardWrites, placeOps } from "./board-place.js";
|
|
8
9
|
import { undoOps } from "../hand-order/undo-ops.js";
|
|
9
10
|
import { useRef, useState } from "react";
|
|
@@ -13,10 +14,12 @@ import { useMutation } from "@tanstack/react-query";
|
|
|
13
14
|
* Moving a board's card to another column: one update of the record with the column's value and the ETag it was listed
|
|
14
15
|
* with, as a named update action sends, so the resource's access rules and `validate` decide. On a ranked board a drop
|
|
15
16
|
* at a place is one batch instead, that update when the column changes and a `reorder` by `rank` of the column's cards.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* With the board's `drop`, a card dropped on another column runs that column's write instead, and then reads the record
|
|
18
|
+
* again to say which column it is in now. The card shows in its new place at once; a saved move says so with **Undo**,
|
|
19
|
+
* which puts back the values it changed or runs the undo the drop's write gave, and a refusal puts it back and says why.
|
|
20
|
+
* Either way every list of the resource reloads, which puts the card where its record now is.
|
|
18
21
|
*/
|
|
19
|
-
const useBoardMove = (model, by, rank) => {
|
|
22
|
+
const useBoardMove = ({ model, by, rank, drop, labelOf }) => {
|
|
20
23
|
const client = useClient();
|
|
21
24
|
const toast = useToast();
|
|
22
25
|
const refresh = useRefreshResource();
|
|
@@ -25,7 +28,7 @@ const useBoardMove = (model, by, rank) => {
|
|
|
25
28
|
const settle = (key) => setMoves((current) => current.filter((move) => move.row.key !== key));
|
|
26
29
|
const fields = rank ? [by, rank] : [by];
|
|
27
30
|
const undo = useMutation({
|
|
28
|
-
mutationFn: (
|
|
31
|
+
mutationFn: (undoing) => undoing(),
|
|
29
32
|
onError: (error) => toast.show({
|
|
30
33
|
state: "error",
|
|
31
34
|
title: text("couldNotUndo"),
|
|
@@ -33,17 +36,41 @@ const useBoardMove = (model, by, rank) => {
|
|
|
33
36
|
}),
|
|
34
37
|
onSettled: () => refresh(model.name)
|
|
35
38
|
});
|
|
39
|
+
const dropped = async (row, to) => {
|
|
40
|
+
const write = drop && dropWrite(drop, to.value);
|
|
41
|
+
if (!write) throw new Error(`The column ${to.label} takes no cards`);
|
|
42
|
+
const key = recordKey(model, row.record);
|
|
43
|
+
const etag = typeof row.record.etag === "string" ? row.record.etag : "*";
|
|
44
|
+
const undoing = await write({
|
|
45
|
+
resource: model.name,
|
|
46
|
+
record: row.record,
|
|
47
|
+
recordKey: key,
|
|
48
|
+
etag,
|
|
49
|
+
to: to.value,
|
|
50
|
+
client
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
landed: (await client.get(model.name, key)).record[by] ?? null,
|
|
54
|
+
...undoing && { undo: undoing }
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const saved = (writes, to) => ({
|
|
58
|
+
landed: to.value,
|
|
59
|
+
undo: () => client.batchWrite(undoOps(model, writes, fields))
|
|
60
|
+
});
|
|
36
61
|
const mutation = useMutation({
|
|
37
62
|
mutationFn: async ({ row, to, place }) => {
|
|
38
63
|
if (place && !rank) throw new Error("A board places a card only by its rank");
|
|
39
|
-
|
|
64
|
+
const from = row.record[by] ?? null;
|
|
65
|
+
if (drop && to.value !== from) return dropped(row, to);
|
|
66
|
+
if (place) return saved(boardWrites(model, place.order, await client.batchWrite(placeOps(model, by, rank, row, to, place.order, place.first, place.last))), to);
|
|
40
67
|
const etag = typeof row.record.etag === "string" ? row.record.etag : "*";
|
|
41
68
|
const stored = await client.update(model.name, recordKey(model, row.record), { [by]: to.value }, etag);
|
|
42
|
-
return [{
|
|
69
|
+
return saved([{
|
|
43
70
|
before: row.record,
|
|
44
71
|
record: stored.record,
|
|
45
72
|
etag: stored.etag
|
|
46
|
-
}];
|
|
73
|
+
}], to);
|
|
47
74
|
},
|
|
48
75
|
onMutate: ({ row, to, place }) => {
|
|
49
76
|
const moved = {
|
|
@@ -60,9 +87,18 @@ const useBoardMove = (model, by, rank) => {
|
|
|
60
87
|
...order && { order }
|
|
61
88
|
}]);
|
|
62
89
|
},
|
|
63
|
-
onSuccess: (
|
|
90
|
+
onSuccess: ({ landed, undo: undoing }, { row, to }) => {
|
|
64
91
|
if (undoToast.current !== void 0) toast.dismiss(undoToast.current);
|
|
65
|
-
|
|
92
|
+
undoToast.current = void 0;
|
|
93
|
+
const from = row.record[by] ?? null;
|
|
94
|
+
const title = landed !== from ? text("movedTo", labelOf(landed)) : from === to.value ? text("orderChanged") : text("stayedIn", labelOf(landed));
|
|
95
|
+
if (!undoing) {
|
|
96
|
+
toast.show({
|
|
97
|
+
state: "success",
|
|
98
|
+
title
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
66
102
|
const id = toast.show({
|
|
67
103
|
state: "success",
|
|
68
104
|
title,
|
|
@@ -71,7 +107,7 @@ const useBoardMove = (model, by, rank) => {
|
|
|
71
107
|
onClick: () => {
|
|
72
108
|
toast.dismiss(id);
|
|
73
109
|
undoToast.current = void 0;
|
|
74
|
-
undo.mutate(
|
|
110
|
+
undo.mutate(undoing);
|
|
75
111
|
}
|
|
76
112
|
}
|
|
77
113
|
});
|
package/dist/app/list-page.js
CHANGED
|
@@ -480,15 +480,8 @@ const LoadedList = ({ model, address, scope, heading }) => {
|
|
|
480
480
|
/* @__PURE__ */ jsx(PageHeader, {
|
|
481
481
|
title: heading?.title ?? plural,
|
|
482
482
|
subtitle: heading ? heading.subtitle : view?.help,
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
displaySwitch(false),
|
|
486
|
-
sortButton,
|
|
487
|
-
saveSearch,
|
|
488
|
-
importButton,
|
|
489
|
-
exportButton,
|
|
490
|
-
createButton
|
|
491
|
-
] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
483
|
+
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
484
|
+
displaySwitch(!desktop),
|
|
492
485
|
sortButton,
|
|
493
486
|
saveSearch,
|
|
494
487
|
importButton,
|
|
@@ -2,7 +2,7 @@ import { ProjectUi } from "./project-ui.js";
|
|
|
2
2
|
//#region src/app/pages/merge-ui.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* One UI config from several, each after the ones it `extends`, the last being the app's own: components, action
|
|
5
|
-
* handlers, field types
|
|
5
|
+
* handlers, field types, drop rules and board drops are all kept, and one name defined by two configs is an error naming both; every shell slot
|
|
6
6
|
* and `beforeSignIn` component is drawn, the extended configs' first, except the logo, the last config's to set one.
|
|
7
7
|
*/
|
|
8
8
|
export declare const mergeUi: (...uis: ProjectUi[]) => ProjectUi;
|
|
@@ -35,7 +35,7 @@ const around = (wrappers) => {
|
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
37
|
* One UI config from several, each after the ones it `extends`, the last being the app's own: components, action
|
|
38
|
-
* handlers, field types
|
|
38
|
+
* handlers, field types, drop rules and board drops are all kept, and one name defined by two configs is an error naming both; every shell slot
|
|
39
39
|
* and `beforeSignIn` component is drawn, the extended configs' first, except the logo, the last config's to set one.
|
|
40
40
|
*/
|
|
41
41
|
const mergeUi = (...uis) => {
|
|
@@ -44,6 +44,7 @@ const mergeUi = (...uis) => {
|
|
|
44
44
|
const actions = union("The action handler", named, (ui) => ui.actions);
|
|
45
45
|
const fields = union("The field type", named, (ui) => ui.fields);
|
|
46
46
|
const dropRules = union("The drop rule", named, (ui) => ui.dropRules);
|
|
47
|
+
const boardDrops = union("The board drop", named, (ui) => ui.boardDrops);
|
|
47
48
|
const actionSlot = slot(named.flatMap(({ ui }) => ui.shell?.actions ? [ui.shell.actions] : []));
|
|
48
49
|
const rightPanel = slot(named.flatMap(({ ui }) => ui.shell?.rightPanel ? [ui.shell.rightPanel] : []));
|
|
49
50
|
const logo = named.findLast(({ ui }) => ui.shell?.logo)?.ui.shell?.logo;
|
|
@@ -55,6 +56,7 @@ const mergeUi = (...uis) => {
|
|
|
55
56
|
actions,
|
|
56
57
|
...Object.keys(fields).length > 0 && { fields },
|
|
57
58
|
...Object.keys(dropRules).length > 0 && { dropRules },
|
|
59
|
+
...Object.keys(boardDrops).length > 0 && { boardDrops },
|
|
58
60
|
...(actionSlot || rightPanel || logo) && { shell: {
|
|
59
61
|
...actionSlot && { actions: actionSlot },
|
|
60
62
|
...rightPanel && { rightPanel },
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ShellLogoProps } from "../../app-shell/logo.js";
|
|
2
2
|
import { FieldWidget } from "../field-types/field-widget.js";
|
|
3
|
+
import { BoardDrop } from "../displays/board-drop.js";
|
|
3
4
|
import { DropRule } from "../hand-order/drop-rule.js";
|
|
4
5
|
import { ComponentType, ReactNode } from "react";
|
|
5
6
|
import { Client } from "@protobase/client";
|
|
@@ -44,8 +45,8 @@ export type BeforeSignInProps = {
|
|
|
44
45
|
/**
|
|
45
46
|
* The project's own React code for composed pages: custom components by the name their layout declares
|
|
46
47
|
* (`component('UsageChart')`) or a widget part names, handlers for named actions that have no built-in behaviour,
|
|
47
|
-
* field types by the name a view's `.widget(name)` chooses, drop rules by the name a list's `drag.rule` gives,
|
|
48
|
-
* shell's slots. `extends` lists UI configs merged in before it (see `mergeUi`), or whole `protobase.ui.tsx` modules
|
|
48
|
+
* field types by the name a view's `.widget(name)` chooses, drop rules by the name a list's `drag.rule` gives, board
|
|
49
|
+
* drops by the name a list's `drag.board` gives, and the shell's slots. `extends` lists UI configs merged in before it (see `mergeUi`), or whole `protobase.ui.tsx` modules
|
|
49
50
|
* (`import * as tasksUi from '...'`), their exported components included; `name` names it in their errors.
|
|
50
51
|
*/
|
|
51
52
|
export type ProjectUi = {
|
|
@@ -57,6 +58,8 @@ export type ProjectUi = {
|
|
|
57
58
|
fields?: Record<string, FieldWidget>;
|
|
58
59
|
/** Where re-ranked records may land, by the name a view's `drag.rule` gives (see `DropRule`). */
|
|
59
60
|
dropRules?: Record<string, DropRule>;
|
|
61
|
+
/** Which columns of a board take a card and what a drop there writes, by the name a view's `drag.board` gives (see `BoardDrop`). */
|
|
62
|
+
boardDrops?: Record<string, BoardDrop>;
|
|
60
63
|
/**
|
|
61
64
|
* Rendered in the shell on every page: `actions` at the end of the top bar, `rightPanel` beside the page, and `logo`
|
|
62
65
|
* at the top of the sidebar in place of Protobase's (see `ShellLogoProps`).
|
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/app-shell/page-header.tsx
|
|
3
|
-
const PageHeader = ({ title, subtitle,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
children: [aside ? /* @__PURE__ */ jsxs("div", {
|
|
14
|
-
className: "flex min-w-0 flex-1 items-center justify-between gap-3",
|
|
15
|
-
children: [heading, aside]
|
|
16
|
-
}) : heading, actions && /* @__PURE__ */ jsx("div", {
|
|
17
|
-
className: "flex w-full flex-wrap items-center gap-2 sm:w-auto",
|
|
18
|
-
children: actions
|
|
3
|
+
const PageHeader = ({ title, subtitle, actions }) => /* @__PURE__ */ jsxs("div", {
|
|
4
|
+
className: "flex flex-col gap-x-4 gap-y-2 md:flex-row md:items-end md:justify-between",
|
|
5
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
6
|
+
className: "min-w-0 break-words md:flex-1",
|
|
7
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
8
|
+
className: "text-xl font-semibold tracking-tight",
|
|
9
|
+
children: title
|
|
10
|
+
}), subtitle && /* @__PURE__ */ jsx("p", {
|
|
11
|
+
className: "text-muted-foreground",
|
|
12
|
+
children: subtitle
|
|
19
13
|
})]
|
|
20
|
-
})
|
|
21
|
-
|
|
14
|
+
}), actions && /* @__PURE__ */ jsx("div", {
|
|
15
|
+
className: "flex flex-wrap items-center gap-2 md:justify-end",
|
|
16
|
+
children: actions
|
|
17
|
+
})]
|
|
18
|
+
});
|
|
22
19
|
//#endregion
|
|
23
20
|
export { PageHeader };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LogoMark, ShellLogoProps } from "./app-shell/logo.js";
|
|
2
2
|
import { FieldWidget, FieldWidgetEditorProps, FieldWidgetProps } from "./app/field-types/field-widget.js";
|
|
3
|
+
import { BoardDrop, BoardDropContext, BoardDropUndo, BoardDropWrite } from "./app/displays/board-drop.js";
|
|
3
4
|
import { Drop, DropRule } from "./app/hand-order/drop-rule.js";
|
|
4
5
|
import { ActionContext, ActionHandler, BeforeSignInProps, ProjectUi, definePlugin, defineUi } from "./app/pages/project-ui.js";
|
|
5
6
|
import { PluginInfo, usePlugin } from "./plugins/plugin-context.js";
|
|
@@ -62,4 +63,4 @@ import { Section } from "./record-view/section.js";
|
|
|
62
63
|
import { Form, FormProps } from "./app/create/form.js";
|
|
63
64
|
import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
|
|
64
65
|
import { Spinner } from "./primitives/spinner.js";
|
|
65
|
-
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
|
|
66
|
+
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
|
package/dist/package.js
CHANGED