@panneau/app 4.1.1 → 5.0.0-alpha.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/README.md +39 -0
- package/dist/Panneau.d.ts +39 -0
- package/dist/Panneau.d.ts.map +1 -0
- package/dist/Panneau.js +92 -0
- package/dist/Panneau.js.map +1 -0
- package/dist/Shell.d.ts +11 -0
- package/dist/Shell.d.ts.map +1 -0
- package/dist/Shell.js +144 -0
- package/dist/Shell.js.map +1 -0
- package/dist/boot.d.ts +10 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +31 -0
- package/dist/boot.js.map +1 -0
- package/dist/createPanneauApp.d.ts +45 -0
- package/dist/createPanneauApp.d.ts.map +1 -0
- package/dist/createPanneauApp.js +32 -0
- package/dist/createPanneauApp.js.map +1 -0
- package/dist/index.d.ts +4 -53
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -2958
- package/dist/location.d.ts +10 -0
- package/dist/location.d.ts.map +1 -0
- package/dist/location.js +69 -0
- package/dist/location.js.map +1 -0
- package/dist/routes.d.ts +38 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +65 -0
- package/dist/routes.js.map +1 -0
- package/dist/unauthorized.d.ts +21 -0
- package/dist/unauthorized.d.ts.map +1 -0
- package/dist/unauthorized.js +50 -0
- package/dist/unauthorized.js.map +1 -0
- package/package.json +22 -106
- package/assets/css/packages.css +0 -6
- package/assets/css/styles.css +0 -6
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @panneau/app
|
|
2
|
+
|
|
3
|
+
The Panneau admin panel: the app entry (`createPanneauApp`), and the shell, routes and pages to come.
|
|
4
|
+
|
|
5
|
+
The shell is a placeholder until Phase 2 of the [roadmap](../../docs/roadmap.md): it loads the definition, lists the resources and follows the client routes. The entry contract below is stable: see [decisions/0019](../../docs/decisions/0019-app-entry.md).
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// resources/js/panneau.tsx
|
|
9
|
+
import { createPanneauApp } from '@panneau/app';
|
|
10
|
+
|
|
11
|
+
createPanneauApp({ element: document.getElementById('panneau')!, plugins: [] });
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The page writes the boot config next to the root element ([protocol.md](../../docs/protocol.md#boot-config)):
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<div id="panneau"></div>
|
|
18
|
+
<script type="application/json" id="panneau-boot">
|
|
19
|
+
{ "protocol": 1, "apiBaseUrl": "/panneau/api", "basePath": "/panneau", "locale": "fr" }
|
|
20
|
+
</script>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
| Export | Content |
|
|
24
|
+
| -------------- | -------------------------------------------------------------------------------------------------- |
|
|
25
|
+
| `@panneau/app` | `createPanneauApp()`, the `<Panneau>` component, `readBootConfig()` and the types of their options |
|
|
26
|
+
|
|
27
|
+
## Setup
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @panneau/app@^5.0.0-alpha.0 react react-dom @tanstack/react-query
|
|
31
|
+
npm install --save-dev vite @vitejs/plugin-react
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- Add `@vitejs/plugin-react` to the Vite config, and the entry to its inputs.
|
|
35
|
+
- The entry imports no CSS and no theme: packages import their own styles, and themes come from plugins.
|
|
36
|
+
- Without `adapter`, the app uses the Laravel preset of the REST adapter (`@panneau/data/laravel`) on `apiBaseUrl`.
|
|
37
|
+
- When the API answers `401` (or `419` from Laravel), the app sends the browser to the login URL. Pass `onUnauthorized` to do something else.
|
|
38
|
+
|
|
39
|
+
The package layout, scripts and API reports are explained in [`docs/packages.md`](../../docs/packages.md).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { type PanneauPlugin } from '@panneau/core';
|
|
3
|
+
import { type DataAdapter } from '@panneau/data';
|
|
4
|
+
import type { BootConfig } from '@panneau/protocol';
|
|
5
|
+
/** What `onUnauthorized` receives. */
|
|
6
|
+
export interface UnauthorizedContext {
|
|
7
|
+
/**
|
|
8
|
+
* Where to send the user: `auth.loginUrl` of the definition in `external` auth mode, or the
|
|
9
|
+
* `loginUrl` of the boot config before the definition is loaded. `null` when there is none, or
|
|
10
|
+
* in `panneau` auth mode, where the login page is part of the app.
|
|
11
|
+
*/
|
|
12
|
+
loginUrl: string | null;
|
|
13
|
+
}
|
|
14
|
+
/** Props of `<Panneau>`. */
|
|
15
|
+
export interface PanneauProps {
|
|
16
|
+
/** The boot config, validated (`readBootConfig()`, or `validateBootConfig()` of the protocol). */
|
|
17
|
+
config: BootConfig;
|
|
18
|
+
/** The data adapter. Defaults to `createLaravelAdapter({ baseUrl: config.apiBaseUrl })`. */
|
|
19
|
+
adapter?: DataAdapter;
|
|
20
|
+
/** Plugins: field types, pages, locales, themes… */
|
|
21
|
+
plugins?: readonly PanneauPlugin[];
|
|
22
|
+
/** The TanStack Query client. Defaults to a new one. */
|
|
23
|
+
queryClient?: QueryClient;
|
|
24
|
+
/**
|
|
25
|
+
* Called once when the API answers that the user isn't authenticated (`401`, or `419` with the
|
|
26
|
+
* Laravel adapter). The app then shows that the session ended. Defaults to sending the browser
|
|
27
|
+
* to `loginUrl` when there is one.
|
|
28
|
+
*/
|
|
29
|
+
onUnauthorized?: (context: UnauthorizedContext) => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The Panneau admin panel, for a React app that renders it itself. `createPanneauApp()` mounts it
|
|
33
|
+
* in an element of the page. The adapter and the query client are read on the first render.
|
|
34
|
+
*
|
|
35
|
+
* It uses the definition inlined in the boot config, or fetches it, then renders the shell. When
|
|
36
|
+
* the API answers that the user isn't authenticated, it calls `onUnauthorized`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function Panneau({ config, adapter, plugins, queryClient, onUnauthorized, }: PanneauProps): import("react").JSX.Element;
|
|
39
|
+
//# sourceMappingURL=Panneau.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Panneau.d.ts","sourceRoot":"","sources":["../src/Panneau.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiC,MAAM,uBAAuB,CAAC;AAGnF,OAAO,EAAE,KAAK,aAAa,EAAwC,MAAM,eAAe,CAAC;AACzF,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,eAAe,CAAC;AAG7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAKpD,sCAAsC;AACtC,MAAM,WAAW,mBAAmB;IAChC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,4BAA4B;AAC5B,MAAM,WAAW,YAAY;IACzB,kGAAkG;IAClG,MAAM,EAAE,UAAU,CAAC;IACnB,4FAA4F;IAC5F,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,oDAAoD;IACpD,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,wDAAwD;IACxD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC3D;AA0ED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EACpB,MAAM,EACN,OAAO,EACP,OAAY,EACZ,WAAW,EACX,cAAgC,GACnC,EAAE,YAAY,+BAyBd"}
|
package/dist/Panneau.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Shell } from "./Shell.js";
|
|
2
|
+
import { createSessionState, getLoginUrl, watchUnauthorized } from "./unauthorized.js";
|
|
3
|
+
import { useEffect, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
|
|
5
|
+
import { PanneauProvider, createHistoryRouter } from "@panneau/core";
|
|
6
|
+
import { isApiError } from "@panneau/data";
|
|
7
|
+
import { createLaravelAdapter } from "@panneau/data/laravel";
|
|
8
|
+
import { DataAdapterProvider, definitionQueryOptions } from "@panneau/data/query";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
//#region src/Panneau.tsx
|
|
11
|
+
function redirectToLogin({ loginUrl }) {
|
|
12
|
+
if (loginUrl !== null) window.location.assign(loginUrl);
|
|
13
|
+
}
|
|
14
|
+
function createQueryClient() {
|
|
15
|
+
return new QueryClient({ defaultOptions: { queries: { retry: (failureCount, error) => !(isApiError(error) && error.status < 500) && failureCount < 3 } } });
|
|
16
|
+
}
|
|
17
|
+
function Content({ config, adapter, plugins, session, onUnauthorized }) {
|
|
18
|
+
const [router] = useState(createHistoryRouter);
|
|
19
|
+
const definitionQuery = useQuery({
|
|
20
|
+
...definitionQueryOptions(adapter),
|
|
21
|
+
initialData: config.definition
|
|
22
|
+
});
|
|
23
|
+
const signedOut = useSyncExternalStore(session.subscribe, session.isSignedOut);
|
|
24
|
+
const loginUrl = getLoginUrl(definitionQuery.data, config);
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (signedOut) onUnauthorized({ loginUrl });
|
|
27
|
+
}, [signedOut]);
|
|
28
|
+
if (signedOut) return /* @__PURE__ */ jsxs("div", {
|
|
29
|
+
role: "alert",
|
|
30
|
+
children: [/* @__PURE__ */ jsx("p", { children: "Your session has ended." }), loginUrl !== null ? /* @__PURE__ */ jsx("a", {
|
|
31
|
+
href: loginUrl,
|
|
32
|
+
children: "Log in"
|
|
33
|
+
}) : null]
|
|
34
|
+
});
|
|
35
|
+
if (definitionQuery.data === void 0) {
|
|
36
|
+
if (definitionQuery.isError) return /* @__PURE__ */ jsxs("div", {
|
|
37
|
+
role: "alert",
|
|
38
|
+
children: [/* @__PURE__ */ jsx("p", { children: "The admin panel could not be loaded." }), /* @__PURE__ */ jsx("button", {
|
|
39
|
+
type: "button",
|
|
40
|
+
onClick: () => void definitionQuery.refetch(),
|
|
41
|
+
children: "Try again"
|
|
42
|
+
})]
|
|
43
|
+
});
|
|
44
|
+
return /* @__PURE__ */ jsx("p", {
|
|
45
|
+
role: "status",
|
|
46
|
+
children: "Loading…"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return /* @__PURE__ */ jsx(PanneauProvider, {
|
|
50
|
+
plugins,
|
|
51
|
+
definition: definitionQuery.data,
|
|
52
|
+
router,
|
|
53
|
+
children: /* @__PURE__ */ jsx(Shell, {
|
|
54
|
+
definition: definitionQuery.data,
|
|
55
|
+
basePath: config.basePath
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The Panneau admin panel, for a React app that renders it itself. `createPanneauApp()` mounts it
|
|
61
|
+
* in an element of the page. The adapter and the query client are read on the first render.
|
|
62
|
+
*
|
|
63
|
+
* It uses the definition inlined in the boot config, or fetches it, then renders the shell. When
|
|
64
|
+
* the API answers that the user isn't authenticated, it calls `onUnauthorized`.
|
|
65
|
+
*/
|
|
66
|
+
function Panneau({ config, adapter, plugins = [], queryClient, onUnauthorized = redirectToLogin }) {
|
|
67
|
+
const [state] = useState(() => {
|
|
68
|
+
const session = createSessionState();
|
|
69
|
+
return {
|
|
70
|
+
session,
|
|
71
|
+
adapter: watchUnauthorized(adapter ?? createLaravelAdapter({ baseUrl: config.apiBaseUrl }), session),
|
|
72
|
+
queryClient: queryClient ?? createQueryClient()
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
return /* @__PURE__ */ jsx(QueryClientProvider, {
|
|
76
|
+
client: state.queryClient,
|
|
77
|
+
children: /* @__PURE__ */ jsx(DataAdapterProvider, {
|
|
78
|
+
adapter: state.adapter,
|
|
79
|
+
children: /* @__PURE__ */ jsx(Content, {
|
|
80
|
+
config,
|
|
81
|
+
adapter: state.adapter,
|
|
82
|
+
plugins,
|
|
83
|
+
session: state.session,
|
|
84
|
+
onUnauthorized
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { Panneau };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=Panneau.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Panneau.js","names":[],"sources":["../src/Panneau.tsx"],"sourcesContent":["import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';\nimport { useEffect, useState, useSyncExternalStore } from 'react';\n\nimport { type PanneauPlugin, PanneauProvider, createHistoryRouter } from '@panneau/core';\nimport { type DataAdapter, isApiError } from '@panneau/data';\nimport { createLaravelAdapter } from '@panneau/data/laravel';\nimport { DataAdapterProvider, definitionQueryOptions } from '@panneau/data/query';\nimport type { BootConfig } from '@panneau/protocol';\n\nimport { Shell } from './Shell';\nimport { createSessionState, getLoginUrl, watchUnauthorized } from './unauthorized';\n\n/** What `onUnauthorized` receives. */\nexport interface UnauthorizedContext {\n /**\n * Where to send the user: `auth.loginUrl` of the definition in `external` auth mode, or the\n * `loginUrl` of the boot config before the definition is loaded. `null` when there is none, or\n * in `panneau` auth mode, where the login page is part of the app.\n */\n loginUrl: string | null;\n}\n\n/** Props of `<Panneau>`. */\nexport interface PanneauProps {\n /** The boot config, validated (`readBootConfig()`, or `validateBootConfig()` of the protocol). */\n config: BootConfig;\n /** The data adapter. Defaults to `createLaravelAdapter({ baseUrl: config.apiBaseUrl })`. */\n adapter?: DataAdapter;\n /** Plugins: field types, pages, locales, themes… */\n plugins?: readonly PanneauPlugin[];\n /** The TanStack Query client. Defaults to a new one. */\n queryClient?: QueryClient;\n /**\n * Called once when the API answers that the user isn't authenticated (`401`, or `419` with the\n * Laravel adapter). The app then shows that the session ended. Defaults to sending the browser\n * to `loginUrl` when there is one.\n */\n onUnauthorized?: (context: UnauthorizedContext) => void;\n}\n\nfunction redirectToLogin({ loginUrl }: UnauthorizedContext) {\n if (loginUrl !== null) {\n window.location.assign(loginUrl);\n }\n}\n\nfunction createQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n // Client errors (401, 403, 404, 422…) don't change on retry.\n retry: (failureCount, error) =>\n !(isApiError(error) && error.status < 500) && failureCount < 3,\n },\n },\n });\n}\n\nfunction Content({\n config,\n adapter,\n plugins,\n session,\n onUnauthorized,\n}: Required<Pick<PanneauProps, 'config' | 'adapter' | 'onUnauthorized'>> & {\n plugins: readonly PanneauPlugin[];\n session: ReturnType<typeof createSessionState>;\n}) {\n const [router] = useState(createHistoryRouter);\n const definitionQuery = useQuery({\n ...definitionQueryOptions(adapter),\n initialData: config.definition,\n });\n const signedOut = useSyncExternalStore(session.subscribe, session.isSignedOut);\n const loginUrl = getLoginUrl(definitionQuery.data, config);\n\n useEffect(() => {\n if (signedOut) {\n onUnauthorized({ loginUrl });\n }\n // Only when the session ends: `loginUrl` and the callback are read at that time.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [signedOut]);\n\n if (signedOut) {\n return (\n <div role=\"alert\">\n <p>Your session has ended.</p>\n {loginUrl !== null ? <a href={loginUrl}>Log in</a> : null}\n </div>\n );\n }\n if (definitionQuery.data === undefined) {\n if (definitionQuery.isError) {\n return (\n <div role=\"alert\">\n <p>The admin panel could not be loaded.</p>\n <button type=\"button\" onClick={() => void definitionQuery.refetch()}>\n Try again\n </button>\n </div>\n );\n }\n return <p role=\"status\">Loading…</p>;\n }\n return (\n <PanneauProvider plugins={plugins} definition={definitionQuery.data} router={router}>\n <Shell definition={definitionQuery.data} basePath={config.basePath} />\n </PanneauProvider>\n );\n}\n\n/**\n * The Panneau admin panel, for a React app that renders it itself. `createPanneauApp()` mounts it\n * in an element of the page. The adapter and the query client are read on the first render.\n *\n * It uses the definition inlined in the boot config, or fetches it, then renders the shell. When\n * the API answers that the user isn't authenticated, it calls `onUnauthorized`.\n */\nexport function Panneau({\n config,\n adapter,\n plugins = [],\n queryClient,\n onUnauthorized = redirectToLogin,\n}: PanneauProps) {\n const [state] = useState(() => {\n const session = createSessionState();\n return {\n session,\n adapter: watchUnauthorized(\n adapter ?? createLaravelAdapter({ baseUrl: config.apiBaseUrl }),\n session,\n ),\n queryClient: queryClient ?? createQueryClient(),\n };\n });\n return (\n <QueryClientProvider client={state.queryClient}>\n <DataAdapterProvider adapter={state.adapter}>\n <Content\n config={config}\n adapter={state.adapter}\n plugins={plugins}\n session={state.session}\n onUnauthorized={onUnauthorized}\n />\n </DataAdapterProvider>\n </QueryClientProvider>\n );\n}\n"],"mappings":";;;;;;;;;;AAwCA,SAAS,gBAAgB,EAAE,YAAiC;CACxD,IAAI,aAAa,MACb,OAAO,SAAS,OAAO,QAAQ;AAEvC;AAEA,SAAS,oBAAiC;CACtC,OAAO,IAAI,YAAY,EACnB,gBAAgB,EACZ,SAAS,EAEL,QAAQ,cAAc,UAClB,EAAE,WAAW,KAAK,KAAK,MAAM,SAAS,QAAQ,eAAe,EACrE,EACJ,EACJ,CAAC;AACL;AAEA,SAAS,QAAQ,EACb,QACA,SACA,SACA,SACA,kBAID;CACC,MAAM,CAAC,UAAU,SAAS,mBAAmB;CAC7C,MAAM,kBAAkB,SAAS;EAC7B,GAAG,uBAAuB,OAAO;EACjC,aAAa,OAAO;CACxB,CAAC;CACD,MAAM,YAAY,qBAAqB,QAAQ,WAAW,QAAQ,WAAW;CAC7E,MAAM,WAAW,YAAY,gBAAgB,MAAM,MAAM;CAEzD,gBAAgB;EACZ,IAAI,WACA,eAAe,EAAE,SAAS,CAAC;CAInC,GAAG,CAAC,SAAS,CAAC;CAEd,IAAI,WACA,OACI,qBAAC,OAAD;EAAK,MAAK;EAAV,UAAA,CACI,oBAAC,KAAD,EAAA,UAAG,0BAA0B,CAAA,GAC5B,aAAa,OAAO,oBAAC,KAAD;GAAG,MAAM;GAAU,UAAA;EAAS,CAAA,IAAI,IACpD;;CAGb,IAAI,gBAAgB,SAAS,KAAA,GAAW;EACpC,IAAI,gBAAgB,SAChB,OACI,qBAAC,OAAD;GAAK,MAAK;GAAV,UAAA,CACI,oBAAC,KAAD,EAAA,UAAG,uCAAuC,CAAA,GAC1C,oBAAC,UAAD;IAAQ,MAAK;IAAS,eAAe,KAAK,gBAAgB,QAAQ;IAAE,UAAA;GAE5D,CAAA,CACP;;EAGb,OAAO,oBAAC,KAAD;GAAG,MAAK;GAAS,UAAA;EAAW,CAAA;CACvC;CACA,OACI,oBAAC,iBAAD;EAA0B;EAAS,YAAY,gBAAgB;EAAc;EACzE,UAAA,oBAAC,OAAD;GAAO,YAAY,gBAAgB;GAAM,UAAU,OAAO;EAAS,CAAA;CACtD,CAAA;AAEzB;;;;;;;;AASA,SAAgB,QAAQ,EACpB,QACA,SACA,UAAU,CAAA,GACV,aACA,iBAAiB,mBACJ;CACb,MAAM,CAAC,SAAS,eAAe;EAC3B,MAAM,UAAU,mBAAmB;EACnC,OAAO;GACH;GACA,SAAS,kBACL,WAAW,qBAAqB,EAAE,SAAS,OAAO,WAAW,CAAC,GAC9D,OACJ;GACA,aAAa,eAAe,kBAAkB;EAClD;CACJ,CAAC;CACD,OACI,oBAAC,qBAAD;EAAqB,QAAQ,MAAM;EAC/B,UAAA,oBAAC,qBAAD;GAAqB,SAAS,MAAM;GAChC,UAAA,oBAAC,SAAD;IACY;IACR,SAAS,MAAM;IACN;IACT,SAAS,MAAM;IACC;GAAe,CAAA;EAElB,CAAA;CACJ,CAAA;AAE7B"}
|
package/dist/Shell.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Definition } from '@panneau/protocol';
|
|
2
|
+
export interface ShellProps {
|
|
3
|
+
definition: Definition;
|
|
4
|
+
basePath: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Placeholder of the app shell: the site name, the resources, and the title of the current route.
|
|
8
|
+
* The real shell (layout, menus, pages) comes in Phase 2.
|
|
9
|
+
*/
|
|
10
|
+
export declare function Shell({ definition, basePath }: ShellProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=Shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Shell.d.ts","sourceRoot":"","sources":["../src/Shell.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAKpD,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACpB;AA6BD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,UAAU,+BAiCzD"}
|
package/dist/Shell.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Link, usePathname } from "./location.js";
|
|
2
|
+
import { matchRoute, routePath } from "./routes.js";
|
|
3
|
+
import { c } from "react/compiler-runtime";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/Shell.tsx
|
|
6
|
+
function getTitle(route, definition) {
|
|
7
|
+
if (route.name === "home") return definition.name;
|
|
8
|
+
if (route.name === "page") {
|
|
9
|
+
const page = definition.pages?.[route.page];
|
|
10
|
+
return page === void 0 ? "Page not found" : page.label ?? route.page;
|
|
11
|
+
}
|
|
12
|
+
if (route.name === "not-found") return "Page not found";
|
|
13
|
+
const resource = definition.resources.find(({ id }) => id === route.resource);
|
|
14
|
+
if (resource === void 0) return "Page not found";
|
|
15
|
+
switch (route.name) {
|
|
16
|
+
case "resource.index": return resource.label.plural;
|
|
17
|
+
case "resource.create": return `New ${resource.label.singular}`;
|
|
18
|
+
case "resource.show": return `${resource.label.singular} ${route.id}`;
|
|
19
|
+
case "resource.edit": return `Edit ${resource.label.singular} ${route.id}`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Placeholder of the app shell: the site name, the resources, and the title of the current route.
|
|
24
|
+
* The real shell (layout, menus, pages) comes in Phase 2.
|
|
25
|
+
*/
|
|
26
|
+
function Shell(t0) {
|
|
27
|
+
const $ = c(34);
|
|
28
|
+
const { definition, basePath } = t0;
|
|
29
|
+
const pathname = usePathname();
|
|
30
|
+
let t1;
|
|
31
|
+
let t2;
|
|
32
|
+
let t3;
|
|
33
|
+
let t4;
|
|
34
|
+
if ($[0] !== basePath || $[1] !== definition || $[2] !== pathname) {
|
|
35
|
+
const route = matchRoute(pathname, basePath) ?? { name: "not-found" };
|
|
36
|
+
t2 = "";
|
|
37
|
+
let t5;
|
|
38
|
+
if ($[7] !== basePath) {
|
|
39
|
+
t5 = routePath({ name: "home" }, basePath);
|
|
40
|
+
$[7] = basePath;
|
|
41
|
+
$[8] = t5;
|
|
42
|
+
} else t5 = $[8];
|
|
43
|
+
let t6;
|
|
44
|
+
if ($[9] !== definition.name || $[10] !== t5) {
|
|
45
|
+
t6 = /* @__PURE__ */ jsx(Link, {
|
|
46
|
+
href: t5,
|
|
47
|
+
children: definition.name
|
|
48
|
+
});
|
|
49
|
+
$[9] = definition.name;
|
|
50
|
+
$[10] = t5;
|
|
51
|
+
$[11] = t6;
|
|
52
|
+
} else t6 = $[11];
|
|
53
|
+
let t7;
|
|
54
|
+
if ($[12] !== definition.user) {
|
|
55
|
+
t7 = definition.user !== void 0 && definition.user !== null ? /* @__PURE__ */ jsx("span", { children: definition.user.name }) : null;
|
|
56
|
+
$[12] = definition.user;
|
|
57
|
+
$[13] = t7;
|
|
58
|
+
} else t7 = $[13];
|
|
59
|
+
if ($[14] !== t6 || $[15] !== t7) {
|
|
60
|
+
t3 = /* @__PURE__ */ jsxs("header", { children: [t6, t7] });
|
|
61
|
+
$[14] = t6;
|
|
62
|
+
$[15] = t7;
|
|
63
|
+
$[16] = t3;
|
|
64
|
+
} else t3 = $[16];
|
|
65
|
+
let t8;
|
|
66
|
+
if ($[17] !== basePath || $[18] !== definition.resources) {
|
|
67
|
+
let t9;
|
|
68
|
+
if ($[20] !== basePath) {
|
|
69
|
+
t9 = (resource) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, {
|
|
70
|
+
href: routePath({
|
|
71
|
+
name: "resource.index",
|
|
72
|
+
resource: resource.id
|
|
73
|
+
}, basePath),
|
|
74
|
+
children: resource.label.plural
|
|
75
|
+
}) }, resource.id);
|
|
76
|
+
$[20] = basePath;
|
|
77
|
+
$[21] = t9;
|
|
78
|
+
} else t9 = $[21];
|
|
79
|
+
t8 = definition.resources.map(t9);
|
|
80
|
+
$[17] = basePath;
|
|
81
|
+
$[18] = definition.resources;
|
|
82
|
+
$[19] = t8;
|
|
83
|
+
} else t8 = $[19];
|
|
84
|
+
if ($[22] !== t8) {
|
|
85
|
+
t4 = /* @__PURE__ */ jsx("nav", {
|
|
86
|
+
"aria-label": "Resources",
|
|
87
|
+
children: /* @__PURE__ */ jsx("ul", { children: t8 })
|
|
88
|
+
});
|
|
89
|
+
$[22] = t8;
|
|
90
|
+
$[23] = t4;
|
|
91
|
+
} else t4 = $[23];
|
|
92
|
+
t1 = getTitle(route, definition);
|
|
93
|
+
$[0] = basePath;
|
|
94
|
+
$[1] = definition;
|
|
95
|
+
$[2] = pathname;
|
|
96
|
+
$[3] = t1;
|
|
97
|
+
$[4] = t2;
|
|
98
|
+
$[5] = t3;
|
|
99
|
+
$[6] = t4;
|
|
100
|
+
} else {
|
|
101
|
+
t1 = $[3];
|
|
102
|
+
t2 = $[4];
|
|
103
|
+
t3 = $[5];
|
|
104
|
+
t4 = $[6];
|
|
105
|
+
}
|
|
106
|
+
let t5;
|
|
107
|
+
if ($[24] !== t1) {
|
|
108
|
+
t5 = /* @__PURE__ */ jsx("h1", { children: t1 });
|
|
109
|
+
$[24] = t1;
|
|
110
|
+
$[25] = t5;
|
|
111
|
+
} else t5 = $[25];
|
|
112
|
+
let t6;
|
|
113
|
+
if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
|
|
114
|
+
t6 = /* @__PURE__ */ jsx("p", { children: "The Panneau 5 app shell is not built yet." });
|
|
115
|
+
$[26] = t6;
|
|
116
|
+
} else t6 = $[26];
|
|
117
|
+
let t7;
|
|
118
|
+
if ($[27] !== t5) {
|
|
119
|
+
t7 = /* @__PURE__ */ jsxs("main", { children: [t5, t6] });
|
|
120
|
+
$[27] = t5;
|
|
121
|
+
$[28] = t7;
|
|
122
|
+
} else t7 = $[28];
|
|
123
|
+
let t8;
|
|
124
|
+
if ($[29] !== t2 || $[30] !== t3 || $[31] !== t4 || $[32] !== t7) {
|
|
125
|
+
t8 = /* @__PURE__ */ jsxs("div", {
|
|
126
|
+
"data-panneau-shell": t2,
|
|
127
|
+
children: [
|
|
128
|
+
t3,
|
|
129
|
+
t4,
|
|
130
|
+
t7
|
|
131
|
+
]
|
|
132
|
+
});
|
|
133
|
+
$[29] = t2;
|
|
134
|
+
$[30] = t3;
|
|
135
|
+
$[31] = t4;
|
|
136
|
+
$[32] = t7;
|
|
137
|
+
$[33] = t8;
|
|
138
|
+
} else t8 = $[33];
|
|
139
|
+
return t8;
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { Shell };
|
|
143
|
+
|
|
144
|
+
//# sourceMappingURL=Shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Shell.js","names":["_c"],"sources":["../src/Shell.tsx"],"sourcesContent":["import type { Definition } from '@panneau/protocol';\n\nimport { Link, usePathname } from './location';\nimport { type AppRoute, matchRoute, routePath } from './routes';\n\nexport interface ShellProps {\n definition: Definition;\n basePath: string;\n}\n\nfunction getTitle(route: AppRoute, definition: Definition): string {\n if (route.name === 'home') {\n return definition.name;\n }\n if (route.name === 'page') {\n const page = definition.pages?.[route.page];\n return page === undefined ? 'Page not found' : (page.label ?? route.page);\n }\n if (route.name === 'not-found') {\n return 'Page not found';\n }\n const resource = definition.resources.find(({ id }) => id === route.resource);\n if (resource === undefined) {\n return 'Page not found';\n }\n switch (route.name) {\n case 'resource.index':\n return resource.label.plural;\n case 'resource.create':\n return `New ${resource.label.singular}`;\n case 'resource.show':\n return `${resource.label.singular} ${route.id}`;\n case 'resource.edit':\n return `Edit ${resource.label.singular} ${route.id}`;\n }\n}\n\n/**\n * Placeholder of the app shell: the site name, the resources, and the title of the current route.\n * The real shell (layout, menus, pages) comes in Phase 2.\n */\nexport function Shell({ definition, basePath }: ShellProps) {\n const pathname = usePathname();\n const route = matchRoute(pathname, basePath) ?? { name: 'not-found' };\n return (\n <div data-panneau-shell=\"\">\n <header>\n <Link href={routePath({ name: 'home' }, basePath)}>{definition.name}</Link>\n {definition.user !== undefined && definition.user !== null ? (\n <span>{definition.user.name}</span>\n ) : null}\n </header>\n <nav aria-label=\"Resources\">\n <ul>\n {definition.resources.map((resource) => (\n <li key={resource.id}>\n <Link\n href={routePath(\n { name: 'resource.index', resource: resource.id },\n basePath,\n )}\n >\n {resource.label.plural}\n </Link>\n </li>\n ))}\n </ul>\n </nav>\n <main>\n <h1>{getTitle(route, definition)}</h1>\n <p>The Panneau 5 app shell is not built yet.</p>\n </main>\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/boot.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type BootConfig } from '@panneau/protocol';
|
|
2
|
+
/** Validates a boot config, and throws an error that lists what is invalid. */
|
|
3
|
+
export declare function parseBootConfig(input: unknown): BootConfig;
|
|
4
|
+
/**
|
|
5
|
+
* Reads the boot config that the server wrote in the page, in
|
|
6
|
+
* `<script type="application/json" id="panneau-boot">`, and validates it. Throws when the element
|
|
7
|
+
* is missing, or when its content isn't a valid boot config.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readBootConfig(target?: Document): BootConfig;
|
|
10
|
+
//# sourceMappingURL=boot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../src/boot.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,UAAU,EAAsB,MAAM,mBAAmB,CAAC;AAEhG,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAS1D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,GAAE,QAAmB,GAAG,UAAU,CAmBtE"}
|
package/dist/boot.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { BOOT_CONFIG_ELEMENT_ID, validateBootConfig } from "@panneau/protocol";
|
|
2
|
+
//#region src/boot.ts
|
|
3
|
+
/** Validates a boot config, and throws an error that lists what is invalid. */
|
|
4
|
+
function parseBootConfig(input) {
|
|
5
|
+
const result = validateBootConfig(input);
|
|
6
|
+
if (!result.success) {
|
|
7
|
+
const issues = result.issues.map(({ path, message }) => `- ${path === "" ? "(root)" : path}: ${message}`);
|
|
8
|
+
throw new Error(`The Panneau boot config is invalid:\n${issues.join("\n")}`);
|
|
9
|
+
}
|
|
10
|
+
return result.data;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reads the boot config that the server wrote in the page, in
|
|
14
|
+
* `<script type="application/json" id="panneau-boot">`, and validates it. Throws when the element
|
|
15
|
+
* is missing, or when its content isn't a valid boot config.
|
|
16
|
+
*/
|
|
17
|
+
function readBootConfig(target = document) {
|
|
18
|
+
const element = target.getElementById(BOOT_CONFIG_ELEMENT_ID);
|
|
19
|
+
if (element === null) throw new Error(`The page has no Panneau boot config: add <script type="application/json" id="${BOOT_CONFIG_ELEMENT_ID}">, or pass \`config\` to createPanneauApp().`);
|
|
20
|
+
let input;
|
|
21
|
+
try {
|
|
22
|
+
input = JSON.parse(element.textContent);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new Error(`The Panneau boot config in #${BOOT_CONFIG_ELEMENT_ID} is not valid JSON.`, { cause: error });
|
|
25
|
+
}
|
|
26
|
+
return parseBootConfig(input);
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { parseBootConfig, readBootConfig };
|
|
30
|
+
|
|
31
|
+
//# sourceMappingURL=boot.js.map
|
package/dist/boot.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot.js","names":[],"sources":["../src/boot.ts"],"sourcesContent":["import { BOOT_CONFIG_ELEMENT_ID, type BootConfig, validateBootConfig } from '@panneau/protocol';\n\n/** Validates a boot config, and throws an error that lists what is invalid. */\nexport function parseBootConfig(input: unknown): BootConfig {\n const result = validateBootConfig(input);\n if (!result.success) {\n const issues = result.issues.map(\n ({ path, message }) => `- ${path === '' ? '(root)' : path}: ${message}`,\n );\n throw new Error(`The Panneau boot config is invalid:\\n${issues.join('\\n')}`);\n }\n return result.data;\n}\n\n/**\n * Reads the boot config that the server wrote in the page, in\n * `<script type=\"application/json\" id=\"panneau-boot\">`, and validates it. Throws when the element\n * is missing, or when its content isn't a valid boot config.\n */\nexport function readBootConfig(target: Document = document): BootConfig {\n const element = target.getElementById(BOOT_CONFIG_ELEMENT_ID);\n if (element === null) {\n throw new Error(\n `The page has no Panneau boot config: add <script type=\"application/json\" id=\"${BOOT_CONFIG_ELEMENT_ID}\">, or pass \\`config\\` to createPanneauApp().`,\n );\n }\n let input: unknown;\n try {\n input = JSON.parse(element.textContent);\n } catch (error) {\n throw new Error(\n `The Panneau boot config in #${BOOT_CONFIG_ELEMENT_ID} is not valid JSON.`,\n {\n cause: error,\n },\n );\n }\n return parseBootConfig(input);\n}\n"],"mappings":";;;AAGA,SAAgB,gBAAgB,OAA4B;CACxD,MAAM,SAAS,mBAAmB,KAAK;CACvC,IAAI,CAAC,OAAO,SAAS;EACjB,MAAM,SAAS,OAAO,OAAO,KACxB,EAAE,MAAM,cAAc,KAAK,SAAS,KAAK,WAAW,KAAI,IAAK,SAClE;EACA,MAAM,IAAI,MAAM,wCAAwC,OAAO,KAAK,IAAI,GAAG;CAC/E;CACA,OAAO,OAAO;AAClB;;;;;;AAOA,SAAgB,eAAe,SAAmB,UAAsB;CACpE,MAAM,UAAU,OAAO,eAAe,sBAAsB;CAC5D,IAAI,YAAY,MACZ,MAAM,IAAI,MACN,gFAAgF,uBAAsB,8CAC1G;CAEJ,IAAI;CACJ,IAAI;EACA,QAAQ,KAAK,MAAM,QAAQ,WAAW;CAC1C,SAAS,OAAO;EACZ,MAAM,IAAI,MACN,+BAA+B,uBAAsB,sBACrD,EACI,OAAO,MACX,CACJ;CACJ;CACA,OAAO,gBAAgB,KAAK;AAChC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
import type { PanneauPlugin } from '@panneau/core';
|
|
3
|
+
import type { DataAdapter } from '@panneau/data';
|
|
4
|
+
import type { BootConfig, BootConfigInput } from '@panneau/protocol';
|
|
5
|
+
import { type UnauthorizedContext } from './Panneau';
|
|
6
|
+
/** Options of `createPanneauApp()`. */
|
|
7
|
+
export interface PanneauAppOptions {
|
|
8
|
+
/** The element the app renders in. */
|
|
9
|
+
element: Element;
|
|
10
|
+
/** Plugins: field types, pages, locales, themes… */
|
|
11
|
+
plugins?: readonly PanneauPlugin[];
|
|
12
|
+
/** The data adapter. Defaults to `createLaravelAdapter({ baseUrl: config.apiBaseUrl })`. */
|
|
13
|
+
adapter?: DataAdapter;
|
|
14
|
+
/**
|
|
15
|
+
* The boot config. Defaults to the one the server wrote in the page, in
|
|
16
|
+
* `<script type="application/json" id="panneau-boot">`.
|
|
17
|
+
*/
|
|
18
|
+
config?: BootConfigInput;
|
|
19
|
+
/** The TanStack Query client. Defaults to a new one. */
|
|
20
|
+
queryClient?: QueryClient;
|
|
21
|
+
/**
|
|
22
|
+
* Called once when the API answers that the user isn't authenticated. Defaults to sending the
|
|
23
|
+
* browser to the login URL, when there is one.
|
|
24
|
+
*/
|
|
25
|
+
onUnauthorized?: (context: UnauthorizedContext) => void;
|
|
26
|
+
}
|
|
27
|
+
/** A mounted app. */
|
|
28
|
+
export interface PanneauApp {
|
|
29
|
+
/** The boot config, validated, with defaults applied. */
|
|
30
|
+
config: BootConfig;
|
|
31
|
+
/** Unmounts the app from its element. */
|
|
32
|
+
unmount(): void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Mounts the Panneau admin panel in an element of the page. Throws when the boot config is
|
|
36
|
+
* missing or invalid.
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { createPanneauApp } from '@panneau/app';
|
|
40
|
+
*
|
|
41
|
+
* createPanneauApp({ element: document.getElementById('panneau')!, plugins: [] });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function createPanneauApp({ element, config: input, ...props }: PanneauAppOptions): PanneauApp;
|
|
45
|
+
//# sourceMappingURL=createPanneauApp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createPanneauApp.d.ts","sourceRoot":"","sources":["../src/createPanneauApp.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAErE,OAAO,EAAW,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAG9D,uCAAuC;AACvC,MAAM,WAAW,iBAAiB;IAC9B,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,oDAAoD;IACpD,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,4FAA4F;IAC5F,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,wDAAwD;IACxD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC3D;AAED,qBAAqB;AACrB,MAAM,WAAW,UAAU;IACvB,yDAAyD;IACzD,MAAM,EAAE,UAAU,CAAC;IACnB,yCAAyC;IACzC,OAAO,IAAI,IAAI,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAC7B,OAAO,EACP,MAAM,EAAE,KAAK,EACb,GAAG,KAAK,EACX,EAAE,iBAAiB,GAAG,UAAU,CAUhC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Panneau } from "./Panneau.js";
|
|
2
|
+
import { parseBootConfig, readBootConfig } from "./boot.js";
|
|
3
|
+
import { StrictMode } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/createPanneauApp.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Mounts the Panneau admin panel in an element of the page. Throws when the boot config is
|
|
9
|
+
* missing or invalid.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { createPanneauApp } from '@panneau/app';
|
|
13
|
+
*
|
|
14
|
+
* createPanneauApp({ element: document.getElementById('panneau')!, plugins: [] });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function createPanneauApp({ element, config: input, ...props }) {
|
|
18
|
+
const config = input !== void 0 ? parseBootConfig(input) : readBootConfig(element.ownerDocument);
|
|
19
|
+
const root = createRoot(element);
|
|
20
|
+
root.render(/* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(Panneau, {
|
|
21
|
+
...props,
|
|
22
|
+
config
|
|
23
|
+
}) }));
|
|
24
|
+
return {
|
|
25
|
+
config,
|
|
26
|
+
unmount: () => root.unmount()
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { createPanneauApp };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=createPanneauApp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createPanneauApp.js","names":[],"sources":["../src/createPanneauApp.tsx"],"sourcesContent":["import type { QueryClient } from '@tanstack/react-query';\nimport { StrictMode } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport type { PanneauPlugin } from '@panneau/core';\nimport type { DataAdapter } from '@panneau/data';\nimport type { BootConfig, BootConfigInput } from '@panneau/protocol';\n\nimport { Panneau, type UnauthorizedContext } from './Panneau';\nimport { parseBootConfig, readBootConfig } from './boot';\n\n/** Options of `createPanneauApp()`. */\nexport interface PanneauAppOptions {\n /** The element the app renders in. */\n element: Element;\n /** Plugins: field types, pages, locales, themes… */\n plugins?: readonly PanneauPlugin[];\n /** The data adapter. Defaults to `createLaravelAdapter({ baseUrl: config.apiBaseUrl })`. */\n adapter?: DataAdapter;\n /**\n * The boot config. Defaults to the one the server wrote in the page, in\n * `<script type=\"application/json\" id=\"panneau-boot\">`.\n */\n config?: BootConfigInput;\n /** The TanStack Query client. Defaults to a new one. */\n queryClient?: QueryClient;\n /**\n * Called once when the API answers that the user isn't authenticated. Defaults to sending the\n * browser to the login URL, when there is one.\n */\n onUnauthorized?: (context: UnauthorizedContext) => void;\n}\n\n/** A mounted app. */\nexport interface PanneauApp {\n /** The boot config, validated, with defaults applied. */\n config: BootConfig;\n /** Unmounts the app from its element. */\n unmount(): void;\n}\n\n/**\n * Mounts the Panneau admin panel in an element of the page. Throws when the boot config is\n * missing or invalid.\n *\n * ```ts\n * import { createPanneauApp } from '@panneau/app';\n *\n * createPanneauApp({ element: document.getElementById('panneau')!, plugins: [] });\n * ```\n */\nexport function createPanneauApp({\n element,\n config: input,\n ...props\n}: PanneauAppOptions): PanneauApp {\n const config =\n input !== undefined ? parseBootConfig(input) : readBootConfig(element.ownerDocument);\n const root = createRoot(element);\n root.render(\n <StrictMode>\n <Panneau {...props} config={config} />\n </StrictMode>,\n );\n return { config, unmount: () => root.unmount() };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmDA,SAAgB,iBAAiB,EAC7B,SACA,QAAQ,OACR,GAAG,SAC2B;CAC9B,MAAM,SACF,UAAU,KAAA,IAAY,gBAAgB,KAAK,IAAI,eAAe,QAAQ,aAAa;CACvF,MAAM,OAAO,WAAW,OAAO;CAC/B,KAAK,OACD,oBAAC,YAAD,EAAA,UACI,oBAAC,SAAD;EAAS,GAAI;EAAe;CAAO,CAAA,EAC3B,CAAA,CAChB;CACA,OAAO;EAAE;EAAQ,eAAe,KAAK,QAAQ;CAAE;AACnD"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,53 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface ContainerProps {
|
|
7
|
-
definition: PanneauDefinition;
|
|
8
|
-
forms?: FormDefinition[] | null;
|
|
9
|
-
fields?: FieldDefinition[] | null;
|
|
10
|
-
components?: ComponentsMap | null;
|
|
11
|
-
user?: User | null;
|
|
12
|
-
memoryRouter?: boolean;
|
|
13
|
-
baseUrl?: string | null;
|
|
14
|
-
uppy?: UppyProviderConfig | null;
|
|
15
|
-
statusCode?: StatusCode | null;
|
|
16
|
-
}
|
|
17
|
-
declare function Container({ definition, forms: providedForms, fields: providedFields, components, user, memoryRouter, baseUrl, uppy, statusCode, }: ContainerProps): react.JSX.Element;
|
|
18
|
-
|
|
19
|
-
interface MainLayoutProps {
|
|
20
|
-
children?: ReactNode;
|
|
21
|
-
loading?: boolean;
|
|
22
|
-
fullscreen?: boolean;
|
|
23
|
-
}
|
|
24
|
-
declare function MainLayout({ fullscreen, loading, children }: MainLayoutProps): react.JSX.Element;
|
|
25
|
-
|
|
26
|
-
interface GuestLayoutProps {
|
|
27
|
-
children: ReactNode;
|
|
28
|
-
fullscreen?: boolean;
|
|
29
|
-
}
|
|
30
|
-
declare function GuestLayout({ fullscreen, children }: GuestLayoutProps): react.JSX.Element;
|
|
31
|
-
|
|
32
|
-
interface PageHeaderProps {
|
|
33
|
-
title?: Label | null;
|
|
34
|
-
actions?: ReactNode;
|
|
35
|
-
small?: boolean;
|
|
36
|
-
className?: string | null;
|
|
37
|
-
children?: ReactNode;
|
|
38
|
-
}
|
|
39
|
-
declare function PageHeader({ title, actions, small, className, children, }: PageHeaderProps): react.JSX.Element;
|
|
40
|
-
|
|
41
|
-
declare function ResourceFilters(props: any): react.JSX.Element;
|
|
42
|
-
|
|
43
|
-
interface ResourceItemsListProps {
|
|
44
|
-
resource: string | {
|
|
45
|
-
id: string;
|
|
46
|
-
};
|
|
47
|
-
[key: string]: any;
|
|
48
|
-
}
|
|
49
|
-
declare function ResourceItemsList({ resource, ...props }: ResourceItemsListProps): react.JSX.Element;
|
|
50
|
-
|
|
51
|
-
declare function ResourceFormWrapper(props: any): react.JSX.Element;
|
|
52
|
-
|
|
53
|
-
export { GuestLayout, MainLayout, PageHeader, ResourceFilters, ResourceFormWrapper as ResourceForm, ResourceItemsList, Container as default };
|
|
1
|
+
export { createPanneauApp, type PanneauApp, type PanneauAppOptions } from './createPanneauApp';
|
|
2
|
+
export { Panneau, type PanneauProps, type UnauthorizedContext } from './Panneau';
|
|
3
|
+
export { readBootConfig } from './boot';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC"}
|