@panneau/app 5.0.0-alpha.4 → 5.0.0-alpha.6

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.
Files changed (103) hide show
  1. package/README.md +33 -5
  2. package/dist/Panneau.css +19 -0
  3. package/dist/Panneau.d.ts +13 -1
  4. package/dist/Panneau.d.ts.map +1 -1
  5. package/dist/Panneau.js +144 -31
  6. package/dist/Panneau.js.map +1 -1
  7. package/dist/Panneau.module.js +12 -0
  8. package/dist/Panneau.module.js.map +1 -0
  9. package/dist/Shell.d.ts +8 -3
  10. package/dist/Shell.d.ts.map +1 -1
  11. package/dist/Shell.js +272 -114
  12. package/dist/Shell.js.map +1 -1
  13. package/dist/createPanneauApp.d.ts +9 -0
  14. package/dist/createPanneauApp.d.ts.map +1 -1
  15. package/dist/createPanneauApp.js.map +1 -1
  16. package/dist/cx.d.ts +3 -0
  17. package/dist/cx.d.ts.map +1 -0
  18. package/dist/cx.js +9 -0
  19. package/dist/cx.js.map +1 -0
  20. package/dist/documentTitle.d.ts +10 -0
  21. package/dist/documentTitle.d.ts.map +1 -0
  22. package/dist/documentTitle.js +49 -0
  23. package/dist/documentTitle.js.map +1 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +2 -1
  27. package/dist/location.d.ts +13 -1
  28. package/dist/location.d.ts.map +1 -1
  29. package/dist/location.js +55 -48
  30. package/dist/location.js.map +1 -1
  31. package/dist/navigation.d.ts +53 -0
  32. package/dist/navigation.d.ts.map +1 -0
  33. package/dist/navigation.js +117 -0
  34. package/dist/navigation.js.map +1 -0
  35. package/dist/pages/HomePage.d.ts +11 -0
  36. package/dist/pages/HomePage.d.ts.map +1 -0
  37. package/dist/pages/HomePage.js +111 -0
  38. package/dist/pages/HomePage.js.map +1 -0
  39. package/dist/pages/ItemPanel.css +164 -0
  40. package/dist/pages/ItemPanel.d.ts +17 -0
  41. package/dist/pages/ItemPanel.d.ts.map +1 -0
  42. package/dist/pages/ItemPanel.js +370 -0
  43. package/dist/pages/ItemPanel.js.map +1 -0
  44. package/dist/pages/ItemPanel.module.js +34 -0
  45. package/dist/pages/ItemPanel.module.js.map +1 -0
  46. package/dist/pages/ItemPreview.d.ts +15 -0
  47. package/dist/pages/ItemPreview.d.ts.map +1 -0
  48. package/dist/pages/ItemPreview.js +179 -0
  49. package/dist/pages/ItemPreview.js.map +1 -0
  50. package/dist/pages/NotFoundPage.d.ts +6 -0
  51. package/dist/pages/NotFoundPage.d.ts.map +1 -0
  52. package/dist/pages/NotFoundPage.js +93 -0
  53. package/dist/pages/NotFoundPage.js.map +1 -0
  54. package/dist/pages/PageHeader.d.ts +21 -0
  55. package/dist/pages/PageHeader.d.ts.map +1 -0
  56. package/dist/pages/PageHeader.js +86 -0
  57. package/dist/pages/PageHeader.js.map +1 -0
  58. package/dist/pages/ResourceFormPage.d.ts +15 -0
  59. package/dist/pages/ResourceFormPage.d.ts.map +1 -0
  60. package/dist/pages/ResourceFormPage.js +204 -0
  61. package/dist/pages/ResourceFormPage.js.map +1 -0
  62. package/dist/pages/ResourceListPage.d.ts +15 -0
  63. package/dist/pages/ResourceListPage.d.ts.map +1 -0
  64. package/dist/pages/ResourceListPage.js +101 -0
  65. package/dist/pages/ResourceListPage.js.map +1 -0
  66. package/dist/pages/itemTitle.d.ts +7 -0
  67. package/dist/pages/itemTitle.d.ts.map +1 -0
  68. package/dist/pages/itemTitle.js +18 -0
  69. package/dist/pages/itemTitle.js.map +1 -0
  70. package/dist/pages/pages.css +162 -0
  71. package/dist/pages/pages.module.js +40 -0
  72. package/dist/pages/pages.module.js.map +1 -0
  73. package/dist/preferences.d.ts +6 -0
  74. package/dist/preferences.d.ts.map +1 -0
  75. package/dist/preferences.js +53 -0
  76. package/dist/preferences.js.map +1 -0
  77. package/dist/recentItems.d.ts +15 -0
  78. package/dist/recentItems.d.ts.map +1 -0
  79. package/dist/recentItems.js +70 -0
  80. package/dist/recentItems.js.map +1 -0
  81. package/dist/shell/AppLayout.css +189 -0
  82. package/dist/shell/AppLayout.d.ts +22 -0
  83. package/dist/shell/AppLayout.d.ts.map +1 -0
  84. package/dist/shell/AppLayout.js +365 -0
  85. package/dist/shell/AppLayout.js.map +1 -0
  86. package/dist/shell/AppLayout.module.js +36 -0
  87. package/dist/shell/AppLayout.module.js.map +1 -0
  88. package/dist/shell/GoTo.d.ts +20 -0
  89. package/dist/shell/GoTo.d.ts.map +1 -0
  90. package/dist/shell/GoTo.js +370 -0
  91. package/dist/shell/GoTo.js.map +1 -0
  92. package/dist/shell/Sidebar.css +313 -0
  93. package/dist/shell/Sidebar.d.ts +29 -0
  94. package/dist/shell/Sidebar.d.ts.map +1 -0
  95. package/dist/shell/Sidebar.js +225 -0
  96. package/dist/shell/Sidebar.js.map +1 -0
  97. package/dist/shell/Sidebar.module.js +60 -0
  98. package/dist/shell/Sidebar.module.js.map +1 -0
  99. package/dist/shell/tokens.d.ts +23 -0
  100. package/dist/shell/tokens.d.ts.map +1 -0
  101. package/dist/shell/tokens.js +75 -0
  102. package/dist/shell/tokens.js.map +1 -0
  103. package/package.json +15 -6
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # @panneau/app
2
2
 
