@gemboss/ui 0.7.2 → 0.8.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/README.md +127 -6
- package/catalog/index.html +234 -6
- package/gemboss-ui.css +82 -27
- package/package.json +1 -1
- package/src/app-shell.css +26 -0
- package/src/assistant/dock.ts +19 -18
- package/src/assistant/host.tsx +40 -5
- package/src/assistant/provider.tsx +43 -19
- package/src/index.ts +3 -0
- package/src/kit-plain.css +31 -12
- package/src/page.tsx +35 -0
- package/src/ui.css +23 -13
package/src/page.tsx
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How wide a page's content may grow right of the rail (0.8.1). The admin's tiers, same numbers:
|
|
3
|
+
* default → `--ui-content-max` (900px): lists, tables, card grids, dashboards. Admin `.gemboss-w-gallery` / `-dashboard`.
|
|
4
|
+
* feed → 720px: something read top to bottom (activity, a log, a timeline). Admin `.gemboss-w-feed`.
|
|
5
|
+
* form → 662px: one column of fields (settings). Admin `.gemboss-w-form`, Polaris' narrowWidth.
|
|
6
|
+
* canvas → 1440px: content that is WIDER than a column of text and was measured to need it (a horizontal path,
|
|
7
|
+
* a wide comparison table). Admin `.gemboss-w-canvas`.
|
|
8
|
+
* full → no cap: a board or an editor that is the whole screen. Say why at the call site.
|
|
9
|
+
* The CSS is `.ui-page` + `.ui-page--<tier>` in app-shell.css; HTML surfaces write the class themselves.
|
|
10
|
+
*
|
|
11
|
+
* Its OWN module, not shell.tsx, and not "use client": the admin imports Shell / ShellMain / Rail* from shell.tsx, a
|
|
12
|
+
* client module the bundler keeps whole, and its first-load shell budget had 0.0 KB of room (240.0 / 240, 04/10/2026).
|
|
13
|
+
* In shell.tsx this code reached the admin's first load; here nothing the admin imports pulls it in. No state, so a
|
|
14
|
+
* server component can render it too.
|
|
15
|
+
*/
|
|
16
|
+
import React from "react";
|
|
17
|
+
|
|
18
|
+
export type PageWidth = "default" | "feed" | "form" | "canvas" | "full";
|
|
19
|
+
|
|
20
|
+
/** `ui-page` plus the tier's modifier: for a surface that renders the wrapper itself (a server template). */
|
|
21
|
+
export function pageWidthClass(width: PageWidth = "default"): string {
|
|
22
|
+
return width === "default" ? "ui-page" : `ui-page ui-page--${width}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `div.ui-page`: the content column, capped at `width`'s tier and centred in the space right of the rail. Put ONE
|
|
27
|
+
* directly inside `AppShell`, with the CURRENT route's tier, so every screen is capped in one place:
|
|
28
|
+
* `<AppShell …><PageContainer width={route === "settings" ? "form" : "default"}>{page}</PageContainer></AppShell>`.
|
|
29
|
+
* A page cannot widen a cap its parent set, so do not nest a second one to get `canvas` or `full`.
|
|
30
|
+
*/
|
|
31
|
+
export function PageContainer({ as: Comp = "div", width = "default", className, children, ...rest }:
|
|
32
|
+
{ as?: React.ElementType; width?: PageWidth; className?: string; children?: React.ReactNode }
|
|
33
|
+
& Omit<React.HTMLAttributes<HTMLElement>, "className" | "children">) {
|
|
34
|
+
return <Comp className={className ? `${pageWidthClass(width)} ${className}` : pageWidthClass(width)} {...rest}>{children}</Comp>;
|
|
35
|
+
}
|
package/src/ui.css
CHANGED
|
@@ -274,10 +274,11 @@ a.ui-metric:focus-visible { outline: 2px solid var(--ui-focus); outline-offset:
|
|
|
274
274
|
|
|
275
275
|
/* ── Modal ────────────────────────────────────────────────────────────────────
|
|
276
276
|
Rendered INLINE (not portaled): a portal escapes the .gemboss-admin-shell token
|
|
277
|
-
scope and would render light. Kept in-tree so --ui-* resolves to the surface.
|
|
277
|
+
scope and would render light. Kept in-tree so --ui-* resolves to the surface.
|
|
278
|
+
0.7.3: Polaris Modal's density (#1099, README "Popup density"). Popup only: --ui-control-h stays 32. */
|
|
278
279
|
.ui-overlay {
|
|
279
280
|
position: fixed; inset: 0; z-index: 500;
|
|
280
|
-
background: rgba(0, 0, 0, 0.
|
|
281
|
+
background: rgba(0, 0, 0, 0.5);
|
|
281
282
|
display: grid; place-items: center; padding: 20px;
|
|
282
283
|
}
|
|
283
284
|
.ui-modal {
|
|
@@ -287,30 +288,39 @@ a.ui-metric:focus-visible { outline: 2px solid var(--ui-focus); outline-offset:
|
|
|
287
288
|
}
|
|
288
289
|
/* Head = title + the one close button every modal used to draw for itself. */
|
|
289
290
|
.ui-modal__head {
|
|
290
|
-
display: flex; align-items:
|
|
291
|
-
padding: 16px
|
|
292
|
-
font-size:
|
|
291
|
+
display: flex; align-items: center; gap: 12px;
|
|
292
|
+
padding: 12px 16px; border-bottom: 1px solid var(--ui-border);
|
|
293
|
+
font-size: 14px; line-height: 20px; font-weight: 650; color: var(--ui-text);
|
|
293
294
|
}
|
|
294
295
|
.ui-modal__title { min-width: 0; flex: 1 1 auto; }
|
|
295
296
|
.ui-modal__close {
|
|
296
297
|
flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
|
|
297
|
-
width:
|
|
298
|
+
width: 28px; height: 28px; padding: 0;
|
|
298
299
|
border: 0; border-radius: var(--ui-radius-md, 8px); background: transparent;
|
|
299
300
|
color: var(--ui-text-muted); cursor: pointer;
|
|
300
301
|
}
|
|
301
302
|
.ui-modal__close svg { width: 20px; height: 20px; }
|
|
302
303
|
.ui-modal__close:hover { background: var(--ui-surface-2); color: var(--ui-text); }
|
|
303
304
|
.ui-modal__close:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: -2px; }
|
|
305
|
+
/* Here, not in kit-plain.css: the admin loads only this file (0.7.3). */
|
|
306
|
+
.ui-modal__body { padding: 16px; }
|
|
307
|
+
.ui-modal__foot {
|
|
308
|
+
display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end;
|
|
309
|
+
padding: 12px 16px; border-top: 1px solid var(--ui-border);
|
|
310
|
+
}
|
|
311
|
+
:is(.ui-modal__head, .ui-modal__foot) .ui-btn { height: 28px; padding: 0 12px; font-size: var(--ui-fs-caption); }
|
|
312
|
+
/* 44px under a thumb or at phone width; -8px keeps the head 53. */
|
|
313
|
+
@media (pointer: coarse), (max-width: 47.9975em) {
|
|
314
|
+
.ui-modal__close { width: 44px; height: 44px; margin: -8px -8px -8px 0; }
|
|
315
|
+
:is(.ui-modal__head, .ui-modal__foot) .ui-btn { min-height: 44px; }
|
|
316
|
+
}
|
|
304
317
|
|
|
305
|
-
/* `size="lg"
|
|
306
|
-
|
|
307
|
-
consumer belongs in that consumer's chunk (see SettingsDialog.module.css). */
|
|
318
|
+
/* `size="lg"`: only the HOOK here (this sheet is paid on every screen); its geometry is in
|
|
319
|
+
SettingsDialog.module.css, the one consumer's chunk. */
|
|
308
320
|
.ui-modal--lg { position: relative; }
|
|
309
321
|
|
|
310
|
-
/* The kit's multi-line field
|
|
311
|
-
|
|
312
|
-
lighter --ui-border box at 14px with no focus ring, so on GemFactory's compose form a textarea sat
|
|
313
|
-
beside selects of a darker border and text fields of a third look (measured on prod). */
|
|
322
|
+
/* The kit's multi-line field is the ONE field family (01/10/2026, Chris: "form control đồng bộ"): same
|
|
323
|
+
border token, hover and focus ring as `gemboss-field` / `.ui-select` (it drew a third look on GemFactory). */
|
|
314
324
|
.ui-textarea {
|
|
315
325
|
width: 100%; min-height: 96px; resize: vertical; box-sizing: border-box;
|
|
316
326
|
background: var(--p-color-input-bg-surface, var(--gemboss-field-bg));
|