@lotics/ui 28.1.0 → 28.2.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/MIGRATION.md CHANGED
@@ -4,6 +4,26 @@ 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
+ ## 28.2.0 — `SubsectionHeadingTitle` takes a `description`
8
+
9
+ Additive; nothing to change. The heading ramp's three rungs are meant to carry the SAME slots
10
+ and differ only in type size — `description` was missing from the middle one, so a group that
11
+ needed a muted line under its title had to borrow the section rung above it or hand-roll a
12
+ `Text` off the ramp.
13
+
14
+ It matters most where a GROUP receives something. A `FileDropTarget`-wrapped region shows no
15
+ dropzone, so `AGENTS.md` requires the heading to name the drag and paste paths — and a surface
16
+ with several PEER file regions (per-kind photo buckets, each its own target, because nothing in
17
+ a dropped file says which bucket it belongs to) carries those regions at subsection rung:
18
+
19
+ ```tsx
20
+ <SubsectionHeading>
21
+ <SubsectionHeadingTitle description="Kéo, dán hoặc bấm để thêm ảnh">Ảnh hạ</SubsectionHeadingTitle>
22
+ </SubsectionHeading>
23
+ ```
24
+
25
+ The rung is unchanged (`###`, lg semibold, level 3) — only the slot is new.
26
+
7
27
  ## 28.1.0 — `ChecklistItem` can announce more than its title
8
28
 
9
29
  Additive; nothing to change. `accessibilityLabel` overrides the row's accessible name, which
package/docs/catalog.md CHANGED
@@ -451,13 +451,17 @@ source (`src/<module>.tsx`/`.ts`) is the API reference.
451
451
  (16, fixed), no body component. `SectionHeadingTitle` is ALWAYS `##` (xl semibold;
452
452
  `weight="medium"` opt-down only) + `info` for an ⓘ provenance popover after the title,
453
453
  same as `CardHeaderTitle.info`. `SubsectionHeadingTitle` is the `###` lg-semibold level-3
454
- title of a named group inside a section (same `info` ⓘ affordance as the section title) —
455
- heading-row siblings ride its right edge. **A section's ADD is one of those siblings** — a
454
+ title of a named group inside a section (same `info` ⓘ AND `description` slots as the section
455
+ title) — heading-row siblings ride its right edge. Its `description` is what a files
456
+ SUBSECTION uses to name its drag/paste paths: a `FileDropTarget`-wrapped region shows no
457
+ dropzone, and a surface with several peer file regions (per-kind photo buckets, each its own
458
+ target) carries that line at THIS rung, not the section above it. **A section's ADD is one of those siblings** — a
456
459
  `primary` `Button` beside the title, rendered whether the collection is empty or full, never
457
460
  under the rows it extends and never repeated in the `EmptyState`
458
461
  (composition.md § The add-placement law). `DialogSectionHeadingTitle` is the `####`
459
462
  md-semibold rung with the SAME `icon`/`description`/`info` slots as the section title, so a
460
- dialog surface loses only the type size, never an affordance. The heading ramp is FIXED:
463
+ dialog surface loses only the type size, never an affordance. **`description` is on every
464
+ rung** — the ramp's slots do not vary by level; only the type size does. The heading ramp is FIXED:
461
465
  `#` xxl / `##` xl / `###` lg / `####` md, no size props.
462
466
  - **`section_stack`** — `SectionStack`, `SubsectionStack` — stacks that own the
463
467
  between-block law, skipping null children: `SectionStack` = a fixed 56px beat + a hairline
@@ -64,8 +64,11 @@ restyle a heading level per-page.
64
64
  stays ABSOLUTE — this is one more fixed size, never a `size`/`compact` knob on a rung above.
65
65
  - **Subsection title** — the level BELOW a section on a long record surface: `Subsection` ›
