@protobase/ui 0.3.3 → 0.3.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/app.d.ts +3 -1
- package/dist/app/app.js +15 -4
- package/dist/app/assistant-shell.js +6 -1
- package/dist/app/runtime/runtime-status.js +25 -0
- package/dist/app/runtime/runtime-update.js +77 -0
- package/dist/app/runtime/use-runtime-status.js +59 -0
- package/dist/data/meta-data.d.ts +4 -0
- package/dist/data/use-meta.js +2 -1
- package/dist/lib/use-visual-viewport.js +27 -0
- package/dist/side-panel/side-panel.d.ts +5 -1
- package/dist/side-panel/side-panel.js +48 -34
- package/package.json +10 -9
package/dist/app/app.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SidebarMode } from "../app-shell/sidebar-mode.js";
|
|
2
2
|
import "../app-shell/index.js";
|
|
3
3
|
import { ProjectUi } from "./pages/project-ui.js";
|
|
4
|
-
import { AssistantClient, AuthSession, Client } from "@protobase/client";
|
|
4
|
+
import { AssistantClient, AuthSession, Client, RuntimeClient } from "@protobase/client";
|
|
5
5
|
//#region src/app/app.d.ts
|
|
6
6
|
export type AppProps = {
|
|
7
7
|
/** Where `/api/v1` lives. Defaults to the current origin. */
|
|
@@ -20,6 +20,8 @@ export type AppProps = {
|
|
|
20
20
|
ui?: ProjectUi;
|
|
21
21
|
/** A ready-made client of the assistant backend, for stories and tests; used only when `/meta` names an assistant. */
|
|
22
22
|
assistant?: AssistantClient;
|
|
23
|
+
/** A ready-made client of the runtime endpoint, for stories and tests; used only when `/meta` names one. */
|
|
24
|
+
runtime?: RuntimeClient;
|
|
23
25
|
};
|
|
24
26
|
/** The admin: sign-in first, then a sidebar from `/meta`, `/:page` composed pages, `/:resource` lists and `/:resource/:key` records. Fills its parent. */
|
|
25
27
|
export declare const App: ({ auth, workspace, ...rest }: AppProps) => import("react").JSX.Element;
|
package/dist/app/app.js
CHANGED
|
@@ -19,11 +19,12 @@ import { ProjectUiProvider, useProjectUi } from "./pages/project-ui.js";
|
|
|
19
19
|
import { ComposedPage } from "./pages/composed-page.js";
|
|
20
20
|
import { RecordPage } from "./record-page.js";
|
|
21
21
|
import { useGlobalSearch } from "./search/use-global-search.js";
|
|
22
|
+
import { RuntimeUpdateButton } from "./runtime/runtime-update.js";
|
|
22
23
|
import { useNavRecent } from "./use-nav-recent.js";
|
|
23
24
|
import { useEffect, useState } from "react";
|
|
24
25
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
25
26
|
//#region src/app/app.tsx
|
|
26
|
-
const Routes = ({ workspace, sidebarMode, assistant }) => {
|
|
27
|
+
const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
27
28
|
const meta = useAdminMeta();
|
|
28
29
|
const { state, signOut } = useAuth();
|
|
29
30
|
if (state.kind !== "signed-in") throw new Error("The shell is only rendered for a signed-in user");
|
|
@@ -63,6 +64,11 @@ const Routes = ({ workspace, sidebarMode, assistant }) => {
|
|
|
63
64
|
page: params.size > 0 ? `${path}?${params}` : path,
|
|
64
65
|
onClose: () => setAssistantOpen(false)
|
|
65
66
|
});
|
|
67
|
+
const runtimeUrl = meta.runtime?.url;
|
|
68
|
+
const runtimeButton = runtimeUrl && /* @__PURE__ */ jsx(RuntimeUpdateButton, {
|
|
69
|
+
url: runtimeUrl,
|
|
70
|
+
client: runtime
|
|
71
|
+
});
|
|
66
72
|
const breadcrumb = breadcrumbFor({
|
|
67
73
|
basePath,
|
|
68
74
|
route,
|
|
@@ -90,7 +96,11 @@ const Routes = ({ workspace, sidebarMode, assistant }) => {
|
|
|
90
96
|
groups: search.groups,
|
|
91
97
|
onSelect: open
|
|
92
98
|
} : void 0,
|
|
93
|
-
actions: (Actions || assistantButton) && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
99
|
+
actions: (Actions || runtimeButton || assistantButton) && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
100
|
+
Actions && /* @__PURE__ */ jsx(Actions, {}),
|
|
101
|
+
runtimeButton,
|
|
102
|
+
assistantButton
|
|
103
|
+
] }),
|
|
94
104
|
rightPanel: (RightPanel || assistantPanel) && /* @__PURE__ */ jsxs(Fragment$1, { children: [RightPanel && /* @__PURE__ */ jsx(RightPanel, {}), assistantPanel] }),
|
|
95
105
|
children: !route.resource ? /* @__PURE__ */ jsx(Notice, {
|
|
96
106
|
title: "Choose a resource",
|
|
@@ -101,7 +111,7 @@ const Routes = ({ workspace, sidebarMode, assistant }) => {
|
|
|
101
111
|
}, `${route.resource}/${route.key}`) : /* @__PURE__ */ jsx(ListPage, { resource: route.resource }, route.resource)
|
|
102
112
|
});
|
|
103
113
|
};
|
|
104
|
-
const Authenticated = ({ baseUrl, client, basePath, initialUrl, workspace, sidebarMode, ui, assistant }) => {
|
|
114
|
+
const Authenticated = ({ baseUrl, client, basePath, initialUrl, workspace, sidebarMode, ui, assistant, runtime }) => {
|
|
105
115
|
const { session, markSignedOut } = useAuth();
|
|
106
116
|
return /* @__PURE__ */ jsx(ApiProvider, {
|
|
107
117
|
baseUrl,
|
|
@@ -116,7 +126,8 @@ const Authenticated = ({ baseUrl, client, basePath, initialUrl, workspace, sideb
|
|
|
116
126
|
children: /* @__PURE__ */ jsx(Routes, {
|
|
117
127
|
workspace,
|
|
118
128
|
sidebarMode,
|
|
119
|
-
assistant
|
|
129
|
+
assistant,
|
|
130
|
+
runtime
|
|
120
131
|
})
|
|
121
132
|
}) }) })
|
|
122
133
|
})
|
|
@@ -5,12 +5,17 @@ import { useAssistantClient } from "../assistant-dock/use-assistant-client.js";
|
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Sparkles } from "lucide-react";
|
|
7
7
|
//#region src/app/assistant-shell.tsx
|
|
8
|
+
/** Below `md`, where search is a button too, only the icon shows; the hidden text still names it. */
|
|
8
9
|
const AssistantButton = ({ open, onToggle }) => /* @__PURE__ */ jsxs(Button, {
|
|
9
10
|
size: "sm",
|
|
10
11
|
variant: open ? "primary" : "secondary",
|
|
11
12
|
"aria-pressed": open,
|
|
12
13
|
onClick: onToggle,
|
|
13
|
-
|
|
14
|
+
className: "max-md:size-9 max-md:p-0",
|
|
15
|
+
children: [/* @__PURE__ */ jsx(Sparkles, { className: "size-3.5" }), /* @__PURE__ */ jsx("span", {
|
|
16
|
+
className: "max-md:sr-only",
|
|
17
|
+
children: "Assistant"
|
|
18
|
+
})]
|
|
14
19
|
});
|
|
15
20
|
const ConnectedDock = ({ client, page, onClose }) => {
|
|
16
21
|
const { state, offline, error, send, act } = useAssistant(client);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { formatDateTime } from "../../format/date.js";
|
|
2
|
+
import { isApiError } from "@protobase/client";
|
|
3
|
+
//#region src/app/runtime/runtime-status.ts
|
|
4
|
+
/** The top bar shows the update button only when there is an update and the endpoint wants it shown. */
|
|
5
|
+
const showsUpdateButton = (status) => Boolean(status?.updateAvailable && status.indicator);
|
|
6
|
+
const policyLabels = {
|
|
7
|
+
immediate: "Updates as soon as a release is out",
|
|
8
|
+
weekly: "Updates at a set time every week",
|
|
9
|
+
scheduled: "Updates at the time set for this release",
|
|
10
|
+
manual: "Updates only when asked to"
|
|
11
|
+
};
|
|
12
|
+
/** When the latest version will be applied on its own, in words. */
|
|
13
|
+
const nextUpdateText = (status) => {
|
|
14
|
+
if (status.updating) return "Updating now";
|
|
15
|
+
if (status.nextUpdateAt) return `${policyLabels[status.policy]}: next on ${formatDateTime(Date.parse(status.nextUpdateAt))} UTC`;
|
|
16
|
+
return policyLabels[status.policy];
|
|
17
|
+
};
|
|
18
|
+
/** Why "Update now" failed, in words. */
|
|
19
|
+
const updateErrorText = (error) => {
|
|
20
|
+
if (isApiError(error) && error.status === 409) return "Already on the latest version, or an update is running.";
|
|
21
|
+
if (isApiError(error) && error.status === 403) return "Only an admin can update the runtime.";
|
|
22
|
+
return "The update could not be started. Try again later.";
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { nextUpdateText, showsUpdateButton, updateErrorText };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Button } from "../../primitives/button.js";
|
|
2
|
+
import { Dialog } from "../../primitives/dialog.js";
|
|
3
|
+
import { nextUpdateText, showsUpdateButton, updateErrorText } from "./runtime-status.js";
|
|
4
|
+
import { useRuntimeClient, useRuntimeStatus } from "./use-runtime-status.js";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { ArrowUpCircle } from "lucide-react";
|
|
8
|
+
//#region src/app/runtime/runtime-update.tsx
|
|
9
|
+
const Row = ({ label, value }) => /* @__PURE__ */ jsxs("div", {
|
|
10
|
+
className: "flex justify-between gap-4 py-1 text-[13px]",
|
|
11
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
12
|
+
className: "text-muted-foreground",
|
|
13
|
+
children: label
|
|
14
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
15
|
+
className: "font-medium tabular-nums",
|
|
16
|
+
children: value
|
|
17
|
+
})]
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* The top bar's runtime update button, shown while the endpoint reports an update and wants it shown, and the dialog
|
|
21
|
+
* it opens: the versions, when the update policy applies the latest one, and "Update now". Below `md` only the icon shows.
|
|
22
|
+
*/
|
|
23
|
+
const RuntimeUpdate = ({ client }) => {
|
|
24
|
+
const { status, update, requesting, error, checkError } = useRuntimeStatus(client);
|
|
25
|
+
const [open, setOpen] = useState(false);
|
|
26
|
+
if (!status || !open && !showsUpdateButton(status)) return null;
|
|
27
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs(Button, {
|
|
28
|
+
size: "sm",
|
|
29
|
+
variant: "ghost",
|
|
30
|
+
"aria-label": "Update available",
|
|
31
|
+
title: "Update available",
|
|
32
|
+
onClick: () => setOpen(true),
|
|
33
|
+
className: "max-md:size-9 max-md:p-0",
|
|
34
|
+
children: [/* @__PURE__ */ jsx(ArrowUpCircle, { className: "size-3.5 text-primary" }), /* @__PURE__ */ jsx("span", {
|
|
35
|
+
className: "max-md:sr-only",
|
|
36
|
+
children: "Update"
|
|
37
|
+
})]
|
|
38
|
+
}), open && /* @__PURE__ */ jsxs(Dialog, {
|
|
39
|
+
title: "Runtime update",
|
|
40
|
+
description: nextUpdateText(status),
|
|
41
|
+
onClose: () => setOpen(false),
|
|
42
|
+
actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
|
|
43
|
+
onClick: () => setOpen(false),
|
|
44
|
+
children: "Close"
|
|
45
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
46
|
+
variant: "primary",
|
|
47
|
+
loading: requesting,
|
|
48
|
+
disabled: status.updating || !status.updateAvailable,
|
|
49
|
+
onClick: () => void update(),
|
|
50
|
+
children: status.updating ? "Updating…" : "Update now"
|
|
51
|
+
})] }),
|
|
52
|
+
children: [
|
|
53
|
+
/* @__PURE__ */ jsx(Row, {
|
|
54
|
+
label: "Current version",
|
|
55
|
+
value: status.version
|
|
56
|
+
}),
|
|
57
|
+
/* @__PURE__ */ jsx(Row, {
|
|
58
|
+
label: "Latest version",
|
|
59
|
+
value: status.latest
|
|
60
|
+
}),
|
|
61
|
+
error !== void 0 && /* @__PURE__ */ jsx("p", {
|
|
62
|
+
role: "alert",
|
|
63
|
+
className: "mt-3 text-[13px] text-danger-text",
|
|
64
|
+
children: updateErrorText(error)
|
|
65
|
+
}),
|
|
66
|
+
checkError !== void 0 && /* @__PURE__ */ jsx("p", {
|
|
67
|
+
className: "mt-3 text-[13px] text-muted-foreground",
|
|
68
|
+
children: "The status could not be checked again; this is the last one read."
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
})] });
|
|
72
|
+
};
|
|
73
|
+
const OwnRuntimeUpdate = ({ url }) => /* @__PURE__ */ jsx(RuntimeUpdate, { client: useRuntimeClient(url) });
|
|
74
|
+
/** The update button for the endpoint `/meta` names, signed in as the user; `client` replaces it in stories and tests. */
|
|
75
|
+
const RuntimeUpdateButton = ({ url, client }) => client ? /* @__PURE__ */ jsx(RuntimeUpdate, { client }) : /* @__PURE__ */ jsx(OwnRuntimeUpdate, { url });
|
|
76
|
+
//#endregion
|
|
77
|
+
export { RuntimeUpdateButton };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useApiToken } from "../../auth/use-api-token.js";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createRuntimeClient } from "@protobase/client";
|
|
4
|
+
//#region src/app/runtime/use-runtime-status.ts
|
|
5
|
+
/** How often the status is read again: rarely, and more often while an update runs so the button goes away when it is done. */
|
|
6
|
+
const refreshMs = 18e5;
|
|
7
|
+
const updatingRefreshMs = 3e4;
|
|
8
|
+
/** A client of the runtime endpoint at `url`, authenticated as the signed-in user; `undefined` until their token is there. */
|
|
9
|
+
const useRuntimeClient = (url) => {
|
|
10
|
+
const token = useApiToken();
|
|
11
|
+
const latest = useRef(token);
|
|
12
|
+
latest.current = token;
|
|
13
|
+
const ready = token !== void 0;
|
|
14
|
+
return useMemo(() => ready ? createRuntimeClient({
|
|
15
|
+
url,
|
|
16
|
+
token: () => latest.current
|
|
17
|
+
}) : void 0, [url, ready]);
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The runtime's status, read on load and every half hour after, and `update` to apply the latest version now.
|
|
21
|
+
* `status` keeps the last answer when a later read fails, which `checkError` then holds; `error` is why "Update now" failed.
|
|
22
|
+
*/
|
|
23
|
+
const useRuntimeStatus = (client) => {
|
|
24
|
+
const [status, setStatus] = useState();
|
|
25
|
+
const [requesting, setRequesting] = useState(false);
|
|
26
|
+
const [error, setError] = useState();
|
|
27
|
+
const [checkError, setCheckError] = useState();
|
|
28
|
+
const updating = status?.updating ?? false;
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!client) return;
|
|
31
|
+
let active = true;
|
|
32
|
+
const read = () => client.status().then((next) => {
|
|
33
|
+
if (!active) return;
|
|
34
|
+
setStatus(next);
|
|
35
|
+
setCheckError(void 0);
|
|
36
|
+
}, (reason) => active && setCheckError(reason));
|
|
37
|
+
read();
|
|
38
|
+
const timer = setInterval(() => void read(), updating ? updatingRefreshMs : refreshMs);
|
|
39
|
+
return () => {
|
|
40
|
+
active = false;
|
|
41
|
+
clearInterval(timer);
|
|
42
|
+
};
|
|
43
|
+
}, [client, updating]);
|
|
44
|
+
return {
|
|
45
|
+
status,
|
|
46
|
+
update: useCallback(async () => {
|
|
47
|
+
if (!client) return;
|
|
48
|
+
setRequesting(true);
|
|
49
|
+
setError(void 0);
|
|
50
|
+
await client.update().then(setStatus, setError);
|
|
51
|
+
setRequesting(false);
|
|
52
|
+
}, [client]),
|
|
53
|
+
requesting,
|
|
54
|
+
error,
|
|
55
|
+
checkError
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
//#endregion
|
|
59
|
+
export { useRuntimeClient, useRuntimeStatus };
|
package/dist/data/meta-data.d.ts
CHANGED
package/dist/data/use-meta.js
CHANGED
|
@@ -27,7 +27,8 @@ const useMeta = () => {
|
|
|
27
27
|
pages: Object.fromEntries((result.meta.pages ?? []).map((model) => [model.name, model])),
|
|
28
28
|
permissions: result.meta.permissions ?? {},
|
|
29
29
|
...result.meta.userMenu && { userMenu: result.meta.userMenu },
|
|
30
|
-
...result.meta.assistant && { assistant: result.meta.assistant }
|
|
30
|
+
...result.meta.assistant && { assistant: result.meta.assistant },
|
|
31
|
+
...result.meta.runtime && { runtime: result.meta.runtime }
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
//#region src/lib/use-visual-viewport.ts
|
|
3
|
+
/**
|
|
4
|
+
* While `active`: the part of the page that is on screen, which shrinks when a phone's on-screen keyboard opens;
|
|
5
|
+
* undefined otherwise, or where the browser has no `visualViewport`.
|
|
6
|
+
*/
|
|
7
|
+
const useVisualViewport = (active) => {
|
|
8
|
+
const [box, setBox] = useState();
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const viewport = window.visualViewport;
|
|
11
|
+
if (!active || !viewport) return setBox(void 0);
|
|
12
|
+
const update = () => setBox({
|
|
13
|
+
top: viewport.offsetTop,
|
|
14
|
+
height: viewport.height
|
|
15
|
+
});
|
|
16
|
+
update();
|
|
17
|
+
viewport.addEventListener("resize", update);
|
|
18
|
+
viewport.addEventListener("scroll", update);
|
|
19
|
+
return () => {
|
|
20
|
+
viewport.removeEventListener("resize", update);
|
|
21
|
+
viewport.removeEventListener("scroll", update);
|
|
22
|
+
};
|
|
23
|
+
}, [active]);
|
|
24
|
+
return active ? box : void 0;
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { useVisualViewport };
|
|
@@ -12,6 +12,10 @@ export type SidePanelProps = {
|
|
|
12
12
|
/** The content; it scrolls on its own, so give it `min-h-0 flex-1`. */
|
|
13
13
|
children: ReactNode;
|
|
14
14
|
};
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* A full-height panel beside the page, for the shell's `rightPanel`: a header with a close button, content and a footer.
|
|
17
|
+
* Below `md` there is no room beside the page, so it covers the screen instead, shrinking with the on-screen keyboard
|
|
18
|
+
* so the footer stays in view.
|
|
19
|
+
*/
|
|
16
20
|
export declare const SidePanel: ({ title, icon, status, onClose, footer, children }: SidePanelProps) => import("react").JSX.Element;
|
|
17
21
|
//#endregion
|
|
@@ -1,40 +1,54 @@
|
|
|
1
|
+
import { useIsDesktop } from "../lib/use-media-query.js";
|
|
1
2
|
import { Button } from "../primitives/button.js";
|
|
3
|
+
import { useVisualViewport } from "../lib/use-visual-viewport.js";
|
|
2
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
5
|
import { X } from "lucide-react";
|
|
4
6
|
//#region src/side-panel/side-panel.tsx
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A full-height panel beside the page, for the shell's `rightPanel`: a header with a close button, content and a footer.
|
|
9
|
+
* Below `md` there is no room beside the page, so it covers the screen instead, shrinking with the on-screen keyboard
|
|
10
|
+
* so the footer stays in view.
|
|
11
|
+
*/
|
|
12
|
+
const SidePanel = ({ title, icon, status, onClose, footer, children }) => {
|
|
13
|
+
const screen = useVisualViewport(!useIsDesktop());
|
|
14
|
+
return /* @__PURE__ */ jsxs("aside", {
|
|
15
|
+
"aria-label": title,
|
|
16
|
+
className: "flex h-full w-[380px] max-w-full shrink-0 flex-col border-l border-border bg-surface max-md:fixed max-md:inset-0 max-md:z-40 max-md:w-full max-md:border-l-0",
|
|
17
|
+
style: screen && {
|
|
18
|
+
top: screen.top,
|
|
19
|
+
bottom: "auto",
|
|
20
|
+
height: screen.height
|
|
21
|
+
},
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsxs("header", {
|
|
24
|
+
className: "flex h-12 shrink-0 items-center gap-2 border-b border-border bg-background px-4",
|
|
25
|
+
children: [
|
|
26
|
+
icon,
|
|
27
|
+
/* @__PURE__ */ jsx("h2", {
|
|
28
|
+
className: "flex-1 text-[13px] font-semibold",
|
|
29
|
+
children: title
|
|
30
|
+
}),
|
|
31
|
+
status && /* @__PURE__ */ jsx("span", {
|
|
32
|
+
className: "truncate text-xs font-medium text-muted-foreground",
|
|
33
|
+
children: status
|
|
34
|
+
}),
|
|
35
|
+
onClose && /* @__PURE__ */ jsx(Button, {
|
|
36
|
+
variant: "ghost",
|
|
37
|
+
size: "sm",
|
|
38
|
+
className: "w-7 px-0",
|
|
39
|
+
"aria-label": "Close",
|
|
40
|
+
onClick: onClose,
|
|
41
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
42
|
+
})
|
|
43
|
+
]
|
|
44
|
+
}),
|
|
45
|
+
children,
|
|
46
|
+
footer && /* @__PURE__ */ jsx("footer", {
|
|
47
|
+
className: "shrink-0 space-y-2 border-t border-border bg-background p-3",
|
|
48
|
+
children: footer
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
};
|
|
39
53
|
//#endregion
|
|
40
54
|
export { SidePanel };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
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": {
|
|
@@ -24,12 +24,19 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist"
|
|
26
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsdown",
|
|
29
|
+
"prepack": "tsdown"
|
|
30
|
+
},
|
|
27
31
|
"publishConfig": {
|
|
28
32
|
"access": "public"
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
31
35
|
"@dnd-kit/core": "^6.3.1",
|
|
32
36
|
"@dnd-kit/sortable": "^10.0.0",
|
|
37
|
+
"@protobase/client": "0.3.5",
|
|
38
|
+
"@protobase/layout": "0.3.5",
|
|
39
|
+
"@protobase/schema": "0.3.5",
|
|
33
40
|
"@tanstack/react-query": "^5.104.1",
|
|
34
41
|
"@tanstack/react-table": "^9.2.6",
|
|
35
42
|
"@tanstack/react-virtual": "^3.14.13",
|
|
@@ -37,12 +44,6 @@
|
|
|
37
44
|
"react": "^19.3.0",
|
|
38
45
|
"react-dom": "^19.3.0",
|
|
39
46
|
"recharts": "^3.10.1",
|
|
40
|
-
"tailwindcss": "^4.3.3"
|
|
41
|
-
"@protobase/client": "0.3.3",
|
|
42
|
-
"@protobase/schema": "0.3.3",
|
|
43
|
-
"@protobase/layout": "0.3.3"
|
|
44
|
-
},
|
|
45
|
-
"scripts": {
|
|
46
|
-
"build": "tsdown"
|
|
47
|
+
"tailwindcss": "^4.3.3"
|
|
47
48
|
}
|
|
48
|
-
}
|
|
49
|
+
}
|