3
- The Panneau admin panel: the app entry (`createPanneauApp`), and the shell, routes and pages to come.
3
+ The Panneau admin panel: the app entry (`createPanneauApp`), the shell with its sidebar, and the pages of resources.
4
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).
5
+ The shell is a first version, for Phase 2 of the [roadmap](../../docs/roadmap.md): a sidebar from the `main` menu of the definition, collapsible to a rail of icons (Ctrl+\\ or ⌘+\\), the page on a raised panel, a drawer on small screens, and the home, list, create, edit and plugin pages, built with `@panneau/lists`, `@panneau/forms` and `@panneau/actions`. The show route opens the item's details in a panel next to its list, with its preview when its resource has one: a component of a plugin (`previews`) or its page on the site in an iframe ([decisions/0028](../../docs/decisions/0028-item-panel.md)). **Go to…** (⌘K or Ctrl+K, or its button in the sidebar) opens a command palette: recent items, navigation, "New {resource}", the `commands` of plugins, the app's actions, and the results of the server's search, grouped by resource; an item opens its form ([decisions/0029](../../docs/decisions/0029-go-to-palette.md)). "Search in" narrows it to one resource, until Backspace in the empty field ([decisions/0030](../../docs/decisions/0030-search-parameters-and-scope.md)). Leaving a form with unsaved changes asks first, by a link or by going back or forward. The tab is named after the page, and focus moves to the title of each new page, for screen readers. The entry contract below is stable: see [decisions/0019](../../docs/decisions/0019-app-entry.md).
6
+
7
+ Try it with `npm run playground` at the root of the repository: the whole app on the mock backend of `@panneau/protocol` (`?theme=flat` for a flat frame).
6
8
 