66
66
  `SubsectionHeading` › `SubsectionHeadingTitle` (`###` — lg semibold, with the same `info` ⓘ
67
- affordance as `SectionHeadingTitle`; siblings — a `Badge`, a
68
- `SectionHeadingMeta`, an action — ride the heading row's right edge). Sibling subsections stack
67
+ and `description` slots as `SectionHeadingTitle`; siblings — a `Badge`, a
68
+ `SectionHeadingMeta`, an action — ride the heading row's right edge). Reach for its
69
+ `description` when the GROUP is what receives something — a files subsection wrapped in its
70
+ own `FileDropTarget` has to name drag and paste itself, since a wrapped region shows no zone
71
+ and the section above it may hold several such groups. Sibling subsections stack
69
72
  in a **`SubsectionStack`** (a fixed 32px beat; no margins, no hand-rolled dividers).
70
73
  It is SPACE-ONLY while the groups are SHORT — the titles carry the grouping and hairlines
71
74
  belong to the SECTION level, one rule per altitude — and takes `divided` once the groups run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/ui",
3
- "version": "28.1.0",
3
+ "version": "28.2.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./vite": {
@@ -87,6 +87,17 @@ export function SubsectionHeading(props: SubsectionHeadingProps) {
87
87
 
88
88
  export interface SubsectionHeadingTitleProps {
89
89
  children: React.ReactNode;
90
+ /**
91
+ * Muted line under the title, as on the rungs either side of this one.
92
+ *
93
+ * The ramp carries the same slots at every rung, and this was the one gap in
94
+ * it. It matters most for a group that RECEIVES something: a files region
95
+ * wrapped in a `FileDropTarget` shows no dropzone, so drag and paste are
96
+ * invisible unless the heading names them — and a surface with several peer
97
+ * file regions (per-kind photo buckets, each its own drop target) puts them at
98
+ * THIS rung, where the prescribed line previously had nowhere to sit.
99
+ */
100
+ description?: string;
90
101
  /** Heading rank. Defaults to 3 — one level under `SectionHeadingTitle`'s 2. */
91
102
  level?: HeadingLevel;
92
103
  /** An ⓘ popover after the title — a short "what this is / where it came from"
@@ -98,14 +109,25 @@ export interface SubsectionHeadingTitleProps {
98
109
  * xl section title and above the md/sm body. Grows to push siblings (a
99
110
  * `SectionHeadingMeta`, a `Badge`, an action) to the right edge. */
100
111
  export function SubsectionHeadingTitle(props: SubsectionHeadingTitleProps) {
101
- const { children, level = 3, info } = props;
112
+ const { children, description, level = 3, info } = props;
102
113
  const words = useLoticsLocale();
114
+ // Same anatomy as `SectionHeadingTitle` one rung down: the title row keeps its
115
+ // own `flexDirection: "row"` so `info` sits beside the text, and the column
116
+ // wrapper carries the `flex: 1` that lets a heading row push meta to its right
117
+ // edge. Without the wrapper the description would land on the title's LINE.
103
118
  return (
104
- <View style={{ flex: 1, flexDirection: "row", alignItems: "center", gap: 6 }}>
105
- <Text level={level} size="lg" weight="semibold">
106
- {children}
107
- </Text>
108
- {info ? <InfoPopover text={info} accessibilityLabel={words.sectionHeading.info} /> : null}
119
+ <View style={{ flex: 1, gap: 2 }}>
120
+ <View style={{ flexDirection: "row", alignItems: "center", gap: 6 }}>
121
+ <Text level={level} size="lg" weight="semibold">
122
+ {children}
123
+ </Text>
124
+ {info ? <InfoPopover text={info} accessibilityLabel={words.sectionHeading.info} /> : null}
125
+ </View>
126
+ {description ? (
127
+ <Text color="zinc-500" size="sm">
128
+ {description}
129
+ </Text>
130
+ ) : null}
109
131
  </View>
110
132
  );
111
133
  }