@lotics/ui 38.0.0 → 39.0.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/AGENTS.md +4 -3
- package/MIGRATION.md +25 -0
- package/docs/catalog.md +23 -6
- package/examples/tpl_record.tsx +12 -1
- package/package.json +1 -1
- package/src/page_header.tsx +20 -14
package/AGENTS.md
CHANGED
|
@@ -118,9 +118,10 @@ CURRENT major only — upgrading an app across majors is `MIGRATION.md`.
|
|
|
118
118
|
anyway. Use `FileRow` / `FileRows`, where the full name is the row and a second line can
|
|
119
119
|
carry a date, a size, a status, a diff. Photos, scans and drawings are identified by their
|
|
120
120
|
CONTENT — the filename is `IMG_4471.jpg` and tells nobody anything — so use
|
|
121
|
-
`FileThumbnailGrid`, where the picture is the identity and many fit on one screen. A mixed
|
|
122
|
-
set follows its
|
|
123
|
-
|
|
121
|
+
`FileThumbnailGrid`, where the picture is the identity and many fit on one screen. **A mixed
|
|
122
|
+
set follows its MAJORITY, and that one surface holds the whole set — never split a pile by
|
|
123
|
+
file type** — the cost lands on the minority by design, not on the reader, who would otherwise
|
|
124
|
+
get two places to look for one thing.
|
|
124
125
|
→ [catalog.md](./docs/catalog.md).
|
|
125
126
|
- **A reference to another record is a FIELD** — it wears the inline editor's own resting
|
|
126
127
|
surface and PEEKS its facts on press; EVERY verb lives in the peek's footer as a `Button`,
|
package/MIGRATION.md
CHANGED
|
@@ -4,6 +4,31 @@ Breaking changes, newest first — normally per major, plus the rare minor that
|
|
|
4
4
|
anyway (recorded under its exact version). The current contract lives in `AGENTS.md` + `docs/`;
|
|
5
5
|
this file exists only to move an app from one release to the next.
|
|
6
6
|
|
|
7
|
+
## 39.0.0 — `PageHeader.leading` is `trailing`, and it renders AFTER the title
|
|
8
|
+
|
|
9
|
+
38.0.0 shipped `leading`, a slot before the title. It is now **`trailing`**, in the same
|
|
10
|
+
place in the props but rendered on the other side of the title text.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
// 38.0.0
|
|
14
|
+
<PageHeader title="Members" leading={<IconButton …/>} actions={…} />
|
|
15
|
+
// 39.0.0
|
|
16
|
+
<PageHeader title="Members" trailing={<IconButton …/>} actions={…} />
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Why the side changed.** The title is the page's first word. A control ahead of it makes
|
|
20
|
+
the eye parse chrome before it learns where it is, and on a page whose heading is the only
|
|
21
|
+
thing telling you which section you are in, that is the wrong order. After the title the
|
|
22
|
+
control still reads as part of the heading — which is the whole point of not putting it
|
|
23
|
+
with `actions` — without standing in front of it.
|
|
24
|
+
|
|
25
|
+
**Why a rename rather than a second slot.** A slot with no consumer is surface that has to
|
|
26
|
+
be understood by everyone who reads the component and used by no one; the kit's cull rule
|
|
27
|
+
says cut it. One slot for "a control belonging to the title" is the whole contract.
|
|
28
|
+
|
|
29
|
+
The layout law is unchanged and now covers both: the TITLE gives way and wraps, while
|
|
30
|
+
`trailing` and `actions` keep their width.
|
|
31
|
+
|
|
7
32
|
## 38.0.0 — the `lg` avatar is a control (40), not a size (48)
|
|
8
33
|
|
|
9
34
|
`AVATAR_PX.lg` was 48 and `AVATAR_TEXT.lg` was `lg` type. They are now **40** and `md`
|
package/docs/catalog.md
CHANGED
|
@@ -286,6 +286,21 @@ says the read succeeded and found nothing, a failed read knows neither.
|
|
|
286
286
|
|
|
287
287
|
### Files
|
|
288
288
|
|
|
289
|
+
**Pick the surface FIRST, and pick exactly one per set.** Documents get a LIST (`FileRows`),
|
|
290
|
+
images get a GRID (`FileThumbnailGrid`) — the discriminator is what identifies the file to the
|
|
291
|
+
person hunting for it: a PDF's thumbnail is a grey page identical to every other grey page, so
|
|
292
|
+
its NAME is the identity; a photo's filename is `IMG_4471.jpg` and tells nobody anything, so its
|
|
293
|
+
CONTENT is. **A mixed pile follows its majority and stays whole.** Sorting one pile by mime type
|
|
294
|
+
into a list *and* a grid is the mistake — it gives the reader two places to look for the same
|
|
295
|
+
thing. The minority is not stranded by this: a non-image tile in a grid renders a `DocumentCard`
|
|
296
|
+
printing its filename, and a photo in a list keeps its full name plus a meta line and is ONE tap
|
|
297
|
+
from the picture, because a `FileRows` row press opens the built-in gallery. (The badge is
|
|
298
|
+
MIME-driven, so that row shows a generic image mark, not the shot itself — which is the cost the
|
|
299
|
+
majority already decided to pay.) A screen showing BOTH surfaces (`tpl_record`) does
|
|
300
|
+
so because it holds two distinct SETS the reader asks different questions of — the paperwork
|
|
301
|
+
that arrived on the record, and the hand-over photos taken against it — not because one set was
|
|
302
|
+
split by type.
|
|
303
|
+
|
|
289
304
|
`FilesEditor` (THE attachment surface: an upload-aware grid whose bar you COMPOSE — the root
|
|
290
305
|
owns selection + gallery + confirmed remove, the bar pieces and any HOST verb go below it via
|
|
291
306
|
`useFilesEditorSelection`),
|
|
@@ -521,13 +536,15 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
|
|
|
521
536
|
- **`container`** — `Container`: centers content at a max width (`ContainerSize` sm|md|lg,
|
|
522
537
|
`CONTAINER_SIZES`).
|
|
523
538
|
- **`page_header`** — `PageHeader`: the page's title band. `actions` puts page-level CTAs on
|
|
524
|
-
the title row (right-aligned) and `
|
|
525
|
-
`left`/`right` form a separate nav row above. Split `
|
|
539
|
+
the title row (right-aligned) and `trailing` puts a control immediately AFTER the title;
|
|
540
|
+
`left`/`right` form a separate nav row above. Split `trailing` from `actions` by what the
|
|
526
541
|
control acts on: `actions` do something to the page's CONTENT (create, sort, export),
|
|
527
|
-
`
|
|
528
|
-
filed under `actions` reads as a peer of "create one of these".
|
|
529
|
-
|
|
530
|
-
|
|
542
|
+
`trailing` changes what is AROUND it (a side-panel toggle, a view switch). A panel toggle
|
|
543
|
+
filed under `actions` reads as a peer of "create one of these". It sits AFTER the title
|
|
544
|
+
because the title is the page's first word and nothing should come between the reader
|
|
545
|
+
and it. Under a title too long for the row the TITLE gives way and wraps while `trailing`
|
|
546
|
+
and `actions` keep their width — a wrapped title is merely taller, a control squeezed
|
|
547
|
+
below its own icon is broken.
|
|
531
548
|
- **`page_content`** — `PageContent` + `PAGE_SIZES`: the page's padded, width-capped content
|
|
532
549
|
region — a centred column with optional `title`/`titleRight`/`description`, `header`/`footer`
|
|
533
550
|
slots and `fullscreen`. **Reach for it before hand-rolling a screen shell**: a scroller, a
|
package/examples/tpl_record.tsx
CHANGED
|
@@ -2683,7 +2683,7 @@ export function TplRecord({ chrome = "page", code = "RC-2026-0418", openSection
|
|
|
2683
2683
|
</Section>
|
|
2684
2684
|
</View>
|
|
2685
2685
|
|
|
2686
|
-
{/* PHOTOS —
|
|
2686
|
+
{/* PHOTOS — a second SET, and therefore the other SURFACE.
|
|
2687
2687
|
|
|
2688
2688
|
Files above is a row LIST because a shipping document is identified by
|
|
2689
2689
|
its NAME: a PDF thumbnail is a grey page identical to every other grey
|
|
@@ -2693,6 +2693,17 @@ export function TplRecord({ chrome = "page", code = "RC-2026-0418", openSection
|
|
|
2693
2693
|
same verbs, different surface, decided by what the reader recognises.
|
|
2694
2694
|
(AGENTS.md: documents get a LIST, images get a GRID.)
|
|
2695
2695
|
|
|
2696
|
+
READ THIS BEFORE COPYING THE SHAPE. Two sections here does NOT mean
|
|
2697
|
+
"sort one pile by mime type into a list and a grid". This record holds
|
|
2698
|
+
two distinct SETS — the paperwork that arrived on it, and the hand-over
|
|
2699
|
+
photos taken against it — which the reader asks different questions of.
|
|
2700
|
+
A SINGLE pile picks ONE surface by its majority and keeps it: the odd
|
|
2701
|
+
.docx among forty phone photos stays in the grid, where it renders a
|
|
2702
|
+
`DocumentCard` printing its filename, and the odd photo among forty
|
|
2703
|
+
PDFs stays in the list, where its row carries the full name and a press
|
|
2704
|
+
opens the gallery. Splitting one set by type would give the reader two
|
|
2705
|
+
places to look for the same thing.
|
|
2706
|
+
|
|
2696
2707
|
The ADD rides the heading row, right edge, exactly as it does on Files
|
|
2697
2708
|
and Fees — a section's add sits in the same spot whether the section
|
|
2698
2709
|
holds nought or forty. Note it is a plain `Button` + `pickFiles`, not
|
package/package.json
CHANGED
package/src/page_header.tsx
CHANGED
|
@@ -10,32 +10,36 @@ interface PageHeaderProps {
|
|
|
10
10
|
/** The nav row ABOVE the title, right-aligned. */
|
|
11
11
|
right?: ReactNode;
|
|
12
12
|
/**
|
|
13
|
-
* A control that belongs to the TITLE, rendered immediately
|
|
14
|
-
* title row — a panel toggle, a
|
|
13
|
+
* A control that belongs to the TITLE, rendered immediately AFTER it on the
|
|
14
|
+
* title row — a panel toggle, a view switch, anything that reframes the page
|
|
15
|
+
* rather than acting on it.
|
|
15
16
|
*
|
|
16
17
|
* The distinction from `actions` is what the control acts ON, not where it
|
|
17
18
|
* looks best. `actions` are things you do to the page's CONTENT (create, sort,
|
|
18
|
-
* export) and collect at the right, where a scanning eye reaches them
|
|
19
|
-
* `
|
|
20
|
-
* it
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* export) and collect at the far right, where a scanning eye reaches them
|
|
20
|
+
* last; `trailing` changes what is AROUND the content and stays with the title
|
|
21
|
+
* it qualifies, so it is read as part of the heading rather than as one more
|
|
22
|
+
* thing you can do to your list.
|
|
23
|
+
*
|
|
24
|
+
* It sits AFTER the title, not before, because the title is the page's first
|
|
25
|
+
* word and nothing should come between the reader and it — a control ahead of
|
|
26
|
+
* the heading makes the eye parse chrome before it learns where it is.
|
|
23
27
|
*/
|
|
24
|
-
|
|
28
|
+
trailing?: ReactNode;
|
|
25
29
|
/** Page-level actions (CTAs), rendered on the title row, right-aligned —
|
|
26
30
|
* distinct from `left`/`right`, which form a separate nav row above. */
|
|
27
31
|
actions?: ReactNode;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
export function PageHeader(props: PageHeaderProps) {
|
|
31
|
-
const { title, description, left, right,
|
|
35
|
+
const { title, description, left, right, trailing, actions } = props;
|
|
32
36
|
|
|
33
37
|
const hasNav = !!left || !!right;
|
|
34
38
|
|
|
35
|
-
// ONE shape, whichever slots are filled. Branching the row on
|
|
36
|
-
// the title two different behaviours under a long string — shrinking
|
|
37
|
-
// panel toggle happened to be present and overflowing when it was not —
|
|
38
|
-
// is a layout law decided by an unrelated prop.
|
|
39
|
+
// ONE shape, whichever slots are filled. Branching the row on the title's own
|
|
40
|
+
// slot gave the title two different behaviours under a long string — shrinking
|
|
41
|
+
// when a panel toggle happened to be present and overflowing when it was not —
|
|
42
|
+
// which is a layout law decided by an unrelated prop.
|
|
39
43
|
//
|
|
40
44
|
// The title is what gives way; `actions` keep their width, because a CTA
|
|
41
45
|
// pushed off the row is unreachable while a wrapped title is merely taller.
|
|
@@ -62,10 +66,12 @@ export function PageHeader(props: PageHeaderProps) {
|
|
|
62
66
|
minWidth: 0,
|
|
63
67
|
}}
|
|
64
68
|
>
|
|
65
|
-
{leading}
|
|
66
69
|
<Text size="xxl" weight="semibold" style={{ flexShrink: 1, minWidth: 0 }}>
|
|
67
70
|
{title}
|
|
68
71
|
</Text>
|
|
72
|
+
{/* Never shrinks: the title is what gives way, and a control squeezed
|
|
73
|
+
below its own icon is not a smaller control, it is a broken one. */}
|
|
74
|
+
{trailing !== undefined && <View style={{ flexShrink: 0 }}>{trailing}</View>}
|
|
69
75
|
</View>
|
|
70
76
|
{actions}
|
|
71
77
|
</View>
|