@cboxdk/cbox-ui 0.8.0 → 0.9.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 +69 -22
- package/dist/app-switcher.d.ts +19 -0
- package/dist/app-switcher.js +7 -0
- package/dist/app-switcher.js.map +1 -0
- package/dist/catalog-BroZWvws.d.ts +100 -0
- package/dist/chunk-AJCYF7SS.js +808 -0
- package/dist/chunk-AJCYF7SS.js.map +1 -0
- package/dist/index.d.ts +21 -69
- package/dist/index.js +22 -239
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ Match id's existing patterns — see id/resources/js/pages/* for examples.
|
|
|
114
114
|
| `OrgSwitcher` | Dropdown listing the user's orgs. Calls `onSwitchOrganization` (or POSTs to `urls.switchOrgUrl`) on pick. |
|
|
115
115
|
| `AppLauncher` | 9-dot grid showing sister Cbox apps the user can jump into. |
|
|
116
116
|
| `UserMenu` | Avatar dropdown with "Manage account ↗" + "Team & billing ↗" (deep-links into id) + Sign out. Pass `LinkComponent` when those rows are pages of the host app, so the visit stays client-side. |
|
|
117
|
-
| `CboxAppCatalog` | The Switch-app control fed live from the host's catalog endpoint (see "App catalog" below). |
|
|
117
|
+
| `CboxAppCatalog` | The Switch-app control fed live from the host's catalog endpoint: the shared `<cbox-app-switcher>` element (see "App catalog" below). |
|
|
118
118
|
| `NotificationsBell` | Bell icon with unread badge, links to id's unified inbox. Renders nothing if context lacks `notifications`. |
|
|
119
119
|
|
|
120
120
|
### Page primitives
|
|
@@ -193,35 +193,82 @@ callback; without it, existing browser formatting is preserved.
|
|
|
193
193
|
|
|
194
194
|
### App catalog (Switch app)
|
|
195
195
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
196
|
+
Every Cbox app, Blade or React, draws the same Switch-app control: the `<cbox-app-switcher>`
|
|
197
|
+
custom element from `@cboxdk/cbox-ui/app-switcher`. It has no React dependency and renders
|
|
198
|
+
in a shadow root with its own styles, so no app stylesheet (a reset, a missing preflight, a
|
|
199
|
+
global `button`, `img` or `svg` rule) changes how it looks. It takes only the host's Cbox
|
|
200
|
+
colour tokens (`--foreground`, `--popover`, `--border`, `--secondary`, `--muted`,
|
|
201
|
+
`--accent-soft`, `--ring`), each with a fallback.
|
|
202
|
+
|
|
203
|
+
**Blade (or any server-rendered page).** Import the module once in the app's Vite entry
|
|
204
|
+
(`resources/js/app.js`):
|
|
205
|
+
|
|
206
|
+
```js
|
|
207
|
+
import '@cboxdk/cbox-ui/app-switcher';
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
and put the tags in the layout:
|
|
211
|
+
|
|
212
|
+
```blade
|
|
213
|
+
<cbox-app-switcher
|
|
214
|
+
endpoint="{{ $appCatalogUrl ?? '' }}"
|
|
215
|
+
current="billing"
|
|
216
|
+
labels="{{ json_encode(['title' => __('Switch app'), 'loading' => __('Loading apps…'), 'empty' => __('No apps available'), 'unavailable' => __('Apps could not be loaded.')], JSON_UNESCAPED_UNICODE) }}"
|
|
217
|
+
></cbox-app-switcher>
|
|
218
|
+
|
|
219
|
+
{{-- The app's own mark (rail, brand), from the same catalog --}}
|
|
220
|
+
<cbox-service-mark service="billing" aria-hidden="true"></cbox-service-mark>
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**React.** `CboxAppCatalog` renders the same element:
|
|
201
224
|
|
|
202
225
|
```tsx
|
|
203
226
|
<CboxAppCatalog endpoint={appCatalogUrl} currentAppKey="infra" />
|
|
204
227
|
```
|
|
205
228
|
|
|
206
|
-
|
|
229
|
+
Attributes of `<cbox-app-switcher>`:
|
|
230
|
+
|
|
231
|
+
| Attribute | |
|
|
232
|
+
|---|---|
|
|
233
|
+
| `endpoint` | The host's same-origin catalog URL. Empty disables the control (pages without an organization). Another origin is refused. |
|
|
234
|
+
| `current` | This app's key: its tile is marked current. |
|
|
235
|
+
| `labels` | JSON with any of `title`, `loading`, `empty`, `unavailable`. Missing keys stay English. |
|
|
236
|
+
| `theme` | `auto` (default): `.dark`/`[data-theme="dark"]` or `.light`/`[data-theme="light"]` on `<html>`, else `prefers-color-scheme`. `class`: only the root marker counts, no marker is light (a host whose tokens only turn dark under `.dark`). `light`/`dark` pin it. |
|
|
237
|
+
|
|
238
|
+
`<cbox-service-mark service="key">` paints that app's icon from the catalog this browser last
|
|
239
|
+
kept, and repaints when a newer one arrives. It is 24px; set
|
|
240
|
+
`cbox-service-mark { --cbox-service-mark-size: 22px; }` to change it. It takes `theme` too.
|
|
241
|
+
|
|
242
|
+
The endpoint contract (`parseAppCatalog` and `signInTarget` are exported, so a host can test
|
|
243
|
+
its own answer):
|
|
207
244
|
|
|
208
245
|
- `200 { apps: [{ key, name, launch_url, icon_url?, icon_dark_url?, description? }], refresh_after?: seconds }`
|
|
209
246
|
— `refresh_after` is clamped to 1–60 s; apps without an `http(s)` launch URL are dropped.
|
|
210
|
-
- `401`/`403
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
`
|
|
247
|
+
- `401`/`403 { signin_url }` — the person has to sign in again. When they open the control
|
|
248
|
+
(or it is open when the answer arrives), the element sends the top window to `signin_url`:
|
|
249
|
+
no message, no button. A page that merely loads in the background is never moved. If the
|
|
250
|
+
same sign-in was tried in the last 30 seconds and the catalog still refuses, it shows "Apps
|
|
251
|
+
could not be loaded." instead, so it never loops. The legacy `{ authorize_url }` is followed
|
|
252
|
+
the same way, with `return_to` set to the current path (same-origin URLs only).
|
|
253
|
+
`signin_url` must be on the app's own origin; anything else is ignored.
|
|
254
|
+
- Anything else, or no network — "Apps could not be loaded.", quietly, and it tries again by
|
|
255
|
+
itself (2 s, 4 s, 8 s … up to 60 s). A grid it already shows stays.
|
|
256
|
+
|
|
257
|
+
Before navigating it fires `cbox-app-switcher:signin` (bubbling, composed, cancelable, with
|
|
258
|
+
`detail.url`); `preventDefault()` keeps the page where it is.
|
|
259
|
+
|
|
260
|
+
App icons come from Cbox ID's catalog only: `icon_url` for light surfaces and
|
|
261
|
+
`icon_dark_url` for dark ones; the element swaps them with the theme. Only `https:` icons
|
|
262
|
+
render; an app without one, or whose icon fails to load, shows its initials. Apps keep no
|
|
263
|
+
local copy of the marks. Each answer is kept in this browser (`localStorage`,
|
|
264
|
+
`cbox-app-catalog`), so the grid and the marks paint at once on the next page and the live
|
|
265
|
+
answer replaces them. The icons are served by Cbox ID, so the host's CSP needs
|
|
266
|
+
`img-src https://cbox.cboxid.com` (the element's styles are constructed stylesheets, which
|
|
267
|
+
`style-src` does not govern).
|
|
268
|
+
|
|
269
|
+
In React, `<CboxServiceMark appKey="infra" />` and the collapsed `CboxBrand` read the same
|
|
270
|
+
kept catalog; `CboxAppIcon` renders any single app's icon, and a host that loads the catalog
|
|
271
|
+
itself calls `rememberAppCatalog(apps)`.
|
|
225
272
|
|
|
226
273
|
### Workspace navigation (current design contract)
|
|
227
274
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { b as CboxAppCatalogState, c as CboxCatalogApp, a as CboxKnownApp, C as CboxThemeMode, d as authorizeHref, i as isDark, p as parseAppCatalog, r as readKnownAppCatalog, e as rememberAppCatalog, s as secureIconUrl, f as signInTarget, g as subscribeKnownAppCatalog } from './catalog-BroZWvws.js';
|
|
2
|
+
|
|
3
|
+
/** The words the control shows. Pass a translation as the `labels` attribute (JSON). */
|
|
4
|
+
type CboxAppSwitcherLabels = {
|
|
5
|
+
title: string;
|
|
6
|
+
loading: string;
|
|
7
|
+
empty: string;
|
|
8
|
+
unavailable: string;
|
|
9
|
+
};
|
|
10
|
+
declare const DEFAULT_LABELS: CboxAppSwitcherLabels;
|
|
11
|
+
/**
|
|
12
|
+
* Fired on `<cbox-app-switcher>` (bubbling, composed, cancelable) just before it sends the
|
|
13
|
+
* person to sign in again; `detail.url` is where. `preventDefault()` keeps the page where it is.
|
|
14
|
+
*/
|
|
15
|
+
declare const SIGNIN_EVENT = "cbox-app-switcher:signin";
|
|
16
|
+
/** Define `<cbox-app-switcher>` and `<cbox-service-mark>` once. A no-op outside a browser. */
|
|
17
|
+
declare function defineCboxAppSwitcher(): void;
|
|
18
|
+
|
|
19
|
+
export { type CboxAppSwitcherLabels, DEFAULT_LABELS, SIGNIN_EVENT, defineCboxAppSwitcher };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { defineCboxAppSwitcher } from './chunk-AJCYF7SS.js';
|
|
2
|
+
export { DEFAULT_LABELS, SIGNIN_EVENT, authorizeHref, defineCboxAppSwitcher, isDark, parseAppCatalog, readKnownAppCatalog, rememberAppCatalog, secureIconUrl, signInTarget, subscribeKnownAppCatalog } from './chunk-AJCYF7SS.js';
|
|
3
|
+
|
|
4
|
+
// src/app-switcher/index.ts
|
|
5
|
+
defineCboxAppSwitcher();
|
|
6
|
+
//# sourceMappingURL=app-switcher.js.map
|
|
7
|
+
//# sourceMappingURL=app-switcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/app-switcher/index.ts"],"names":[],"mappings":";;;;AAcA,qBAAA,EAAsB","file":"app-switcher.js","sourcesContent":["/**\n * `@cboxdk/cbox-ui/app-switcher` — the one Switch-app control for every Cbox app, framework\n * free. Importing this module defines `<cbox-app-switcher>` and `<cbox-service-mark>`:\n *\n * import '@cboxdk/cbox-ui/app-switcher';\n *\n * <cbox-app-switcher endpoint=\"/acme/apps/catalog\" current=\"billing\"\n * labels='{\"title\":\"Skift app\"}'></cbox-app-switcher>\n * <cbox-service-mark service=\"billing\" aria-hidden=\"true\"></cbox-service-mark>\n *\n * Both render in a shadow root with their own styles, so no app stylesheet changes them.\n */\nimport { defineCboxAppSwitcher } from './elements';\n\ndefineCboxAppSwitcher();\n\nexport { defineCboxAppSwitcher, DEFAULT_LABELS, SIGNIN_EVENT, type CboxAppSwitcherLabels } from './elements';\nexport {\n authorizeHref,\n parseAppCatalog,\n readKnownAppCatalog,\n rememberAppCatalog,\n secureIconUrl,\n signInTarget,\n subscribeKnownAppCatalog,\n type CboxAppCatalogState,\n type CboxCatalogApp,\n type CboxKnownApp,\n} from './catalog';\nexport { isDark, type CboxThemeMode } from './theme';\n"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which theme the host page shows, read the way every Cbox app marks it:
|
|
3
|
+
*
|
|
4
|
+
* - `.dark` or `[data-theme="dark"]` on the root element: dark;
|
|
5
|
+
* - `.light` or `[data-theme="light"]` on the root element: light;
|
|
6
|
+
* - neither: the system's `prefers-color-scheme`, unless the element says `theme="class"`
|
|
7
|
+
* (a host whose tokens only turn dark under `.dark`, so no marker means light).
|
|
8
|
+
*
|
|
9
|
+
* `theme="light"` or `theme="dark"` on the element pins it.
|
|
10
|
+
*/
|
|
11
|
+
type CboxThemeMode = 'auto' | 'class' | 'light' | 'dark';
|
|
12
|
+
declare function isDark(mode?: CboxThemeMode): boolean;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The app catalog contract every Cbox app serves, and the copy of it this browser keeps.
|
|
16
|
+
* Framework-free: the `<cbox-app-switcher>` element and the React components both read it.
|
|
17
|
+
*
|
|
18
|
+
* Every app's icon comes from Cbox ID's catalog: one light and one dark upload per app
|
|
19
|
+
* (`icon_url`, `icon_dark_url`). No app keeps a local copy of the marks.
|
|
20
|
+
*/
|
|
21
|
+
/** One app as the host's catalog endpoint lists it. */
|
|
22
|
+
type CboxCatalogApp = {
|
|
23
|
+
key: string;
|
|
24
|
+
name: string;
|
|
25
|
+
launch_url: string;
|
|
26
|
+
/** Cbox ID's uploaded icon for light surfaces. */
|
|
27
|
+
icon_url?: string | null;
|
|
28
|
+
/** Cbox ID's uploaded icon for dark surfaces. */
|
|
29
|
+
icon_dark_url?: string | null;
|
|
30
|
+
description?: string | null;
|
|
31
|
+
};
|
|
32
|
+
type CboxAppCatalogState = {
|
|
33
|
+
status: 'idle' | 'loading';
|
|
34
|
+
} | {
|
|
35
|
+
status: 'ready';
|
|
36
|
+
apps: CboxCatalogApp[];
|
|
37
|
+
refreshAfter: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The person has to sign in again. `signinUrl` is the new contract (the host's `401
|
|
41
|
+
* { signin_url }`), `authorizeUrl` the legacy one (`{ authorize_url }`).
|
|
42
|
+
*/
|
|
43
|
+
| {
|
|
44
|
+
status: 'signin_required';
|
|
45
|
+
signinUrl: string | null;
|
|
46
|
+
authorizeUrl: string | null;
|
|
47
|
+
} | {
|
|
48
|
+
status: 'unavailable';
|
|
49
|
+
};
|
|
50
|
+
/** One app as this browser keeps it between pages. */
|
|
51
|
+
type CboxKnownApp = {
|
|
52
|
+
key: string;
|
|
53
|
+
name: string;
|
|
54
|
+
icon_url: string | null;
|
|
55
|
+
icon_dark_url: string | null;
|
|
56
|
+
launch_url?: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Interpret the catalog endpoint's answer:
|
|
60
|
+
*
|
|
61
|
+
* - `200 { apps: [{ key, name, launch_url, icon_url?, icon_dark_url?, description? }], refresh_after?: seconds }`
|
|
62
|
+
* - `401`/`403 { signin_url }`: the person signs in again there. The legacy `{ authorize_url }`
|
|
63
|
+
* and `{ state: 'authorization_required' }` mean the same.
|
|
64
|
+
* - anything else: unavailable right now.
|
|
65
|
+
*
|
|
66
|
+
* Only `http(s)` launch URLs are kept: the tiles are links the person will click.
|
|
67
|
+
*/
|
|
68
|
+
declare function parseAppCatalog(ok: boolean, body: unknown): CboxAppCatalogState;
|
|
69
|
+
type Here = {
|
|
70
|
+
origin: string;
|
|
71
|
+
pathname: string;
|
|
72
|
+
search: string;
|
|
73
|
+
};
|
|
74
|
+
/** The legacy authorize link with `return_to` set to where the person is, on this origin only. */
|
|
75
|
+
declare function authorizeHref(authorizeUrl: string | null, location: Here): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Where to send the person when the catalog says they must sign in again: the host's
|
|
78
|
+
* `signin_url` when it is on this origin (the app's own sign-in; never another site), else the legacy authorize
|
|
79
|
+
* link with `return_to`. `null` when neither is usable.
|
|
80
|
+
*/
|
|
81
|
+
declare function signInTarget(state: {
|
|
82
|
+
signinUrl: string | null;
|
|
83
|
+
authorizeUrl: string | null;
|
|
84
|
+
}, location: Here): string | null;
|
|
85
|
+
/** Only `https:` icon URLs are rendered: the icons are hosted by Cbox ID. */
|
|
86
|
+
declare function secureIconUrl(url: unknown): string | null;
|
|
87
|
+
/** The last catalog this browser kept (`localStorage` key `cbox-app-catalog`); empty without one. */
|
|
88
|
+
declare function readKnownAppCatalog(): CboxKnownApp[];
|
|
89
|
+
/** Keep the catalog the person just loaded, and tell every mark on this page to repaint. */
|
|
90
|
+
declare function rememberAppCatalog(apps: ReadonlyArray<{
|
|
91
|
+
key: string;
|
|
92
|
+
name: string;
|
|
93
|
+
launch_url?: string | null;
|
|
94
|
+
icon_url?: string | null;
|
|
95
|
+
icon_dark_url?: string | null;
|
|
96
|
+
}>): void;
|
|
97
|
+
/** Call `change` whenever the kept catalog changes, on this page or in another tab. */
|
|
98
|
+
declare function subscribeKnownAppCatalog(change: () => void): () => void;
|
|
99
|
+
|
|
100
|
+
export { type CboxThemeMode as C, type CboxKnownApp as a, type CboxAppCatalogState as b, type CboxCatalogApp as c, authorizeHref as d, rememberAppCatalog as e, signInTarget as f, subscribeKnownAppCatalog as g, isDark as i, parseAppCatalog as p, readKnownAppCatalog as r, secureIconUrl as s };
|