7
9
  ```ts
8
10
  // resources/js/panneau.tsx
@@ -20,9 +22,9 @@ The page writes the boot config next to the root element ([protocol.md](../../do
20
22
  </script>
21
23
  ```
22
24
 
23
- | Export | Content |
24
- | -------------- | -------------------------------------------------------------------------------------------------- |
25
- | `@panneau/app` | `createPanneauApp()`, the `<Panneau>` component, `readBootConfig()` and the types of their options |
25
+ | Export | Content |
26
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
27
+ | `@panneau/app` | `createPanneauApp()`, the `<Panneau>` component, `readBootConfig()` and the types of their options, the component tokens of the shell |
26
28
 
27
29
  ## Setup
28
30
 
@@ -33,7 +35,33 @@ npm install --save-dev vite @vitejs/plugin-react
33
35
 
34
36
  - Add `@vitejs/plugin-react` to the Vite config, and the entry to its inputs.
35
37
  - The entry imports no CSS and no theme: packages import their own styles, and themes come from plugins.
38
+ - Named icons (`"icon": "users"` in the definition) are Lucide icons, each loaded when shown. Pass `iconLoader` to load fewer, or `null` for none.
36
39
  - Without `adapter`, the app uses the Laravel preset of the REST adapter (`@panneau/data/laravel`) on `apiBaseUrl`.
40
+ - `location` fields place points on OpenStreetMap's tiles, without an address search. Pass `maps` for other tiles or a search: `maps: { geocoding: createNominatimSource() }`, from `@panneau/fields`.
37
41
  - 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
42
 
43
+ ## Theming the shell
44
+
45
+ The frame reads component tokens ([decisions/0024](../../docs/decisions/0024-theme-model.md)), which a theme sets like those of `@panneau/ui` once `@panneau/app` is imported:
46
+
47
+ | Tokens | What they style |
48
+ | ------------------------ | ------------------------------------------------------------------------------------------------ |
49
+ | `--panneau-shell-*` | The window's background (`bg`), the inset of the page (`inset`) and its panel (`panel-*`) |
50
+ | `--panneau-item-panel-*` | The widths (normal and widened), background, radius and shadow of the panel of an item's details |
51
+ | `--panneau-sidebar-*` | The width (expanded and collapsed), background and border of the sidebar, its links and its logo |
52
+
53
+ ```ts
54
+ import { defineTheme } from '@panneau/ui';
55
+
56
+ // A flat frame: the sidebar on a surface, and the page flush against it.
57
+ export const flatTheme = defineTheme({
58
+ tokens: {
59
+ 'panneau-shell-inset': '0',
60
+ 'panneau-shell-panel-radius': '0',
61
+ 'panneau-sidebar-bg': 'var(--panneau-color-surface)',
62
+ 'panneau-sidebar-border': 'var(--panneau-border-width) solid var(--panneau-color-border)',
63
+ },
64
+ });
65
+ ```
66
+
39
67
  The package layout, scripts and API reports are explained in [`docs/packages.md`](../../docs/packages.md).
@@ -0,0 +1,19 @@
1
+ .panneau-panneau-root {
2
+ min-height: 100dvh;
3
+ }
4
+
5
+ /* Loading, a definition that failed to load, and an ended session: centered in the window. */
6
+ .panneau-panneau-state {
7
+ display: flex;
8
+ flex-direction: column;
9
+ align-items: center;
10
+ justify-content: center;
11
+ gap: var(--panneau-space-3);
12
+ min-height: 100dvh;
13
+ padding: var(--panneau-space-4);
14
+ text-align: center;
15
+ }
16
+
17
+ .panneau-panneau-state p {
18
+ margin: 0;
19
+ }
package/dist/Panneau.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { QueryClient } from '@tanstack/react-query';
2
2
  import { type PanneauPlugin } from '@panneau/core';
