@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.
- package/README.md +33 -5
- package/dist/Panneau.css +19 -0
- package/dist/Panneau.d.ts +13 -1
- package/dist/Panneau.d.ts.map +1 -1
- package/dist/Panneau.js +144 -31
- package/dist/Panneau.js.map +1 -1
- package/dist/Panneau.module.js +12 -0
- package/dist/Panneau.module.js.map +1 -0
- package/dist/Shell.d.ts +8 -3
- package/dist/Shell.d.ts.map +1 -1
- package/dist/Shell.js +272 -114
- package/dist/Shell.js.map +1 -1
- package/dist/createPanneauApp.d.ts +9 -0
- package/dist/createPanneauApp.d.ts.map +1 -1
- package/dist/createPanneauApp.js.map +1 -1
- package/dist/cx.d.ts +3 -0
- package/dist/cx.d.ts.map +1 -0
- package/dist/cx.js +9 -0
- package/dist/cx.js.map +1 -0
- package/dist/documentTitle.d.ts +10 -0
- package/dist/documentTitle.d.ts.map +1 -0
- package/dist/documentTitle.js +49 -0
- package/dist/documentTitle.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/location.d.ts +13 -1
- package/dist/location.d.ts.map +1 -1
- package/dist/location.js +55 -48
- package/dist/location.js.map +1 -1
- package/dist/navigation.d.ts +53 -0
- package/dist/navigation.d.ts.map +1 -0
- package/dist/navigation.js +117 -0
- package/dist/navigation.js.map +1 -0
- package/dist/pages/HomePage.d.ts +11 -0
- package/dist/pages/HomePage.d.ts.map +1 -0
- package/dist/pages/HomePage.js +111 -0
- package/dist/pages/HomePage.js.map +1 -0
- package/dist/pages/ItemPanel.css +164 -0
- package/dist/pages/ItemPanel.d.ts +17 -0
- package/dist/pages/ItemPanel.d.ts.map +1 -0
- package/dist/pages/ItemPanel.js +370 -0
- package/dist/pages/ItemPanel.js.map +1 -0
- package/dist/pages/ItemPanel.module.js +34 -0
- package/dist/pages/ItemPanel.module.js.map +1 -0
- package/dist/pages/ItemPreview.d.ts +15 -0
- package/dist/pages/ItemPreview.d.ts.map +1 -0
- package/dist/pages/ItemPreview.js +179 -0
- package/dist/pages/ItemPreview.js.map +1 -0
- package/dist/pages/NotFoundPage.d.ts +6 -0
- package/dist/pages/NotFoundPage.d.ts.map +1 -0
- package/dist/pages/NotFoundPage.js +93 -0
- package/dist/pages/NotFoundPage.js.map +1 -0
- package/dist/pages/PageHeader.d.ts +21 -0
- package/dist/pages/PageHeader.d.ts.map +1 -0
- package/dist/pages/PageHeader.js +86 -0
- package/dist/pages/PageHeader.js.map +1 -0
- package/dist/pages/ResourceFormPage.d.ts +15 -0
- package/dist/pages/ResourceFormPage.d.ts.map +1 -0
- package/dist/pages/ResourceFormPage.js +204 -0
- package/dist/pages/ResourceFormPage.js.map +1 -0
- package/dist/pages/ResourceListPage.d.ts +15 -0
- package/dist/pages/ResourceListPage.d.ts.map +1 -0
- package/dist/pages/ResourceListPage.js +101 -0
- package/dist/pages/ResourceListPage.js.map +1 -0
- package/dist/pages/itemTitle.d.ts +7 -0
- package/dist/pages/itemTitle.d.ts.map +1 -0
- package/dist/pages/itemTitle.js +18 -0
- package/dist/pages/itemTitle.js.map +1 -0
- package/dist/pages/pages.css +162 -0
- package/dist/pages/pages.module.js +40 -0
- package/dist/pages/pages.module.js.map +1 -0
- package/dist/preferences.d.ts +6 -0
- package/dist/preferences.d.ts.map +1 -0
- package/dist/preferences.js +53 -0
- package/dist/preferences.js.map +1 -0
- package/dist/recentItems.d.ts +15 -0
- package/dist/recentItems.d.ts.map +1 -0
- package/dist/recentItems.js +70 -0
- package/dist/recentItems.js.map +1 -0
- package/dist/shell/AppLayout.css +189 -0
- package/dist/shell/AppLayout.d.ts +22 -0
- package/dist/shell/AppLayout.d.ts.map +1 -0
- package/dist/shell/AppLayout.js +365 -0
- package/dist/shell/AppLayout.js.map +1 -0
- package/dist/shell/AppLayout.module.js +36 -0
- package/dist/shell/AppLayout.module.js.map +1 -0
- package/dist/shell/GoTo.d.ts +20 -0
- package/dist/shell/GoTo.d.ts.map +1 -0
- package/dist/shell/GoTo.js +370 -0
- package/dist/shell/GoTo.js.map +1 -0
- package/dist/shell/Sidebar.css +313 -0
- package/dist/shell/Sidebar.d.ts +29 -0
- package/dist/shell/Sidebar.d.ts.map +1 -0
- package/dist/shell/Sidebar.js +225 -0
- package/dist/shell/Sidebar.js.map +1 -0
- package/dist/shell/Sidebar.module.js +60 -0
- package/dist/shell/Sidebar.module.js.map +1 -0
- package/dist/shell/tokens.d.ts +23 -0
- package/dist/shell/tokens.d.ts.map +1 -0
- package/dist/shell/tokens.js +75 -0
- package/dist/shell/tokens.js.map +1 -0
- package/package.json +15 -6
package/dist/location.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location.d.ts","sourceRoot":"","sources":["../src/location.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAyC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"location.d.ts","sourceRoot":"","sources":["../src/location.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAyC,MAAM,OAAO,CAAC;AASnF,6EAA6E;AAC7E,MAAM,WAAW,YAAY;IACzB,8DAA8D;IAC9D,UAAU,IAAI,OAAO,CAAC;IACtB,iDAAiD;IACjD,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACpC;AA+DD,uEAAuE;AACvE,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,IAAI,CAO9D;AAED,6FAA6F;AAC7F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,QAIpC;AAED,qFAAqF;AACrF,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,+BAkBvF"}
|
package/dist/location.js
CHANGED
|
@@ -1,69 +1,76 @@
|
|
|
1
1
|
import { useSyncExternalStore } from "react";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import "react/compiler-runtime";
|
|
3
|
+
import "react/jsx-runtime";
|
|
4
4
|
//#region src/location.tsx
|
|
5
|
+
var INDEX_KEY = "panneauIndex";
|
|
5
6
|
var listeners = /* @__PURE__ */ new Set();
|
|
7
|
+
var guard = null;
|
|
8
|
+
var current = 0;
|
|
9
|
+
var confirmed = false;
|
|
10
|
+
function readIndex(state) {
|
|
11
|
+
const value = state !== null && typeof state === "object" ? state[INDEX_KEY] : void 0;
|
|
12
|
+
return typeof value === "number" ? value : null;
|
|
13
|
+
}
|
|
14
|
+
function notify() {
|
|
15
|
+
listeners.forEach((listener) => listener());
|
|
16
|
+
}
|
|
17
|
+
function onPopState(event) {
|
|
18
|
+
const index = readIndex(event.state);
|
|
19
|
+
if (index !== null && index !== current && !confirmed && guard?.isBlocking() === true) {
|
|
20
|
+
const delta = index - current;
|
|
21
|
+
window.history.go(-delta);
|
|
22
|
+
guard.confirmLeave().then((leave) => {
|
|
23
|
+
if (leave) {
|
|
24
|
+
confirmed = true;
|
|
25
|
+
window.history.go(delta);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
confirmed = false;
|
|
31
|
+
if (index !== null) current = index;
|
|
32
|
+
notify();
|
|
33
|
+
}
|
|
6
34
|
function subscribe(listener) {
|
|
35
|
+
if (listeners.size === 0) {
|
|
36
|
+
const index = readIndex(window.history.state);
|
|
37
|
+
if (index === null) window.history.replaceState({
|
|
38
|
+
...window.history.state,
|
|
39
|
+
[INDEX_KEY]: current
|
|
40
|
+
}, "");
|
|
41
|
+
else current = index;
|
|
42
|
+
window.addEventListener("popstate", onPopState);
|
|
43
|
+
}
|
|
7
44
|
listeners.add(listener);
|
|
8
|
-
window.addEventListener("popstate", listener);
|
|
9
45
|
return () => {
|
|
10
46
|
listeners.delete(listener);
|
|
11
|
-
window.removeEventListener("popstate",
|
|
47
|
+
if (listeners.size === 0) window.removeEventListener("popstate", onPopState);
|
|
12
48
|
};
|
|
13
49
|
}
|
|
14
50
|
/** The current path, updated on navigation and on back and forward. */
|
|
15
51
|
function usePathname() {
|
|
16
52
|
return useSyncExternalStore(subscribe, _temp);
|
|
17
53
|
}
|
|
18
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Sets what guards going back and forward: the shell's leave guards. Returns a function that
|
|
56
|
+
* removes it.
|
|
57
|
+
*/
|
|
19
58
|
function _temp() {
|
|
20
59
|
return window.location.pathname;
|
|
21
60
|
}
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
61
|
+
function setHistoryGuard(next) {
|
|
62
|
+
guard = next;
|
|
63
|
+
return () => {
|
|
64
|
+
if (guard === next) guard = null;
|
|
65
|
+
};
|
|
25
66
|
}
|
|
26
|
-
/**
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
let t1;
|
|
32
|
-
if ($[0] !== t0) {
|
|
33
|
-
const { href: t2, onClick, ...t3 } = t0;
|
|
34
|
-
href = t2;
|
|
35
|
-
props = t3;
|
|
36
|
-
t1 = (event) => {
|
|
37
|
-
onClick?.(event);
|
|
38
|
-
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || props.target !== void 0 && props.target !== "_self") return;
|
|
39
|
-
event.preventDefault();
|
|
40
|
-
navigate(href);
|
|
41
|
-
};
|
|
42
|
-
$[0] = t0;
|
|
43
|
-
$[1] = href;
|
|
44
|
-
$[2] = props;
|
|
45
|
-
$[3] = t1;
|
|
46
|
-
} else {
|
|
47
|
-
href = $[1];
|
|
48
|
-
props = $[2];
|
|
49
|
-
t1 = $[3];
|
|
50
|
-
}
|
|
51
|
-
const handleClick = t1;
|
|
52
|
-
let t2;
|
|
53
|
-
if ($[4] !== handleClick || $[5] !== href || $[6] !== props) {
|
|
54
|
-
t2 = /* @__PURE__ */ jsx("a", {
|
|
55
|
-
...props,
|
|
56
|
-
href,
|
|
57
|
-
onClick: handleClick
|
|
58
|
-
});
|
|
59
|
-
$[4] = handleClick;
|
|
60
|
-
$[5] = href;
|
|
61
|
-
$[6] = props;
|
|
62
|
-
$[7] = t2;
|
|
63
|
-
} else t2 = $[7];
|
|
64
|
-
return t2;
|
|
67
|
+
/** Navigates to a path of the app, without reloading the page. It doesn't ask the guards. */
|
|
68
|
+
function navigate(path) {
|
|
69
|
+
current += 1;
|
|
70
|
+
window.history.pushState({ [INDEX_KEY]: current }, "", path);
|
|
71
|
+
notify();
|
|
65
72
|
}
|
|
66
73
|
//#endregion
|
|
67
|
-
export {
|
|
74
|
+
export { navigate, setHistoryGuard, usePathname };
|
|
68
75
|
|
|
69
76
|
//# sourceMappingURL=location.js.map
|
package/dist/location.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"location.js","names":["href","handleClick"
|
|
1
|
+
{"version":3,"file":"location.js","names":["href","handleClick"],"sources":["../src/location.tsx"],"sourcesContent":["import { type ComponentProps, type MouseEvent, useSyncExternalStore } from 'react';\n\n// A minimal history-mode location. Each entry of the app keeps its position in the history\n// (`panneauIndex`), so that a guard can undo going back or forward and ask first.\n\nconst INDEX_KEY = 'panneauIndex';\n\nconst listeners = new Set<() => void>();\n\n/** What guards going back or forward, such as forms with unsaved changes. */\nexport interface HistoryGuard {\n /** Whether going back or forward needs a confirmation now. */\n isBlocking(): boolean;\n /** Resolves `true` to leave, `false` to stay. */\n confirmLeave(): Promise<boolean>;\n}\n\nlet guard: HistoryGuard | null = null;\n// The position of the current entry, and whether the next move is already confirmed\nlet current = 0;\nlet confirmed = false;\n\nfunction readIndex(state: unknown): number | null {\n const value =\n state !== null && typeof state === 'object'\n ? (state as Record<string, unknown>)[INDEX_KEY]\n : undefined;\n return typeof value === 'number' ? value : null;\n}\n\nfunction notify() {\n listeners.forEach((listener) => listener());\n}\n\nfunction onPopState(event: PopStateEvent) {\n const index = readIndex(event.state);\n if (index !== null && index !== current && !confirmed && guard?.isBlocking() === true) {\n // Undo the move, then make it again once the user chose to leave\n const delta = index - current;\n window.history.go(-delta);\n void guard.confirmLeave().then((leave) => {\n if (leave) {\n confirmed = true;\n window.history.go(delta);\n }\n });\n return;\n }\n confirmed = false;\n if (index !== null) {\n current = index;\n }\n notify();\n}\n\nfunction subscribe(listener: () => void) {\n if (listeners.size === 0) {\n // The entry the app starts on\n const index = readIndex(window.history.state);\n if (index === null) {\n window.history.replaceState(\n { ...(window.history.state as object | null), [INDEX_KEY]: current },\n '',\n );\n } else {\n current = index;\n }\n window.addEventListener('popstate', onPopState);\n }\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n window.removeEventListener('popstate', onPopState);\n }\n };\n}\n\n/** The current path, updated on navigation and on back and forward. */\nexport function usePathname(): string {\n return useSyncExternalStore(subscribe, () => window.location.pathname);\n}\n\n/**\n * Sets what guards going back and forward: the shell's leave guards. Returns a function that\n * removes it.\n */\nexport function setHistoryGuard(next: HistoryGuard): () => void {\n guard = next;\n return () => {\n if (guard === next) {\n guard = null;\n }\n };\n}\n\n/** Navigates to a path of the app, without reloading the page. It doesn't ask the guards. */\nexport function navigate(path: string) {\n current += 1;\n window.history.pushState({ [INDEX_KEY]: current }, '', path);\n notify();\n}\n\n/** A link to a path of the app. Plain clicks navigate without reloading the page. */\nexport function Link({ href, onClick, ...props }: ComponentProps<'a'> & { href: string }) {\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n onClick?.(event);\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey ||\n (props.target !== undefined && props.target !== '_self')\n ) {\n return;\n }\n event.preventDefault();\n navigate(href);\n };\n return <a {...props} href={href} onClick={handleClick} />;\n}\n"],"mappings":";;;;AAKA,IAAM,YAAY;AAElB,IAAM,4BAAY,IAAI,IAAgB;AAUtC,IAAI,QAA6B;AAEjC,IAAI,UAAU;AACd,IAAI,YAAY;AAEhB,SAAS,UAAU,OAA+B;CAC9C,MAAM,QACF,UAAU,QAAQ,OAAO,UAAU,WAC5B,MAAkC,aACnC,KAAA;CACV,OAAO,OAAO,UAAU,WAAW,QAAQ;AAC/C;AAEA,SAAS,SAAS;CACd,UAAU,SAAS,aAAa,SAAS,CAAC;AAC9C;AAEA,SAAS,WAAW,OAAsB;CACtC,MAAM,QAAQ,UAAU,MAAM,KAAK;CACnC,IAAI,UAAU,QAAQ,UAAU,WAAW,CAAC,aAAa,OAAO,WAAW,MAAM,MAAM;EAEnF,MAAM,QAAQ,QAAQ;EACtB,OAAO,QAAQ,GAAG,CAAC,KAAK;EACxB,MAAW,aAAa,CAAC,CAAC,MAAM,UAAU;GACtC,IAAI,OAAO;IACP,YAAY;IACZ,OAAO,QAAQ,GAAG,KAAK;GAC3B;EACJ,CAAC;EACD;CACJ;CACA,YAAY;CACZ,IAAI,UAAU,MACV,UAAU;CAEd,OAAO;AACX;AAEA,SAAS,UAAU,UAAsB;CACrC,IAAI,UAAU,SAAS,GAAG;EAEtB,MAAM,QAAQ,UAAU,OAAO,QAAQ,KAAK;EAC5C,IAAI,UAAU,MACV,OAAO,QAAQ,aACX;GAAE,GAAI,OAAO,QAAQ;IAA0B,YAAY;EAAQ,GACnE,EACJ;OAEA,UAAU;EAEd,OAAO,iBAAiB,YAAY,UAAU;CAClD;CACA,UAAU,IAAI,QAAQ;CACtB,aAAa;EACT,UAAU,OAAO,QAAQ;EACzB,IAAI,UAAU,SAAS,GACnB,OAAO,oBAAoB,YAAY,UAAU;CAEzD;AACJ;;AAGA,SAAO,cAAA;CAAA,OACI,qBAAqB,WAAW,KAA8B;AAAC;;;;;AADnE,SAAA,QAAA;CAAA,OAC0C,OAAM,SAAS;AAAS;AAOzE,SAAgB,gBAAgB,MAAgC;CAC5D,QAAQ;CACR,aAAa;EACT,IAAI,UAAU,MACV,QAAQ;CAEhB;AACJ;;AAGA,SAAgB,SAAS,MAAc;CACnC,WAAW;CACX,OAAO,QAAQ,UAAU,GAAG,YAAY,QAAQ,GAAG,IAAI,IAAI;CAC3D,OAAO;AACX"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Definition } from '@panneau/protocol';
|
|
2
|
+
import { type AppRoute } from './routes';
|
|
3
|
+
/** What a link of the navigation leads to, for its default icon and to know when it is current. */
|
|
4
|
+
export type NavTarget = {
|
|
5
|
+
kind: 'home';
|
|
6
|
+
} | {
|
|
7
|
+
kind: 'resource';
|
|
8
|
+
resource: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'page';
|
|
11
|
+
page: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'url';
|
|
14
|
+
external: boolean;
|
|
15
|
+
};
|
|
16
|
+
/** A link of the navigation. */
|
|
17
|
+
export interface NavLink {
|
|
18
|
+
type: 'link';
|
|
19
|
+
key: string;
|
|
20
|
+
label: string;
|
|
21
|
+
href: string;
|
|
22
|
+
/** A Lucide icon name from the definition (`"icon": "file-text"`). */
|
|
23
|
+
icon: string | undefined;
|
|
24
|
+
target: NavTarget;
|
|
25
|
+
}
|
|
26
|
+
/** A titled group of links. */
|
|
27
|
+
export interface NavGroup {
|
|
28
|
+
type: 'group';
|
|
29
|
+
key: string;
|
|
30
|
+
label: string | undefined;
|
|
31
|
+
children: NavLink[];
|
|
32
|
+
}
|
|
33
|
+
export interface NavSeparator {
|
|
34
|
+
type: 'separator';
|
|
35
|
+
key: string;
|
|
36
|
+
}
|
|
37
|
+
export type NavEntry = NavLink | NavGroup | NavSeparator;
|
|
38
|
+
/**
|
|
39
|
+
* The path of the user's profile: the user's item in the resource of users (`user.resource`, or
|
|
40
|
+
* the resource `users`), or `null` when the definition has no such resource.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getProfilePath(definition: Definition, basePath: string): string | null;
|
|
43
|
+
/** The first letter in upper case: resource labels are written in lower case (`pages`). */
|
|
44
|
+
export declare function capitalize(label: string, locale?: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* The entries of the main navigation: the `main` menu of the definition, or every resource and
|
|
47
|
+
* page without one. Resources the user can't list (`abilities.viewAny`) and entries of unknown
|
|
48
|
+
* types are left out. Groups keep one level: the links of nested groups join their parent.
|
|
49
|
+
*/
|
|
50
|
+
export declare function getNavigation(definition: Definition, basePath: string): NavEntry[];
|
|
51
|
+
/** Whether a link leads to the current route: a resource is current on all of its pages. */
|
|
52
|
+
export declare function isCurrent(target: NavTarget, route: AppRoute): boolean;
|
|
53
|
+
//# sourceMappingURL=navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,mBAAmB,CAAC;AAE9D,OAAO,EAAE,KAAK,QAAQ,EAAa,MAAM,UAAU,CAAC;AAEpD,mGAAmG;AACnG,MAAM,MAAM,SAAS,GACf;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAEzC,gCAAgC;AAChC,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,EAAE,SAAS,CAAC;CACrB;AAED,+BAA+B;AAC/B,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEzD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUtF;AAED,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjE;AAgED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,EAAE,CAgBlF;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,CAWrE"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { routePath } from "./routes.js";
|
|
2
|
+
import { can } from "@panneau/core";
|
|
3
|
+
//#region src/navigation.ts
|
|
4
|
+
/**
|
|
5
|
+
* The path of the user's profile: the user's item in the resource of users (`user.resource`, or
|
|
6
|
+
* the resource `users`), or `null` when the definition has no such resource.
|
|
7
|
+
*/
|
|
8
|
+
function getProfilePath(definition, basePath) {
|
|
9
|
+
const user = definition.user;
|
|
10
|
+
if (user === void 0 || user === null) return null;
|
|
11
|
+
const id = user.resource ?? "users";
|
|
12
|
+
const resource = definition.resources.find((candidate) => candidate.id === id);
|
|
13
|
+
return resource !== void 0 ? routePath({
|
|
14
|
+
name: "resource.edit",
|
|
15
|
+
resource: resource.id,
|
|
16
|
+
id: user.id
|
|
17
|
+
}, basePath) : null;
|
|
18
|
+
}
|
|
19
|
+
/** The first letter in upper case: resource labels are written in lower case (`pages`). */
|
|
20
|
+
function capitalize(label, locale) {
|
|
21
|
+
return label.charAt(0).toLocaleUpperCase(locale) + label.slice(1);
|
|
22
|
+
}
|
|
23
|
+
function toLinks(item, definition, basePath, key) {
|
|
24
|
+
const resourceLink = (id, label, icon) => {
|
|
25
|
+
const resource = definition.resources.find((candidate) => candidate.id === id);
|
|
26
|
+
if (resource === void 0 || !can(resource.abilities, "viewAny")) return [];
|
|
27
|
+
return [{
|
|
28
|
+
type: "link",
|
|
29
|
+
key: `${key}:${id}`,
|
|
30
|
+
label: label ?? capitalize(resource.label.plural, definition.locale),
|
|
31
|
+
href: routePath({
|
|
32
|
+
name: "resource.index",
|
|
33
|
+
resource: id
|
|
34
|
+
}, basePath),
|
|
35
|
+
icon: icon ?? resource.icon,
|
|
36
|
+
target: {
|
|
37
|
+
kind: "resource",
|
|
38
|
+
resource: id
|
|
39
|
+
}
|
|
40
|
+
}];
|
|
41
|
+
};
|
|
42
|
+
switch (item.type) {
|
|
43
|
+
case "resources": return definition.resources.flatMap(({ id }) => resourceLink(id));
|
|
44
|
+
case "resource": return item.resource !== void 0 ? resourceLink(item.resource, item.label, item.icon) : [];
|
|
45
|
+
case "page": {
|
|
46
|
+
const page = item.page !== void 0 ? definition.pages?.[item.page] : void 0;
|
|
47
|
+
if (item.page === void 0 || page === void 0) return [];
|
|
48
|
+
return [{
|
|
49
|
+
type: "link",
|
|
50
|
+
key,
|
|
51
|
+
label: item.label ?? page.label ?? item.page,
|
|
52
|
+
href: routePath({
|
|
53
|
+
name: "page",
|
|
54
|
+
page: item.page
|
|
55
|
+
}, basePath),
|
|
56
|
+
icon: item.icon,
|
|
57
|
+
target: {
|
|
58
|
+
kind: "page",
|
|
59
|
+
page: item.page
|
|
60
|
+
}
|
|
61
|
+
}];
|
|
62
|
+
}
|
|
63
|
+
case "link": return item.url !== void 0 ? [{
|
|
64
|
+
type: "link",
|
|
65
|
+
key,
|
|
66
|
+
label: item.label ?? item.url,
|
|
67
|
+
href: item.url,
|
|
68
|
+
icon: item.icon,
|
|
69
|
+
target: {
|
|
70
|
+
kind: "url",
|
|
71
|
+
external: item.external === true
|
|
72
|
+
}
|
|
73
|
+
}] : [];
|
|
74
|
+
case "group": return (item.children ?? []).flatMap((child, index) => toLinks(child, definition, basePath, `${key}.${String(index)}`));
|
|
75
|
+
default: return [];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The entries of the main navigation: the `main` menu of the definition, or every resource and
|
|
80
|
+
* page without one. Resources the user can't list (`abilities.viewAny`) and entries of unknown
|
|
81
|
+
* types are left out. Groups keep one level: the links of nested groups join their parent.
|
|
82
|
+
*/
|
|
83
|
+
function getNavigation(definition, basePath) {
|
|
84
|
+
return (definition.menus?.main ?? [{ type: "resources" }, ...Object.keys(definition.pages ?? {}).map((page) => ({
|
|
85
|
+
type: "page",
|
|
86
|
+
page
|
|
87
|
+
}))]).flatMap((item, index) => {
|
|
88
|
+
const key = String(index);
|
|
89
|
+
if (item.type === "separator") return [{
|
|
90
|
+
type: "separator",
|
|
91
|
+
key
|
|
92
|
+
}];
|
|
93
|
+
if (item.type === "group") {
|
|
94
|
+
const children = toLinks(item, definition, basePath, key);
|
|
95
|
+
return children.length > 0 ? [{
|
|
96
|
+
type: "group",
|
|
97
|
+
key,
|
|
98
|
+
label: item.label,
|
|
99
|
+
children
|
|
100
|
+
}] : [];
|
|
101
|
+
}
|
|
102
|
+
return toLinks(item, definition, basePath, key);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Whether a link leads to the current route: a resource is current on all of its pages. */
|
|
106
|
+
function isCurrent(target, route) {
|
|
107
|
+
switch (target.kind) {
|
|
108
|
+
case "home": return route.name === "home";
|
|
109
|
+
case "resource": return "resource" in route && route.resource === target.resource;
|
|
110
|
+
case "page": return route.name === "page" && route.page === target.page;
|
|
111
|
+
case "url": return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { capitalize, getNavigation, getProfilePath, isCurrent };
|
|
116
|
+
|
|
117
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.js","names":[],"sources":["../src/navigation.ts"],"sourcesContent":["import { can } from '@panneau/core';\nimport type { Definition, MenuItem } from '@panneau/protocol';\n\nimport { type AppRoute, routePath } from './routes';\n\n/** What a link of the navigation leads to, for its default icon and to know when it is current. */\nexport type NavTarget =\n | { kind: 'home' }\n | { kind: 'resource'; resource: string }\n | { kind: 'page'; page: string }\n | { kind: 'url'; external: boolean };\n\n/** A link of the navigation. */\nexport interface NavLink {\n type: 'link';\n key: string;\n label: string;\n href: string;\n /** A Lucide icon name from the definition (`\"icon\": \"file-text\"`). */\n icon: string | undefined;\n target: NavTarget;\n}\n\n/** A titled group of links. */\nexport interface NavGroup {\n type: 'group';\n key: string;\n label: string | undefined;\n children: NavLink[];\n}\n\nexport interface NavSeparator {\n type: 'separator';\n key: string;\n}\n\nexport type NavEntry = NavLink | NavGroup | NavSeparator;\n\n/**\n * The path of the user's profile: the user's item in the resource of users (`user.resource`, or\n * the resource `users`), or `null` when the definition has no such resource.\n */\nexport function getProfilePath(definition: Definition, basePath: string): string | null {\n const user = definition.user;\n if (user === undefined || user === null) {\n return null;\n }\n const id = user.resource ?? 'users';\n const resource = definition.resources.find((candidate) => candidate.id === id);\n return resource !== undefined\n ? routePath({ name: 'resource.edit', resource: resource.id, id: user.id }, basePath)\n : null;\n}\n\n/** The first letter in upper case: resource labels are written in lower case (`pages`). */\nexport function capitalize(label: string, locale?: string): string {\n return label.charAt(0).toLocaleUpperCase(locale) + label.slice(1);\n}\n\nfunction toLinks(item: MenuItem, definition: Definition, basePath: string, key: string): NavLink[] {\n const resourceLink = (id: string, label?: string, icon?: string): NavLink[] => {\n const resource = definition.resources.find((candidate) => candidate.id === id);\n if (resource === undefined || !can(resource.abilities, 'viewAny')) {\n return [];\n }\n return [\n {\n type: 'link',\n key: `${key}:${id}`,\n label: label ?? capitalize(resource.label.plural, definition.locale),\n href: routePath({ name: 'resource.index', resource: id }, basePath),\n icon: icon ?? resource.icon,\n target: { kind: 'resource', resource: id },\n },\n ];\n };\n switch (item.type) {\n case 'resources':\n return definition.resources.flatMap(({ id }) => resourceLink(id));\n case 'resource':\n return item.resource !== undefined\n ? resourceLink(item.resource, item.label, item.icon)\n : [];\n case 'page': {\n const page = item.page !== undefined ? definition.pages?.[item.page] : undefined;\n if (item.page === undefined || page === undefined) {\n return [];\n }\n return [\n {\n type: 'link',\n key,\n label: item.label ?? page.label ?? item.page,\n href: routePath({ name: 'page', page: item.page }, basePath),\n icon: item.icon,\n target: { kind: 'page', page: item.page },\n },\n ];\n }\n case 'link':\n return item.url !== undefined\n ? [\n {\n type: 'link',\n key,\n label: item.label ?? item.url,\n href: item.url,\n icon: item.icon,\n target: { kind: 'url', external: item.external === true },\n },\n ]\n : [];\n case 'group':\n return (item.children ?? []).flatMap((child, index) =>\n toLinks(child, definition, basePath, `${key}.${String(index)}`),\n );\n default:\n return [];\n }\n}\n\n/**\n * The entries of the main navigation: the `main` menu of the definition, or every resource and\n * page without one. Resources the user can't list (`abilities.viewAny`) and entries of unknown\n * types are left out. Groups keep one level: the links of nested groups join their parent.\n */\nexport function getNavigation(definition: Definition, basePath: string): NavEntry[] {\n const menu: MenuItem[] = definition.menus?.main ?? [\n { type: 'resources' },\n ...Object.keys(definition.pages ?? {}).map((page) => ({ type: 'page', page })),\n ];\n return menu.flatMap((item, index): NavEntry[] => {\n const key = String(index);\n if (item.type === 'separator') {\n return [{ type: 'separator', key }];\n }\n if (item.type === 'group') {\n const children = toLinks(item, definition, basePath, key);\n return children.length > 0 ? [{ type: 'group', key, label: item.label, children }] : [];\n }\n return toLinks(item, definition, basePath, key);\n });\n}\n\n/** Whether a link leads to the current route: a resource is current on all of its pages. */\nexport function isCurrent(target: NavTarget, route: AppRoute): boolean {\n switch (target.kind) {\n case 'home':\n return route.name === 'home';\n case 'resource':\n return 'resource' in route && route.resource === target.resource;\n case 'page':\n return route.name === 'page' && route.page === target.page;\n case 'url':\n return false;\n }\n}\n"],"mappings":";;;;;;;AA0CA,SAAgB,eAAe,YAAwB,UAAiC;CACpF,MAAM,OAAO,WAAW;CACxB,IAAI,SAAS,KAAA,KAAa,SAAS,MAC/B,OAAO;CAEX,MAAM,KAAK,KAAK,YAAY;CAC5B,MAAM,WAAW,WAAW,UAAU,MAAM,cAAc,UAAU,OAAO,EAAE;CAC7E,OAAO,aAAa,KAAA,IACd,UAAU;EAAE,MAAM;EAAiB,UAAU,SAAS;EAAI,IAAI,KAAK;CAAG,GAAG,QAAQ,IACjF;AACV;;AAGA,SAAgB,WAAW,OAAe,QAAyB;CAC/D,OAAO,MAAM,OAAO,CAAC,CAAC,CAAC,kBAAkB,MAAM,IAAI,MAAM,MAAM,CAAC;AACpE;AAEA,SAAS,QAAQ,MAAgB,YAAwB,UAAkB,KAAwB;CAC/F,MAAM,gBAAgB,IAAY,OAAgB,SAA6B;EAC3E,MAAM,WAAW,WAAW,UAAU,MAAM,cAAc,UAAU,OAAO,EAAE;EAC7E,IAAI,aAAa,KAAA,KAAa,CAAC,IAAI,SAAS,WAAW,SAAS,GAC5D,OAAO,CAAA;EAEX,OAAO,CACH;GACI,MAAM;GACN,KAAK,GAAG,IAAG,GAAI;GACf,OAAO,SAAS,WAAW,SAAS,MAAM,QAAQ,WAAW,MAAM;GACnE,MAAM,UAAU;IAAE,MAAM;IAAkB,UAAU;GAAG,GAAG,QAAQ;GAClE,MAAM,QAAQ,SAAS;GACvB,QAAQ;IAAE,MAAM;IAAY,UAAU;GAAG;EAC7C,CAAC;CAET;CACA,QAAQ,KAAK,MAAb;EACI,KAAK,aACD,OAAO,WAAW,UAAU,SAAS,EAAE,SAAS,aAAa,EAAE,CAAC;EACpE,KAAK,YACD,OAAO,KAAK,aAAa,KAAA,IACnB,aAAa,KAAK,UAAU,KAAK,OAAO,KAAK,IAAI,IACjD,CAAA;EACV,KAAK,QAAQ;GACT,MAAM,OAAO,KAAK,SAAS,KAAA,IAAY,WAAW,QAAQ,KAAK,QAAQ,KAAA;GACvE,IAAI,KAAK,SAAS,KAAA,KAAa,SAAS,KAAA,GACpC,OAAO,CAAA;GAEX,OAAO,CACH;IACI,MAAM;IACN;IACA,OAAO,KAAK,SAAS,KAAK,SAAS,KAAK;IACxC,MAAM,UAAU;KAAE,MAAM;KAAQ,MAAM,KAAK;IAAK,GAAG,QAAQ;IAC3D,MAAM,KAAK;IACX,QAAQ;KAAE,MAAM;KAAQ,MAAM,KAAK;IAAK;GAC5C,CAAC;EAET;EACA,KAAK,QACD,OAAO,KAAK,QAAQ,KAAA,IACd,CACI;GACI,MAAM;GACN;GACA,OAAO,KAAK,SAAS,KAAK;GAC1B,MAAM,KAAK;GACX,MAAM,KAAK;GACX,QAAQ;IAAE,MAAM;IAAO,UAAU,KAAK,aAAa;GAAK;EAC5D,CAAC,IAEL,CAAA;EACV,KAAK,SACD,QAAQ,KAAK,YAAY,CAAA,EAAA,CAAI,SAAS,OAAO,UACzC,QAAQ,OAAO,YAAY,UAAU,GAAG,IAAG,GAAI,OAAO,KAAK,GAAG,CAClE;EACJ,SACI,OAAO,CAAA;CACf;AACJ;;;;;;AAOA,SAAgB,cAAc,YAAwB,UAA8B;CAKhF,QAJyB,WAAW,OAAO,QAAQ,CAC/C,EAAE,MAAM,YAAY,GACpB,GAAG,OAAO,KAAK,WAAW,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,UAAU;EAAE,MAAM;EAAQ;CAAK,EAAE,CAAC,EAAA,CAEtE,SAAS,MAAM,UAAsB;EAC7C,MAAM,MAAM,OAAO,KAAK;EACxB,IAAI,KAAK,SAAS,aACd,OAAO,CAAC;GAAE,MAAM;GAAa;EAAI,CAAC;EAEtC,IAAI,KAAK,SAAS,SAAS;GACvB,MAAM,WAAW,QAAQ,MAAM,YAAY,UAAU,GAAG;GACxD,OAAO,SAAS,SAAS,IAAI,CAAC;IAAE,MAAM;IAAS;IAAK,OAAO,KAAK;IAAO;GAAS,CAAC,IAAI,CAAA;EACzF;EACA,OAAO,QAAQ,MAAM,YAAY,UAAU,GAAG;CAClD,CAAC;AACL;;AAGA,SAAgB,UAAU,QAAmB,OAA0B;CACnE,QAAQ,OAAO,MAAf;EACI,KAAK,QACD,OAAO,MAAM,SAAS;EAC1B,KAAK,YACD,OAAO,cAAc,SAAS,MAAM,aAAa,OAAO;EAC5D,KAAK,QACD,OAAO,MAAM,SAAS,UAAU,MAAM,SAAS,OAAO;EAC1D,KAAK,OACD,OAAO;CACf;AACJ"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Definition } from '@panneau/protocol';
|
|
2
|
+
export interface HomePageProps {
|
|
3
|
+
definition: Definition;
|
|
4
|
+
basePath: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The home page, until the dashboard of 5.x: a welcome, and a card for each resource the user can
|
|
8
|
+
* list.
|
|
9
|
+
*/
|
|
10
|
+
export declare function HomePage({ definition, basePath }: HomePageProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=HomePage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HomePage.d.ts","sourceRoot":"","sources":["../../src/pages/HomePage.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AASpD,MAAM,WAAW,aAAa;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,aAAa,+BA4C/D"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useDocumentTitle } from "../documentTitle.js";
|
|
2
|
+
import { routePath } from "../routes.js";
|
|
3
|
+
import { capitalize } from "../navigation.js";
|
|
4
|
+
import pages_module_default from "./pages.module.js";
|
|
5
|
+
import { PageHeader } from "./PageHeader.js";
|
|
6
|
+
import { c } from "react/compiler-runtime";
|
|
7
|
+
import { can } from "@panneau/core";
|
|
8
|
+
import { appMessages } from "@panneau/intl";
|
|
9
|
+
import { Icon, getNamedIcon, usePanneauIntl } from "@panneau/ui";
|
|
10
|
+
import { Link } from "react-aria-components";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { List } from "lucide-react";
|
|
13
|
+
//#region src/pages/HomePage.tsx
|
|
14
|
+
/**
|
|
15
|
+
* The home page, until the dashboard of 5.x: a welcome, and a card for each resource the user can
|
|
16
|
+
* list.
|
|
17
|
+
*/
|
|
18
|
+
function HomePage(t0) {
|
|
19
|
+
const $ = c(17);
|
|
20
|
+
const { definition, basePath } = t0;
|
|
21
|
+
const intl = usePanneauIntl();
|
|
22
|
+
const resources = definition.resources.filter(_temp);
|
|
23
|
+
const user = definition.user ?? null;
|
|
24
|
+
let t1;
|
|
25
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
26
|
+
t1 = [];
|
|
27
|
+
$[0] = t1;
|
|
28
|
+
} else t1 = $[0];
|
|
29
|
+
useDocumentTitle(t1);
|
|
30
|
+
const t2 = pages_module_default;
|
|
31
|
+
let t3;
|
|
32
|
+
if ($[1] !== definition.name) {
|
|
33
|
+
t3 = /* @__PURE__ */ jsx(PageHeader, { title: definition.name });
|
|
34
|
+
$[1] = definition.name;
|
|
35
|
+
$[2] = t3;
|
|
36
|
+
} else t3 = $[2];
|
|
37
|
+
let t4;
|
|
38
|
+
if ($[3] !== intl || $[4] !== user) {
|
|
39
|
+
t4 = user !== null ? /* @__PURE__ */ jsx("p", {
|
|
40
|
+
className: pages_module_default.intro,
|
|
41
|
+
children: intl.formatMessage(appMessages.welcome, { name: user.name })
|
|
42
|
+
}) : null;
|
|
43
|
+
$[3] = intl;
|
|
44
|
+
$[4] = user;
|
|
45
|
+
$[5] = t4;
|
|
46
|
+
} else t4 = $[5];
|
|
47
|
+
const t5 = pages_module_default;
|
|
48
|
+
let t6;
|
|
49
|
+
if ($[6] !== basePath || $[7] !== definition.locale) {
|
|
50
|
+
t6 = (resource_0) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Link, {
|
|
51
|
+
href: routePath({
|
|
52
|
+
name: "resource.index",
|
|
53
|
+
resource: resource_0.id
|
|
54
|
+
}, basePath),
|
|
55
|
+
className: pages_module_default.card,
|
|
56
|
+
children: [
|
|
57
|
+
/* @__PURE__ */ jsx("span", {
|
|
58
|
+
className: pages_module_default.cardIcon,
|
|
59
|
+
"aria-hidden": "true",
|
|
60
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: resource_0.icon !== void 0 ? getNamedIcon(resource_0.icon) : List })
|
|
61
|
+
}),
|
|
62
|
+
/* @__PURE__ */ jsx("span", {
|
|
63
|
+
className: pages_module_default.cardTitle,
|
|
64
|
+
children: capitalize(resource_0.label.plural, definition.locale)
|
|
65
|
+
}),
|
|
66
|
+
resource_0.description !== void 0 ? /* @__PURE__ */ jsx("span", {
|
|
67
|
+
className: pages_module_default.cardText,
|
|
68
|
+
children: resource_0.description
|
|
69
|
+
}) : null
|
|
70
|
+
]
|
|
71
|
+
}) }, resource_0.id);
|
|
72
|
+
$[6] = basePath;
|
|
73
|
+
$[7] = definition.locale;
|
|
74
|
+
$[8] = t6;
|
|
75
|
+
} else t6 = $[8];
|
|
76
|
+
const t7 = resources.map(t6);
|
|
77
|
+
let t8;
|
|
78
|
+
if ($[9] !== t5.cards || $[10] !== t7) {
|
|
79
|
+
t8 = /* @__PURE__ */ jsx("ul", {
|
|
80
|
+
className: t5.cards,
|
|
81
|
+
children: t7
|
|
82
|
+
});
|
|
83
|
+
$[9] = t5.cards;
|
|
84
|
+
$[10] = t7;
|
|
85
|
+
$[11] = t8;
|
|
86
|
+
} else t8 = $[11];
|
|
87
|
+
let t9;
|
|
88
|
+
if ($[12] !== t2.page || $[13] !== t3 || $[14] !== t4 || $[15] !== t8) {
|
|
89
|
+
t9 = /* @__PURE__ */ jsxs("div", {
|
|
90
|
+
className: t2.page,
|
|
91
|
+
children: [
|
|
92
|
+
t3,
|
|
93
|
+
t4,
|
|
94
|
+
t8
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
$[12] = t2.page;
|
|
98
|
+
$[13] = t3;
|
|
99
|
+
$[14] = t4;
|
|
100
|
+
$[15] = t8;
|
|
101
|
+
$[16] = t9;
|
|
102
|
+
} else t9 = $[16];
|
|
103
|
+
return t9;
|
|
104
|
+
}
|
|
105
|
+
function _temp(resource) {
|
|
106
|
+
return can(resource.abilities, "viewAny");
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { HomePage };
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=HomePage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":["styles","resource","_c","styles"],"sources":["../../src/pages/HomePage.tsx"],"sourcesContent":["import { List } from 'lucide-react';\nimport { Link } from 'react-aria-components';\n\nimport { can } from '@panneau/core';\nimport { appMessages } from '@panneau/intl';\nimport type { Definition } from '@panneau/protocol';\nimport { Icon, getNamedIcon, usePanneauIntl } from '@panneau/ui';\n\nimport { useDocumentTitle } from '../documentTitle';\nimport { capitalize } from '../navigation';\nimport { routePath } from '../routes';\nimport { PageHeader } from './PageHeader';\nimport styles from './pages.module.css';\n\nexport interface HomePageProps {\n definition: Definition;\n basePath: string;\n}\n\n/**\n * The home page, until the dashboard of 5.x: a welcome, and a card for each resource the user can\n * list.\n */\nexport function HomePage({ definition, basePath }: HomePageProps) {\n const intl = usePanneauIntl();\n const resources = definition.resources.filter((resource) => can(resource.abilities, 'viewAny'));\n const user = definition.user ?? null;\n useDocumentTitle([]);\n return (\n <div className={styles.page}>\n <PageHeader title={definition.name} />\n {user !== null ? (\n <p className={styles.intro}>\n {intl.formatMessage(appMessages.welcome, { name: user.name })}\n </p>\n ) : null}\n <ul className={styles.cards}>\n {resources.map((resource) => (\n <li key={resource.id}>\n <Link\n href={routePath(\n { name: 'resource.index', resource: resource.id },\n basePath,\n )}\n className={styles.card}\n >\n <span className={styles.cardIcon} aria-hidden=\"true\">\n <Icon\n icon={\n resource.icon !== undefined\n ? getNamedIcon(resource.icon)\n : List\n }\n />\n </span>\n <span className={styles.cardTitle}>\n {capitalize(resource.label.plural, definition.locale)}\n </span>\n {resource.description !== undefined ? (\n <span className={styles.cardText}>{resource.description}</span>\n ) : null}\n </Link>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuBA,SAAO,SAAA,IAAA;CAAA,MAAA,IAAAE,EAAA,EAAA;CAAkB,MAAA,EAAA,YAAA,aAAA;CACrB,MAAA,OAAa,eAAe;CAC5B,MAAA,YAAkB,WAAU,UAAU,OAAQ,KAAgD;CAC9F,MAAA,OAAa,WAAU,QAAV;CAAwB,IAAA;CAAA,IAAA,EAAA,OAAA,OAAA,IAAA,2BAAA,GAAA;EACpB,KAAA,CAAA;EAAE,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAnB,iBAAiB,EAAE;CAECF,MAAAA,KAAAG;CAAM,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,MAAA;EAClB,KAAA,oBAAC,YAAD,EAAmB,OAAA,WAAU,KAAK,CAAA;EAAI,EAAA,KAAA,WAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,QAAA,EAAA,OAAA,MAAA;EACrC,KAAA,SAAS,OACN,oBAAA,KAAA;GAAc,WAAAA,qBAAM;GACf,UAAA,KAAI,cAAe,YAAW,SAAU,EAAA,MAAQ,KAAI,KAAM,CAAC;EAC5D,CAAA,IAHP;EAIO,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CACOH,MAAAA,KAAAG;CAAM,IAAA;CAAA,IAAA,EAAA,OAAA,YAAA,EAAA,OAAA,WAAA,QAAA;EACF,MAAA,eACX,oBAAA,MAAA,EAAA,UACI,qBAAC,MAAD;GACU,MAAA,UACF;IAAA,MAAQ;IAAgB,UAAYF,WAAQ;GAAI,GAChD,QACJ;GACW,WAAAE,qBAAM;GALrB,UAAA;IAOI,oBAAA,QAAA;KAAiB,WAAAA,qBAAM;KAAuB,eAAA;KAC1C,UAAA,oBAAC,MAAD,EAEQ,MAAAF,WAAQ,SAAU,KAAA,IACZ,aAAaA,WAAQ,IAClB,IAFT,KAEU,CAAA;IAGf,CAAA;IACP,oBAAA,QAAA;KAAiB,WAAAE,qBAAM;KAClB,UAAA,WAAWF,WAAQ,MAAM,QAAS,WAAU,MAAO;IACjD,CAAA;IACNA,WAAQ,gBAAiB,KAAA,IACtB,oBAAA,QAAA;KAAiB,WAAAE,qBAAM;KAAYF,UAAAA,WAAQ;IAAoB,CAAA,IADlE;GAnBA;EAuBJ,CAAA,EAAA,GAxBIA,WAAQ,EAwBZ;EACR,EAAA,KAAA;EAAA,EAAA,KAAA,WAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CA1BA,MAAA,KAAA,UAAS,IAAK,EA0Bd;CAAC,IAAA;CAAA,IAAA,EAAA,OAAA,GAAA,SAAA,EAAA,QAAA,IAAA;EA3BN,KAAA,oBAAA,MAAA;GAAe,WAAAD,GAAM;GAChB,UAAA;EA2BA,CAAA;EAAA,EAAA,KAAA,GAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,QAAA,GAAA,QAAA,EAAA,QAAA,MAAA,EAAA,QAAA,MAAA,EAAA,QAAA,IAAA;EAnCT,KAAA,qBAAA,OAAA;GAAgB,WAAAA,GAAM;GAAtB,UAAA;IACI;IACC;IAKD;GA6BE;;EAAA,EAAA,MAAA,GAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OApCN;AAoCM;AA1CP,SAAA,MAAA,UAAA;CAAA,OAEyD,IAAI,SAAQ,WAAY,SAAS;AAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/* The details of an item next to its list. Beside the list on a wide screen, over it otherwise. */
|
|
2
|
+
|
|
3
|
+
.panneau-item-panel-panel {
|
|
4
|
+
position: fixed;
|
|
5
|
+
inset-block: 0;
|
|
6
|
+
inset-inline-end: 0;
|
|
7
|
+
z-index: var(--panneau-z-overlay);
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
width: min(100%, var(--panneau-item-panel-width, 24rem));
|
|
11
|
+
background: var(--panneau-item-panel-bg, var(--panneau-color-surface));
|
|
12
|
+
border-inline-start: var(--panneau-border-width) solid var(--panneau-color-border);
|
|
13
|
+
box-shadow: var(--panneau-shadow-lg);
|
|
14
|
+
outline: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Widened, for previews. */
|
|
18
|
+
.panneau-item-panel-wide {
|
|
19
|
+
width: min(100%, var(--panneau-item-panel-width-wide, 50%));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.panneau-item-panel-panel:focus-visible {
|
|
23
|
+
box-shadow:
|
|
24
|
+
var(--panneau-shadow-lg),
|
|
25
|
+
inset 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.panneau-item-panel-header {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: flex-start;
|
|
31
|
+
justify-content: space-between;
|
|
32
|
+
gap: var(--panneau-space-2);
|
|
33
|
+
padding: var(--panneau-space-4) var(--panneau-space-4) var(--panneau-space-2);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.panneau-item-panel-header-buttons {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex: none;
|
|
39
|
+
gap: var(--panneau-space-1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.panneau-item-panel-title {
|
|
43
|
+
min-width: 0;
|
|
44
|
+
margin: 0;
|
|
45
|
+
overflow-wrap: anywhere;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.panneau-item-panel-actions {
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-wrap: wrap;
|
|
51
|
+
gap: var(--panneau-space-2);
|
|
52
|
+
padding: 0 var(--panneau-space-4) var(--panneau-space-3);
|
|
53
|
+
border-bottom: var(--panneau-border-width) solid var(--panneau-color-border);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.panneau-item-panel-action {
|
|
57
|
+
display: inline-flex;
|
|
58
|
+
align-items: center;
|
|
59
|
+
gap: var(--panneau-space-1);
|
|
60
|
+
min-height: var(--panneau-control-height-sm);
|
|
61
|
+
padding: 0 var(--panneau-space-3);
|
|
62
|
+
border: var(--panneau-border-width) solid var(--panneau-color-border-control);
|
|
63
|
+
border-radius: var(--panneau-radius-md);
|
|
64
|
+
color: var(--panneau-color-text);
|
|
65
|
+
font-size: var(--panneau-font-size-sm);
|
|
66
|
+
font-weight: var(--panneau-font-weight-medium);
|
|
67
|
+
text-decoration: none;
|
|
68
|
+
outline: none;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.panneau-item-panel-action[data-hovered] {
|
|
72
|
+
background: var(--panneau-color-hover);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.panneau-item-panel-action[data-focus-visible] {
|
|
76
|
+
box-shadow: 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.panneau-item-panel-body {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex: 1 1 auto;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
min-height: 0;
|
|
84
|
+
padding: var(--panneau-space-4);
|
|
85
|
+
overflow-y: auto;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.panneau-item-panel-tabs {
|
|
89
|
+
display: flex;
|
|
90
|
+
flex: 1 1 auto;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
gap: var(--panneau-space-3);
|
|
93
|
+
min-height: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.panneau-item-panel-tab-panel {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex: 1 1 auto;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
min-height: 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.panneau-item-panel-preview {
|
|
104
|
+
display: flex;
|
|
105
|
+
flex: 1 1 auto;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
gap: var(--panneau-space-2);
|
|
108
|
+
min-height: 0;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.panneau-item-panel-preview-link {
|
|
112
|
+
display: inline-flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
align-self: flex-end;
|
|
115
|
+
gap: var(--panneau-space-1);
|
|
116
|
+
border-radius: var(--panneau-radius-sm);
|
|
117
|
+
color: var(--panneau-color-link);
|
|
118
|
+
font-size: var(--panneau-font-size-sm);
|
|
119
|
+
text-decoration: none;
|
|
120
|
+
outline: none;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.panneau-item-panel-preview-link[data-hovered] {
|
|
124
|
+
text-decoration: underline;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.panneau-item-panel-preview-link[data-focus-visible] {
|
|
128
|
+
box-shadow: 0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* The page of the site, as tall as the panel allows. */
|
|
132
|
+
.panneau-item-panel-frame {
|
|
133
|
+
flex: 1 1 auto;
|
|
134
|
+
width: 100%;
|
|
135
|
+
min-height: 28rem;
|
|
136
|
+
border: var(--panneau-border-width) solid var(--panneau-color-border);
|
|
137
|
+
border-radius: var(--panneau-radius-md);
|
|
138
|
+
background: var(--panneau-color-surface);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* From the `xl` width of the root: beside the list, in the flow of the page. */
|
|
142
|
+
@container panneau-root (min-width: 80rem) {
|
|
143
|
+
.panneau-item-panel-panel {
|
|
144
|
+
position: sticky;
|
|
145
|
+
top: var(--panneau-space-4);
|
|
146
|
+
z-index: auto;
|
|
147
|
+
width: auto;
|
|
148
|
+
max-height: calc(100dvh - 2 * var(--panneau-space-4));
|
|
149
|
+
min-height: min(40rem, calc(100dvh - 2 * var(--panneau-space-4)));
|
|
150
|
+
border: var(--panneau-border-width) solid var(--panneau-color-border);
|
|
151
|
+
border-radius: var(--panneau-item-panel-radius, var(--panneau-radius-lg));
|
|
152
|
+
box-shadow: var(--panneau-item-panel-shadow, var(--panneau-shadow-md));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.panneau-item-panel-wide {
|
|
156
|
+
width: auto;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.panneau-item-panel-panel:focus-visible {
|
|
160
|
+
box-shadow:
|
|
161
|
+
var(--panneau-item-panel-shadow, var(--panneau-shadow-md)),
|
|
162
|
+
0 0 0 var(--panneau-focus-width) var(--panneau-color-focus);
|
|
163
|
+
}
|
|
164
|
+
}
|