@loopbacktools/app 0.0.0-stage → 0.4.1
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/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/src/app.d.ts +8 -0
- package/dist/src/app.js +145 -0
- package/dist/src/header.d.ts +11 -0
- package/dist/src/header.js +67 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +4 -0
- package/dist/src/location.d.ts +2 -0
- package/dist/src/location.js +2 -0
- package/dist/src/page.d.ts +18 -0
- package/dist/src/page.js +11 -0
- package/dist/src/paths.d.ts +11 -0
- package/dist/src/paths.js +16 -0
- package/dist/src/ports.d.ts +79 -0
- package/dist/src/ports.js +1 -0
- package/dist/src/theme.d.ts +2 -0
- package/dist/src/theme.js +7 -0
- package/package.json +67 -3
- package/src/app.tsx +335 -0
- package/src/header.tsx +196 -0
- package/src/index.ts +14 -0
- package/src/location.ts +2 -0
- package/src/page.tsx +62 -0
- package/src/paths.ts +21 -0
- package/src/ports.ts +87 -0
- package/src/theme.ts +8 -0
- package/styles.css +5 -0
package/src/ports.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { LoopbackApi } from "@loopbacktools/sdk";
|
|
2
|
+
import type { Icon } from "@phosphor-icons/react";
|
|
3
|
+
import type { ComponentType } from "react";
|
|
4
|
+
|
|
5
|
+
// What the app needs from the deployment it runs in, and nothing more: who is signed in, which
|
|
6
|
+
// workspaces they can open, and anything of the deployment's own. Each deployment fills these in,
|
|
7
|
+
// in a short entry file of its own. See docs/design/one-app.md.
|
|
8
|
+
|
|
9
|
+
/** Someone signed in, as the app shows them. */
|
|
10
|
+
export interface SignedIn {
|
|
11
|
+
/** How to show them, such as their name or email address, when the deployment knows. */
|
|
12
|
+
readonly name?: string;
|
|
13
|
+
/** Signs them out, where there is something to sign out of. */
|
|
14
|
+
readonly signOut?: () => void | Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface Session {
|
|
18
|
+
/**
|
|
19
|
+
* Who is signed in: `undefined` while that is being found out, `null` when nobody is. A React
|
|
20
|
+
* hook, so the deployment can read its own session however it keeps it.
|
|
21
|
+
*/
|
|
22
|
+
readonly useSignedIn: () => SignedIn | null | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The page for someone not signed in, at `/sign-in`. Once they are, the app sends them on to
|
|
25
|
+
* `next`, the path they were going to.
|
|
26
|
+
*/
|
|
27
|
+
readonly SignIn: ComponentType<{ readonly next: string }>;
|
|
28
|
+
/**
|
|
29
|
+
* Where someone at the sign-in page goes once signed in, when it is a page of the server's
|
|
30
|
+
* rather than the app's, as an authorization an agent started is. The app loads it in full, in
|
|
31
|
+
* place of routing to `next`. Given the sign-in page's query; `undefined` for no such page.
|
|
32
|
+
*/
|
|
33
|
+
readonly continueAt?: (search: URLSearchParams) => string | undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A workspace someone can open: one Loopback space. */
|
|
37
|
+
export interface WorkspaceSummary {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Workspaces {
|
|
43
|
+
/** The workspaces the person signed in can open: `undefined` while loading. A React hook. */
|
|
44
|
+
readonly useList: () => readonly WorkspaceSummary[] | undefined;
|
|
45
|
+
/** Which of them the app opens first: the one they used last, say. A React hook. */
|
|
46
|
+
readonly usePreferred?: () => string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* The client for one workspace, carrying a credential for it. A React hook, giving back the same
|
|
49
|
+
* client for as long as the workspace stays open.
|
|
50
|
+
*/
|
|
51
|
+
readonly useClient: (workspace: string) => LoopbackApi;
|
|
52
|
+
/** Makes a new workspace: its id, or `undefined` when none was made. */
|
|
53
|
+
readonly create?: () => Promise<string | undefined>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** An entry in one of the app's menus: somewhere to go, or something to do. */
|
|
57
|
+
export interface MenuItem {
|
|
58
|
+
readonly label: string;
|
|
59
|
+
readonly icon?: Icon;
|
|
60
|
+
readonly to?: string;
|
|
61
|
+
readonly onSelect?: () => void | Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A page of the deployment's own. It stands alone, without the inbox's header: see `Page`. */
|
|
65
|
+
export interface ExtensionPage {
|
|
66
|
+
/** Where it is, as a route path: `/account`, or `/w/:workspace/settings`. */
|
|
67
|
+
readonly path: string;
|
|
68
|
+
readonly Page: ComponentType;
|
|
69
|
+
/** Whether it is for someone signed in; anyone else is sent to sign in, and back. */
|
|
70
|
+
readonly signedIn: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface Extensions {
|
|
74
|
+
readonly pages?: readonly ExtensionPage[];
|
|
75
|
+
/** Items for a workspace's menu, below the workspaces. */
|
|
76
|
+
readonly workspaceMenu?: (workspace: string) => readonly MenuItem[];
|
|
77
|
+
/** Items for the menu of the person signed in, above signing out. */
|
|
78
|
+
readonly accountMenu?: readonly MenuItem[];
|
|
79
|
+
/** Shown above a workspace's inbox. */
|
|
80
|
+
readonly Banner?: ComponentType<{ readonly workspace: string }>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface LoopbackAppOptions {
|
|
84
|
+
readonly session: Session;
|
|
85
|
+
readonly workspaces: Workspaces;
|
|
86
|
+
readonly extensions?: Extensions;
|
|
87
|
+
}
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Kumo themes by `data-mode` on the root element, so it follows the system's light or dark setting. */
|
|
2
|
+
export const followSystemTheme = (): void => {
|
|
3
|
+
const dark = window.matchMedia("(prefers-color-scheme: dark)");
|
|
4
|
+
const apply = () =>
|
|
5
|
+
document.documentElement.setAttribute("data-mode", dark.matches ? "dark" : "light");
|
|
6
|
+
apply();
|
|
7
|
+
dark.addEventListener("change", apply);
|
|
8
|
+
};
|