3
3
  import { type DataAdapter } from '@panneau/data';
4
+ import { type MapsConfig } from '@panneau/fields';
4
5
  import type { BootConfig } from '@panneau/protocol';
6
+ import { type NamedIconLoader } from '@panneau/ui';
5
7
  /** What `onUnauthorized` receives. */
6
8
  export interface UnauthorizedContext {
7
9
  /**
@@ -21,12 +23,22 @@ export interface PanneauProps {
21
23
  plugins?: readonly PanneauPlugin[];
22
24
  /** The TanStack Query client. Defaults to a new one. */
23
25
  queryClient?: QueryClient;
26
+ /**
27
+ * Loads the Lucide icons that the definition names (`"icon": "users"`). Defaults to every
28
+ * Lucide icon, each one in its own chunk, loaded when shown. `null` shows no named icons.
29
+ */
30
+ iconLoader?: NamedIconLoader | null;
24
31
  /**
25
32
  * Called once when the API answers that the user isn't authenticated (`401`, or `419` with the
26
33
  * Laravel adapter). The app then shows that the session ended. Defaults to sending the browser
27
34
  * to `loginUrl` when there is one.
28
35
  */
29
36
  onUnauthorized?: (context: UnauthorizedContext) => void;
37
+ /**
38
+ * The tiles and the address search of the maps of `location` fields. Defaults to
39
+ * OpenStreetMap's tiles, without an address search (decision 0033).
40
+ */
41
+ maps?: MapsConfig | null;
30
42
  }
31
43
  /**
32
44
  * The Panneau admin panel, for a React app that renders it itself. `createPanneauApp()` mounts it
@@ -35,5 +47,5 @@ export interface PanneauProps {
35
47
  * It uses the definition inlined in the boot config, or fetches it, then renders the shell. When
36
48
  * the API answers that the user isn't authenticated, it calls `onUnauthorized`.
37
49
  */
38
- export declare function Panneau({ config, adapter, plugins, queryClient, onUnauthorized, }: PanneauProps): import("react").JSX.Element;
50
+ export declare function Panneau({ config, adapter, plugins, queryClient, iconLoader, onUnauthorized, maps, }: PanneauProps): import("react").JSX.Element;
39
51
  //# sourceMappingURL=Panneau.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"Panneau.d.ts","sourceRoot":"","sources":["../src/Panneau.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiC,MAAM,uBAAuB,CAAC;AAGnF,OAAO,EACH,KAAK,aAAa,EAKrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,KAAK,WAAW,EAAc,MAAM,eAAe,CAAC;AAG7D,OAAO,EAAkB,KAAK,UAAU,EAAqB,MAAM,iBAAiB,CAAC;AAErF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAU,KAAK,eAAe,EAAwC,MAAM,aAAa,CAAC;AASjG,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;;;OAGG;IACH,UAAU,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACpC;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACxD;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CAC5B;AAsHD;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,EACpB,MAAM,EACN,OAAO,EACP,OAAY,EACZ,WAAW,EACX,UAA6B,EAC7B,cAAgC,EAChC,IAAW,GACd,EAAE,YAAY,+BA2Bd"}
package/dist/Panneau.js CHANGED
@@ -1,11 +1,19 @@
1
+ import Panneau_module_default from "./Panneau.module.js";
2
+ import { usePreference } from "./preferences.js";
3
+ import { COLOR_SCHEMES } from "./shell/Sidebar.js";
1
4
  import { Shell } from "./Shell.js";
2
5
  import { createSessionState, getLoginUrl, watchUnauthorized } from "./unauthorized.js";
3
6
  import { useEffect, useState, useSyncExternalStore } from "react";
7
+ import { c } from "react/compiler-runtime";
4
8
  import { QueryClient, QueryClientProvider, useQuery } from "@tanstack/react-query";
