@apliteni/apliteni-ui 0.79.0 → 0.81.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 +5 -3
- package/guidelines/account-and-settings.md +71 -0
- package/guidelines/component-choice.md +1 -1
- package/guidelines/drawer.md +17 -3
- package/package.json +1 -1
- package/src/components/shell.js +4 -60
- package/src/index.d.ts +0 -1
- package/src/styles/table.css +19 -0
- package/src/styles/topbar.css +25 -3
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ install, one version and one version pin.
|
|
|
36
36
|
| Import | What it is |
|
|
37
37
|
| --- | --- |
|
|
38
38
|
| `@apliteni/apliteni-ui/css` | Tokens and every component stylesheet. Import once at the app root. |
|
|
39
|
-
| `@apliteni/apliteni-ui` | The HTML-string factories: `button()`, `card()`, `topbar()`, `
|
|
39
|
+
| `@apliteni/apliteni-ui` | The HTML-string factories: `button()`, `card()`, `topbar()`, `appShell()` and the rest. |
|
|
40
40
|
| `@apliteni/apliteni-ui/react` | The React components, TypeScript types included. |
|
|
41
41
|
| `@apliteni/apliteni-ui/react/css` | Their shell styles. |
|
|
42
42
|
| `@apliteni/apliteni-ui/inline` | The same CSS as strings, for a server that inlines it. |
|
|
@@ -59,9 +59,11 @@ import '@apliteni/apliteni-ui/react/css';
|
|
|
59
59
|
import { DataTable, Modal } from '@apliteni/apliteni-ui/react';
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
The `
|
|
63
|
-
|
|
62
|
+
The `appShell()` factory provides the complete product layout: the sticky rail, the page body
|
|
63
|
+
and a topbar over it when you ask for one, so no product rebuilds it. The
|
|
64
64
|
[component catalog](docs/library.md#component-catalog) lists this factory and every other one.
|
|
65
|
+
Account and personal settings belong in a modal over the product rather than on a page of
|
|
66
|
+
their own — see [Guidelines / Account and settings](guidelines/account-and-settings.md).
|
|
65
67
|
|
|
66
68
|
### Fonts
|
|
67
69
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Account and settings
|
|
2
|
+
|
|
3
|
+
## Open the account area in one modal
|
|
4
|
+
|
|
5
|
+
<!-- rule: modal -->
|
|
6
|
+
|
|
7
|
+
**Rule:** Open account and settings in one modal over the product, with its navigation beside the pane.
|
|
8
|
+
|
|
9
|
+
**Why:** The reader has not left the work behind it, and one overlay holds every setting.
|
|
10
|
+
|
|
11
|
+
**Do:** Open a panel over the work, navigation beside the pane.
|
|
12
|
+
|
|
13
|
+
**Don't:** Slide a full-width sheet in with columns inside it.
|
|
14
|
+
|
|
15
|
+
**Except:** On a phone the navigation stands above the pane, where a column beside it would leave the pane narrower than its rows.
|
|
16
|
+
|
|
17
|
+
**Gap #553:** The kit has no modal that takes a navigation pane or a width, so this shape is built by hand.
|
|
18
|
+
|
|
19
|
+
## Give the modal its own navigation
|
|
20
|
+
|
|
21
|
+
<!-- rule: nav -->
|
|
22
|
+
|
|
23
|
+
**Rule:** Give the modal one entry per group a reader arrives to change.
|
|
24
|
+
|
|
25
|
+
**Why:** A reader already knows which group they came for, and a list of names is read faster than every group in turn.
|
|
26
|
+
|
|
27
|
+
**Do:** Stand the entries beside the pane — above it on a phone — and mark the one on screen.
|
|
28
|
+
|
|
29
|
+
**Don't:** Run all five groups down one scroll.
|
|
30
|
+
|
|
31
|
+
**Except:** Merge two groups when neither fills a pane alone.
|
|
32
|
+
|
|
33
|
+
## Limit the account menu to identity and the way in
|
|
34
|
+
|
|
35
|
+
<!-- rule: menu -->
|
|
36
|
+
|
|
37
|
+
**Rule:** Keep the account menu to the reader, the row that opens the modal, and Sign out.
|
|
38
|
+
|
|
39
|
+
**Why:** A setting answered inside a menu has no address to link, search or come back to.
|
|
40
|
+
|
|
41
|
+
**Do:** Name the reader, then the way in and the way out.
|
|
42
|
+
|
|
43
|
+
**Don't:** Answer the settings in the menu, a row per setting.
|
|
44
|
+
|
|
45
|
+
## Change a single field where it sits
|
|
46
|
+
|
|
47
|
+
<!-- rule: in-row -->
|
|
48
|
+
|
|
49
|
+
**Rule:** Change a single field in the row that names it, with no overlay.
|
|
50
|
+
|
|
51
|
+
**Why:** Opening the account area to flip one switch costs two presses back to the row.
|
|
52
|
+
|
|
53
|
+
**Do:** Flip the weekly digest where it is named.
|
|
54
|
+
|
|
55
|
+
**Don't:** Open the modal on a pane to flip one switch.
|
|
56
|
+
|
|
57
|
+
**Except:** A question that must be answered before anything else still stops the page.
|
|
58
|
+
|
|
59
|
+
## Name the action after its change
|
|
60
|
+
|
|
61
|
+
<!-- rule: one-action -->
|
|
62
|
+
|
|
63
|
+
**Rule:** Name an account pane’s action after the change it makes.
|
|
64
|
+
|
|
65
|
+
**Why:** On a pane of agents and the tokens they hold, Save does not say what the press changes.
|
|
66
|
+
|
|
67
|
+
**Do:** End the tokens pane with “Create token”.
|
|
68
|
+
|
|
69
|
+
**Don't:** End the same pane with “Save”.
|
|
70
|
+
|
|
71
|
+
**Except:** A pane whose choices apply as they are made carries no action.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
**Why:** A confirm keeps keyboard focus until answered; a callout does not interrupt.
|
|
22
22
|
|
|
23
|
-
**Except:** Use a content-sized modal for a short form and a
|
|
23
|
+
**Except:** Use a content-sized modal or a drawer for a short form, and a page for a long one.
|
|
24
24
|
|
|
25
25
|
**Do:** Ask “Rotate this token?” in a confirm with “Keep token” and “Rotate token” buttons.
|
|
26
26
|
|
package/guidelines/drawer.md
CHANGED
|
@@ -8,11 +8,25 @@
|
|
|
8
8
|
|
|
9
9
|
**Why:** Closing the drawer returns the user to the opened row.
|
|
10
10
|
|
|
11
|
-
**Do:**
|
|
11
|
+
**Do:** Open one for a quick edit, a new key, a filter, or a peek at a row’s detail.
|
|
12
12
|
|
|
13
|
-
**Don't:**
|
|
13
|
+
**Don't:** Open one for a long form, several steps, or a view with its own address and Back.
|
|
14
14
|
|
|
15
|
-
**Except:**
|
|
15
|
+
**Except:** A two-answer question uses confirm, not a drawer.
|
|
16
|
+
|
|
17
|
+
## Drawer or page
|
|
18
|
+
|
|
19
|
+
<!-- rule: drawer-or-page -->
|
|
20
|
+
|
|
21
|
+
**Rule:** Use a page to edit more than one group, and a drawer for one short change.
|
|
22
|
+
|
|
23
|
+
**Why:** Several groups need room to move between them and check them before saving.
|
|
24
|
+
|
|
25
|
+
**Do:** Edit the roles and the per-unit switches on a page, under a way back to the list.
|
|
26
|
+
|
|
27
|
+
**Don't:** Edit the same groups in a drawer, stacked in one narrow panel over the list.
|
|
28
|
+
|
|
29
|
+
**Except:** Change one field inline, without a panel. A drawer may show groups a reader only reads.
|
|
16
30
|
|
|
17
31
|
## Grouped content
|
|
18
32
|
|
package/package.json
CHANGED
package/src/components/shell.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The kit's one page shell: a full-height rail beside one <main>, in two layouts.
|
|
2
|
-
//
|
|
3
|
-
// after mounting wires the fold, the nav's groups and the reader's menu.
|
|
2
|
+
// `topbar` switches the product band on; it is off unless the caller hands one over.
|
|
3
|
+
// wireShell() once after mounting wires the fold, the nav's groups and the reader's menu.
|
|
4
4
|
// why: docs/specification.md#the-page-shell
|
|
5
5
|
import { topbar as productTopbar } from './topbar.js';
|
|
6
6
|
import { esc, icon } from './index.js';
|
|
@@ -26,8 +26,8 @@ const isRecord = (v) => typeof v === 'object' && v !== null;
|
|
|
26
26
|
// settled here, before the first sink sees it. Adding one to appShell() means adding it
|
|
27
27
|
// to SHAPES or deciding in the open that it needs nothing.
|
|
28
28
|
|
|
29
|
-
//
|
|
30
|
-
//
|
|
29
|
+
// The account menu's nav takes the old tuple — [id, icon, label, href?, target?] — and
|
|
30
|
+
// nav.js's object shape side by side. A nav that is not a list falls back to ACCOUNT_NAV;
|
|
31
31
|
// an entry that is neither shape is dropped. An empty list is an answer and stays.
|
|
32
32
|
const toItems = (nav) => (Array.isArray(nav) ? nav : ACCOUNT_NAV)
|
|
33
33
|
.filter(isRecord)
|
|
@@ -434,59 +434,3 @@ export function wireShell(root = document, { persist } = {}) {
|
|
|
434
434
|
setRail(app, auto ? saved : app.classList.contains('is-collapsed'));
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
|
-
|
|
438
|
-
// The /account preset: appShell() with the topbar switched on, and the old
|
|
439
|
-
// `cap` + `crumb` strings folded into the trail the caller now owns.
|
|
440
|
-
export function accountShell({
|
|
441
|
-
word = 'Account',
|
|
442
|
-
versions,
|
|
443
|
-
account = {},
|
|
444
|
-
nav = ACCOUNT_NAV,
|
|
445
|
-
active = 'prefs',
|
|
446
|
-
cap = 'Account',
|
|
447
|
-
showSwitch = false,
|
|
448
|
-
crumb,
|
|
449
|
-
title = '',
|
|
450
|
-
sub = '',
|
|
451
|
-
body = '',
|
|
452
|
-
signOutHref = '#logout',
|
|
453
|
-
collapsible = true,
|
|
454
|
-
collapsed,
|
|
455
|
-
layout,
|
|
456
|
-
width,
|
|
457
|
-
search,
|
|
458
|
-
} = {}) {
|
|
459
|
-
// The same normaliser appShell() runs, called once here so the rail and the
|
|
460
|
-
// topbar menu are handed one list rather than two readings of `nav`.
|
|
461
|
-
const items = toItems(nav);
|
|
462
|
-
const trail = [{ label: cap }, { label: crumb || title }];
|
|
463
|
-
// The preset hands the topbar the caller's text, as it does the rail. toTopbar()
|
|
464
|
-
// escapes for the menu's raw sinks and runs once inside appShell(); escaping here as
|
|
465
|
-
// well would reach the menu as entities.
|
|
466
|
-
return appShell({
|
|
467
|
-
word,
|
|
468
|
-
nav: items,
|
|
469
|
-
active,
|
|
470
|
-
navLabel: cap,
|
|
471
|
-
crumbs: trail,
|
|
472
|
-
title,
|
|
473
|
-
sub,
|
|
474
|
-
body,
|
|
475
|
-
account,
|
|
476
|
-
signOutHref,
|
|
477
|
-
collapsible,
|
|
478
|
-
collapsed,
|
|
479
|
-
// Settled nowhere but appShell(). Under `layout: 'topbar'` the bag below is not
|
|
480
|
-
// drawn, which is what that layout costs the preset.
|
|
481
|
-
layout,
|
|
482
|
-
width,
|
|
483
|
-
search,
|
|
484
|
-
topbar: {
|
|
485
|
-
word,
|
|
486
|
-
view: 'text',
|
|
487
|
-
showSwitch,
|
|
488
|
-
versions,
|
|
489
|
-
account: { ...(isRecord(account) ? account : {}), active, nav: items },
|
|
490
|
-
},
|
|
491
|
-
});
|
|
492
|
-
}
|
package/src/index.d.ts
CHANGED
|
@@ -56,7 +56,6 @@ export declare const illoNames: string[];
|
|
|
56
56
|
// Vanilla factories accept their existing option bags and return HTML.
|
|
57
57
|
export declare function accentPicker(opts?: Record<string, unknown>): string;
|
|
58
58
|
export declare function accountMenu(opts?: Record<string, unknown>): string;
|
|
59
|
-
export declare function accountShell(opts?: Record<string, unknown>): string;
|
|
60
59
|
export declare function appShell(opts?: Record<string, unknown>): string;
|
|
61
60
|
export declare function breadcrumbs(opts?: Record<string, unknown>): string;
|
|
62
61
|
export declare function busyRegion(opts?: Record<string, unknown>): string;
|
package/src/styles/table.css
CHANGED
|
@@ -125,6 +125,25 @@
|
|
|
125
125
|
.ui-table :where(td, th, caption) a:not([class]) {
|
|
126
126
|
display: inline-block;
|
|
127
127
|
border-radius: var(--radius-xs);
|
|
128
|
+
position: relative;
|
|
129
|
+
}
|
|
130
|
+
/* A bare link in a cell is only as tall as its line box — 21px in a dense table,
|
|
131
|
+
under the 24px pointer floor. This layer gives a pointer the floor without
|
|
132
|
+
moving anything: growing the link's own box would grow every dense row holding
|
|
133
|
+
one. Written one cell kind per line because the measuring gate splits a
|
|
134
|
+
selector list on commas, and `:where(td, th, caption)` carries two of its own.
|
|
135
|
+
why: guidelines/accessibility-floor.md#minimum-target-size
|
|
136
|
+
held by: stories/guidelines/accessibility-floor.test.js */
|
|
137
|
+
.ui-table td a:not([class])::after,
|
|
138
|
+
.ui-table th a:not([class])::after,
|
|
139
|
+
.ui-table caption a:not([class])::after {
|
|
140
|
+
content: '';
|
|
141
|
+
position: absolute;
|
|
142
|
+
height: 24px;
|
|
143
|
+
left: 0;
|
|
144
|
+
right: 0;
|
|
145
|
+
top: 50%;
|
|
146
|
+
translate: 0 -50%;
|
|
128
147
|
}
|
|
129
148
|
/* And on keyboard focus every link in a table takes the kit ring, at whatever corner it
|
|
130
149
|
carries, so none falls back to the browser's own outline. `.ui-btn` has the ring already.
|
package/src/styles/topbar.css
CHANGED
|
@@ -210,9 +210,31 @@
|
|
|
210
210
|
}
|
|
211
211
|
.vbadge.live { color: var(--chip-success-ink); background: var(--chip-success-fill); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
|
|
212
212
|
.vbadge.arch { /* muted-ink: state — archived version */ color: var(--muted); background: var(--surface); /* ring-gap: inherit — decorative or text-only surface has no nested focus target. */ }
|
|
213
|
-
/* The
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
/* The phone step — the same one the rail folds at: docs/specification.md#breakpoints.
|
|
214
|
+
The switcher keeps its glyph and drops its label.
|
|
215
|
+
The menu hangs the other way here. Anchored by its left edge it opened
|
|
216
|
+
rightwards off a band that is as wide as the page, and a CLOSED menu is still
|
|
217
|
+
laid out, so it scrolled the page sideways with nothing visible to explain it.
|
|
218
|
+
`.amenu` beside it has always been anchored right. On a 320px viewport the
|
|
219
|
+
page's scroll width was 527px and is 397px; the band's own controls still want
|
|
220
|
+
more room than that page has, which is #558.
|
|
221
|
+
why: guidelines/layout-and-density.md#use-the-three-breakpoints
|
|
222
|
+
held by: stories/overlay-css.test.js */
|
|
223
|
+
@media (max-width: 720px) {
|
|
224
|
+
.vsw__btn .lbl { display: none; }
|
|
225
|
+
.vsw__menu { left: auto; right: 0; }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* One column, and the band is at its tightest. The row is already full at 390,
|
|
229
|
+
and at the gap above this one the account avatar stood from 365 to 397 and was
|
|
230
|
+
cut at the screen edge — the one control on this screen that opens the account
|
|
231
|
+
area. Packed to --space-2 the band needs 352 of the 362 it has there, so the
|
|
232
|
+
avatar is whole with the band's own gutter beside it. 320 is still short of the
|
|
233
|
+
room these controls want, which stays #558.
|
|
234
|
+
why: docs/specification.md#breakpoints */
|
|
235
|
+
@media (max-width: 560px) {
|
|
236
|
+
.topbar__in { gap: var(--space-2); }
|
|
237
|
+
}
|
|
216
238
|
|
|
217
239
|
/* Account — avatar + dropdown. Gated: hidden until the app confirms a session
|
|
218
240
|
and adds `.on` (mirrors the portal's /auth/me reveal). */
|