@protobase/ui 0.26.9 → 0.28.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/app.d.ts +1 -1
- package/dist/app/app.js +2 -0
- package/dist/app/main.js +2 -1
- package/dist/app/page-api-origin.js +18 -0
- package/dist/app-shell/app-shell.js +2 -1
- package/dist/app-shell/logo.d.ts +4 -2
- package/dist/app-shell/logo.js +11 -6
- package/dist/app-shell/sidebar.js +3 -2
- package/dist/auth/auth-provider.js +9 -3
- package/dist/data/meta-data.d.ts +2 -0
- package/dist/data/use-meta.js +1 -0
- package/dist/package.js +1 -1
- package/dist/shell/assets/{app-CKIJTg2U.js → app-DY0ReSvA.js} +42 -42
- package/dist/shell/assets/index-BF_9NTir.js +9 -0
- package/dist/shell/assets/{records-map-DG0dbm9k.js → records-map-7UYIVM03.js} +1 -1
- package/dist/shell/assets/{shared-protobase-ui-CvcWMNa3.js → shared-protobase-ui-CsNp8ORv.js} +1 -1
- package/dist/shell/index.html +5 -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/index-nDDDN3FR.js +0 -9
package/dist/app/app.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type AppProps = {
|
|
|
12
12
|
client?: Client;
|
|
13
13
|
/** Ready-made clients of the sources `/meta` names, by their URL, for stories and tests. */
|
|
14
14
|
sources?: Record<string, Client>;
|
|
15
|
-
/** Sign-in state for the app; made from
|
|
15
|
+
/** Sign-in state for the app; made from `baseUrl`'s origin when omitted. */
|
|
16
16
|
auth?: AuthSession;
|
|
17
17
|
/** Path prefix the app is mounted under, for example `/admin`. */
|
|
18
18
|
basePath?: string;
|
package/dist/app/app.js
CHANGED
|
@@ -201,6 +201,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
201
201
|
signIn: user ? void 0 : signIn.link(),
|
|
202
202
|
workspace: current?.organization?.name ?? workspace,
|
|
203
203
|
appName: meta.appName,
|
|
204
|
+
appIcon: meta.appIcon,
|
|
204
205
|
logo: ProjectLogo,
|
|
205
206
|
navGroups: groups,
|
|
206
207
|
bottomBar: bottomBarFromMeta(meta, basePath, place),
|
|
@@ -309,6 +310,7 @@ const App = ({ auth, workspace, basePath, initialUrl, pluginLoader, locale, ...r
|
|
|
309
310
|
setFallbackLocale(locale ?? "en");
|
|
310
311
|
return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(AuthProvider, {
|
|
311
312
|
session: auth,
|
|
313
|
+
...rest.baseUrl && { origin: new URL(rest.baseUrl, window.location.href).origin },
|
|
312
314
|
children: /* @__PURE__ */ jsx(Router, {
|
|
313
315
|
basePath,
|
|
314
316
|
initialUrl,
|
package/dist/app/main.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { browserLocale } from "../locale/active-locale.js";
|
|
2
2
|
import { uiFromModule } from "./pages/ui-from-module.js";
|
|
3
3
|
import { App } from "./app.js";
|
|
4
|
+
import { pageApiOrigin } from "./page-api-origin.js";
|
|
4
5
|
import { StrictMode } from "react";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
import { createRoot } from "react-dom/client";
|
|
@@ -10,7 +11,7 @@ import "../styles.css";
|
|
|
10
11
|
const root = document.getElementById("root");
|
|
11
12
|
if (!root) throw new Error("index.html needs an element with id \"root\"");
|
|
12
13
|
createRoot(root).render(/* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(App, {
|
|
13
|
-
baseUrl: "/api/v1
|
|
14
|
+
baseUrl: `${pageApiOrigin() ?? ""}/api/v1`,
|
|
14
15
|
ui: uiFromModule(projectUi),
|
|
15
16
|
locale: browserLocale()
|
|
16
17
|
}) }));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { apiOriginMeta } from "@protobase/schema/internal";
|
|
2
|
+
//#region src/app/page-api-origin.ts
|
|
3
|
+
/**
|
|
4
|
+
* The API origin the page's `<meta name="protobase-api-origin">` tags name: `undefined` without one. Anything but one
|
|
5
|
+
* tag holding exactly an http or https origin, such as `https://shop.example.com`, is an error.
|
|
6
|
+
*/
|
|
7
|
+
const apiOriginOf = (contents) => {
|
|
8
|
+
if (contents.length === 0) return void 0;
|
|
9
|
+
if (contents.length > 1) throw new Error(`The page has ${contents.length} ${apiOriginMeta} meta tags; it may have one`);
|
|
10
|
+
const value = contents[0];
|
|
11
|
+
const url = URL.canParse(value) ? new URL(value) : void 0;
|
|
12
|
+
if (!url || url.protocol !== "http:" && url.protocol !== "https:" || url.origin !== value) throw new Error(`The page's ${apiOriginMeta} meta tag holds "${value}", which is not an http or https origin such as https://shop.example.com`);
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
15
|
+
/** The API origin this page names in its `<meta name="protobase-api-origin">` (see `apiOriginOf`). */
|
|
16
|
+
const pageApiOrigin = () => apiOriginOf([...document.querySelectorAll(`meta[name="${apiOriginMeta}"]`)].map((meta) => meta.content));
|
|
17
|
+
//#endregion
|
|
18
|
+
export { apiOriginOf, pageApiOrigin };
|
|
@@ -10,7 +10,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
10
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
11
|
//#region src/app-shell/app-shell.tsx
|
|
12
12
|
const drawerId = "app-drawer";
|
|
13
|
-
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appName, logo, actions, rightPanel, forceExpanded, profileMenuOpen, navGroups, userMenu, bottomBar, onNavigate, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
13
|
+
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appName, appIcon, logo, actions, rightPanel, forceExpanded, profileMenuOpen, navGroups, userMenu, bottomBar, onNavigate, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
14
14
|
const desktop = useIsDesktop();
|
|
15
15
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
16
16
|
useEffect(() => {
|
|
@@ -31,6 +31,7 @@ const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, appNam
|
|
|
31
31
|
user,
|
|
32
32
|
workspace,
|
|
33
33
|
appName,
|
|
34
|
+
appIcon,
|
|
34
35
|
forceExpanded,
|
|
35
36
|
profileMenuOpen,
|
|
36
37
|
groups: navGroups,
|
package/dist/app-shell/logo.d.ts
CHANGED
|
@@ -4,12 +4,14 @@ export declare const LogoMark: ({ className }: {
|
|
|
4
4
|
className?: string;
|
|
5
5
|
}) => import("react").JSX.Element;
|
|
6
6
|
/**
|
|
7
|
-
* What the logo at the top of the sidebar gets: `compact` when the sidebar shows only icons, the workspace's name,
|
|
8
|
-
*
|
|
7
|
+
* What the logo at the top of the sidebar gets: `compact` when the sidebar shows only icons, the workspace's name, the
|
|
8
|
+
* app's name, such as `Property Finder` (`PROTOBASE_APP_NAME` or `createAuth`'s `appName`), and the path of its own
|
|
9
|
+
* icon, its `protobase.favicon.svg`, when the project has one.
|
|
9
10
|
*/
|
|
10
11
|
export type ShellLogoProps = {
|
|
11
12
|
compact: boolean;
|
|
12
13
|
workspace?: string;
|
|
13
14
|
appName?: string;
|
|
15
|
+
appIcon?: string;
|
|
14
16
|
};
|
|
15
17
|
//#endregion
|
package/dist/app-shell/logo.js
CHANGED
|
@@ -31,16 +31,21 @@ const Wordmark = ({ className }) => /* @__PURE__ */ jsx("svg", {
|
|
|
31
31
|
});
|
|
32
32
|
const brandName = "Protobase";
|
|
33
33
|
/**
|
|
34
|
-
* The logo at the top of the sidebar: Protobase's mark, then the app's name, as the brand's
|
|
35
|
-
* Protobase's name, with the workspace's name under it; the
|
|
34
|
+
* The logo at the top of the sidebar: the app's own icon, else Protobase's mark, then the app's name, as the brand's
|
|
35
|
+
* wordmark when the app goes by Protobase's name and has no icon of its own, with the workspace's name under it; the
|
|
36
|
+
* icon alone when compact.
|
|
36
37
|
*/
|
|
37
|
-
const Logo = ({ compact, workspace, appName }) => /* @__PURE__ */ jsxs("div", {
|
|
38
|
+
const Logo = ({ compact, workspace, appName, appIcon }) => /* @__PURE__ */ jsxs("div", {
|
|
38
39
|
className: "flex items-center gap-2.5 overflow-hidden",
|
|
39
|
-
children: [
|
|
40
|
+
children: [appIcon ? /* @__PURE__ */ jsx("img", {
|
|
41
|
+
src: appIcon,
|
|
42
|
+
alt: "",
|
|
43
|
+
className: "size-7 shrink-0 object-contain"
|
|
44
|
+
}) : /* @__PURE__ */ jsx(LogoMark, {}), !compact && /* @__PURE__ */ jsxs("div", {
|
|
40
45
|
className: "flex min-w-0 flex-col gap-0.5 leading-tight",
|
|
41
|
-
children: [appName && appName !== brandName ? /* @__PURE__ */ jsx("div", {
|
|
46
|
+
children: [appIcon || appName && appName !== brandName ? /* @__PURE__ */ jsx("div", {
|
|
42
47
|
className: "truncate text-sm font-semibold text-foreground",
|
|
43
|
-
children: appName
|
|
48
|
+
children: appName ?? brandName
|
|
44
49
|
}) : /* @__PURE__ */ jsx(Wordmark, { className: "self-start" }), workspace && /* @__PURE__ */ jsx("div", {
|
|
45
50
|
className: "truncate text-[11px] text-muted-foreground",
|
|
46
51
|
children: workspace
|
|
@@ -11,7 +11,7 @@ import { SidebarGroup } from "./sidebar-group.js";
|
|
|
11
11
|
import { LogIn } from "lucide-react";
|
|
12
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
//#region src/app-shell/sidebar.tsx
|
|
14
|
-
const Sidebar = ({ mode, activeItem, user, workspace, appName, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky, logo: SidebarLogo = Logo }) => {
|
|
14
|
+
const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky, logo: SidebarLogo = Logo }) => {
|
|
15
15
|
const expanding = mode === "icon-expand";
|
|
16
16
|
const expanded = expanding && forceExpanded;
|
|
17
17
|
const inline = isTextMode(mode) || expanding;
|
|
@@ -26,7 +26,8 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, forceExpanded, pr
|
|
|
26
26
|
children: /* @__PURE__ */ jsx(SidebarLogo, {
|
|
27
27
|
compact: !inline,
|
|
28
28
|
workspace,
|
|
29
|
-
appName
|
|
29
|
+
appName,
|
|
30
|
+
appIcon
|
|
30
31
|
})
|
|
31
32
|
}),
|
|
32
33
|
/* @__PURE__ */ jsx("div", {
|
|
@@ -10,8 +10,11 @@ const useAuth = () => {
|
|
|
10
10
|
if (!api) throw new Error("useAuth must be used inside AuthProvider");
|
|
11
11
|
return api;
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Finds out whether the server has a login and who is signed in, and keeps that state for the app. Without a `session`,
|
|
15
|
+
* it makes one on `origin`, the page's own by default.
|
|
16
|
+
*/
|
|
17
|
+
const AuthProvider = ({ session: given, origin, children }) => {
|
|
15
18
|
const [state, setState] = useState({ kind: "loading" });
|
|
16
19
|
const [signInMethods, setSignInMethods] = useState(["password"]);
|
|
17
20
|
const [passwordReset, setPasswordReset] = useState(false);
|
|
@@ -29,7 +32,10 @@ const AuthProvider = ({ session: given, children }) => {
|
|
|
29
32
|
const markSignedOut = useCallback(() => setState((current) => current.kind === "signed-in" ? { kind: "signed-out" } : current), []);
|
|
30
33
|
const signedOut = useRef(markSignedOut);
|
|
31
34
|
signedOut.current = markSignedOut;
|
|
32
|
-
const session = useMemo(() => given ?? createAuthSession({
|
|
35
|
+
const session = useMemo(() => given ?? createAuthSession({
|
|
36
|
+
...origin && { origin },
|
|
37
|
+
onSignedOut: () => signedOut.current()
|
|
38
|
+
}), [given, origin]);
|
|
33
39
|
const enter = useCallback(async (user) => {
|
|
34
40
|
const [{ missing }, staff] = await Promise.all([session.account.signInMethods(), session.staff.current()]);
|
|
35
41
|
setState(missing.length > 0 ? {
|
package/dist/data/meta-data.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type MetaData = {
|
|
|
7
7
|
etag: string;
|
|
8
8
|
/** The app's name, which ends the tab titles. */
|
|
9
9
|
appName: string;
|
|
10
|
+
/** The path of the app's own icon, when the project has one, which the sidebar's logo draws. */
|
|
11
|
+
appIcon?: string;
|
|
10
12
|
resources: Record<string, ResourceModel>;
|
|
11
13
|
views: Record<string, ViewModel>;
|
|
12
14
|
/** Composed pages by name. */
|
package/dist/data/use-meta.js
CHANGED
|
@@ -27,6 +27,7 @@ const useMeta = () => {
|
|
|
27
27
|
return {
|
|
28
28
|
etag: result.etag,
|
|
29
29
|
appName: result.meta.appName,
|
|
30
|
+
...result.meta.appIcon && { appIcon: result.meta.appIcon },
|
|
30
31
|
resources: Object.fromEntries(result.meta.resources.map((model) => [model.name, model])),
|
|
31
32
|
views: Object.fromEntries(result.meta.views.map((model) => [model.resource, model])),
|
|
32
33
|
pages: Object.fromEntries((result.meta.pages ?? []).map((model) => [model.name, model])),
|
package/dist/package.js
CHANGED