5
- import { PanneauProvider, createHistoryRouter } from "@panneau/core";
9
+ import { PanneauProvider, createHistoryRouter, createPluginRegistry, resolveTheme } from "@panneau/core";
6
10
  import { isApiError } from "@panneau/data";
7
11
  import { createLaravelAdapter } from "@panneau/data/laravel";
8
12
  import { DataAdapterProvider, definitionQueryOptions } from "@panneau/data/query";
13
+ import { FieldsProvider, createEmbedSource } from "@panneau/fields";
14
+ import { appMessages } from "@panneau/intl";
15
+ import { Button, PanneauRoot, Spinner, usePanneauIntl } from "@panneau/ui";
16
+ import { lucideIconLoader } from "@panneau/ui/named-icons";
9
17
  import { jsx, jsxs } from "react/jsx-runtime";
10
18
  //#region src/Panneau.tsx
11
19
  function redirectToLogin({ loginUrl }) {
@@ -14,47 +22,150 @@ function redirectToLogin({ loginUrl }) {
14
22
  function createQueryClient() {
15
23
  return new QueryClient({ defaultOptions: { queries: { retry: (failureCount, error) => !(isApiError(error) && error.status < 500) && failureCount < 3 } } });
16
24
  }
17
- function Content({ config, adapter, plugins, session, onUnauthorized }) {
25
+ function Content({ config, adapter, plugins, iconLoader, session, onUnauthorized, maps }) {
18
26
  const [router] = useState(createHistoryRouter);
19
27
  const definitionQuery = useQuery({
20
28
  ...definitionQueryOptions(adapter),
21
29
  initialData: config.definition
22
30
  });
23
31
  const signedOut = useSyncExternalStore(session.subscribe, session.isSignedOut);
24
- const loginUrl = getLoginUrl(definitionQuery.data, config);
32
+ const [colorScheme, setColorScheme] = usePreference("color-scheme", COLOR_SCHEMES);
33
+ const definition = definitionQuery.data;
34
+ const loginUrl = getLoginUrl(definition, config);
25
35
  useEffect(() => {
26
36
  if (signedOut) onUnauthorized({ loginUrl });
27
37
  }, [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]
38
+ const scheme = colorScheme ?? definition?.theme?.colorScheme ?? "light";
39
+ return /* @__PURE__ */ jsx(PanneauRoot, {
40
+ theme: definition !== void 0 ? resolveTheme(definition.theme, createPluginRegistry(plugins).themes) : null,
41
+ colorScheme: scheme,
42
+ locale: definition?.locale ?? config.locale,
43
+ iconLoader,
44
+ className: Panneau_module_default.root,
45
+ children: signedOut ? /* @__PURE__ */ jsx(SessionEnded, { loginUrl }) : definition === void 0 ? /* @__PURE__ */ jsx(Loading, {
46
+ isError: definitionQuery.isError,
47
+ onRetry: () => void definitionQuery.refetch()
48
+ }) : /* @__PURE__ */ jsx(PanneauProvider, {
49
+ plugins,
50
+ definition,
51
+ router,
52
+ children: /* @__PURE__ */ jsx(FieldsProvider, {
53
+ embedSource: createEmbedSource(adapter),
54
+ maps,
55
+ children: /* @__PURE__ */ jsx(Shell, {
56
+ definition,
57
+ basePath: config.basePath,
58
+ colorScheme: scheme,
59
+ onColorSchemeChange: setColorScheme
60
+ })
61
+ })
62
+ })
34
63
  });
35
- if (definitionQuery.data === void 0) {
36
- if (definitionQuery.isError) return /* @__PURE__ */ jsxs("div", {
64
+ }
65
+ function SessionEnded(t0) {
66
+ const $ = c(10);
67
+ const { loginUrl } = t0;
68
+ const intl = usePanneauIntl();
69
+ let t1;
70
+ if ($[0] !== intl) {
71
+ t1 = intl.formatMessage(appMessages.sessionEnded);
72
+ $[0] = intl;
73
+ $[1] = t1;
74
+ } else t1 = $[1];
75
+ let t2;
76
+ if ($[2] !== t1) {
77
+ t2 = /* @__PURE__ */ jsx("p", { children: t1 });
78
+ $[2] = t1;
79
+ $[3] = t2;
80
+ } else t2 = $[3];
81
+ let t3;
82
+ if ($[4] !== intl || $[5] !== loginUrl) {
83
+ t3 = loginUrl !== null ? /* @__PURE__ */ jsx("a", {
84
+ href: loginUrl,
85
+ children: intl.formatMessage(appMessages.logIn)
86
+ }) : null;
87
+ $[4] = intl;
88
+ $[5] = loginUrl;
89
+ $[6] = t3;
90
+ } else t3 = $[6];
91
+ let t4;
92
+ if ($[7] !== t2 || $[8] !== t3) {
93
+ t4 = /* @__PURE__ */ jsxs("div", {
37
94
  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…"
95
+ className: Panneau_module_default.state,
96
+ children: [t2, t3]
47
97
  });
98
+ $[7] = t2;
99
+ $[8] = t3;
100
+ $[9] = t4;
101
+ } else t4 = $[9];
102
+ return t4;
103
+ }
104
+ function Loading(t0) {
105
+ const $ = c(16);
106
+ const { isError, onRetry } = t0;
107
+ const intl = usePanneauIntl();
108
+ if (isError) {
109
+ let t1;
110
+ if ($[0] !== intl) {
111
+ t1 = intl.formatMessage(appMessages.loadError);
112
+ $[0] = intl;
113
+ $[1] = t1;
114
+ } else t1 = $[1];
115
+ let t2;
116
+ if ($[2] !== t1) {
117
+ t2 = /* @__PURE__ */ jsx("p", { children: t1 });
118
+ $[2] = t1;
119
+ $[3] = t2;
120
+ } else t2 = $[3];
121
+ let t3;
122
+ if ($[4] !== intl) {
123
+ t3 = intl.formatMessage(appMessages.retry);
124
+ $[4] = intl;
125
+ $[5] = t3;
126
+ } else t3 = $[5];
127
+ let t4;
128
+ if ($[6] !== onRetry || $[7] !== t3) {
129
+ t4 = /* @__PURE__ */ jsx(Button, {
130
+ onPress: onRetry,
131
+ children: t3
132
+ });
133
+ $[6] = onRetry;
134
+ $[7] = t3;
135
+ $[8] = t4;
136
+ } else t4 = $[8];
137
+ let t5;
138
+ if ($[9] !== t2 || $[10] !== t4) {
139
+ t5 = /* @__PURE__ */ jsxs("div", {
140
+ role: "alert",
141
+ className: Panneau_module_default.state,
142
+ children: [t2, t4]
143
+ });
144
+ $[9] = t2;
145
+ $[10] = t4;
146
+ $[11] = t5;
147
+ } else t5 = $[11];
148
+ return t5;
48
149
  }
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
- });
150
+ let t1;
151
+ if ($[12] !== intl) {
152
+ t1 = intl.formatMessage(appMessages.loading);
153
+ $[12] = intl;
154
+ $[13] = t1;
155
+ } else t1 = $[13];
156
+ let t2;
157
+ if ($[14] !== t1) {
158
+ t2 = /* @__PURE__ */ jsx("div", {
159
+ className: Panneau_module_default.state,
160
+ children: /* @__PURE__ */ jsx(Spinner, {
161
+ showLabel: true,
162
+ label: t1
163
+ })
164
+ });
165
+ $[14] = t1;
166
+ $[15] = t2;
167
+ } else t2 = $[15];
168
+ return t2;
58
169
  }
59
170
  /**
60
171
  * The Panneau admin panel, for a React app that renders it itself. `createPanneauApp()` mounts it
@@ -63,7 +174,7 @@ function Content({ config, adapter, plugins, session, onUnauthorized }) {
63
174
  * It uses the definition inlined in the boot config, or fetches it, then renders the shell. When
64
175
  * the API answers that the user isn't authenticated, it calls `onUnauthorized`.
65
176
  */
66
- function Panneau({ config, adapter, plugins = [], queryClient, onUnauthorized = redirectToLogin }) {
177
+ function Panneau({ config, adapter, plugins = [], queryClient, iconLoader = lucideIconLoader, onUnauthorized = redirectToLogin, maps = null }) {
67
178
  const [state] = useState(() => {
68
179
  const session = createSessionState();
69
180
  return {
@@ -80,8 +191,10 @@ function Panneau({ config, adapter, plugins = [], queryClient, onUnauthorized =
80
191
  config,
81
192
  adapter: state.adapter,
82
193
  plugins,
194
+ iconLoader,
83
195
  session: state.session,
84
- onUnauthorized
196
+ onUnauthorized,
197
+ maps
85
198
  })
86
199
  })
87
200
  });
@@ -1 +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"}
1
+ {"version":3,"file":"Panneau.js","names":["loginUrl","onRetry","styles","_c"],"sources":["../src/Panneau.tsx"],"sourcesContent":["import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';\nimport { useEffect, useState, useSyncExternalStore } from 'react';\n\nimport {\n type PanneauPlugin,\n PanneauProvider,\n createHistoryRouter,\n createPluginRegistry,\n resolveTheme,\n} from '@panneau/core';\nimport { type DataAdapter, isApiError } from '@panneau/data';\nimport { createLaravelAdapter } from '@panneau/data/laravel';\nimport { DataAdapterProvider, definitionQueryOptions } from '@panneau/data/query';\nimport { FieldsProvider, type MapsConfig, createEmbedSource } from '@panneau/fields';\nimport { appMessages } from '@panneau/intl';\nimport type { BootConfig } from '@panneau/protocol';\nimport { Button, type NamedIconLoader, PanneauRoot, Spinner, usePanneauIntl } from '@panneau/ui';\nimport { lucideIconLoader } from '@panneau/ui/named-icons';\n\nimport styles from './Panneau.module.css';\nimport { Shell } from './Shell';\nimport { usePreference } from './preferences';\nimport { COLOR_SCHEMES } from './shell/Sidebar';\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 * Loads the Lucide icons that the definition names (`\"icon\": \"users\"`). Defaults to every\n * Lucide icon, each one in its own chunk, loaded when shown. `null` shows no named icons.\n */\n iconLoader?: NamedIconLoader | null;\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 * The tiles and the address search of the maps of `location` fields. Defaults to\n * OpenStreetMap's tiles, without an address search (decision 0033).\n */\n maps?: MapsConfig | null;\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 iconLoader,\n session,\n onUnauthorized,\n maps,\n}: Required<Pick<PanneauProps, 'config' | 'adapter' | 'onUnauthorized'>> & {\n plugins: readonly PanneauPlugin[];\n iconLoader: NamedIconLoader | null;\n maps: MapsConfig | null;\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 [colorScheme, setColorScheme] = usePreference('color-scheme', COLOR_SCHEMES);\n const definition = definitionQuery.data;\n const loginUrl = getLoginUrl(definition, 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 const scheme = colorScheme ?? definition?.theme?.colorScheme ?? 'light';\n return (\n <PanneauRoot\n theme={\n definition !== undefined\n ? resolveTheme(definition.theme, createPluginRegistry(plugins).themes)\n : null\n }\n colorScheme={scheme}\n locale={definition?.locale ?? config.locale}\n iconLoader={iconLoader}\n className={styles.root}\n >\n {signedOut ? (\n <SessionEnded loginUrl={loginUrl} />\n ) : definition === undefined ? (\n <Loading\n isError={definitionQuery.isError}\n onRetry={() => void definitionQuery.refetch()}\n />\n ) : (\n <PanneauProvider plugins={plugins} definition={definition} router={router}>\n {/* What `embed` and `location` fields need, in every form */}\n <FieldsProvider embedSource={createEmbedSource(adapter)} maps={maps}>\n <Shell\n definition={definition}\n basePath={config.basePath}\n colorScheme={scheme}\n onColorSchemeChange={setColorScheme}\n />\n </FieldsProvider>\n </PanneauProvider>\n )}\n </PanneauRoot>\n );\n}\n\nfunction SessionEnded({ loginUrl }: { loginUrl: string | null }) {\n const intl = usePanneauIntl();\n return (\n <div role=\"alert\" className={styles.state}>\n <p>{intl.formatMessage(appMessages.sessionEnded)}</p>\n {loginUrl !== null ? (\n <a href={loginUrl}>{intl.formatMessage(appMessages.logIn)}</a>\n ) : null}\n </div>\n );\n}\n\nfunction Loading({ isError, onRetry }: { isError: boolean; onRetry: () => void }) {\n const intl = usePanneauIntl();\n if (isError) {\n return (\n <div role=\"alert\" className={styles.state}>\n <p>{intl.formatMessage(appMessages.loadError)}</p>\n <Button onPress={onRetry}>{intl.formatMessage(appMessages.retry)}</Button>\n </div>\n );\n }\n return (\n <div className={styles.state}>\n <Spinner showLabel label={intl.formatMessage(appMessages.loading)} />\n </div>\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 iconLoader = lucideIconLoader,\n onUnauthorized = redirectToLogin,\n maps = null,\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 iconLoader={iconLoader}\n session={state.session}\n onUnauthorized={onUnauthorized}\n maps={maps}\n />\n </DataAdapterProvider>\n </QueryClientProvider>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import "./Panneau.css";
2
+ //#region src/Panneau.module.css
3
+ var root = "panneau-panneau-root";
4
+ var state = "panneau-panneau-state";
5
+ var Panneau_module_default = {
6
+ root,
7
+ state
8
+ };
9
+ //#endregion
10
+ export { Panneau_module_default as default, root, state };
11
+
12
+ //# sourceMappingURL=Panneau.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Panneau.module.js","names":[],"sources":["../src/Panneau.module.css"],"sourcesContent":[".root {\n min-height: 100dvh;\n}\n\n/* Loading, a definition that failed to load, and an ended session: centered in the window. */\n.state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--panneau-space-3);\n min-height: 100dvh;\n padding: var(--panneau-space-4);\n text-align: center;\n}\n\n.state p {\n margin: 0;\n}\n"],"mappings":""}
package/dist/Shell.d.ts CHANGED
@@ -1,11 +1,16 @@
1
1
  import type { Definition } from '@panneau/protocol';
2
+ import type { ColorSchemePreference } from './shell/Sidebar';
2
3
  export interface ShellProps {
3
4
  definition: Definition;
4
5
  basePath: string;
6
+ colorScheme: ColorSchemePreference;
7
+ onColorSchemeChange: (scheme: ColorSchemePreference) => void;
5
8
  }
6
9
  /**
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.
10
+ * The app shell: the layout with its sidebar, and the page of the current route. Links go
11
+ * through the history of the browser without reloading the page, and actions run through the
12
+ * adapter, refreshing what their result changed. Leaving a page with unsaved changes, by a link
13
+ * or by going back or forward, asks first.
9
14
  */
10
- export declare function Shell({ definition, basePath }: ShellProps): import("react").JSX.Element;
15
+ export declare function Shell({ definition, basePath, colorScheme, onColorSchemeChange }: ShellProps): import("react").JSX.Element;
11
16
  //# sourceMappingURL=Shell.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"Shell.d.ts","sourceRoot":"","sources":["../src/Shell.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAkB,MAAM,mBAAmB,CAAC;AAWpE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE7D,MAAM,WAAW,UAAU;IACvB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,qBAAqB,CAAC;IACnC,mBAAmB,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAChE;AAuFD;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,EAAE,UAAU,+BAmD3F"}