@protobase/ui 0.19.0 → 0.19.2
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 +2 -2
- package/dist/app/app.js +14 -9
- package/dist/app/custom-section.js +1 -1
- package/dist/app/displays/custom-display.js +1 -1
- package/dist/app/displays/map-display.js +1 -1
- package/dist/app/displays/record-card.js +1 -1
- package/dist/app/favicon.svg +1 -0
- package/dist/app/pages/merge-ui.d.ts +1 -1
- package/dist/app/pages/merge-ui.js +16 -4
- package/dist/app/pages/project-ui.d.ts +30 -5
- package/dist/app/pages/project-ui.js +3 -3
- package/dist/app/pages/render-node.js +1 -1
- package/dist/app/project-logo.js +17 -0
- package/dist/app-shell/app-shell.d.ts +1 -1
- package/dist/app-shell/app-shell.js +3 -2
- package/dist/app-shell/index.d.ts +1 -0
- package/dist/app-shell/logo.d.ts +11 -0
- package/dist/app-shell/logo.js +30 -32
- package/dist/app-shell/sidebar.d.ts +2 -1
- package/dist/app-shell/sidebar.js +3 -3
- package/dist/app-shell/wordmark-path.js +4 -0
- package/dist/assistant-dock/widget-view.js +1 -1
- package/dist/auth/auth-gate.js +7 -1
- package/dist/auth/auth-layout.d.ts +10 -0
- package/dist/auth/auth-provider.js +10 -0
- package/dist/auth/code-form.d.ts +29 -0
- package/dist/auth/resend-code-button.d.ts +11 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +12 -5
- package/dist/package.js +1 -1
- package/dist/plugins/before-sign-in.js +49 -0
- package/dist/plugins/plugin-gate.js +8 -8
- package/dist/plugins/plugin-source.js +32 -0
- package/dist/plugins/plugin-ui.js +5 -3
- package/dist/plugins/use-plugin-loads.js +3 -4
- package/dist/primitives/button.d.ts +11 -0
- package/dist/primitives/input.d.ts +11 -0
- package/dist/primitives/spinner.d.ts +5 -0
- package/dist/shell/assets/app-Rbouc_gR.js +66 -0
- package/dist/shell/assets/{index-CLXqLf4p.js → index-Crd6_WsY.js} +1 -1
- package/dist/shell/assets/index-DaFzUWmg.css +2 -0
- package/dist/shell/assets/maplibre-Gu5hHDDK.js +814 -0
- package/dist/shell/assets/{record-map-Bvls7qdn.js → record-map-DQ0oyZff.js} +1 -1
- package/dist/shell/assets/records-map-Dp97bL3p.js +1 -0
- package/dist/shell/assets/shared-protobase-ui-DvvdY2OO.js +1 -0
- package/dist/shell/index.html +5 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +6 -5
- package/dist/shell/assets/app-BS7ufaBT.js +0 -66
- package/dist/shell/assets/index-BLnOMDD8.css +0 -2
- package/dist/shell/assets/maplibre-r-WEXdlV.js +0 -814
- package/dist/shell/assets/records-map-iNBfciYy.js +0 -1
- package/dist/shell/assets/shared-protobase-ui-9PLdWR62.js +0 -1
package/dist/app/app.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type AppProps = {
|
|
|
25
25
|
assistant?: AssistantClient;
|
|
26
26
|
/** A ready-made client of the runtime endpoint, for stories and tests; used only when `/meta` names one. */
|
|
27
27
|
runtime?: RuntimeClient;
|
|
28
|
-
/** How the plugins
|
|
28
|
+
/** How the plugins are loaded, for stories and tests; the page's `fetch` and `import()` by default. */
|
|
29
29
|
pluginLoader?: PluginLoader;
|
|
30
30
|
};
|
|
31
31
|
/**
|
|
@@ -33,5 +33,5 @@ export type AppProps = {
|
|
|
33
33
|
* then a sidebar from `/meta`, `/:page` composed pages, `/:resource` lists and `/:resource/:key` records. Fills its
|
|
34
34
|
* parent, in the theme this browser chose, light, dark or the system's.
|
|
35
35
|
*/
|
|
36
|
-
export declare const App: ({ auth, workspace, basePath, initialUrl, ...rest }: AppProps) => import("react").JSX.Element;
|
|
36
|
+
export declare const App: ({ auth, workspace, basePath, initialUrl, pluginLoader, ...rest }: AppProps) => import("react").JSX.Element;
|
|
37
37
|
//#endregion
|
package/dist/app/app.js
CHANGED
|
@@ -7,6 +7,9 @@ import { ApiProvider } from "../data/api-provider.js";
|
|
|
7
7
|
import { Router, useRouter } from "./router.js";
|
|
8
8
|
import { useSignInAddress } from "../auth/use-sign-in-address.js";
|
|
9
9
|
import { managesOrganization, organizationMenuItems, organizationPageTitles } from "../organizations/organization-paths.js";
|
|
10
|
+
import { startErrorReporting } from "./error-reporting.js";
|
|
11
|
+
import { CustomBoundary } from "./pages/custom-boundary.js";
|
|
12
|
+
import { PluginSourceProvider } from "../plugins/plugin-source.js";
|
|
10
13
|
import { AuthGate } from "../auth/auth-gate.js";
|
|
11
14
|
import { isAdmin, userToShell } from "../auth/user-to-shell.js";
|
|
12
15
|
import { useRecord } from "../data/use-record.js";
|
|
@@ -14,7 +17,6 @@ import { recordTitle } from "../live/model-helpers.js";
|
|
|
14
17
|
import { humanize } from "../live/naming.js";
|
|
15
18
|
import { ThemeProvider, useTheme } from "../theme/theme-provider.js";
|
|
16
19
|
import { ToastProvider } from "../toasts/toast-provider.js";
|
|
17
|
-
import { startErrorReporting } from "./error-reporting.js";
|
|
18
20
|
import { useProjectUi } from "./pages/project-ui.js";
|
|
19
21
|
import { AssistantButton, AssistantPanel } from "./assistant-shell.js";
|
|
20
22
|
import { bottomBarFromMeta } from "./bottom-bar-from-meta.js";
|
|
@@ -22,7 +24,6 @@ import { breadcrumbFor } from "./breadcrumb-for.js";
|
|
|
22
24
|
import { sourceStatusOf } from "../data/with-sources.js";
|
|
23
25
|
import { MetaGate, Notice, useAdminMeta } from "./meta-gate.js";
|
|
24
26
|
import { useAppAddress } from "./use-app-address.js";
|
|
25
|
-
import { CustomBoundary } from "./pages/custom-boundary.js";
|
|
26
27
|
import { CreatePage } from "./create/create-page.js";
|
|
27
28
|
import { ListPage } from "./list-page.js";
|
|
28
29
|
import { MembersPage } from "../organizations/members-page.js";
|
|
@@ -35,6 +36,7 @@ import { useActiveRole } from "../roles/use-active-role.js";
|
|
|
35
36
|
import { userMenuFromMeta } from "./user-menu-from-meta.js";
|
|
36
37
|
import { navFromMeta } from "./nav-from-meta.js";
|
|
37
38
|
import { ComposedPage } from "./pages/composed-page.js";
|
|
39
|
+
import { ProjectLogo } from "./project-logo.js";
|
|
38
40
|
import { PluginGate, usePluginsOff } from "../plugins/plugin-gate.js";
|
|
39
41
|
import { PluginNotice } from "../plugins/plugin-notice.js";
|
|
40
42
|
import { RecordPage } from "./record-page.js";
|
|
@@ -177,6 +179,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
177
179
|
onSignOut: user && (() => void signOut()),
|
|
178
180
|
signIn: user ? void 0 : signIn.link(),
|
|
179
181
|
workspace: current?.organization?.name ?? workspace,
|
|
182
|
+
logo: ProjectLogo,
|
|
180
183
|
navGroups: groups,
|
|
181
184
|
bottomBar: bottomBarFromMeta(meta, basePath, place),
|
|
182
185
|
userMenu: [
|
|
@@ -244,7 +247,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
244
247
|
]
|
|
245
248
|
});
|
|
246
249
|
};
|
|
247
|
-
const Authenticated = ({ baseUrl, client, sources, workspace, sidebarMode, ui, assistant, runtime
|
|
250
|
+
const Authenticated = ({ baseUrl, client, sources, workspace, sidebarMode, ui, assistant, runtime }) => {
|
|
248
251
|
const { state, session, markSignedOut } = useAuth();
|
|
249
252
|
return /* @__PURE__ */ jsx(ApiProvider, {
|
|
250
253
|
baseUrl,
|
|
@@ -254,7 +257,6 @@ const Authenticated = ({ baseUrl, client, sources, workspace, sidebarMode, ui, a
|
|
|
254
257
|
onUnauthenticated: markSignedOut,
|
|
255
258
|
children: /* @__PURE__ */ jsx(MetaGate, { children: /* @__PURE__ */ jsx(ToastProvider, { children: /* @__PURE__ */ jsx(PluginGate, {
|
|
256
259
|
ui,
|
|
257
|
-
...pluginLoader && { loader: pluginLoader },
|
|
258
260
|
children: /* @__PURE__ */ jsx(Routes, {
|
|
259
261
|
workspace,
|
|
260
262
|
sidebarMode,
|
|
@@ -269,16 +271,19 @@ const Authenticated = ({ baseUrl, client, sources, workspace, sidebarMode, ui, a
|
|
|
269
271
|
* then a sidebar from `/meta`, `/:page` composed pages, `/:resource` lists and `/:resource/:key` records. Fills its
|
|
270
272
|
* parent, in the theme this browser chose, light, dark or the system's.
|
|
271
273
|
*/
|
|
272
|
-
const App = ({ auth, workspace, basePath, initialUrl, ...rest }) => /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(AuthProvider, {
|
|
274
|
+
const App = ({ auth, workspace, basePath, initialUrl, pluginLoader, ...rest }) => /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx(AuthProvider, {
|
|
273
275
|
session: auth,
|
|
274
276
|
children: /* @__PURE__ */ jsx(Router, {
|
|
275
277
|
basePath,
|
|
276
278
|
initialUrl,
|
|
277
|
-
children: /* @__PURE__ */ jsx(
|
|
278
|
-
|
|
279
|
-
children: /* @__PURE__ */ jsx(
|
|
279
|
+
children: /* @__PURE__ */ jsx(PluginSourceProvider, {
|
|
280
|
+
...pluginLoader && { loader: pluginLoader },
|
|
281
|
+
children: /* @__PURE__ */ jsx(AuthGate, {
|
|
280
282
|
workspace,
|
|
281
|
-
|
|
283
|
+
children: /* @__PURE__ */ jsx(Authenticated, {
|
|
284
|
+
workspace,
|
|
285
|
+
...rest
|
|
286
|
+
})
|
|
282
287
|
})
|
|
283
288
|
})
|
|
284
289
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useProjectUi } from "./pages/project-ui.js";
|
|
2
1
|
import { Problem } from "./pages/problem.js";
|
|
3
2
|
import { CustomBoundary } from "./pages/custom-boundary.js";
|
|
3
|
+
import { useProjectUi } from "./pages/project-ui.js";
|
|
4
4
|
import { Section } from "../record-view/section.js";
|
|
5
5
|
import { SidebarCard } from "../record-view/record-sidebar.js";
|
|
6
6
|
import { RecordScope } from "./pages/record-scope.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useProjectUi } from "../pages/project-ui.js";
|
|
2
1
|
import { Problem } from "../pages/problem.js";
|
|
3
2
|
import { CustomBoundary } from "../pages/custom-boundary.js";
|
|
3
|
+
import { useProjectUi } from "../pages/project-ui.js";
|
|
4
4
|
import { ListDisplayProvider } from "./display-context.js";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { unmarkFieldRefs } from "@protobase/schema/internal";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
2
|
import { Button } from "../../primitives/button.js";
|
|
3
|
+
import { Problem } from "../pages/problem.js";
|
|
3
4
|
import { humanize } from "../../live/naming.js";
|
|
4
5
|
import { mapCards } from "./list-displays.js";
|
|
5
6
|
import { useAdminMeta } from "../meta-gate.js";
|
|
6
7
|
import { ErrorBanner } from "../error-banner.js";
|
|
7
|
-
import { Problem } from "../pages/problem.js";
|
|
8
8
|
import { useDebounced } from "../../lib/use-debounced.js";
|
|
9
9
|
import { RecordCard } from "./record-card.js";
|
|
10
10
|
import { boundsAround, worldArea } from "../map/map-area.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Badge } from "../../primitives/badge.js";
|
|
2
|
+
import { CustomBoundary } from "../pages/custom-boundary.js";
|
|
2
3
|
import { recordKey } from "../../live/model-helpers.js";
|
|
3
4
|
import { humanize } from "../../live/naming.js";
|
|
4
5
|
import { useProjectUi } from "../pages/project-ui.js";
|
|
@@ -6,7 +7,6 @@ import { displayValue } from "../../live/display-value.js";
|
|
|
6
7
|
import { amountParts, cardFields, filledAmount, filledMeta } from "./list-displays.js";
|
|
7
8
|
import { AppLink } from "../use-app-address.js";
|
|
8
9
|
import { shownWidget } from "../field-types/shown-widget.js";
|
|
9
|
-
import { CustomBoundary } from "../pages/custom-boundary.js";
|
|
10
10
|
import { WidgetPart } from "../field-types/widget-part.js";
|
|
11
11
|
import { namedIcon } from "../named-icon.js";
|
|
12
12
|
import { FavoriteButton } from "../saved/favorite-button.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><style>.c{fill:#FF5A1F}.r{fill:#14161A}@media (prefers-color-scheme:dark){.c{fill:#FF6A33}.r{fill:#F1F0EA}}</style><path class="c" fill-rule="evenodd" d="M0 5a5 5 0 0 1 5-5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zM4 5a1 1 0 0 1 1-1h5v6H4z"/><path class="r" d="M17 0h10a5 5 0 0 1 5 5v22a5 5 0 0 1-5 5H5a5 5 0 0 1-5-5V17h17z"/></svg>
|
|
@@ -3,7 +3,7 @@ import { ProjectUi } from "./project-ui.js";
|
|
|
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
5
|
* handlers and field types are all kept, and one name defined by two configs is an error naming both; every shell slot
|
|
6
|
-
* is drawn, the extended configs' first.
|
|
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;
|
|
9
9
|
//#endregion
|
|
@@ -25,10 +25,18 @@ const slot = (slots) => {
|
|
|
25
25
|
const All = () => slots.map((Slot, index) => /* @__PURE__ */ jsx(Slot, {}, index));
|
|
26
26
|
return All;
|
|
27
27
|
};
|
|
28
|
+
const around = (wrappers) => {
|
|
29
|
+
if (wrappers.length <= 1) return wrappers[0];
|
|
30
|
+
const All = ({ children, ...given }) => wrappers.reduceRight((inner, Wrapper) => /* @__PURE__ */ jsx(Wrapper, {
|
|
31
|
+
...given,
|
|
32
|
+
children: inner
|
|
33
|
+
}), children);
|
|
34
|
+
return All;
|
|
35
|
+
};
|
|
28
36
|
/**
|
|
29
37
|
* One UI config from several, each after the ones it `extends`, the last being the app's own: components, action
|
|
30
38
|
* handlers and field types are all kept, and one name defined by two configs is an error naming both; every shell slot
|
|
31
|
-
* is drawn, the extended configs' first.
|
|
39
|
+
* and `beforeSignIn` component is drawn, the extended configs' first, except the logo, the last config's to set one.
|
|
32
40
|
*/
|
|
33
41
|
const mergeUi = (...uis) => {
|
|
34
42
|
const named = uis.flatMap((ui, index) => flatten(ui, ui.name ?? `UI config ${index + 1}`));
|
|
@@ -37,16 +45,20 @@ const mergeUi = (...uis) => {
|
|
|
37
45
|
const fields = union("The field type", named, (ui) => ui.fields);
|
|
38
46
|
const actionSlot = slot(named.flatMap(({ ui }) => ui.shell?.actions ? [ui.shell.actions] : []));
|
|
39
47
|
const rightPanel = slot(named.flatMap(({ ui }) => ui.shell?.rightPanel ? [ui.shell.rightPanel] : []));
|
|
48
|
+
const logo = named.findLast(({ ui }) => ui.shell?.logo)?.ui.shell?.logo;
|
|
49
|
+
const beforeSignIn = around(named.flatMap(({ ui }) => ui.beforeSignIn ? [ui.beforeSignIn] : []));
|
|
40
50
|
const name = uis.at(-1)?.name;
|
|
41
51
|
return {
|
|
42
52
|
...name && { name },
|
|
43
53
|
components,
|
|
44
54
|
actions,
|
|
45
55
|
...Object.keys(fields).length > 0 && { fields },
|
|
46
|
-
...(actionSlot || rightPanel) && { shell: {
|
|
56
|
+
...(actionSlot || rightPanel || logo) && { shell: {
|
|
47
57
|
...actionSlot && { actions: actionSlot },
|
|
48
|
-
...rightPanel && { rightPanel }
|
|
49
|
-
|
|
58
|
+
...rightPanel && { rightPanel },
|
|
59
|
+
...logo && { logo }
|
|
60
|
+
} },
|
|
61
|
+
...beforeSignIn && { beforeSignIn }
|
|
50
62
|
};
|
|
51
63
|
};
|
|
52
64
|
//#endregion
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ShellLogoProps } from "../../app-shell/logo.js";
|
|
1
2
|
import { FieldWidget } from "../field-types/field-widget.js";
|
|
2
|
-
import { ComponentType } from "react";
|
|
3
|
+
import { ComponentType, ReactNode } from "react";
|
|
3
4
|
import { Client } from "@protobase/client";
|
|
4
5
|
import { AppAddress } from "@protobase/schema";
|
|
5
6
|
//#region src/app/pages/project-ui.d.ts
|
|
@@ -19,6 +20,21 @@ export type ActionContext = {
|
|
|
19
20
|
navigate: (to: AppAddress) => void;
|
|
20
21
|
};
|
|
21
22
|
export type ActionHandler = (context: ActionContext) => void | Promise<void>;
|
|
23
|
+
/** What a plugin's `beforeSignIn` component gets from the app, drawn around the pages someone signed out sees. */
|
|
24
|
+
export type BeforeSignInProps = {
|
|
25
|
+
/** What the app shows someone signed out: the sign-in page, or the app a visitor browses. */
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* Enters the app with a session something other than the sign-in page started, such as an endpoint that traded a
|
|
29
|
+
* one-time code for the session cookie: reads the session again and shows the app signed in. Resolves `false`, and
|
|
30
|
+
* changes nothing, when there is no session.
|
|
31
|
+
*/
|
|
32
|
+
signedIn: () => Promise<boolean>;
|
|
33
|
+
/** The query parameters of the page's address, such as a one-time code a link carries. */
|
|
34
|
+
params: URLSearchParams;
|
|
35
|
+
/** Takes these query parameters, such as a used one-time code, out of the address, in place of it in the tab's history. */
|
|
36
|
+
removeParams: (names: string[]) => void;
|
|
37
|
+
};
|
|
22
38
|
/**
|
|
23
39
|
* The project's own React code for composed pages: custom components by the name their layout declares
|
|
24
40
|
* (`component('UsageChart')`) or a widget part names, handlers for named actions that have no built-in behaviour,
|
|
@@ -33,11 +49,20 @@ export type ProjectUi = {
|
|
|
33
49
|
actions?: Record<string, ActionHandler>;
|
|
34
50
|
/** Field types by name, each with the storage types it shows and its components (see `FieldWidget`). */
|
|
35
51
|
fields?: Record<string, FieldWidget>;
|
|
36
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Rendered in the shell on every page: `actions` at the end of the top bar, `rightPanel` beside the page, and `logo`
|
|
54
|
+
* at the top of the sidebar in place of Protobase's (see `ShellLogoProps`).
|
|
55
|
+
*/
|
|
37
56
|
shell?: {
|
|
38
57
|
actions?: ComponentType;
|
|
39
58
|
rightPanel?: ComponentType;
|
|
59
|
+
logo?: ComponentType<ShellLogoProps>;
|
|
40
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Drawn around the pages someone signed out sees, only for a plugin the app loads before sign-in (`beforeSignIn` in
|
|
63
|
+
* `options.plugins`): its code runs for them, and it can sign them in (see `BeforeSignInProps`).
|
|
64
|
+
*/
|
|
65
|
+
beforeSignIn?: ComponentType<BeforeSignInProps>;
|
|
41
66
|
};
|
|
42
67
|
/** A `protobase.ui.tsx` module as `import * as` gives it: components exported by name, the rest as the default export. */
|
|
43
68
|
export type UiModule = {
|
|
@@ -47,9 +72,9 @@ export type UiModule = {
|
|
|
47
72
|
/** Typed identity, for `export default defineUi({ extends, components, actions })` in `protobase.ui.tsx`. */
|
|
48
73
|
export declare const defineUi: (ui: ProjectUi) => ProjectUi;
|
|
49
74
|
/**
|
|
50
|
-
* Typed identity, for `export default definePlugin({ name, components, actions, fields, shell })` in a
|
|
51
|
-
* `protobase.ui.tsx`: a plugin is a UI config the app loads at runtime, its components exported by name as in
|
|
52
|
-
* project's. A project's own protobase.ui.tsx is one too, which `protobase build` builds as the plugin `app`.
|
|
75
|
+
* Typed identity, for `export default definePlugin({ name, components, actions, fields, shell, beforeSignIn })` in a
|
|
76
|
+
* plugin's `protobase.ui.tsx`: a plugin is a UI config the app loads at runtime, its components exported by name as in
|
|
77
|
+
* a project's. A project's own protobase.ui.tsx is one too, which `protobase build` builds as the plugin `app`.
|
|
53
78
|
*/
|
|
54
79
|
export declare const definePlugin: (plugin: ProjectUi) => ProjectUi;
|
|
55
80
|
//#endregion
|
|
@@ -3,9 +3,9 @@ import { createContext, useContext } from "react";
|
|
|
3
3
|
/** Typed identity, for `export default defineUi({ extends, components, actions })` in `protobase.ui.tsx`. */
|
|
4
4
|
const defineUi = (ui) => ui;
|
|
5
5
|
/**
|
|
6
|
-
* Typed identity, for `export default definePlugin({ name, components, actions, fields, shell })` in a
|
|
7
|
-
* `protobase.ui.tsx`: a plugin is a UI config the app loads at runtime, its components exported by name as in
|
|
8
|
-
* project's. A project's own protobase.ui.tsx is one too, which `protobase build` builds as the plugin `app`.
|
|
6
|
+
* Typed identity, for `export default definePlugin({ name, components, actions, fields, shell, beforeSignIn })` in a
|
|
7
|
+
* plugin's `protobase.ui.tsx`: a plugin is a UI config the app loads at runtime, its components exported by name as in
|
|
8
|
+
* a project's. A project's own protobase.ui.tsx is one too, which `protobase build` builds as the plugin `app`.
|
|
9
9
|
*/
|
|
10
10
|
const definePlugin = (plugin) => plugin;
|
|
11
11
|
/** The project's UI, merged with the UI configs it extends and the plugins loaded; `PluginGate` or `ProjectUiProvider` provides it. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useProjectUi } from "./project-ui.js";
|
|
2
1
|
import { Problem } from "./problem.js";
|
|
3
2
|
import { CustomBoundary } from "./custom-boundary.js";
|
|
3
|
+
import { useProjectUi } from "./project-ui.js";
|
|
4
4
|
import { ActionBlock } from "./blocks/action.js";
|
|
5
5
|
import { CardBlock } from "./blocks/card.js";
|
|
6
6
|
import { CardRowBlock } from "./blocks/card-row.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Logo } from "../app-shell/logo.js";
|
|
2
|
+
import { CustomBoundary } from "./pages/custom-boundary.js";
|
|
3
|
+
import { useProjectUi } from "./pages/project-ui.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/app/project-logo.tsx
|
|
6
|
+
/** The project's `shell.logo` at the top of the sidebar, or Protobase's while it has none or when it throws. */
|
|
7
|
+
const ProjectLogo = (props) => {
|
|
8
|
+
const Own = useProjectUi().shell?.logo;
|
|
9
|
+
if (!Own) return /* @__PURE__ */ jsx(Logo, { ...props });
|
|
10
|
+
return /* @__PURE__ */ jsx(CustomBoundary, {
|
|
11
|
+
name: "shell.logo",
|
|
12
|
+
fallback: /* @__PURE__ */ jsx(Logo, { ...props }),
|
|
13
|
+
children: /* @__PURE__ */ jsx(Own, { ...props })
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { ProjectLogo };
|
|
@@ -9,7 +9,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
9
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
//#region src/app-shell/app-shell.tsx
|
|
11
11
|
const drawerId = "app-drawer";
|
|
12
|
-
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, actions, rightPanel, forceExpanded, profileMenuOpen, navGroups, userMenu, bottomBar, onNavigate, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
12
|
+
const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, logo, actions, rightPanel, forceExpanded, profileMenuOpen, navGroups, userMenu, bottomBar, onNavigate, onSignOut, signIn, organizations, roles, theme, search, pageScroll: scrollsByDefault = false, className, children }) => {
|
|
13
13
|
const desktop = useIsDesktop();
|
|
14
14
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
15
15
|
useEffect(() => {
|
|
@@ -37,7 +37,8 @@ const AppShell = ({ sidebarMode, activeItem, breadcrumb, user, workspace, action
|
|
|
37
37
|
signIn,
|
|
38
38
|
organizations,
|
|
39
39
|
roles,
|
|
40
|
-
theme
|
|
40
|
+
theme,
|
|
41
|
+
logo
|
|
41
42
|
});
|
|
42
43
|
return /* @__PURE__ */ jsx(PageScrollContext.Provider, {
|
|
43
44
|
value: setPageScroll,
|
|
@@ -4,6 +4,7 @@ import "./profile-menu.js";
|
|
|
4
4
|
import { SidebarMode, sidebarModes } from "./sidebar-mode.js";
|
|
5
5
|
import "./global-search.js";
|
|
6
6
|
import "./breadcrumb.js";
|
|
7
|
+
import { LogoMark, ShellLogoProps } from "./logo.js";
|
|
7
8
|
import "./app-shell.js";
|
|
8
9
|
import "./page-header.js";
|
|
9
10
|
import "./page-scroll.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/app-shell/logo.d.ts
|
|
2
|
+
/** The Protobase mark, the Corner: the outlined corner is what an app describes, the solid rest what Protobase runs. */
|
|
3
|
+
export declare const LogoMark: ({ className }: {
|
|
4
|
+
className?: string;
|
|
5
|
+
}) => import("react").JSX.Element;
|
|
6
|
+
/** What the logo at the top of the sidebar gets: `compact` when the sidebar shows only icons, and the workspace's name. */
|
|
7
|
+
export type ShellLogoProps = {
|
|
8
|
+
compact: boolean;
|
|
9
|
+
workspace?: string;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
package/dist/app-shell/logo.js
CHANGED
|
@@ -1,46 +1,44 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { wordmarkPath } from "./wordmark-path.js";
|
|
2
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
//#region src/app-shell/logo.tsx
|
|
5
|
+
const corner = "fill-[#ff5a1f] dark:fill-[#ff6a33]";
|
|
6
|
+
const solid = "fill-[#14161a] dark:fill-[#f1f0ea]";
|
|
7
|
+
/** The Protobase mark, the Corner: the outlined corner is what an app describes, the solid rest what Protobase runs. */
|
|
4
8
|
const LogoMark = ({ className }) => /* @__PURE__ */ jsxs("svg", {
|
|
5
|
-
viewBox: "0 0
|
|
9
|
+
viewBox: "0 0 32 32",
|
|
6
10
|
className: cn("size-7 shrink-0", className),
|
|
7
11
|
"aria-hidden": true,
|
|
8
|
-
children: [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
strokeWidth: "2",
|
|
28
|
-
strokeLinecap: "round"
|
|
29
|
-
})
|
|
30
|
-
]
|
|
12
|
+
children: [/* @__PURE__ */ jsx("path", {
|
|
13
|
+
className: corner,
|
|
14
|
+
fillRule: "evenodd",
|
|
15
|
+
d: "M0 5a5 5 0 0 1 5-5h7a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zM4 5a1 1 0 0 1 1-1h5v6H4z"
|
|
16
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
17
|
+
className: solid,
|
|
18
|
+
d: "M17 0h10a5 5 0 0 1 5 5v22a5 5 0 0 1-5 5H5a5 5 0 0 1-5-5V17h17z"
|
|
19
|
+
})]
|
|
20
|
+
});
|
|
21
|
+
/** The name "protobase", outlined as the brand draws it rather than set in a font. */
|
|
22
|
+
const Wordmark = ({ className }) => /* @__PURE__ */ jsx("svg", {
|
|
23
|
+
viewBox: "54.8 -720 3743.8 925",
|
|
24
|
+
className: cn("h-3.5 w-auto", className),
|
|
25
|
+
role: "img",
|
|
26
|
+
"aria-label": "Protobase",
|
|
27
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
28
|
+
className: solid,
|
|
29
|
+
d: wordmarkPath
|
|
30
|
+
})
|
|
31
31
|
});
|
|
32
|
-
|
|
32
|
+
/** Protobase's logo at the top of the sidebar, the workspace's name under the wordmark, or the mark alone when compact. */
|
|
33
|
+
const Logo = ({ compact, workspace }) => /* @__PURE__ */ jsxs("div", {
|
|
33
34
|
className: "flex items-center gap-2.5 overflow-hidden",
|
|
34
|
-
children: [/* @__PURE__ */ jsx(LogoMark, {}),
|
|
35
|
-
className: "min-w-0 leading-tight",
|
|
36
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
37
|
-
className: "text-[13px] font-semibold tracking-tight",
|
|
38
|
-
children: "Protobase"
|
|
39
|
-
}), workspace && /* @__PURE__ */ jsx("div", {
|
|
35
|
+
children: [/* @__PURE__ */ jsx(LogoMark, {}), !compact && /* @__PURE__ */ jsxs("div", {
|
|
36
|
+
className: "flex min-w-0 flex-col gap-0.5 leading-tight",
|
|
37
|
+
children: [/* @__PURE__ */ jsx(Wordmark, { className: "self-start" }), workspace && /* @__PURE__ */ jsx("div", {
|
|
40
38
|
className: "truncate text-[11px] text-muted-foreground",
|
|
41
39
|
children: workspace
|
|
42
40
|
})]
|
|
43
41
|
})]
|
|
44
42
|
});
|
|
45
43
|
//#endregion
|
|
46
|
-
export { Logo, LogoMark };
|
|
44
|
+
export { Logo, LogoMark, Wordmark };
|
|
@@ -10,7 +10,7 @@ import { SidebarGroup } from "./sidebar-group.js";
|
|
|
10
10
|
import { LogIn } from "lucide-react";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
12
|
//#region src/app-shell/sidebar.tsx
|
|
13
|
-
const Sidebar = ({ mode, activeItem, user, workspace, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky }) => {
|
|
13
|
+
const Sidebar = ({ mode, activeItem, user, workspace, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky, logo: SidebarLogo = Logo }) => {
|
|
14
14
|
const expanding = mode === "icon-expand";
|
|
15
15
|
const expanded = expanding && forceExpanded;
|
|
16
16
|
const inline = isTextMode(mode) || expanding;
|
|
@@ -22,8 +22,8 @@ const Sidebar = ({ mode, activeItem, user, workspace, forceExpanded, profileMenu
|
|
|
22
22
|
children: [
|
|
23
23
|
/* @__PURE__ */ jsx("div", {
|
|
24
24
|
className: cn("flex h-12 shrink-0 items-center px-3.5", mode === "icon-label" && "justify-center px-0"),
|
|
25
|
-
children: /* @__PURE__ */ jsx(
|
|
26
|
-
|
|
25
|
+
children: /* @__PURE__ */ jsx(SidebarLogo, {
|
|
26
|
+
compact: !inline,
|
|
27
27
|
workspace
|
|
28
28
|
})
|
|
29
29
|
}),
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
//#region src/app-shell/wordmark-path.ts
|
|
2
|
+
const wordmarkPath = "M312.3 9.9Q257 9.9 217.1 -21.1Q177.3 -52.1 168.4 -108.8L181.1 -110.3V205H54.8V-510H176.7V-404.5L168.3 -407Q179.7 -460.8 221.2 -490.4Q262.7 -520 319.8 -520Q376.2 -520 417 -488.4Q457.8 -456.8 479.8 -398.6Q501.8 -340.5 501.8 -259.9Q501.8 -177.6 478.3 -116.8Q454.9 -55.9 412.5 -23Q370.1 9.9 312.3 9.9ZM274.4 -92.6Q320.2 -92.6 345.8 -133.8Q371.4 -175.1 371.4 -256.4Q371.4 -337.7 345.7 -377.6Q320.1 -417.5 274 -417.5Q228.6 -417.5 202.7 -376.4Q176.9 -335.3 176.9 -254Q176.9 -173.3 202.7 -132.9Q228.4 -92.6 274.4 -92.6Z M549.8 0V-510H671.7V-388.3H676.1V0H549.8ZM676.1 -286.5 661.3 -386.2Q677.9 -455.2 711.8 -487.6Q745.7 -520 794.3 -520Q809 -520 818 -516.6V-394.2Q814.2 -395.6 807.1 -396.1Q800 -396.6 789.4 -396.6Q734.6 -396.6 705.3 -370.5Q676.1 -344.4 676.1 -286.5Z M1043.8 10Q974.7 10 922.6 -23.6Q870.6 -57.2 842.1 -117.5Q813.6 -177.8 813.6 -257Q813.6 -336.5 842.2 -395.5Q870.8 -454.5 922.5 -487.2Q974.2 -520 1043.8 -520Q1113.7 -520 1165.4 -487.2Q1217.1 -454.5 1245.6 -395.5Q1274 -336.5 1274 -257Q1274 -177.8 1245.5 -117.5Q1217 -57.2 1165.3 -23.6Q1113.6 10 1043.8 10ZM1043.8 -90.5Q1073.5 -90.5 1095.9 -108.9Q1118.2 -127.3 1130.9 -164.4Q1143.6 -201.5 1143.6 -257.5Q1143.6 -339.8 1116 -379.7Q1088.4 -419.5 1043.8 -419.5Q999.3 -419.5 971.6 -379.5Q944 -339.5 944 -257.5Q944 -201.5 956.7 -164.4Q969.4 -127.3 991.8 -108.9Q1014.3 -90.5 1043.8 -90.5Z M1509 10.2Q1427.2 10.2 1387.6 -29.2Q1348 -68.6 1348 -147.7V-613.8L1474.2 -666.1V-146.7Q1474.2 -118.4 1489.9 -104.3Q1505.6 -90.3 1539.2 -90.3Q1552 -90.3 1562.7 -92.5Q1573.3 -94.8 1582.5 -98.7V-1.9Q1571.6 4 1552 7.1Q1532.4 10.2 1509 10.2ZM1270.1 -411V-510H1580.6V-411H1270.1Z M1810.8 10Q1741.7 10 1689.6 -23.6Q1637.6 -57.2 1609.1 -117.5Q1580.6 -177.8 1580.6 -257Q1580.6 -336.5 1609.2 -395.5Q1637.8 -454.5 1689.5 -487.2Q1741.2 -520 1810.8 -520Q1880.7 -520 1932.4 -487.2Q1984.1 -454.5 2012.6 -395.5Q2041 -336.5 2041 -257Q2041 -177.8 2012.5 -117.5Q1984 -57.2 1932.3 -23.6Q1880.6 10 1810.8 10ZM1810.8 -90.5Q1840.5 -90.5 1862.9 -108.9Q1885.2 -127.3 1897.9 -164.4Q1910.6 -201.5 1910.6 -257.5Q1910.6 -339.8 1883 -379.7Q1855.4 -419.5 1810.8 -419.5Q1766.3 -419.5 1738.6 -379.5Q1711 -339.5 1711 -257.5Q1711 -201.5 1723.7 -164.4Q1736.4 -127.3 1758.8 -108.9Q1781.3 -90.5 1810.8 -90.5Z M2343.3 9.9Q2288 9.9 2247.7 -21.1Q2207.4 -52.1 2198.5 -108.8L2205.8 -110.3V0H2085.8V-720H2212.1V-404.5L2203.7 -407Q2215.1 -460.8 2255.6 -490.4Q2296.2 -520 2351 -520Q2407.3 -520 2448 -488.4Q2488.8 -456.8 2510.8 -398.6Q2532.8 -340.5 2532.8 -259.9Q2532.8 -177.6 2509.3 -116.8Q2485.9 -55.9 2443.5 -23Q2401.1 9.9 2343.3 9.9ZM2305.4 -92.6Q2350.7 -92.6 2376.6 -133.8Q2402.4 -175.1 2402.4 -256.4Q2402.4 -337.7 2376.5 -377.6Q2350.6 -417.5 2305 -417.5Q2260.5 -417.5 2234.2 -376.4Q2207.9 -335.3 2207.9 -254Q2207.9 -173.3 2234.1 -132.9Q2260.3 -92.6 2305.4 -92.6Z M2834 0Q2828 -19.1 2825.7 -41.7Q2823.3 -64.2 2823.3 -91.8H2818.6V-356Q2818.6 -391.9 2803.1 -409.1Q2787.5 -426.3 2756.2 -426.3Q2724.2 -426.3 2706.4 -408.7Q2688.7 -391.2 2686.3 -358.9H2571.2Q2574.6 -429.6 2625.4 -474.8Q2676.1 -520 2763.5 -520Q2850.2 -520 2896.1 -476.5Q2941.9 -432.9 2941.9 -352.2V-91.8Q2941.9 -69.5 2944.7 -46.7Q2947.5 -23.8 2954.1 0H2834ZM2690.6 10Q2631.9 10 2595.4 -26.3Q2558.9 -62.5 2558.9 -124.5Q2558.9 -185 2594.3 -224Q2629.8 -263 2704.5 -287.6L2845.4 -337.4V-248.7L2753.4 -219.3Q2716.5 -206.1 2699.3 -186.6Q2682.2 -167.2 2682.2 -139Q2682.2 -112.4 2697.5 -98.4Q2712.9 -84.3 2739.4 -84.3Q2773.2 -84.3 2795.9 -104.1Q2818.6 -123.9 2818.6 -153.1L2829.9 -85.8Q2812.6 -37.4 2776.8 -13.7Q2741 10 2690.6 10Z M3168 10Q3080.6 10 3030.1 -34.2Q2979.6 -78.4 2974.7 -158.1H3082.4Q3086.3 -120 3108.9 -100.5Q3131.5 -80.9 3169.8 -80.9Q3202 -80.9 3218.9 -93.9Q3235.8 -106.9 3235.8 -131.3Q3235.8 -151.1 3224.3 -165.6Q3212.9 -180.1 3178.9 -195.4L3111.2 -224.8Q3053 -247.7 3023.8 -284.2Q2994.5 -320.8 2994.5 -373.9Q2994.5 -440.1 3040.1 -480Q3085.7 -520 3162.8 -520Q3242.2 -520 3288.4 -480Q3334.6 -440 3339.5 -367.9H3231.8Q3228.6 -399.7 3211.3 -414.4Q3194 -429.1 3164 -429.1Q3136.1 -429.1 3120.5 -417Q3104.8 -405 3104.8 -382.7Q3104.8 -364.2 3117.7 -349.8Q3130.6 -335.5 3162.4 -321.6L3236.6 -289.6Q3288.6 -269.2 3317.4 -229.2Q3346.1 -189.1 3346.1 -136.9Q3346.1 -69 3299.2 -29.5Q3252.3 10 3168 10Z M3587.8 10Q3520 10 3469.2 -23.3Q3418.5 -56.6 3390.5 -116.5Q3362.6 -176.3 3362.6 -255.8Q3362.6 -336.4 3390.9 -395.7Q3419.3 -454.9 3470.7 -487.5Q3522 -520 3591.1 -520Q3656.6 -520 3702.7 -489.8Q3748.8 -459.7 3773.7 -403.1Q3798.6 -346.5 3798.6 -266.5Q3798.6 -253.7 3798 -242.5Q3797.5 -231.3 3795.8 -221.4H3440V-306.9H3709L3681.8 -277.9Q3681.6 -355.2 3657.7 -390.7Q3633.8 -426.3 3589.3 -426.3Q3541.7 -426.3 3515.6 -384Q3489.5 -341.7 3489.5 -255Q3489.5 -168.4 3515 -126.1Q3540.5 -83.7 3590.6 -83.7Q3623.7 -83.7 3644.6 -102Q3665.5 -120.3 3673.8 -156.3H3788.9Q3772.2 -81.6 3721.5 -35.8Q3670.8 10 3587.8 10Z";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { wordmarkPath };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ActionCard } from "../action-card/action-card.js";
|
|
2
1
|
import { reportRenderError } from "../app/error-reporting.js";
|
|
2
|
+
import { ActionCard } from "../action-card/action-card.js";
|
|
3
3
|
import { useProjectUi } from "../app/pages/project-ui.js";
|
|
4
4
|
import { Component } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
package/dist/auth/auth-gate.js
CHANGED
|
@@ -19,6 +19,7 @@ import { TwoFactorPage } from "./two-factor-page.js";
|
|
|
19
19
|
import { useSignInAddress } from "./use-sign-in-address.js";
|
|
20
20
|
import { readInvitationLink, withoutInvitationLink } from "../organizations/invitation-link.js";
|
|
21
21
|
import { InvitationPage } from "../organizations/invitation-page.js";
|
|
22
|
+
import { BeforeSignIn } from "../plugins/before-sign-in.js";
|
|
22
23
|
import { useCallback, useEffect, useState } from "react";
|
|
23
24
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
24
25
|
import { signInPath } from "@protobase/schema";
|
|
@@ -62,9 +63,14 @@ const Redirect = ({ to }) => {
|
|
|
62
63
|
* browse, and the right page otherwise: the step of the sign-in route the address names, or the page an emailed reset
|
|
63
64
|
* link, an invitation link or a staff link opened. Someone signed out without a visitor's browsing goes to the sign-in
|
|
64
65
|
* route, and signing in comes back to the page the route was opened from. An invited person with an account signs in
|
|
65
|
-
* first and comes back to the invitation.
|
|
66
|
+
* first and comes back to the invitation. The plugins the app loads before sign-in draw around what someone signed
|
|
67
|
+
* out sees (see `BeforeSignIn`), while the gate keeps its state.
|
|
66
68
|
*/
|
|
67
69
|
const AuthGate = ({ workspace, children }) => {
|
|
70
|
+
const page = useAuthPage(workspace, children);
|
|
71
|
+
return /* @__PURE__ */ jsx(BeforeSignIn, { children: page });
|
|
72
|
+
};
|
|
73
|
+
const useAuthPage = (workspace, children) => {
|
|
68
74
|
const { state, recheck, passwordReset, signOut, signInProviders, canSignUp, browse } = useAuth();
|
|
69
75
|
const { path, params, basePath, navigate } = useRouter();
|
|
70
76
|
const { address, next, open, link, leave } = useSignInAddress();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/auth/auth-layout.d.ts
|
|
3
|
+
export type AuthLayoutProps = {
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
/** The frame of the sign-in and first-run pages: logo, title and a card. */
|
|
9
|
+
export declare const AuthLayout: ({ title, description, children }: AuthLayoutProps) => import("react").JSX.Element;
|
|
10
|
+
//#endregion
|
|
@@ -21,6 +21,7 @@ const AuthProvider = ({ session: given, children }) => {
|
|
|
21
21
|
const [staffSignIn, setStaffSignIn] = useState();
|
|
22
22
|
const [organizations, setOrganizations] = useState();
|
|
23
23
|
const [anonymous, setAnonymous] = useState(false);
|
|
24
|
+
const [beforeSignIn, setBeforeSignIn] = useState([]);
|
|
24
25
|
const signedOutState = useCallback(() => anonymous ? { kind: "visitor" } : { kind: "signed-out" }, [anonymous]);
|
|
25
26
|
const markSignedOut = useCallback(() => setState((current) => current.kind === "signed-in" ? { kind: "signed-out" } : current), []);
|
|
26
27
|
const signedOut = useRef(markSignedOut);
|
|
@@ -54,6 +55,7 @@ const AuthProvider = ({ session: given, children }) => {
|
|
|
54
55
|
setStaffSignIn(status.staffSignIn);
|
|
55
56
|
setOrganizations(status.organizations);
|
|
56
57
|
setAnonymous(Boolean(status.anonymous));
|
|
58
|
+
setBeforeSignIn(status.plugins ?? []);
|
|
57
59
|
if (status.needsAdmin) return setState({ kind: "needs-admin" });
|
|
58
60
|
const user = await session.session();
|
|
59
61
|
return user ? enter(user) : setState({ kind: status.anonymous ? "visitor" : "signed-out" });
|
|
@@ -79,6 +81,13 @@ const AuthProvider = ({ session: given, children }) => {
|
|
|
79
81
|
...organizations && { organizations },
|
|
80
82
|
markSignedOut,
|
|
81
83
|
recheck,
|
|
84
|
+
beforeSignIn,
|
|
85
|
+
adoptSession: async () => {
|
|
86
|
+
const user = await session.session();
|
|
87
|
+
if (!user) return false;
|
|
88
|
+
await enter(user);
|
|
89
|
+
return true;
|
|
90
|
+
},
|
|
82
91
|
...anonymous && { browse: () => {
|
|
83
92
|
setState((current) => current.kind === "signed-out" ? { kind: "visitor" } : current);
|
|
84
93
|
} },
|
|
@@ -116,6 +125,7 @@ const AuthProvider = ({ session: given, children }) => {
|
|
|
116
125
|
staffSignIn,
|
|
117
126
|
organizations,
|
|
118
127
|
anonymous,
|
|
128
|
+
beforeSignIn,
|
|
119
129
|
signedOutState,
|
|
120
130
|
markSignedOut,
|
|
121
131
|
recheck,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/auth/code-form.d.ts
|
|
3
|
+
export type CodeFormProps = {
|
|
4
|
+
title: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
/** Above the code: which address it went to, and how long it works. */
|
|
7
|
+
explanation: ReactNode;
|
|
8
|
+
/** The button that checks the code, which a complete code presses by itself. */
|
|
9
|
+
submitLabel: string;
|
|
10
|
+
/** Called with the code; the page shows `error` when it is not right. */
|
|
11
|
+
onSubmit: (code: string) => void;
|
|
12
|
+
/** Sends a new code to the same address. */
|
|
13
|
+
onResend: () => void;
|
|
14
|
+
/** Back to the step that asked for the address, for another one. */
|
|
15
|
+
onChangeAddress: () => void;
|
|
16
|
+
changeAddressLabel: string;
|
|
17
|
+
busy?: boolean;
|
|
18
|
+
error?: string;
|
|
19
|
+
/** Shown once a new code is on its way. */
|
|
20
|
+
notice?: string;
|
|
21
|
+
/** Seconds before "Send a new code" works, after each code; 30 by default. */
|
|
22
|
+
resendCooldown?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* A step that checks an emailed code on its own: where it went, a box per digit, a new code after a wait, and a way back
|
|
26
|
+
* to another address. Nothing else is asked for until the code is right.
|
|
27
|
+
*/
|
|
28
|
+
export declare const CodeForm: ({ title, description, explanation, submitLabel, onSubmit, onResend, onChangeAddress, changeAddressLabel, busy, error, notice, resendCooldown }: CodeFormProps) => import("react").JSX.Element;
|
|
29
|
+
//#endregion
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/auth/resend-code-button.d.ts
|
|
2
|
+
export type ResendCodeButtonProps = {
|
|
3
|
+
/** Sends a new code; the button waits `cooldown` seconds again after it. */
|
|
4
|
+
onResend: () => void;
|
|
5
|
+
/** Seconds to wait after a code went out before another is asked for; 30 by default. */
|
|
6
|
+
cooldown?: number;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
};
|
|
9
|
+
/** "Send a new code", counting down first: the screen opens right after a code went out, and so does every resend. */
|
|
10
|
+
export declare const ResendCodeButton: ({ onResend, cooldown, disabled }: ResendCodeButtonProps) => import("react").JSX.Element;
|
|
11
|
+
//#endregion
|