@gemboss/ui 0.35.7 → 0.36.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/CHANGELOG.md +83 -0
- package/README.md +115 -3
- package/catalog/editor.html +1 -1
- package/catalog/index.html +1882 -120
- package/gemboss-ui.css +3 -3
- package/package.json +8 -2
- package/parts/components.css +1 -1
- package/parts/core.css +1 -1
- package/parts/shell.css +1 -1
- package/src/assistant-chat.css +1 -1
- package/src/brand.tsx +9 -3
- package/src/chat-composer.css +1 -1
- package/src/chat.css +1 -1
- package/src/collections.css +1 -1
- package/src/editor.css +1 -1
- package/src/icons.tsx +2 -0
- package/src/index.ts +6 -0
- package/src/service-switcher-plain.js +1 -0
- package/src/service-switcher.css +1 -0
- package/src/service-switcher.tsx +215 -0
- package/src/services.ts +89 -0
- package/src/shell.css +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,89 @@
|
|
|
2
2
|
|
|
3
3
|
Versions up to 0.9.0 are described in the README, each in the section that introduced it.
|
|
4
4
|
|
|
5
|
+
## 0.36.1 (15/10/2026)
|
|
6
|
+
|
|
7
|
+
**The package reads only tokens it declares.** It read eight `--gemboss-*` tokens it never declared, each with a
|
|
8
|
+
fallback, so nothing failed: the browser painted the fallback hex. That hex is light-theme only, and in one place it
|
|
9
|
+
was wrong in light too. gemcare's copy of gemboss-ui.css failed its token gate on six of them (15/10/2026).
|
|
10
|
+
|
|
11
|
+
Fixed
|
|
12
|
+
- Editor inspector repeater (`.gemboss-ei-rep-*`): the rows and the add button had a `#2a2a3a` border on the white
|
|
13
|
+
panel, and the move/remove buttons hovered to that same near-black behind `#1b1c1e` glyphs. Now `--gemboss-divider`
|
|
14
|
+
and `--gemboss-hover`, like the rest of the panel.
|
|
15
|
+
- Chat markdown in dark mode outside a bubble: table headers, code blocks, inline code and images kept `#f7f7f7`; quotes
|
|
16
|
+
and h3 kept `#616161`. They now read `--gemboss-primary-soft`, `--gemboss-divider` and `--gemboss-text-muted`, which
|
|
17
|
+
`theme-dark.css` redefines. In light the shift is a hair (header `#f7f7f7` → `#f2f3f5`, border `#e3e3e3` → `#e5e7ea`);
|
|
18
|
+
the code-language label goes `#8a8a8a` → `#686b71`.
|
|
19
|
+
- `.gemboss-ec-stop`: `--gemboss-danger` with the send button's `--gemboss-on-primary` ink. Light is unchanged (white
|
|
20
|
+
on `#d72c0d`, 4.9:1); dark is `#0d0d12` on `#ff6b5a` (6.9:1), where white would be 2.8:1.
|
|
21
|
+
- `.gemboss-page-draft-status button` border: `--gemboss-divider` (was `#ccc`), as kit buttons. Lifted board card
|
|
22
|
+
(`[data-gb-lifted]`): `--gemboss-shadow-raised`.
|
|
23
|
+
|
|
24
|
+
Unchanged on screen
|
|
25
|
+
- Focus rings read `var(--ui-focus, #5661f5)` (the undeclared `--gemboss-focus` in front of it is gone); the trace
|
|
26
|
+
`code` font is the literal mono stack; the assistant thread rail's selected row is the literal `rgba(0,0,0,0.07)`
|
|
27
|
+
the export already wrote for it (`--gemboss-active-bg` is lighter than the row's `--gemboss-hover`, so it would
|
|
28
|
+
read as less selected than a hovered row).
|
|
29
|
+
|
|
30
|
+
Tests: `tokens-declared.test.ts` (every `var(--gemboss-*)` in the package's CSS and JS names a token the package
|
|
31
|
+
declares; only the editor's runtime `--gemboss-pv-*` and `--gemboss-editor-topbar-h` are exempt). On 0.36.0 it fails
|
|
32
|
+
with 30 reads of eight tokens. `chat-plain.test.ts` (0.35.4) removed the bubble rules and expected a dark answer to fall
|
|
33
|
+
under 3:1; with theme tokens underneath it no longer does, so that control now holds it at 4.5:1 or more (6.54 at its
|
|
34
|
+
weakest). The seller's bubble, light and dark, still falls under 3:1 without its rules.
|
|
35
|
+
|
|
36
|
+
## 0.36.0 (15/10/2026)
|
|
37
|
+
|
|
38
|
+
**ServiceSwitcher: the logo goes home, the button beside it opens every other Gem service.** Chris 15/10/2026: on an
|
|
39
|
+
internal service, a click on the logo goes home, and beside it a dropdown to the other Gem services, ordered and organised,
|
|
40
|
+
working on a phone, a full panel when needed: a grid of each service's logo with a line under it saying what it is for.
|
|
41
|
+
README "ServiceSwitcher".
|
|
42
|
+
|
|
43
|
+
Added
|
|
44
|
+
- `ServiceSwitcher` (client): a nine-dot button after the brand (`aria-haspopup="dialog"`, `aria-expanded`; 32px, 44 under a
|
|
45
|
+
finger or at phone width) opening a native `<dialog class="gb-svc">` through `useModalDialog`. Desktop: under the topbar
|
|
46
|
+
from the brand's left edge, `min(760px, 100vw - 24px)` wide, at most 80% of the height. ≤640px: the whole screen, two
|
|
47
|
+
columns, `env(safe-area-inset-*)`. A search box (name, purpose, group; Enter follows the first match), the groups in
|
|
48
|
+
order, tiles that are links to each home (same tab); the current service's tile is `aria-current="page"` with the accent
|
|
49
|
+
ring and still links home. Arrows by place on screen, Home / End, one tile in the Tab order; it opens on the current tile.
|
|
50
|
+
- `BrandLockup switcher={{ current, services?, labels?, allHref?, search? }}`: the logo link as before, the button after it.
|
|
51
|
+
Without the prop the lockup renders exactly as before.
|
|
52
|
+
- `GEM_SERVICES`, `SERVICE_GROUPS`, `SERVICE_TINTS`, `groupServices()` and the types `GemService`, `ServiceId`,
|
|
53
|
+
`ServiceGroup`, `ServiceGroupId`, `ServiceTint` (src/services.ts, plain data, also `@gemboss/ui/services`): sixteen
|
|
54
|
+
services in six groups, in the order a store is built and run: GemBoss; Build the store (GemFactory, GemMedia, GemForm);
|
|
55
|
+
Read the market (GemWatcher, GemMarket, GemCapture, Search Radar); Content and community (GemPress, Content Radar,
|
|
56
|
+
GemCommunity, GemCommunity News); Customers (GemCare, GemFeedback); Team and partners (Partner Radar, Taskdesk).
|
|
57
|
+
- `SERVICES_HEALTH_URL` (GemFactory's `/services`), the panel's default foot link.
|
|
58
|
+
- `service-switcher.css`: its own entry `./service-switcher.css`, loaded by the component; `gemboss-ui.css` and the core
|
|
59
|
+
unchanged. Nine tint hues as `--gb-svc-*` tokens on `:root`, lighter under `:root[data-theme="dark"]`, always mixed with
|
|
60
|
+
the theme's own surface and text.
|
|
61
|
+
- `Icon name="apps"`: nine dots, drawn as `more` draws its three (the set is 45 icons).
|
|
62
|
+
- Plain HTML: `service-switcher-plain.js` (`@gemboss/ui/service-switcher-plain.js`, its own entry, in `sideEffects`),
|
|
63
|
+
`gembossUI.initServiceSwitcher(options?)` and `gembossUI.GEM_SERVICES`. A placeholder
|
|
64
|
+
`<span data-gb-service-switcher="<service id>" hidden>` right after `.gb-brand` is replaced by the component's button and
|
|
65
|
+
panel, the same DOM (asserted node for node against the React component in Chromium), so `service-switcher.css` draws it
|
|
66
|
+
unchanged, with the same behaviour. Options are the component's props (`labels`, `allHref`, `search`, `services`,
|
|
67
|
+
`className`, `current`) plus `root`. Idempotent; a call in `<head>` draws the rest at `DOMContentLoaded`. Adds to
|
|
68
|
+
`gembossUI` (after `kit-plain.js`, which it never replaces; alone it defines it), closes an open kit menu when the panel
|
|
69
|
+
opens. The catalogue is a literal block held to `services.ts` + `icons.tsx` by `service-switcher-plain-data.test.ts`
|
|
70
|
+
(`UPDATE_PLAIN_CATALOGUE=1` writes it). README "ServiceSwitcher" › "Plain HTML". `kit-plain.js` is unchanged.
|
|
71
|
+
- Catalog section "Chuyển dịch vụ" (the panel open at 1280, in dark, at 390, and a live plain-HTML frame running
|
|
72
|
+
`service-switcher-plain.js`); the shell section's lockup carries the switcher.
|
|
73
|
+
- `packages/ui/weight-budget.json`: ceilings for `packages/ui/src/service-switcher.css` and
|
|
74
|
+
`packages/ui/src/service-switcher-plain.js` (new entries; no existing ceiling raised).
|
|
75
|
+
- Tests: `service-switcher.test.tsx` (catalogue, markup, BrandLockup with and without it, the CSS reaching every class),
|
|
76
|
+
`service-switcher-browser.test.tsx` (Chromium, in `test:ui-browser`), `css-on-demand.test.ts` rows for the switcher,
|
|
77
|
+
BrandLockup and services.ts; `service-switcher-plain-data.test.ts` (Node: the plain catalogue against the TypeScript,
|
|
78
|
+
with its negative control, and the packaging) and `service-switcher-plain-browser.test.ts` (Chromium, in
|
|
79
|
+
`test:ui-browser`: DOM parity with the component, behaviour, phone, beside kit-plain.js, its own contract).
|
|
80
|
+
|
|
81
|
+
Changed
|
|
82
|
+
- `BrandLockup`'s module imports the switcher, so it brings `service-switcher.css` (1.8 KB gzip) and the switcher's code
|
|
83
|
+
wherever a bundler keeps what is imported, also without the prop.
|
|
84
|
+
- The phone topbar with a switcher in it: under 360px the search's floor drops from 96 to 56px (the bar has no other
|
|
85
|
+
room: ☰, mark, button, search, assistant, account). A bar without the switcher is unchanged.
|
|
86
|
+
|
|
87
|
+
|
|
5
88
|
## 0.35.7 (15/10/2026)
|
|
6
89
|
|
|
7
90
|
**The rail's scrollbar runs down the rail's edge.** The 12px side gutter was padding on `.icon-nav`, so the scroll box
|
package/README.md
CHANGED
|
@@ -68,6 +68,7 @@ its CSS out of the build with it (`sideEffects`). gzip of the minified file, as
|
|
|
68
68
|
| assistant-chat | `@gemboss/ui/assistant-chat.css` | `packages/ui/src/assistant-chat.css` | the chat pieces the package draws (0.29.0): column, starters that wrap, meta, errors, notes, paperclip | 0.9 KB | `AssistantChat` and its pieces import it |
|
|
69
69
|
| minichart | `@gemboss/ui/minichart.css` | `packages/ui/src/minichart.css` | `Sparkline`, `ShareBar` (0.30.0) | 0.5 KB | those components import it |
|
|
70
70
|
| setup-guide | `@gemboss/ui/setup-guide.css` | `packages/ui/src/setup-guide.css` | `SetupGuide` (0.30.0) | 0.9 KB | `SetupGuide` imports it |
|
|
71
|
+
| service-switcher | `@gemboss/ui/service-switcher.css` | `packages/ui/src/service-switcher.css` | `ServiceSwitcher`: the button after the brand and its panel of Gem services (0.36.0) | 1.8 KB | `ServiceSwitcher` imports it, and `BrandLockup` through it (README "ServiceSwitcher") |
|
|
71
72
|
| 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) |
|
|
72
73
|
|
|
73
74
|
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).
|
|
@@ -1203,6 +1204,114 @@ import { Icon, SettingsDialog } from "@gemboss/ui";
|
|
|
1203
1204
|
`packages/ui/src/settings-dialog-browser.test.tsx` measures it in Chromium; its negative control runs without
|
|
1204
1205
|
`settings-dialog.css` and finds no 280px rail. Catalog section "Hộp cài đặt".
|
|
1205
1206
|
|
|
1207
|
+
## ServiceSwitcher (0.36.0): the logo goes home, the button beside it opens the other Gem services
|
|
1208
|
+
|
|
1209
|
+
Chris 15/10/2026: on an internal service, a click on the logo goes home (or reloads it), and beside it there should be a
|
|
1210
|
+
way to switch to the other Gem services, "ordered and organised logically", working on a phone, opening when needed as a
|
|
1211
|
+
full panel: a grid of each service's logo with a line under it saying what it is for.
|
|
1212
|
+
|
|
1213
|
+
```tsx
|
|
1214
|
+
import { AppShell, BrandLockup, ServiceSwitcher } from "@gemboss/ui";
|
|
1215
|
+
|
|
1216
|
+
<AppShell brand={<BrandLockup name="GemFactory" href="/" switcher={{ current: "gemfactory" }} />} … />
|
|
1217
|
+
|
|
1218
|
+
// A surface with a logo of its own (the GemBoss admin draws an image): the button right after it.
|
|
1219
|
+
<><Logo /><ServiceSwitcher current="gemboss" /></>
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
**When.** On the topbar of an INTERNAL service, beside its logo. Not on a public surface (GemFeedback's board,
|
|
1223
|
+
GemCommunity, a storefront): the panel names internal tools and links to their admin pages.
|
|
1224
|
+
|
|
1225
|
+
| | |
|
|
1226
|
+
| --- | --- |
|
|
1227
|
+
| Props | `current` (a `ServiceId`, required): the service this page belongs to. `services` (default `GEM_SERVICES`; another list keeps the groups' order). `labels`: `trigger` ("Switch service", the button's name and tooltip), `title` ("Gem services"), `close`, `search` ("Find a service"), `noMatch` ("No service matches “{query}”."), `all`. `allHref`: the foot link, default `SERVICES_HEALTH_URL` (GemFactory's `/services`, every service and its health); `null` draws no foot. `search` (default true). `className` (the button). `BrandLockup switcher={…}` takes the same, less `className`. Every prop is plain data, so a server component can pass it (BrandLockup is not a client module; ServiceSwitcher is) |
|
|
1228
|
+
| The button | `button.gb-svc-trigger`, the nine-dot `apps` icon (new in the set), `aria-haspopup="dialog"`, `aria-expanded`, `aria-controls` the panel. 32px under a mouse, 4px from the brand; 44 under a finger or at phone width; ≤460px, where the brand is its 44px mark alone, 40 wide and touching it |
|
|
1229
|
+
| The panel | `<dialog class="gb-svc">` through the kit's `useModalDialog` (as `Dialog`, `Sheet`, `SettingsDialog`): Escape (one press, also from the search box), ✕ (the kit popup's `ui-modal__close`), a press that starts on the backdrop; the page behind is inert and holds still; focus goes back to the button. It opens on the current service's tile (not on ✕, not on the search box, which would pull up a phone's keyboard). Each opening starts with every service shown |
|
|
1230
|
+
| Where | desktop: under the topbar, from the brand's left edge (measured when it opens, kept inside the window), `min(760px, 100vw - 24px)` wide, at most 80% of the height, its body scrolling. ≤640px: the whole screen, a head row (title + ✕), two columns, padded by `env(safe-area-inset-*)` |
|
|
1231
|
+
| A tile | an `<a href>` to the service's home, same tab: a 40px mark (a kit `Icon` on a square tinted per service), the name (600), what it is for (muted, two lines at most). The current service's tile carries `aria-current="page"` and the accent ring, and still links to its own home. A followed tile closes the panel (a page back from the back/forward cache does not come back with it open); a new-tab click leaves it |
|
|
1232
|
+
| Search | name, purpose, id and group title; the groups keep their order; "No service matches “…”." when nothing does; Enter follows the first match ("med⏎" is GemMedia); ↓ goes to the first tile |
|
|
1233
|
+
| Keys | arrows move by place on screen (↓ from a tile is the one under it, also across groups), Home / End the first and the last, ↑ from the first row the search box. One tile is in the Tab order at a time |
|
|
1234
|
+
| Tints | `data-tint` on the tile, one of `SERVICE_TINTS` (iris, blue, teal, green, amber, orange, rose, violet, slate). A hue is never painted as it is: the mark is the hue mixed into the theme's surface (14%, 22% in dark), the glyph the hue mixed with the theme's text, so one value reads in light and in `data-theme="dark"` (lighter hues there). Measured in Chromium, both themes: every glyph ≥ 3:1 on its mark, names and purposes ≥ 4.5:1 |
|
|
1235
|
+
| CSS | `service-switcher.css`, its own entry, 1.8 KB gzip, imported by the component. `BrandLockup` imports the component, so a surface that renders `BrandLockup` gets the entry (and the switcher's code) even without the prop: 1.8 KB of CSS, and 5.8 KB gzip of JS for brand.tsx's module graph against 0.5 before (most of it the icon set and the popup hook, which a surface with a rail already has) |
|
|
1236
|
+
| The topbar | `.gemboss-topbar-start` holds ☰, the brand and the button. ≤460 the button is 40 wide and touches the mark, so a full bar (search at its 96px floor, the assistant, the account) still fits at 360. Under 360 nothing else can give: in a bar that holds the switcher, the search's floor drops from 96 to 56 (still a box to tap), rather than the account leaving the screen |
|
|
1237
|
+
|
|
1238
|
+
The groups, in order, are how a store is built and then run (`SERVICE_GROUPS`, services.ts):
|
|
1239
|
+
|
|
1240
|
+
| Group | Services |
|
|
1241
|
+
| --- | --- |
|
|
1242
|
+
| GemBoss | GemBoss (the seller app and the ops console) |
|
|
1243
|
+
| Build the store | GemFactory, GemMedia, GemForm |
|
|
1244
|
+
| Read the market | GemWatcher, GemMarket, GemCapture, Search Radar |
|
|
1245
|
+
| Content and community | GemPress, Content Radar, GemCommunity, GemCommunity News |
|
|
1246
|
+
| Customers | GemCare, GemFeedback |
|
|
1247
|
+
| Team and partners | Partner Radar, Taskdesk |
|
|
1248
|
+
|
|
1249
|
+
**Adding a service** (or moving one): one entry in `GEM_SERVICES` (`packages/ui/src/services.ts`), in its group, at its
|
|
1250
|
+
place in that group's story: `{ id, name, url, purpose, group, icon, tint }`. Its id joins `ServiceId`; the purpose is one
|
|
1251
|
+
plain sentence of at most 60 characters; the icon is a name of the set (draw a missing one in icons.tsx, by the set's
|
|
1252
|
+
rules); the tint one of `SERVICE_TINTS`, not the same as the tile before it. `service-switcher.test.tsx` checks all of that,
|
|
1253
|
+
and the homes as listed. Then run `UPDATE_PLAIN_CATALOGUE=1 npx vitest run packages/ui/src/service-switcher-plain-data.test.ts`:
|
|
1254
|
+
it writes the plain script's copy of the list (below), and without it that test is red. Every surface gets it at its next
|
|
1255
|
+
bump. A server reads the list from `@gemboss/ui/services`.
|
|
1256
|
+
|
|
1257
|
+
`packages/ui/src/service-switcher-browser.test.tsx` measures it in Chromium (where it sits at 1280, Escape / ✕ / backdrop,
|
|
1258
|
+
search, keys, a phone at 390 and 320, the topbar at 320 / 360 / 390 / 461, the tints in both themes, kit-plain.js beside
|
|
1259
|
+
it); its negative control runs without `service-switcher.css` and finds the browser's centred dialog. Catalog section
|
|
1260
|
+
"Chuyển dịch vụ", and the shell section's lockup carries it.
|
|
1261
|
+
|
|
1262
|
+
### Plain HTML: `gembossUI.initServiceSwitcher()` (0.36.0)
|
|
1263
|
+
|
|
1264
|
+
For a page with no React (GemCapture's ops admin, GemPress's admin, GemCare, Taskdesk): the server writes a placeholder
|
|
1265
|
+
right after its `.gb-brand`, and `service-switcher-plain.js` draws the component's button and panel in its place, the
|
|
1266
|
+
same DOM (classes, ARIA, order), so `service-switcher.css` draws it unchanged, and the same behaviour. Its own file, not
|
|
1267
|
+
`kit-plain.js` (which has 485 bytes gzip left under its ceiling and stays as it was): 4.5 KB gzip, loaded only where the
|
|
1268
|
+
switcher is.
|
|
1269
|
+
|
|
1270
|
+
```html
|
|
1271
|
+
<head>
|
|
1272
|
+
<link rel="stylesheet" href="/ui/service-switcher.css"> <!-- require.resolve("@gemboss/ui/service-switcher.css") -->
|
|
1273
|
+
</head>
|
|
1274
|
+
…
|
|
1275
|
+
<div class="gemboss-topbar-start">
|
|
1276
|
+
<a class="gb-brand" href="/admin" aria-label="GemCapture home">…</a>
|
|
1277
|
+
<span data-gb-service-switcher="gemcapture" hidden></span> <!-- the value: this service's id (services.ts) -->
|
|
1278
|
+
</div>
|
|
1279
|
+
…
|
|
1280
|
+
<script src="/ui/kit-plain.js"></script> <!-- @gemboss/ui/kit-plain.js, if the page has it: FIRST -->
|
|
1281
|
+
<script src="/ui/service-switcher-plain.js"></script> <!-- @gemboss/ui/service-switcher-plain.js -->
|
|
1282
|
+
<script>gembossUI.initServiceSwitcher();</script>
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
```js
|
|
1286
|
+
gembossUI.initServiceSwitcher({ // every option is optional, and the component's prop of the same name
|
|
1287
|
+
labels: { trigger: "Chuyển dịch vụ", title: "Dịch vụ Gem", close: "Đóng", search: "Tìm dịch vụ",
|
|
1288
|
+
noMatch: "Không có dịch vụ nào khớp “{query}”.", all: "Mọi dịch vụ và tình trạng" },
|
|
1289
|
+
allHref: null, // the foot link; default GemFactory's /services; null draws none
|
|
1290
|
+
search: false, // the search box (default true)
|
|
1291
|
+
services: gembossUI.GEM_SERVICES.filter((s) => s.group !== "team"), // default every service
|
|
1292
|
+
className: "…", // added to the button's class
|
|
1293
|
+
current: "gemcapture", // instead of the placeholder's value
|
|
1294
|
+
root: element, // where to look for placeholders (default the document)
|
|
1295
|
+
}); // → [{ button, dialog, open(), close() }] for what it drew
|
|
1296
|
+
```
|
|
1297
|
+
|
|
1298
|
+
| | |
|
|
1299
|
+
| --- | --- |
|
|
1300
|
+
| The placeholder | any element with `data-gb-service-switcher="<service id>"`, right after the brand; `hidden`, so it takes no room (nor the topbar's gap) before the script runs or where it never does. It is REPLACED by `button.gb-svc-trigger` + `dialog.gb-svc`: the button has to be the brand's next sibling, as `BrandLockup` draws it, for `.gb-brand + .gb-svc-trigger` (4px from the logo; at ≤460 40 wide and touching it, so the phone bar fits at 360) and for the panel to hang from the brand's left edge. A wrapper around the button would break all three |
|
|
1301
|
+
| Calls | idempotent: a placeholder is drawn once (it is gone after), so a second call draws nothing, and markup added later (`root`) is drawn by the next call. Called while the page still loads (in `<head>`), it draws what is parsed and the rest at `DOMContentLoaded`. Each switcher gets ids of its own |
|
|
1302
|
+
| `gembossUI` | a classic script that ADDS to it: after `kit-plain.js` when the page has it, because `kit-plain.js` defines nothing when `gembossUI` already exists (loaded the other way round it says so in the console). On its own it defines `gembossUI` with `initServiceSwitcher` and `GEM_SERVICES` only. With `kit-plain.js`, opening the panel closes an open kit menu (`closeMenus`: an open account menu would take the first Escape) |
|
|
1303
|
+
| Behaviour | the component's, item for item (table above): `showModal()`, Escape / ✕ / a press that starts on the backdrop, focus back to the button, opens on the current tile, search (Enter follows the first match, ↓ to the tiles), arrows by place on screen, Home / End, ↑ to the search box, one tile in the Tab order, a followed tile closes it. Strings are text |
|
|
1304
|
+
| The list | `GEM_SERVICES` as data inside the script, with the paths of the icons it names, held to `services.ts` + `icons.tsx` character for character by `service-switcher-plain-data.test.ts` (red when either moves without it; `UPDATE_PLAIN_CATALOGUE=1` writes it). A literal copy and a test, as the other plain scripts carry their TypeScript twins: the published file is the source minified, with no build step that could write it. A list of your own (`services`) uses the icons the catalogue names |
|
|
1305
|
+
| Inline | it holds no closing script tag, so a server can inline it (`readFileSync(require.resolve("@gemboss/ui/service-switcher-plain.js"))`), as it does `kit-plain.js` |
|
|
1306
|
+
|
|
1307
|
+
`packages/ui/src/service-switcher-plain-browser.test.ts` (Chromium, in `test:ui-browser`) renders the React component and
|
|
1308
|
+
this script for the same props on the same shell and compares the two DOMs node for node (generated ids aside): closed,
|
|
1309
|
+
open, filtered, nothing matching, after the keys, closed again, and with labels, no foot, no search and a list of its own;
|
|
1310
|
+
its negative control changes one attribute and the comparison sees it. Then the component's own checks on the plain page
|
|
1311
|
+
(1280, Escape / ✕ / backdrop, search, Enter, keys, 390 and 320 full screen, the bar at 320 / 360 / 390, kit-plain.js
|
|
1312
|
+
beside it, idempotence, `<head>`, alone, the wrong order). Catalog section "Chuyển dịch vụ", last frame: the plain page,
|
|
1313
|
+
live.
|
|
1314
|
+
|
|
1206
1315
|
## Resizable panels
|
|
1207
1316
|
|
|
1208
1317
|
`ui-resizer` is the seam a person drags between two panels; `ui-resizing` on `<body>` is the drag in
|
|
@@ -1259,7 +1368,8 @@ const open = dock.presentation !== "hidden";
|
|
|
1259
1368
|
- No provider and no state: pass `hidden` while your assistant is open.
|
|
1260
1369
|
- The shortcut is a hint only: the surface owns the key binding.
|
|
1261
1370
|
- The phone topbar (≤760px) gives in this order when it is short: the search (to a 96px floor), then the brand name
|
|
1262
|
-
(ellipsis). The actions never shrink, so the account chip stays on screen (0.6.1; `topbar.test.tsx`).
|
|
1371
|
+
(ellipsis). The actions never shrink, so the account chip stays on screen (0.6.1; `topbar.test.tsx`). With a
|
|
1372
|
+
`ServiceSwitcher` in the bar (0.36.0), under 360px the search goes below its floor, to 56px: README "ServiceSwitcher".
|
|
1263
1373
|
- 0.6.0, not a patch: an existing `<AssistantLauncher>` call changes meaning (no longer fixed; the default copy is the
|
|
1264
1374
|
rail one, hidden on phones). A surface on `^0.5.x` must move to `^0.6.0` in the same PR that places the two copies.
|
|
1265
1375
|
|
|
@@ -1284,12 +1394,13 @@ import { AppShell, BrandLockup, AccountMenu, AccountMenuItem, Icon, RailItem } f
|
|
|
1284
1394
|
```
|
|
1285
1395
|
|
|
1286
1396
|
- `BrandLockup`: the admin wordmark's gem and proportions (26px tall, 13.3px/700 name) with your
|
|
1287
|
-
product's name. The name hides below 460px, as the admin's wordmark does.
|
|
1397
|
+
product's name. The name hides below 460px, as the admin's wordmark does. `switcher={{ current: "gemfactory" }}`
|
|
1398
|
+
(0.36.0) adds the button that opens every other Gem service right after it: README "ServiceSwitcher".
|
|
1288
1399
|
- `AccountMenu`: the admin's 32px initials chip and popover (same CSS). A `<details>`, so it opens
|
|
1289
1400
|
without JavaScript on a page that never hydrates; with React it also closes on an outside click and
|
|
1290
1401
|
on Escape. `AccountMenuItem` is a link (`href`), a POST form (`formAction`), or a button.
|
|
1291
1402
|
- `TopbarSearch` (0.6.3): see "Topbar search" below.
|
|
1292
|
-
- `Icon`: the GemBoss set,
|
|
1403
|
+
- `Icon`: the GemBoss set, 45 icons on a 20px grid with 1.5px strokes, drawn to sit next to Polaris.
|
|
1293
1404
|
`ICON_NAMES` lists them. Internal tools cannot use `@shopify/polaris-icons`: its licence only covers
|
|
1294
1405
|
applications that integrate with Shopify. Draw a missing one HERE, not in the surface: every rail
|
|
1295
1406
|
icon must keep `fill="none"` on its inner `<g>`, because `.gnav-ico svg { fill: currentColor }`
|
|
@@ -1690,6 +1801,7 @@ something the base cannot express, add a token or a prop here, and every surface
|
|
|
1690
1801
|
| `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". |
|
|
1691
1802
|
| `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`. |
|
|
1692
1803
|
| `packages/ui/src/editor-layers-plain.js` | 0.22.0: `gembossUI.initEditorLayers()`, the layers', the outline's and the drawers' behaviour for plain HTML. Load after `kit-plain.js`. |
|
|
1804
|
+
| `packages/ui/src/service-switcher-plain.js` | 0.36.0: `gembossUI.initServiceSwitcher()`, the ServiceSwitcher drawn and driven for plain HTML (`@gemboss/ui/service-switcher-plain.js`). After `kit-plain.js` when the page has it; works alone. See "ServiceSwitcher". |
|
|
1693
1805
|
| `catalog/editor.html` | 0.14.0: the frame rendered from the components and running (`packages/ui/scripts/build-catalog-editor.mjs`), linked from `catalog/index.html`. |
|
|
1694
1806
|
| `packages/ui/src/editor.css` | 0.9.0: the editors (frame, top bar, preview, Configure dock, inspector and controls, Layers, settings drawer, page picker, menus). A separate entry, NOT inside `gemboss-ui.css`; the admin imports it from the editor components, never from a layout. See "Editor CSS" above. |
|
|
1695
1807
|
| `packages/ui/src/chat.css`, `chat-composer.css`, `assistant.css`, `auth.css`, `app-shell.css` | the chat body, the composer, the assistant frame, the sign-in screen, the topbar slots + base text + the page width tiers (`ui-page`, 0.8.1). All inside `gemboss-ui.css`; the admin @imports `chat.css` and `chat-composer.css` itself. |
|
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.36.1 · 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";
|