@gemboss/ui 0.47.0 → 0.50.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/CHANGELOG.md +315 -0
- package/README.md +162 -18
- package/catalog/editor.html +1 -1
- package/catalog/index.html +53 -49
- package/gemboss-ui.css +4 -4
- package/package.json +23 -2
- package/parts/components.css +3 -3
- package/parts/core.css +1 -1
- package/parts/shell.css +2 -2
- package/src/app-shell.css +1 -1
- package/src/assistant/chat.tsx +50 -6
- package/src/assistant/host.tsx +19 -7
- package/src/assistant/index.ts +1 -1
- package/src/assistant/sessions.tsx +2 -1
- package/src/assistant-chat.css +1 -1
- package/src/assistant.css +1 -1
- package/src/auth-signin-plain.js +1 -0
- package/src/auth-sso-plain.js +1 -1
- package/src/auth.tsx +105 -8
- package/src/briefing-card.css +1 -1
- package/src/collections.css +1 -1
- package/src/create-shell.css +1 -0
- package/src/create-shell.tsx +221 -0
- package/src/editor-insert.css +1 -0
- package/src/editor.css +1 -1
- package/src/empty-stage.css +1 -0
- package/src/empty-stage.tsx +31 -0
- package/src/icons.tsx +2 -0
- package/src/index-table.tsx +7 -4
- package/src/index.ts +16 -2
- package/src/kit-plain.css +1 -1
- package/src/kit-plain.js +1 -1
- package/src/language-switcher-plain.js +1 -1
- package/src/language-switcher.css +1 -1
- package/src/language-switcher.tsx +10 -41
- package/src/list-page.css +1 -0
- package/src/list-page.tsx +239 -0
- package/src/menu-extras.css +1 -0
- package/src/menu-keys.ts +199 -0
- package/src/menu-layer.ts +2 -1
- package/src/menu-plain.js +1 -0
- package/src/menu.css +1 -0
- package/src/menu.tsx +83 -35
- package/src/pager-extras.css +1 -0
- package/src/pager.css +1 -0
- package/src/pager.tsx +88 -0
- package/src/portal.css +1 -1
- package/src/portal.tsx +19 -4
- package/src/segmented.tsx +12 -2
- package/src/selection.tsx +3 -1
- package/src/session-rail-plain.js +1 -1
- package/src/settings-dialog.tsx +29 -2
- package/src/shell.css +1 -1
- package/src/stage-path.css +1 -0
- package/src/stage-path.tsx +89 -0
- package/src/stage.css +1 -0
- package/src/stage.tsx +52 -0
- package/src/starting-point.css +1 -0
- package/src/starting-point.tsx +93 -0
- package/src/status-list.css +1 -0
- package/src/status-list.tsx +65 -0
- package/src/status-matrix.css +1 -0
- package/src/status-matrix.tsx +131 -0
- package/src/team-chat.css +1 -1
- package/src/topnav-fold.css +1 -0
- package/src/translation-row.css +1 -0
- package/src/translation-row.tsx +113 -0
- package/src/ui.css +1 -1
package/README.md
CHANGED
|
@@ -54,17 +54,20 @@ its CSS out of the build with it (`sideEffects`). gzip of the minified file, as
|
|
|
54
54
|
| Part | Import (Node / bundler) | File in the tarball | What | gzip | Who loads it |
|
|
55
55
|
|---|---|---|---|---:|---|
|
|
56
56
|
| core | `@gemboss/ui/core.css` | `parts/core.css` | the box-sizing reset + the tokens | 1.0 KB | every surface, first |
|
|
57
|
-
| components | `@gemboss/ui/components.css` | `parts/components.css` | the kit (`ui.css`), the field look, the plain-HTML primitives (`kit-plain.css`:
|
|
57
|
+
| components | `@gemboss/ui/components.css` | `parts/components.css` | the kit (`ui.css`), the field look, the ⋯ menu and the icon button (`menu.css`), the plain-HTML primitives (`kit-plain.css`: chips, report pieces, popup body) | 8.2 KB | every surface, second |
|
|
58
|
+
| menu | `@gemboss/ui/menu.css` | `packages/ui/src/menu.css` | the ⋯ menu and its icon button (23/10/2026, moved out of `kit-plain.css`, same rules): part of the core (components, just before `kit-plain.css`, where its rules stood) AND its own file | 0.6 KB | `Menu` imports it, so a surface without the core draws the menu: the admin loads `ui.css` and the shell, never `kit-plain.css` |
|
|
58
59
|
| shell | `@gemboss/ui/shell.css` | `parts/shell.css` | rail, topbar, main, mobile drawer, the thread rail, topbar slots, base text, page widths (`app-shell.css`) | 5.5 KB | every surface, third |
|
|
59
60
|
| auth | `@gemboss/ui/auth.css` | `packages/ui/src/auth.css` | the sign-in screen (+ the grid backdrop, 0.30.0) | 2.8 KB | `AuthLayout` & co. import it |
|
|
60
61
|
| assistant | `@gemboss/ui/assistant.css` | `packages/ui/src/assistant.css` | the dock and the launcher | 2.2 KB | `AssistantHost`, the launcher import it |
|
|
61
62
|
| portal | `@gemboss/ui/portal.css` | `packages/ui/src/portal.css` | `TopbarNav`, `SearchDialog`, `Avatar`, `Leaderboard`, `Podium`, the `ThemeToggle` button | 2.1 KB | those components import it |
|
|
63
|
+
| topnav-fold | `@gemboss/ui/topnav-fold.css` | `packages/ui/src/topnav-fold.css` | `TopbarNav`'s folded menu after the frozen bundle (unreleased): `foot`, `foldAt={760}`, `gb-topnav-unfolded`, the open tab's ellipsis | 0.3 KB | `TopbarNav` imports it (after portal.css) |
|
|
62
64
|
| site | `@gemboss/ui/site.css` | `packages/ui/src/site.css` | the public site layer (0.40.0): the bar, the lockup at the site size, links, buttons, the footer | 1.0 KB | `SiteHeader` / `SiteBrand` import it; a plain-HTML page links it |
|
|
63
65
|
| theme-dark | `@gemboss/ui/theme-dark.css` | `packages/ui/src/theme-dark.css` | the dark palette | 0.6 KB | `ThemeToggle` and `themeScript` (`theme-script.ts`, without portal.css) import it |
|
|
64
66
|
| collections | `@gemboss/ui/collections.css` | `packages/ui/src/collections.css` | tables, lists, grids, boards | 4.6 KB | `IndexTable`, `ResourceList`, `Board`… import it |
|
|
65
67
|
| editor | `@gemboss/ui/editor.css` | `packages/ui/src/editor.css` | the editors | 7.9 KB | the `@gemboss/ui/editor` components import it |
|
|
66
68
|
| editor-repeater | `@gemboss/ui/editor-repeater.css` | `packages/ui/src/editor-repeater.css` | `RepeaterList`: a setting that is a list, as rows | 0.9 KB | `RepeaterList` imports it |
|
|
67
69
|
| editor-tiles | `@gemboss/ui/editor-tiles.css` | `packages/ui/src/editor-tiles.css` | `ChoiceTiles`: a closed choice of shapes as picture tiles | 0.5 KB | `ChoiceTiles` imports it |
|
|
70
|
+
| editor-insert | `@gemboss/ui/editor-insert.css` | `packages/ui/src/editor-insert.css` | the page editor's insert menu (the + on a picked element) | 0.6 KB | admin `InsertElementMenu` imports it |
|
|
68
71
|
| reactions | `@gemboss/ui/reactions.css` | `packages/ui/src/reactions.css` | the emoji reactions and their picker | 0.8 KB | `Reactions` / `ReactionPicker` import it |
|
|
69
72
|
| assistant-engine | `@gemboss/ui/assistant-engine.css` | `packages/ui/src/assistant-engine.css` | the engine picker (chip + Engine/Setup/Status popup) and the bridge notice | 1.9 KB | `EnginePicker` / `EnginePanel` / `BridgeNotice` import it |
|
|
70
73
|
| chat | `@gemboss/ui/chat.css` + `chat-composer.css` | `packages/ui/src/chat.css`, `packages/ui/src/chat-composer.css` | the chat body, the composer | 3.0 + 0.9 KB | the chat components (0.29.0); a page writing the markup links them |
|
|
@@ -81,6 +84,10 @@ its CSS out of the build with it (`sideEffects`). gzip of the minified file, as
|
|
|
81
84
|
| progress-ring | `@gemboss/ui/progress-ring.css` | `packages/ui/src/progress-ring.css` | `ProgressRing`: "3 of 5" as a ring (0.43.0) | 0.2 KB | `ProgressRing` imports it |
|
|
82
85
|
| spot-art | `@gemboss/ui/spot-art.css` | `packages/ui/src/spot-art.css` | `SpotArt`: six brand pictures for pauses and empty states (0.43.0) | 0.4 KB | `SpotArt` imports it |
|
|
83
86
|
| logo-sheet | `@gemboss/ui/logo-sheet.css` | `packages/ui/src/logo-sheet.css` | `LogoSheet`: one logo as a person judges it, the mark on a checkerboard, the lockup on a light and a dark header, the favicon at its real 32px (unreleased) | 0.5 KB | `LogoSheet` imports it |
|
|
87
|
+
| stage-path | `@gemboss/ui/stage-path.css` | `packages/ui/src/stage-path.css` | `StagePath`: stages that unlock in order, each a card with its number (✓ when done), its count and a thin bar; the current one in ink (unreleased) | 0.7 KB | `StagePath` imports it; a plain-HTML page links it (README "Review pieces") |
|
|
88
|
+
| status-matrix | `@gemboss/ui/status-matrix.css` | `packages/ui/src/status-matrix.css` | `StatusMatrix`: rows × columns of "live / total" cells on the kit table, all live or has drafts (unreleased) | 0.3 KB | `StatusMatrix` imports it; a plain-HTML page links it |
|
|
89
|
+
| translation-row | `@gemboss/ui/translation-row.css` | `packages/ui/src/translation-row.css` | `TranslationRow`: one string under review, source beside target, the checks and the action (unreleased) | 0.6 KB | `TranslationRow` imports it; a plain-HTML page links it |
|
|
90
|
+
| status-list | `@gemboss/ui/status-list.css` | `packages/ui/src/status-list.css` | `StatusList`: a quiet system-health strip, a dot, a name and one sentence each (unreleased) | 0.5 KB | `StatusList` imports it; a plain-HTML page links it |
|
|
84
91
|
| bundle | `@gemboss/ui/gemboss-ui.css` | `gemboss-ui.css` | core + components + shell + auth + assistant + chat + portal + theme-dark | 21.9 KB | compatibility only (see CHANGELOG 0.23.0) |
|
|
85
92
|
|
|
86
93
|
The core alone is 14.7 KB against 21.7 for the bundle (each part opens with a short header naming the gemboss commit, kept through minify, so a surface serving the parts can still say which commit its CSS is); a list page adds collections (4.6), a sign-in page auth (2.6).
|
|
@@ -129,7 +136,7 @@ in order and reversed, and holds every element's computed style to the bundle's
|
|
|
129
136
|
CI; negative control: two entries that only agree by order). An entry loaded BEFORE the core is not supported: measured,
|
|
130
137
|
`.ui-empty--no-match` loses its 32px padding to `.ui-empty`. Two kit-plain.css rules that overrode shell.css by coming
|
|
131
138
|
after it (the menu row's flex, the thread rail's 44px buttons at phone width) moved to the end of app-shell.css, same
|
|
132
|
-
weight, so they still follow it.
|
|
139
|
+
weight, so they still follow it. (The menu row's flex is shell.css's own since 23/10/2026: one owner for the row.)
|
|
133
140
|
|
|
134
141
|
**A new component** gets its own entry: a file in `src/`, listed in `ENTRIES` in `packages/ui/scripts/parts.mjs`, exported,
|
|
135
142
|
imported by the component that draws it, with a ceiling in `weight-budget.json`. It never goes into the core or into
|
|
@@ -141,6 +148,37 @@ component that draws an entry's class without importing it.
|
|
|
141
148
|
built part now). Its editor, assistant and sign-in modules already imported the same entry files the components now
|
|
142
149
|
import, so nothing moves.
|
|
143
150
|
|
|
151
|
+
## ListPage (23/10/2026): the frame of a list page
|
|
152
|
+
|
|
153
|
+
One component for a page that lists one kind of thing (the admin's Rules first; the other lists move one PR at a time):
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
<ListPage title="Rules" subtitle="12 rules · 9 on." back={{ href: "/app/content", label: "Types" }} linkAs={Link}
|
|
157
|
+
primaryAction={<Button variant="primary">New rule</Button>}
|
|
158
|
+
secondaryActions={[{ content: "Simulator", onAction }, { content: "Export CSV", url: "/export" }]}
|
|
159
|
+
notices={<Banner …/>} toolbar={<ListToolbar …/>}
|
|
160
|
+
state={saved === null ? (unread ? "error" : "loading") : saved.length ? "ready" : "empty"}
|
|
161
|
+
empty={<EmptyStage …/>} noMatch={{ onClear }} error={{ title: "Your rules couldn’t be read.", message, onRetry }}
|
|
162
|
+
cardTitle="Your rules" after={<PlaybookLibrary …/>}>
|
|
163
|
+
{rows}
|
|
164
|
+
</ListPage>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- The page says WHICH state its list is in; the kit draws it, the same on every list: placeholder rows while loading, the
|
|
168
|
+
page's own empty state, "Nothing matches" + Clear filters, a read that failed + Try again. A change that failed over a list
|
|
169
|
+
that IS there is a notice, not the error state.
|
|
170
|
+
- `primaryAction` is the page's own button (so a Publish button that counts changes stays itself). Secondary actions are data:
|
|
171
|
+
two stand as buttons from a 560px page column, the rest are behind ⋯; under 560 or below 768px, all are behind ⋯.
|
|
172
|
+
- `cardLead`: rows at the top of the card in every state, before the state's loading, empty or error (a row that does not
|
|
173
|
+
wait for the list's read, as Funnels' Website). A page with a lead always has its card, so the row stays the same element.
|
|
174
|
+
`card={false}` is for a list that draws its own cards (a grid); `cardProps` stay on the card in every state (a drop zone).
|
|
175
|
+
- `actionGroups`: choices of one kind (a cadence, a view) behind ⋯ at every width, titled, the one that is on `active`; never
|
|
176
|
+
header buttons. The rows are `MenuItem checked` in a `MenuGroup` (`menuitemradio`, a check before the chosen one).
|
|
177
|
+
- Numbers: Polaris Page in the admin (title 20/24/700 on a 28px row, the line under it 12/16 muted 2px below, actions 28px 8
|
|
178
|
+
apart, 24px above and under the header from 768, 16 below). Below 768 the header is two rows and its controls are 44px.
|
|
179
|
+
Colours are Polaris' where Polaris is loaded (`--p-color-text`, `--p-color-text-secondary`, `--p-color-bg-fill-tertiary`),
|
|
180
|
+
the kit's tokens elsewhere.
|
|
181
|
+
|
|
144
182
|
## Page width (0.8.1)
|
|
145
183
|
|
|
146
184
|
Chris, 04/10/2026: the rail on the left, and on the right a content column with the same max width on every
|
|
@@ -923,6 +961,16 @@ gembossUI.initShell(); // AppShell markup written by hand: ☰ drawer,
|
|
|
923
961
|
gembossUI.initPasswordFields(); // the sign-in's reveal button; resetPasswordFields() on sign-out
|
|
924
962
|
```
|
|
925
963
|
|
|
964
|
+
Menus (23/10/2026): a menu's keys are a SECOND script, `menu-plain.js`, loaded after this one (`<script src>`, inlined, or
|
|
965
|
+
`import "@gemboss/ui/menu-plain.js"` after the kit-plain.js import): kit-plain.js has no room for them under its weight
|
|
966
|
+
ceiling. It runs by itself and gives every `details.ui-menu` what the React `Menu` does (README "Menu"): the roles its
|
|
967
|
+
markup leaves out, the keys on a row (↑ ↓ wrapping past disabled rows, Home End, Escape with focus back on ⋯, Tab leaves;
|
|
968
|
+
↓ / ↑ on ⋯ open it), the first row focused on an opening by a click, Enter or Space, one menu open at a time. Without it a
|
|
969
|
+
plain menu works as before (Tab walks its rows). `initMenus()` (opt-in, as before) closes a menu on a chosen row, now with
|
|
970
|
+
focus back on ⋯ BEFORE the row's own handler, and on an outside click, the window's blur and Escape from anywhere; a page
|
|
971
|
+
that never calls it keeps its menus open on a chosen row. Both scripts return at once where there is no DOM, so a Next
|
|
972
|
+
client module that imports them renders on the server (`plain-ssr.test.ts`).
|
|
973
|
+
|
|
926
974
|
In a bundled app (Next, Vite) load it once with a bare `import "@gemboss/ui/kit-plain.js";` in a client module: the
|
|
927
975
|
package lists the file in `sideEffects`, so the bundler keeps it (before 0.5.0 it dropped it and `gembossUI` was
|
|
928
976
|
undefined in production builds). Loading it writes nothing on `<html>` (0.37.1): the `data-gb-menus` flag kit-plain.css
|
|
@@ -1463,8 +1511,8 @@ import { FLEET_LANGUAGES, LanguageSwitcher, matchLanguage } from "@gemboss/ui";
|
|
|
1463
1511
|
| The menu | the kit's, not a new one: `details.ui-menu` (it opens with no JavaScript), its list `ui-menu__list gemboss-popover-surface` floated in the top layer by `./menu-layer` as `Menu`'s is, its rows `ui-menu__item`, so it looks like the account menu. `role="menu"` named `trigger`; each row a `menuitemradio` with `aria-checked`, `data-lang`, a check column (`.gb-lang__check`, shown on the checked row only) and the name in its own `lang` (`span.gb-lang__name lang="zh"`), so a screen reader says "中文" in Chinese. The suggested row carries `ui-menu__hint`, then `hr.ui-menu__sep` |
|
|
1464
1512
|
| Keys | an opening (a click, Enter / Space, ↓ / ↑ on the button) puts focus on the checked row; ↑ ↓ wrap, Home / End; Escape closes it, focus back on the button; Tab closes it and focus goes on from the button, forward or back; a click outside closes it; a row closes it, focus on the button, and calls `onChange` when it is not the current one. Rows are out of the Tab order (`tabindex="-1"`), as a menu's are |
|
|
1465
1513
|
| Helpers | `matchLanguage(languages, wanted)`: the first of `navigator.languages`, an Accept-Language header or one tag that the list has, exactly or by its base language ("zh-CN" → "zh"), else undefined; pure, so a server reads the header with it. `languageShort(language)` |
|
|
1466
|
-
| CSS | `language-switcher.css`, its own entry, 1.1 KB gzip, imported by the component. The menu's base rules (the core's `ui-menu` ones) are repeated there at zero specificity (`:where`)
|
|
1467
|
-
| The topbar | at 320 / 360 / 390 the
|
|
1514
|
+
| CSS | `language-switcher.css`, its own entry, 1.1 KB gzip, imported by the component. The menu's base rules (the core's `ui-menu` ones) are repeated there with the core's values, so a page that links only this file (gemboss.ai, whose pages the core would reset; GemNews's extension) draws the same menu. The surface and the box at zero specificity (`:where`); the rows, the hint and the separator at ONE class (0,1,0, since 22/10/2026), the core's own weight: an app's `button { font: inherit }` (0,0,1) cannot make them 16px, and one class of the app's (`.gb-lang__item { … }` after it, `.my-bar .gb-lang__item`) restyles them on purpose (the gap and corners take two: `.gb-lang__item.ui-menu__item`, so app-shell.css's unset tokens on a page without the core cannot zero the gap). Tokens, so `data-theme="dark"` follows, each with its light value as the fallback: no core, no `gemboss-tokens.css`, still right (light) |
|
|
1515
|
+
| The topbar | AppShell's phone bar (☰, brand, search, toggle, switcher, account chip) fits it at 320 / 360 / 390, between ThemeToggle and the chip, no horizontal scroll. A portal `Topbar` with `TopbarNav` does NOT have that room (GemFeedback: brand, tab menu, search, theme, Sign in, no room for a 55px button at 320–390, and at 601–687 the tabs ran under it): there the switcher goes in the folded tab menu, `TopbarNav foot`, and its end-slot copy wears `gb-topnav-unfolded` (README "Public portal pieces" › TopbarNav) |
|
|
1468
1516
|
|
|
1469
1517
|
### Plain HTML: `gembossUI.languageSwitcher()` (21/10/2026)
|
|
1470
1518
|
|
|
@@ -1495,7 +1543,8 @@ the page has it; it holds no closing script tag, so a server can inline it.
|
|
|
1495
1543
|
| | |
|
|
1496
1544
|
| --- | --- |
|
|
1497
1545
|
| Options | the component's props, same names and defaults (`value`, `languages`, `onChange`, `suggested`, `labels`, `variant`, `end`, `className`), plus `mount`: an element the switcher REPLACES (a placeholder the server wrote) |
|
|
1498
|
-
| Returns | `{ el, value(), set(code), open(), close(), destroy() }`. A chosen row updates the switcher itself before `onChange` (the component waits for its `value` prop); `set` is for a change from elsewhere; `destroy` removes it and its two document listeners |
|
|
1546
|
+
| Returns | `{ el, value(), set(code), update(props), setLabels(labels), open(), close(), destroy() }`. A chosen row updates the switcher itself before `onChange` (the component waits for its `value` prop); `set` is for a change from elsewhere; `destroy` removes it and its two document listeners |
|
|
1547
|
+
| `update` / `setLabels` (22/10/2026) | the page's language changed, or its list: `sw.setLabels({ trigger: T("menu.lang"), suggested: T("menu.lang_suggested") })`, `sw.update({ value, labels, suggested, languages })` (any of them). In place, as the component re-renders: the button's name and label, the menu's name, the hint; the rows drawn again only when `languages` or `suggested` change, the row that had focus focused again. An open menu stays open, placed again; the same `el`. `labels` given replace those words only; `suggested: null` takes the row away. No more destroy + new + carrying the focus by hand (GemNews's `paintLang`) |
|
|
1499
1548
|
| `gembossUI` | `languageSwitcher`, `FLEET_LANGUAGES`, `LANGUAGE_SWITCHER_LABELS`, `matchLanguage`, `languageShort`. With `kit-plain.js` the open list floats in the top layer (`gembossUI.floatMenu`) and `initMenus` closes it like any kit menu; without it the list hangs under the button (`position: absolute`), as every kit menu does where there is no popover API |
|
|
1500
1549
|
| A Chrome extension | a popup or options page loads both files by `src` (no inline script: an extension's CSP forbids it) and calls it from its own script; `onChange` writes `chrome.storage` |
|
|
1501
1550
|
|
|
@@ -1505,10 +1554,18 @@ two DOMs node for node, attribute for attribute, with focus, open state and the
|
|
|
1505
1554
|
script: closed, opened by a click, ↓ ↓ Home End ↑, Escape, ↓ on the button, Home + Enter, the current row, a click outside,
|
|
1506
1555
|
Tab; with the defaults and with Vietnamese labels, a suggested row, the inline variant, left-aligned, links. NEGATIVE
|
|
1507
1556
|
CONTROL: one `lang` changed on the plain page and the comparison sees it. Then: the short label per language, the list
|
|
1508
|
-
under the button's right edge, one tick; 36 / 44px and
|
|
1557
|
+
under the button's right edge, one tick; 36 / 44px and rows of 44 under a finger; the phone bar at 390 / 360 / 320; contrast in both
|
|
1509
1558
|
themes (label ≥ 4.5:1, tick ≥ 3:1); the standalone page's menu equal to the core's (negative control: without the file,
|
|
1510
|
-
the browser's popover box). `
|
|
1511
|
-
|
|
1559
|
+
the browser's popover box). 22/10/2026: `update` / `setLabels` with the menu open and a row focused (the plain DOM equal to
|
|
1560
|
+
the component's re-render, focus and open state kept); an app's `button { font: inherit; font-size: 16px; padding: 0 }`,
|
|
1561
|
+
`a {}` and `hr {}` before or after the kit, and a page with only this file (+ app-shell.css): 13px rows at the core's
|
|
1562
|
+
padding (4px 6px under a mouse, 12px 16px under a finger), the tick 8px from the name (negative controls: the rows back at
|
|
1563
|
+
`:where` → 16px; without the two-class gap rule, app-shell.css after it → 0px gap);
|
|
1564
|
+
GemFeedback's portal bar with the switcher in `TopbarNav foot`, `foldAt={760}`: one language control at 320–1024, nothing
|
|
1565
|
+
overlapping or cut (negative control: at 320 without the foot the tab menu is squeezed out).
|
|
1566
|
+
`LANGUAGE_SWITCHER_SHOTS=<dir>` writes the screenshots (closed / open, both variants, both themes, vi / en / zh).
|
|
1567
|
+
`language-switcher.test.tsx` holds the markup, the labels, the helpers, the plain file's defaults and the CSS contract (the
|
|
1568
|
+
rows at one class, every token with its fallback).
|
|
1512
1569
|
|
|
1513
1570
|
## Repeater rows (16/10/2026): `RepeaterList`, `useKeyedRows`
|
|
1514
1571
|
|
|
@@ -1653,9 +1710,23 @@ import { AppShell, BrandLockup, AccountMenu, AccountMenuItem, Icon, RailItem } f
|
|
|
1653
1710
|
outranks an attribute on the `<svg>` itself and paints an outline icon solid.
|
|
1654
1711
|
- `Menu` / `MenuItem` / `MenuSeparator` (0.2.1): the "⋯" action menu. Same `<details>` rule as the account
|
|
1655
1712
|
menu (works as plain markup; with React it closes on an outside click, on Escape with focus back on the
|
|
1656
|
-
trigger, and after a
|
|
1657
|
-
(`href`) or a button; `disabled` on a link drops the href and sets `aria-disabled`; `className` is added
|
|
1713
|
+
trigger, and after a row is chosen). Rows share the account menu's row style. `MenuItem` is a link
|
|
1714
|
+
(`href`, `target`, `rel`) or a button; `disabled` on a link drops the href and sets `aria-disabled`; `className` is added
|
|
1658
1715
|
to the row's class.
|
|
1716
|
+
A menu (23/10/2026, `menu-keys.ts`, shared with `LanguageSwitcher`; `menu-plain.js` after kit-plain.js for plain markup):
|
|
1717
|
+
|
|
1718
|
+
| | |
|
|
1719
|
+
|---|---|
|
|
1720
|
+
| Semantics | the list is `role="menu"` named by `label` (the trigger's name), the trigger `aria-haspopup="menu"`, each row `role="menuitem"` with `tabindex="-1"`: the arrows move between rows, Tab does not |
|
|
1721
|
+
| Opening | a click, Enter or Space on ⋯ puts focus on the first row that can act; ↓ on ⋯ opens on the first, ↑ on the last; a script's `open = true` moves no focus |
|
|
1722
|
+
| Keys | on a ROW (a field or a checkbox in the list keeps its own): ↑ ↓ wrap and skip disabled rows (`disabled`, `aria-disabled="true"`); Home / End; Enter and Space act on the row (Space on a link row too); Escape closes with focus back on ⋯ and is taken (a popup around the menu stays open); Tab / Shift+Tab close it and focus goes on from ⋯ |
|
|
1723
|
+
| Choosing | focus goes back on ⋯ BEFORE the row's handler (a popup it opens returns focus there, not to a hidden row; a handler that moves focus on purpose keeps it), then the menu closes, link rows included. Plain markup: with `initMenus()`, opt-in as before |
|
|
1724
|
+
| One open | opening a menu closes every other open `details.ui-menu`, by a click, a key or a script (a key is not a click outside); a menu that holds the one opening (a menu in a menu, a kit menu in the account menu) stays open |
|
|
1725
|
+
| Listeners | one page-wide set for every mounted `Menu` (`registerMenu`): outside click, Escape, the float on its toggle, one open. A 100-row table has 100 Menus and one set of listeners, not 200 |
|
|
1726
|
+
| In a row that opens on click | `IndexTable` rows with `href` and `tr[data-href]` leave the menu be: opening, moving and choosing never follow the row |
|
|
1727
|
+
| CSS | `menu.css`, imported by `menu.tsx`: placement, the floated list, hint, separator, disabled row, the square ⋯ (`ui-btn--icon`). The surface and the row are shell.css's (which the admin loads too): `.ui-menu__item` a flex row (label, then the hint on the right; the account menu's `.gemboss-account-menu-item` stays a block), 32px under a mouse, 44 under a finger or below 768. So a page with `ui.css` + `shell.css` + `menu.css` and no `app-shell.css` (the admin), in either order, draws what the core draws |
|
|
1728
|
+
|
|
1729
|
+
`menu-browser.test.ts` runs each of these on `Menu` and on plain markup with kit-plain.js, each with a negative control.
|
|
1659
1730
|
|
|
1660
1731
|
## Topbar search (0.6.3)
|
|
1661
1732
|
|
|
@@ -1696,7 +1767,25 @@ import { Avatar, Dialog, Leaderboard, Podium, SearchDialog, ThemeToggle, themeSc
|
|
|
1696
1767
|
- `TopbarNav`: section tabs in the topbar after the brand, the active one underlined on the topbar's
|
|
1697
1768
|
edge. A topbar holding tabs drops the centre search column (`:has(.gb-topnav)`), so the tabs get the
|
|
1698
1769
|
room. 36px under a mouse, 44px under a finger. Under 600px the tabs fold into one button naming the
|
|
1699
|
-
open tab, with the tabs in a list under it (a row that scrolls sideways hid the third tab at 390).
|
|
1770
|
+
open tab, with the tabs in a list under it (a row that scrolls sideways hid the third tab at 390). The open tab's name
|
|
1771
|
+
gives way with an ellipsis before it runs under the topbar's end (22/10/2026: "Changelog" at 320).
|
|
1772
|
+
- `foot` (22/10/2026): the app's own section at the foot of the folded list, under a line; shown only folded. What the
|
|
1773
|
+
topbar's end has no room for once the tabs fold, the language first. Its copy in the end slot wears
|
|
1774
|
+
`gb-topnav-unfolded`, hidden wherever the tabs fold, so the bar has one at every width, one at a time:
|
|
1775
|
+
|
|
1776
|
+
```tsx
|
|
1777
|
+
const Lang = (p: Partial<LanguageSwitcherProps>) => <LanguageSwitcher value={lang} languages={withHrefs} {...p} />;
|
|
1778
|
+
<div className="gemboss-topbar-start"><BrandLockup name="GemFeedback" href="/" />
|
|
1779
|
+
<TopbarNav as={Link} items={tabs} foldAt={760} foot={<Lang variant="inline" end={false} />} /></div>
|
|
1780
|
+
<div className="gemboss-topbar-center" />
|
|
1781
|
+
<div className="gemboss-topbar-end"><SearchDialog … /><ThemeToggle /><Lang className="gb-topnav-unfolded" /><SignInButton … /></div>
|
|
1782
|
+
```
|
|
1783
|
+
- `foldAt={760}`: fold over the whole phone topbar (≤760px) instead of under 600px, for a bar whose end holds more
|
|
1784
|
+
(GemFeedback signed out: search, theme, language, Sign in; its three tabs ran under the switcher at 601–687). Measured
|
|
1785
|
+
in Chromium: brand + folded tabs + search + theme + Sign in fit from 320 with the language in the foot, and from 761 the
|
|
1786
|
+
tabs + all four controls.
|
|
1787
|
+
- These three and the ellipsis are `topnav-fold.css`, an entry of their own that `TopbarNav` imports after `portal.css`
|
|
1788
|
+
(which is in the frozen `gemboss-ui.css`). A page that renders TopbarNav's markup without the component links it.
|
|
1700
1789
|
- `SearchDialog`: the topbar search button opening a 640px popup (Featurebase's, the admin's ⌘K).
|
|
1701
1790
|
`initial` before typing, `search(q)` while typing (150 ms debounce, latest answer wins); ↑ ↓ Enter
|
|
1702
1791
|
Esc; ⌘K / Ctrl+K and "/" open it. Results are links; without JavaScript the button goes to
|
|
@@ -2574,6 +2663,41 @@ thumb and at phone width.
|
|
|
2574
2663
|
]} />
|
|
2575
2664
|
```
|
|
2576
2665
|
|
|
2666
|
+
## Review pieces (unreleased, after 0.49.0): `StagePath`, `StatusMatrix`, `TranslationRow`, `StatusList`
|
|
2667
|
+
|
|
2668
|
+
GemPress's redesign (Chris approved it 23/10/2026) drew four things with its own CSS (`gp-unlock`, `gp-cov`, `gp-str`, `gp-sys`)
|
|
2669
|
+
that are not about GemPress: a path of stages that unlock in order, a map of "how much is live" per row and column, one string
|
|
2670
|
+
under review, and a strip of system health. GemNews and GemCommunity translations review the same way, and every service has a
|
|
2671
|
+
health line to show, so they live here. Each is its own entry, presentational and server-renderable (no state, no effect, no
|
|
2672
|
+
inline style), painted by tokens (the dark palette follows), and usable from plain HTML: link `@gemboss/ui/<name>.css` beside the
|
|
2673
|
+
core and write the classes listed at the top of that file. The tests hold those lists to the components' markup.
|
|
2674
|
+
|
|
2675
|
+
| Piece | What it draws |
|
|
2676
|
+
| --- | --- |
|
|
2677
|
+
| `StagePath` | `stages` (`{ key, name, done, total, unit?, href? }`) as an `<ol>` of cards in a grid (`repeat(auto-fit, minmax(190px, 1fr))`): a number badge (a green ✓ once `done >= total`; a stage with nothing in it is done), the name (a link when `href`, the whole card its target), "3 / 35 pages approved" and a thin `<progress>` bar (hidden from a screen reader: the words say it). `current` (default the first stage not done, `null` for none) has an ink outline and an ink number, and is `aria-current="step"`. `label` names the list (default "Unlock path"); `caption` and `format` make the words the surface's. `stagePathDone` is the rule, pure. |
|
|
2678
|
+
| `StatusMatrix` | the kit table (`ui-table-wrap` + `ui-table`) with `columns` across and `rows` down, each row's name its `th scope="row"` (a link when `href`). A cell is "**live** / total" with a second line for what waits ("5 draft · 2 changed", "3 missing"); its tone is all live (green number) or has drafts (amber number), never colour alone. A `null` or absent cell, or a row whose `total` is 0, is "—". `rowHeading`, `totalHeading` (a column of the rows' totals, e.g. the source language), `caption` (what the numbers mean, under the table), `format`, `labels`. `statusMatrixTone` is the rule, pure. |
|
|
2679
|
+
| `TranslationRow` | one string: a head (`keyName` in code, `state` as a kit badge: `draft` "To review", `approved`, `stale` "Source changed", `missing` "Not translated"; `by`), the `source` beside the `target` (two columns; one under 900px, or when the row is narrower than 600px: it is a size container), and a foot with `checks` (`{ ok, text }`: a green "✓ …" or the problems in amber words) on the left and the surface's `action` on the right. The editable control is the surface's: `target` is a node, or a function given the ids of the key, the source and the checks so the field can say `aria-describedby`. `current` draws an ink border and a 3px ink bar inside the left edge (`aria-current`). The row is `tabIndex={-1}`: a J / K list moves focus to it (pass `id`, or find it by `data-*` the row passes through), and it is never a Tab stop. `as="li"` inside an `<ol>`. |
|
|
2680
|
+
| `StatusList` | `items` (`{ key, label, text, tone, href? }`) as a grid (`repeat(auto-fill, minmax(260px, 1fr))`): a dot, a bold name, one muted sentence (a link when `href`). `ok`, `warning`, `critical` fill the dot with the success, warning and danger tokens; `muted` is an empty ring (off, not applicable); an unknown tone is muted. |
|
|
2681
|
+
| `stage-path.css`, `status-matrix.css`, `translation-row.css`, `status-list.css` | one entry each, loaded by its component; `status-matrix.css` needs the core's table (`ui.css`), and `TranslationRow`'s badge tones are the core's (`kit-plain.css`, in `components.css`) |
|
|
2682
|
+
|
|
2683
|
+
**Quiet when fine, a banner is for when a person must act.** The status list is the line a page ends on: nothing in it asks for
|
|
2684
|
+
anything. Something a person must do today goes in a `ui-banner` above the page, not in an amber dot at the bottom of it.
|
|
2685
|
+
|
|
2686
|
+
```tsx
|
|
2687
|
+
import { StagePath, StatusMatrix, TranslationRow, StatusList } from "@gemboss/ui";
|
|
2688
|
+
|
|
2689
|
+
<StagePath label="Unlock path" stages={[
|
|
2690
|
+
{ key: "frame", name: "Header & footer", done: 2, total: 2, unit: "parts" },
|
|
2691
|
+
{ key: "pages", name: "Pages", done: 3, total: 35, unit: "pages", href: "#/review?stage=pages" },
|
|
2692
|
+
]} />
|
|
2693
|
+
<StatusMatrix rowHeading="Collection" totalHeading="EN" caption="Live / published." columns={[{ key: "vi", label: "VI" }]}
|
|
2694
|
+
rows={[{ key: "pages", label: "Pages", total: 35, cells: { vi: { live: 30, draft: 5, changed: 2 } } }]} />
|
|
2695
|
+
<TranslationRow as="li" id="vi-hero" keyName="hero.title" state="draft" source="Ship in 3 days" sourceLang="en" by="gpt-4o" current={at === 0}
|
|
2696
|
+
target={(ids) => <textarea className="ui-textarea" lang="vi" aria-describedby={`${ids.source} ${ids.checks}`} value={text} onChange={…} />}
|
|
2697
|
+
checks={{ ok: true, text: "Numbers and tags match" }} action={<button className="ui-btn ui-btn--sm" onClick={approve}>Approve</button>} />
|
|
2698
|
+
<StatusList label="System" items={[{ key: "backups", label: "Backups", text: "nightly · last 22 Oct", tone: "ok", href: "#/settings" }]} />
|
|
2699
|
+
```
|
|
2700
|
+
|
|
2577
2701
|
## Collections (0.13.0): tables, lists, grids, boards
|
|
2578
2702
|
|
|
2579
2703
|
Chris settled these on 05/10/2026 after an inventory of 136 hand-made collections on 11 surfaces (86 tables scrolling
|
|
@@ -2615,17 +2739,28 @@ the admin's first load (`kit-plain-bundles.test.ts` checks the shell import carr
|
|
|
2615
2739
|
|
|
2616
2740
|
### The rules, and what checks them
|
|
2617
2741
|
|
|
2742
|
+
- **One header for both tables (23/10/2026).** An index table's header is the data table's: the surface, 13px regular, 12px
|
|
2743
|
+
above and below (45 tall), opaque so the sticky headers hide what scrolls under them; its cells 8px 6px. The index table
|
|
2744
|
+
takes these from the data table's rules (ui.css), so the two cannot drift. Polaris draws an index header as a grey 12px /
|
|
2745
|
+
550 band over 6px cells; the admin's unselectable Polaris IndexTables (all of them on 23/10/2026) take the kit's instead
|
|
2746
|
+
(gemboss-shell.css, #5185).
|
|
2747
|
+
|
|
2618
2748
|
- **The whole row opens the item; the title stays a real link** (Cmd-click, a screen reader). A click on a checkbox, a
|
|
2619
2749
|
button, the ⋯ menu, a select or a second link does only that; so does a click that ends a text selection. A row with
|
|
2620
2750
|
two or more in-place editors is not linked (only its title is). HTML: `tr[data-href]` + kit-plain.js; React:
|
|
2621
2751
|
`IndexTable` rows with `href`; a resource list's title link covers its row (no script).
|
|
2752
|
+
- **The referrer is the page's to set, not the row's.** A row opens with `noopener` and the browser's default policy
|
|
2753
|
+
(`strict-origin-when-cross-origin`): another site learns this site's origin, never the path. A surface whose rows open
|
|
2754
|
+
other sites and must send nothing sets it once for every link: `Referrer-Policy: same-origin` (header) or
|
|
2755
|
+
`<meta name="referrer" content="same-origin">`. Per-link `noreferrer` in the kit was tried (#5176, then #5180's first
|
|
2756
|
+
draft) and dropped: it missed ways in (a Cmd-click straight on the title is the browser's) and every other link type.
|
|
2622
2757
|
- **One visible button per row at most**, the rest in the ⋯ menu (`ui-menu`). **Never shown only on hover.**
|
|
2623
2758
|
- **An open ⋯ menu is never cut by its frame (0.18.0).** It floats in the browser's top layer (`popover="manual"`,
|
|
2624
2759
|
`position: fixed` against its button): below the button, above it when the room below is short and the room above is
|
|
2625
2760
|
larger, 8px from the viewport's edges, scrolling inside when taller than the room. It closes when a scroll moves its
|
|
2626
2761
|
button (the page, or a frame around the menu) or the window resizes; a scroll inside the list or elsewhere (a log
|
|
2627
|
-
following its output) does not count.
|
|
2628
|
-
menu is not a click on the row" are unchanged: the list is still the details' child. React `Menu` does it itself;
|
|
2762
|
+
following its output) does not count. The keys (README "Menu", 23/10/2026: arrows, Escape with focus back on ⋯) and "a
|
|
2763
|
+
click in the menu is not a click on the row" are unchanged: the list is still the details' child. React `Menu` does it itself;
|
|
2629
2764
|
plain HTML gets it from kit-plain.js (`initMenuLayer()`, runs by itself). No popover API (Safari < 17) or no script:
|
|
2630
2765
|
the absolute list, as before. GemFactory Members (#197) is the case: at 820 / 900 / 1280 the last rows' menus were
|
|
2631
2766
|
half hidden inside the table's scrolling frame.
|
|
@@ -2639,7 +2774,8 @@ the admin's first load (`kit-plain-bundles.test.ts` checks the shell import carr
|
|
|
2639
2774
|
activity's detail wrap instead of ending in "…", because the cut text's `title` is mouse-only. A side rail of short
|
|
2640
2775
|
labels keeps one line. `CompactItem.body` (activity only) is the event's own text under them (`.ui-list__content`,
|
|
2641
2776
|
body size, never cut; paragraphs as `<p>`); `labelText` puts the full label in `title`.
|
|
2642
|
-
- **Below 768 of its container an index table becomes a list** (title line
|
|
2777
|
+
- **Below 768 of its container an index table becomes a list** (title line: a linked title is cut, an unlinked one and free
|
|
2778
|
+
text under it wrap, a long token breaks; the other cells on one caption line,
|
|
2643
2779
|
`data-label` prefixes, `data-condensed="hide"` drops; checkbox and actions kept; rows ≥ 44); sort moves to the chips
|
|
2644
2780
|
of `ui-index__sortbar`. **A data table never folds:** it scrolls in its frame with `--sticky-first`.
|
|
2645
2781
|
- **A data table reads from the top of its rows (0.16.0)**, as Polaris DataTable (`verticalAlign` "top"): one-line
|
|
@@ -2650,7 +2786,8 @@ the admin's first load (`kit-plain-bundles.test.ts` checks the shell import carr
|
|
|
2650
2786
|
`minWidth` (DataTable) is the floor of a column whose cells wrap, and replaces the row header's (`0` for a row
|
|
2651
2787
|
header that is always short: a date, an id). A link alone on its
|
|
2652
2788
|
line is 24px in the row header and in a list inside a cell (`li > a`), as `td > a` already was.
|
|
2653
|
-
- **A data table in a card is `flush
|
|
2789
|
+
- **A data table or an index table in a card is `flush`** (DataTable 0.34.0; IndexTable unreleased, its bulk bar
|
|
2790
|
+
then lies on the card's top edge), as a resource list in a card: the card is the frame, so the wrap drops its
|
|
2654
2791
|
own border, radius and shadow (two frames otherwise). The last row has no bottom rule, row header included. The card
|
|
2655
2792
|
clips its corners (`overflow: hidden`, as GemWatcher's Panel): the last row's hover, the totals and the footer are square.
|
|
2656
2793
|
- **A media card is 16:10, 4:3 or 1:1 (0.18.0)** (`MediaCard ratio`, `data-ratio="4/3" | "1/1"`): 1:1 for a square
|
|
@@ -2738,6 +2875,11 @@ gap. Under 576px only Previous · the current page · Next stay and the range wr
|
|
|
2738
2875
|
took three to four rows at 320). The old form `<span aria-current="page">2 / 9</span>` keeps its muted look. The surface computes which numbers to
|
|
2739
2876
|
show (first, last, current ± 1, a gap only where it hides two or more pages: GemFactory `pageWindow`).
|
|
2740
2877
|
|
|
2878
|
+
React: `<Pager page pageCount range href={(n) => \`?p=${n}\`} linkAs={Link} />` draws that markup (`pageWindow` is exported
|
|
2879
|
+
too). A list that pages in STATE (its search and filters not in the URL, as the admin's Products) passes `onPage` instead of
|
|
2880
|
+
`href` and gets buttons with the same look (pager-extras.css), Previous / Next marked `data-rel`; `busy` while a page is on its
|
|
2881
|
+
way. The rules are `pager.css` (in the core, moved out of kit-plain.css 24/10/2026 with Chris's OK) and `pager-extras.css`.
|
|
2882
|
+
|
|
2741
2883
|
`ui-more`: one centred row. Idle: a `ui-btn` ("Show 24 more"). Loading: `<span class="ui-spinner" aria-hidden="true">` +
|
|
2742
2884
|
`<span role="status">Loading…</span>`. Error: `<span role="alert">Couldn't load.</span>` + "Try again". End: a line. As an
|
|
2743
2885
|
IntersectionObserver sentinel it has height from its padding; zero the padding and it has none.
|
|
@@ -2789,10 +2931,12 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
2789
2931
|
| Path | What |
|
|
2790
2932
|
|---|---|
|
|
2791
2933
|
| `parts/core.css`, `parts/components.css`, `parts/shell.css` | 0.23.0: the core, built by `packages/ui/scripts/build-css.mjs` from the same sections as `gemboss-ui.css` (see "Load only what you use"). |
|
|
2792
|
-
| `gemboss-ui.css` | built at stage time and shipped minified (0.19.0); since 0.23.0 the COMPATIBILITY BUNDLE, frozen: `apps/admin/scripts/export-ui-css.mjs` (tokens + kit + field + `packages/ui/src/shell.css`, with Polaris `--p-*` resolved to their fallbacks) + `packages/ui/src/app-shell.css` + `auth.css` + `assistant.css` + `kit-plain.css` + `chat.css` + `chat-composer.css` (`packages/ui/scripts/build-css.mjs`) |
|
|
2934
|
+
| `gemboss-ui.css` | built at stage time and shipped minified (0.19.0); since 0.23.0 the COMPATIBILITY BUNDLE, frozen: `apps/admin/scripts/export-ui-css.mjs` (tokens + kit + field + `packages/ui/src/shell.css`, with Polaris `--p-*` resolved to their fallbacks) + `packages/ui/src/app-shell.css` + `auth.css` + `assistant.css` + `menu.css` + `kit-plain.css` + `chat.css` + `chat-composer.css` (`packages/ui/scripts/build-css.mjs`) |
|
|
2793
2935
|
| `packages/ui/src/shell.css` | the SHARED SHELL (rail, topbar, main, mobile drawer, the assistant's thread rail, popover surface, account chip). Exported as `@gemboss/ui/shell-source.css` since 0.23.0 (`@gemboss/ui/shell.css` is the built part). The admin's `gemboss-shell.css` @imports it on line 2; the export refuses a shared-shell rule in the admin's sheet or an admin-only rule here (one owner). |
|
|
2794
|
-
| `packages/ui/src/
|
|
2795
|
-
| `packages/ui/src/
|
|
2936
|
+
| `packages/ui/src/menu-plain.js` | 23/10/2026: `gembossUI.initMenuKeys()`, a menu's roles and keys and one menu open at a time for plain HTML (`@gemboss/ui/menu-plain.js`), the same as `menu-keys.ts`. After `kit-plain.js`; runs by itself. See "Plain-HTML behaviour". |
|
|
2937
|
+
| `packages/ui/src/menu.css` | 23/10/2026: the ⋯ menu and its icon button, moved out of `kit-plain.css` (same rules, same order in the core). Imported by `Menu`; see "Menu". |
|
|
2938
|
+
| `packages/ui/src/kit-plain.css` | primitives for surfaces that render plain HTML, not React (0.2.1): key/value list (the menu and the icon button are `menu.css` since 23/10/2026); 0.3.0 adds `ui-link` / `ui-prose`, the media card (`ui-card--media`, `__media`, `__body`, `__foot`) and the popup's `ui-modal__body` / `__foot`. Shipped inside `gemboss-ui.css`; the admin does not import it (its first-load CSS budget has ~0.1 KB headroom).; (0.4.0) report pages: delta, large KPI grid, bar list, column chart, legend, funnel, card grid, section head. |
|
|
2939
|
+
| `packages/ui/src/kit-plain.js` | 0.3.0: the behaviour those surfaces each wrote by hand: `gembossUI.openModal` (Escape, backdrop, Tab trap, stacking, ask-before-close, focus back), `toast` (text, error tone), `initMenus` (outside click, item with focus back on ⋯ first, blur, Escape closes the menu before the popup); 0.5.0: `confirm`, `initShell`, `initPasswordFields` / `resetPasswordFields`. A classic script: inline it or `<script src>`; see "Plain-HTML behaviour" above. |
|
|
2796
2940
|
| `packages/ui/src/collections.css` | 0.13.0: tables, lists, grids, boards and their states (see "Collections"). A separate entry, NOT inside `gemboss-ui.css`; the admin does not load it. |
|
|
2797
2941
|
| `packages/ui/src/editor/` | 0.14.0, `@gemboss/ui/editor`: `EditorFrame`, `EditorPaneTabs`, `EditorTopbar`, `EditorBack`, `EditorChatToggle`, `EditorSaveState`, `DeviceToggle`. See "Editor frame". 0.17.0: `PanelTabs`, `PanelHeader`, `PanelAction`, `InspectorGroup`, `Field`, `Seg`, `ColorControl`, `RangeControl`, `EdgeBox`, `ImageControl`, `ChipButton`, `swatchState` (`./editor/swatch` too); 0.20.1 `cssUrl`. See "Settings panel". 0.22.0: `LayerList`, `SortableLayerList`, `LayerTree`, `EditorDrawer`. See "Layers and drawer". |
|
|
2798
2942
|
| `packages/ui/src/editor-plain.js` | 0.14.0: `gembossUI.initEditorFrame()`, the frame's behaviour for plain HTML; 0.17.0: `gembossUI.initEditorPanel()`, the settings panel's. Load after `kit-plain.js`. |
|
package/catalog/editor.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
.cat-code summary{padding:10px 16px;font-size:13px;cursor:pointer;color:var(--gemboss-text-muted);border-top:1px solid var(--gemboss-divider);min-height:24px}
|
|
9
9
|
.cat-code pre,.cat-api{margin:0;padding:12px 16px;overflow:auto;background:#0f1012;color:#e8e8e6;font-size:12px;line-height:1.5;border-radius:0}
|
|
10
10
|
.cat-api{border-radius:12px;margin:0 0 16px}</style></head><body><div class="cat">
|
|
11
|
-
<h1>@gemboss/ui 0.
|
|
11
|
+
<h1>@gemboss/ui 0.50.0 · Editor</h1>
|
|
12
12
|
<p>Khung của mọi editor: <code>EditorFrame</code> (cột trái · khe kéo · preview · cột phải dock trên mép phải preview) và <code>EditorTopbar</code> (← · tiêu đề · cụm giữa · cụm phải · dòng trạng thái). Là đúng khung của theme / page / section editor trong admin. Gói chỉ vẽ KHUNG; chat, Configure, canvas, page picker, menu, Publish là của editor, đi vào bằng slot. Trang HTML thuần nạp <code>editor.css</code> + <code>editor-plain.js</code> rồi gọi <code>gembossUI.initEditorFrame()</code>: kéo khe (chuột, chạm, phím mũi tên / Home / End), đổi thiết bị, mở ⋯ và Publish ▾, ẩn cột trái, Chat | Preview trên điện thoại. <a href="index.html">← Catalog</a></p>
|
|
13
13
|
<h2>React</h2><pre class="cat-api"><code>import "@gemboss/ui/editor.css";
|
|
14
14
|
import { EditorFrame, EditorTopbar, EditorBack, EditorChatToggle, EditorPaneTabs, EditorSaveState, DeviceToggle } from "@gemboss/ui/editor";
|