fold-ng 0.8.1 → 0.9.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 CHANGED
@@ -8,6 +8,213 @@ All notable changes to **fold-ng** are documented here. The format follows
8
8
 
9
9
  _Nothing yet._
10
10
 
11
+ ## [0.9.0] - 2026-08-04
12
+
13
+ ### Changed
14
+
15
+ - **BREAKING — the shell owns the content scroll by default; pages flow.** The
16
+ all-in-one scroll model (`docs/scroll.md`), slice A. `fold-app-shell`'s
17
+ `contentScroll="clip" | "auto"` input is **renamed and reshaped** to
18
+ `scroll="scroll" | "stage"`, and the **default flips**: the shell's content
19
+ region now owns the scroll (`scroll`, was `clip`/page-owns), so a
20
+ `fold-page-layout` inside it no longer double-scrolls. `fold-page-layout` gains
21
+ `scroll="flow" | "own"` and **defaults to `flow`** — it owns no scroll box and
22
+ flows inside the shell. Net effect for a normal page: identical, minus the P0
23
+ bug where a `footerBehavior="scroll"` footer sat below an unreachable
24
+ `overscroll-behavior: contain` boundary (this **deletes the LaFolieDouce B2B
25
+ `!important` workaround**). The scroll lives on an **inner** box, never the
26
+ content region itself, so a docked panel anchored to the region stays fixed
27
+ over the frame. Migration: a page that must scroll as a self-contained unit
28
+ (a split view whose shell must not move) sets `fold-app-shell scroll="stage"`
29
+ and/or `fold-page-layout scroll="own"`. A short page still pins a trailing
30
+ `scroll` footer to the bottom (the content grows to fill), replacing the old
31
+ `margin-top: auto` glue.
32
+
33
+ - **`@angular/router` is now a declared (optional) peer dependency.** `fold-view-nav`
34
+ imports `RouterLink`/`RouterLinkActive`, but the package only listed router as a
35
+ devDependency — an undeclared peer that happened to resolve because every Angular
36
+ app ships Router. It's now in `peerDependencies` with `peerDependenciesMeta:
37
+ { "@angular/router": { optional: true } }`, so apps that use the router-coupled
38
+ nav components (view-nav, and the new breadcrumb / back-link) get a correct
39
+ install signal, while apps that don't aren't forced to add it (those components
40
+ tree-shake out). No API change.
41
+
42
+ - **`FoldPanelHostService.open()` accepts an optional-data panel without a manual
43
+ type widen.** A panel whose `data` input is optional (`data = input<T>()` →
44
+ `InputSignal<T | undefined>`) used to force `open<T | undefined, R>()` to dodge
45
+ a `TS2345` (hit by the LaFolieDouce B2B PickupPanel). `FoldPanelContent<T>.data`
46
+ is now typed as the covariant **read** side (`Signal<T | undefined>`) instead of
47
+ the invariant `InputSignal<T>`, so both a required (`input.required<T>()`) and an
48
+ optional data input satisfy the contract — `open(Cmp, { data })` infers `T` from
49
+ the value with no widen, and the data value stays type-checked. Non-breaking for
50
+ existing panels (an `InputSignal<T>` still assigns to the contract).
51
+
52
+ ### Added
53
+
54
+ - **House scrollbar tokens + `overflow-anchor` — scroll-system Slice C.** The
55
+ shell content scroll box and every `[foldScrollRegion]` now paint one tokenised
56
+ scrollbar: `--fold-scrollbar-size` / `-radius` / `-thumb` / `-track`, with the
57
+ thumb derived from the surface's own text so it adapts per theme **and** per
58
+ surface (a chrome rail vs the page). Standard `scrollbar-width`/`scrollbar-color`
59
+ everywhere, plus a `@supports selector(::-webkit-scrollbar)` layer (shipped in
60
+ `tokens.css`) for the thumb radius on Blink/WebKit. Both regions also set
61
+ `overflow-anchor: auto` so the reading position survives content reflowing above
62
+ them. (The `--fold-scrollbar-*` knobs are component vars, out of the colour
63
+ catalogue — retune them on any ancestor. The viewport-**resize** anchoring
64
+ correction, which native `overflow-anchor` doesn't cover, is a deferred
65
+ follow-up — see `docs/scroll.md`.)
66
+
67
+ - **`[foldScrollRegion]` + the shell scroll registry — scroll-system Slice B.**
68
+ The one opt-in of the scroll model (`docs/scroll.md`): with `fold-app-shell`
69
+ owning the page scroll, a layout that needs an independently-scrolling area (a
70
+ split list/detail, a data-table body, a sticky sidebar, a panel body) marks it
71
+ with `[foldScrollRegion]` instead of hand-rolling `overflow`. The directive sets
72
+ the three foot-guns (`overflow`, `min-*: 0`, `overscroll-behavior: contain`) and
73
+ the thin house scrollbar, and takes an axis (`block` default · `inline` · `both`).
74
+ It **registers with the new `ScrollRegionRegistry`**, which the shell also feeds
75
+ with its own content scroll box; the panel host freezes the registry when a
76
+ modal opens, so the page stops scrolling behind the overlay even though the
77
+ scroll owner is an inner box, not `document.body`. Freezing toggles a
78
+ `.fold-scroll-frozen` class (`overflow: hidden !important`, shipped in
79
+ `tokens.css`), never an inline write, so a region's own overflow is never
80
+ clobbered. Registry injection is optional, so the directive is a useful bounded
81
+ scroll box even without a shell. Gallery `/scroll-region`; 19 specs. (Migrating
82
+ the data-table / panel bodies onto it is deferred — they already scroll
83
+ correctly; the win there is registry coordination, not the overflow.)
84
+
85
+ - **`fold-back-link` — the “← Back” affordance for a detail page.** Three modes,
86
+ picked by which input is set: an in-app `routerLink`, a plain `href`, or — with
87
+ neither — a `<button>` that goes **back in history** (`Location.back()`).
88
+ Router-coupled but degradable (the history mode needs no router; `RouterLink`
89
+ only instantiates on a `routerLink`). Gallery `/back-link`; 4 specs.
90
+
91
+ - **`fold-breadcrumb` — a hierarchical link trail.** Data-driven: pass `[items]`
92
+ where each crumb links by an Angular `routerLink` **or** a plain `href`, so it
93
+ works in a router app and degrades to anchors without one (importing it never
94
+ forces `@angular/router` — `RouterLink` only instantiates on a crumb that sets
95
+ `routerLink`). The last item renders as the current page (`aria-current="page"`),
96
+ never a link; it's a `navigation` landmark with decorative chevron separators.
97
+ Gallery `/breadcrumb`; 5 specs.
98
+
99
+ - **Panel bottom sheet (`side: 'bottom'`) + responsive `side: 'auto'`.** The panel
100
+ host gained two docking edges beyond `left`/`right`: `bottom` is a full-width
101
+ sheet whose height is content-driven up to a max (`85dvh`) with the body
102
+ scrolling, slides up, rounded top, and a top **grabber** that taps to dismiss
103
+ (honours `disableClose`). `auto` docks **right on a wide host, bottom on a
104
+ narrow one** — the switch is container-driven via `@container` on the panel
105
+ host's own inline-size (fold's "responsive on its own width", not the viewport),
106
+ so it reacts to the content region, not the screen. All the modal machinery
107
+ (focus-trap, `inert` barrier, scroll-lock, `disableClose`) is edge-agnostic and
108
+ reused as-is. Motivated by the LaFolieDouce storefront cart on mobile. Gallery
109
+ `/panel` gained “Bottom sheet” + “Auto (by width)” triggers; +6 host specs.
110
+ (Pointer-drag-to-dismiss on the grabber is a deferred nice-to-have.)
111
+
112
+ - **`fold-panel-footer` — the action bar for panels/dialogs.** Pairs with
113
+ `fold-panel-header`: the tokenised bar at a panel's bottom edge (glass top
114
+ border + padding + button alignment), so a panel no longer hand-rolls a
115
+ `<footer class="foot">`. `align="end"` (default — the Annuler/Confirmer pair)
116
+ · `between` (a leading total + trailing actions) · `start`. Sits with
117
+ `flex: none`, so it stays pinned while the body scrolls — no `position: sticky`.
118
+ Probed against the 2nd consumer (LaFolieDouce): **17** hand-rolled panel footers
119
+ across its 3 apps, so it earns a primitive. Gallery `/panel` “Panel footer”; 3 specs.
120
+
121
+ - **`fold-danger-zone` — the destructive-action block.** A framed region for
122
+ “delete X” settings: a title, a projected explanation, and a guarded action.
123
+ **Two appearances** — `filled` (alert-tinted block) and `section`, a danger
124
+ _section_ with a **normal-background body** so it can host ordinary content, only
125
+ the frame + heading signalling danger (the GitHub “Danger Zone” look). The
126
+ destructive control's confirm **reveals on click**: an `actionLabel` button
127
+ opens an in-place `fold-inline-confirm` — a plain “are you sure?”, or a
128
+ type-to-confirm field when `confirmPhrase` is set (the input is never shown until
129
+ the button is clicked). `(confirmed)` emits the typed text (or `""`); omit
130
+ `actionLabel` for a framed section with no action. `role="group"` +
131
+ `aria-labelledby`. Gallery `/danger-zone`; 5 specs.
132
+
133
+ - **`fold-multiselect` bulk actions — `allowSelectAll` / `allowClear`.** A sticky
134
+ bar at the top of the panel offers **Select all** (adds every enabled option,
135
+ skipping disabled rows and preserving an already-picked disabled one) and
136
+ **Clear** (empties the set). Each button is gated — select-all disables once
137
+ everything enabled is picked, clear disables while empty. Labels are overridable
138
+ (`selectAllLabel` / `clearLabel`). The panel is now a wrapper around the
139
+ `role="listbox"` (the bar sits outside it, so it stays valid ARIA). Gallery
140
+ `/listbox` multiselect tab enables both; 5 specs.
141
+
142
+ - **`fold-optgroup` — labelled option groups for the styleable selects.** The
143
+ counterpart to the native `<optgroup>`: wrap `<fold-option>`s in
144
+ `<fold-optgroup label="…">` to sort a long list into sections. Purely
145
+ presentational — the owning `fold-listbox` / `fold-multiselect` now discovers
146
+ options with a `descendants: true` query, so grouped options join the same
147
+ flat, document-ordered list the roving keyboard core walks; the header carries
148
+ `role="group"` + `aria-labelledby` (no `role="option"`), so nav skips straight
149
+ over it. Also supported in the data-driven **`[options]` array API**: an entry
150
+ is a `FoldSelectOption<T>` or a labelled `FoldSelectOptionGroup<T>` (mix both),
151
+ narrowed by the exported `isFoldSelectOptionGroup` guard. Gallery `/listbox`
152
+ “grouped” tab shows both forms; specs cover projected + array discovery,
153
+ cross-group roving and selection.
154
+
155
+ - **The icon devtool browses by category.** `FoldIconDevtoolComponent` now
156
+ groups the live registry into **collapsible sections** — UI · Navigation ·
157
+ Commerce · Music · Status · People · Brands (+ a **Custom** bucket for
158
+ host-registered icons) — each with its icon count; a search only surfaces the
159
+ categories it hits. New public `FOLD_BUILTIN_ICON_CATEGORIES` (+ `FoldIconCategoryId`)
160
+ is the single source of truth for the grouping (each icon file owns its slice).
161
+
162
+ - **`fold-ng/devtools` now builds as a real ng-packagr secondary entry**, so
163
+ `import("fold-ng/devtools")` resolves for **published (npm) consumers**, not
164
+ only source-consumed ones. It compiles to its own FESM + `d.ts` and imports the
165
+ primary `fold-ng` by name; `finalize-dist` normalises ng-packagr's flattened
166
+ `./src/devtools` export to the public `./devtools` subpath (+ a node10 directory
167
+ manifest). `attw` all-🟢, `publint` clean. The api-surface guard was generalised
168
+ to snapshot every published entry point (`.` + `./devtools`).
169
+
170
+ - **`fold-ng/devtools` — an opt-in dev-tools entry, starting with
171
+ `FoldIconDevtoolComponent`.** A **dev-only** floating panel that browses the live
172
+ `FoldIconRegistry` (built-ins + whatever the host app registered), with a search,
173
+ a preview grid, and a mini playground that builds a `<fold-icon>` snippet and
174
+ copies it. The panel is **draggable** (grab the header) and **minimisable**
175
+ (collapses to a pill). Published from a **separate entry** so it never lands in a
176
+ bundle that doesn't ask for it — import it behind a dev guard
177
+ (`if (isDevMode())` + a dynamic `import("fold-ng/devtools")`) so production
178
+ tree-shakes it away. Dogfooded in the gallery (primary-rail "Dev tools" + the
179
+ `/icons` hero CTA).
180
+ - **`FoldIconRegistry.names()`** — the sorted list of every registered icon name
181
+ (built-ins + runtime additions), reactive. Powers catalogue tooling (the icon
182
+ devtool).
183
+
184
+ - **A `commerce` icon category — 21 e-commerce glyphs.** The built-in set had no
185
+ cart, catalogue, payment or fulfilment icons, so a consumer reused `package` as a
186
+ placeholder for both a cart and a delivery tab (`docs/consumer-friction.md` Round
187
+ 4 #3). New `COMMERCE_ICONS` (the 7th category, wired into `FOLD_BUILTIN_ICONS` and
188
+ the `/icons` gallery): `shopping-cart` · `shopping-bag` · `basket` · `package` ·
189
+ `package-check` · `tag` · `tags` · `barcode` · `qr-code` · `gift` · `credit-card`
190
+ · `wallet` · `receipt` · `coins` · `banknote` · `percent` · `truck` · `store` ·
191
+ `warehouse` · `map-pin` · `package-return`. Same self-contained inlined-SVG /
192
+ `currentColor` contract; names autocomplete on `FoldIconName`.
193
+
194
+ - **`fold-textarea` — the multiline sibling of `fold-input`.** Same box chrome
195
+ (tokens, sizes, `panel` variant, focus/disabled) via `input-shell.scss` and the
196
+ same label / required / hint / error chrome via `fold-input-base` — so a note
197
+ field is no longer a hand-rolled native `<textarea>` + copied box CSS. **No
198
+ resize handle by design**: the box keeps its `rows` height and **wraps +
199
+ scrolls** overflow (`resize: none; overflow-y: auto`), so a user-dragged corner
200
+ can't break a panel layout. `FormValueControl<string>` (`[formField]` or
201
+ `[(value)]`).
202
+ - **`fold-date` + `fold-time` — the temporal-field wrappers.** Two sibling
203
+ controls (distinct selectors for call-site clarity, the same "one control, one
204
+ job" split as `fold-input` vs `fold-number-input`): `fold-date` wraps the native
205
+ `<input type="date">` family (`type`: `date` · `datetime-local` · `month` ·
206
+ `week`), `fold-time` wraps `<input type="time">`. Both wrap the native control
207
+ the way `fold-select` wraps `<select>` — keeping the OS calendar/clock + mobile
208
+ keyboard — and hand back a **typed `[(value)]`** (the native string, `YYYY-MM-DD`
209
+ / `HH:mm`), so consumers stop hand-writing an `inputValue($event)` reader.
210
+ `min` / `max` / `step` pass through; both share the `fold-input` box + field
211
+ chrome. **Not** a calendar popover (that's the `fold-calendar` family) — the plain
212
+ fields. `FormValueControl<string>`.
213
+ - Surfaced by the 2nd consumer (LaFolieDouce B2B); see `docs/consumer-friction.md`
214
+ Round 4 #2. The shared `_field-box.scss` `size()` mixin gained a `$height: false`
215
+ opt-out (a `<textarea>`'s height is content-driven), and `readInputValue` now
216
+ reads `<textarea>` targets.
217
+
11
218
  ## [0.8.1] - 2026-08-02
12
219
 
13
220
  ### Changed
@@ -1056,7 +1263,8 @@ design-token stylesheet.
1056
1263
  `currentColor`; `prefers-reduced-motion` + `forced-colors` are respected;
1057
1264
  strings localise via inputs / providers (`provideFoldPanelLabels`).
1058
1265
 
1059
- [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.8.1...HEAD
1266
+ [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.9.0...HEAD
1267
+ [0.9.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.9.0
1060
1268
  [0.8.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.8.1
1061
1269
  [0.8.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.8.0
1062
1270
  [0.7.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.7.0
package/README.md CHANGED
@@ -287,9 +287,46 @@ radius / text / icon-size / space / motion / blur scales — see
287
287
  All standalone, signals-first, styled against the semantic tokens. Import from
288
288
  the package root.
289
289
 
290
+ ### Find one by what you need
291
+
292
+ The reference table below is keyed by component **name** — but you usually know
293
+ your **intent**, not the name (that's how an uppercase mini-title gets hand-rolled
294
+ instead of reaching for `fold-element-title`). Start here; **before hand-rolling a
295
+ label, field, badge, card or overlay, scan this table** — fold almost certainly
296
+ ships it.
297
+
298
+ | I need to… | Reach for |
299
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
300
+ | a small **uppercase label / eyebrow** over a group | `fold-element-title` (`variant="eyebrow"` · `bar` · `title`) |
301
+ | a **section** with a title + description + actions | `fold-page-section` (semantic `<section>` + `aria-labelledby`) |
302
+ | show **read-only label/value pairs** (a recap) | `fold-field-list` / `fold-field` (`dl/dt/dd`) |
303
+ | a **text** field | `fold-input` — number → `fold-number-input` · multiline note → `fold-textarea` |
304
+ | a **date** / **time** field | `fold-date` (date·datetime-local·month·week) · `fold-time` |
305
+ | a **dropdown** | `fold-select` (native) — custom rows → `fold-listbox` · multi → `fold-multiselect` |
306
+ | an **on/off** field · a **password** field | `fold-checkbox` · `fold-password-field` |
307
+ | a **range** / **debounced search** | `fold-slider` / `fold-range-slider` · `fold-search` |
308
+ | a **button** · **icon-only** action · icon **on/off** | `fold-button` (`<button>`/`<a>`) · `fold-button-icon` · `fold-toggle-icon` · text link → `fold-link` |
309
+ | an inline **"are you sure?"** guard | `fold-inline-confirm` (no modal — simple · type-to-confirm · secret) |
310
+ | a **"delete X" danger block** (type-to-confirm) | `fold-danger-zone` (alert frame + blast-radius text + retype-to-arm) |
311
+ | a **status / count pill** · status→colour | `fold-badge` · `fold-status-badge` |
312
+ | a **tinted message / alert** row | `fold-callout` (`inset` for in-flow) |
313
+ | a transient **toast** | `fold-toast` + `FoldToastService` |
314
+ | a **card** · titled info card · page **splash** | `fold-card` · `fold-context-card` · `fold-hero-section` (bordered → `fold-hero-card`) |
315
+ | in-page **tabs** · a routed **nav bar** · a segmented control | `fold-tabs` · `fold-view-nav` · `fold-view-toggle` / `fold-choice-row` |
316
+ | a **breadcrumb** trail (routerLink or href) | `fold-breadcrumb` (`[items]`, last = current page) |
317
+ | a **"← Back"** link (route, href, or history) | `fold-back-link` (`routerLink` / `href` / history-back button) |
318
+ | a **table** + **pagination** | `fold-data-table` · `fold-paginator` |
319
+ | an **avatar** · a cluster | `fold-avatar` (+ `…Detail`) · `fold-avatar-list` |
320
+ | an **empty** / **loading** state | `fold-empty-state` · `fold-loading` / `fold-spinner` |
321
+ | a **side panel** · anchored **popover** · actions **menu** | `fold-panel-host` · `fold-popover` · `fold-dropdown` |
322
+ | a **collapsible** section | `fold-disclosure` (a modal dialog is roadmap — use a modal `fold-panel-host` or `fold-inline-confirm`) |
323
+ | an **icon** · a **calendar** · a **timeline / stepper** | `fold-icon` · `fold-calendar-month`/`-week`/`-day`/`-list`/`-agenda`/`-timegrid` · `fold-timeline` |
324
+ | a **nav rail** · app **skeleton** · a **detail page** with rails | `fold-menu` · `fold-app-shell` · `fold-aside-layout` (page scaffold → `fold-page-layout`) |
325
+ | **drag-drop** file upload | `fold-file-dropzone` |
326
+
290
327
  | Component | Selector | What it is |
291
328
  | -------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
292
- | `FoldAppShellComponent` | `fold-app-shell` | Responsive app skeleton (rails + header + content + self-collapsing `footer` slots; `headerLayout`/`footerLayout` inset·full, `footerBehavior` pinned·scroll; `mobileNav` drawer·none — `[(mobileNavOpen)]` off-canvas drawer for the primary rail on mobile, or `none` to compose an `fold-nav-launcher`; `contentScroll` clip·auto; built-in skip-link to a focusable `<main>`. Regions float per-surface via `foldElevated`, not a shell flag). |
329
+ | `FoldAppShellComponent` | `fold-app-shell` | Responsive app skeleton (rails + header + content + self-collapsing `footer` slots; `headerLayout`/`footerLayout` inset·full, `footerBehavior` pinned·scroll; `mobileNav` drawer·none — `[(mobileNavOpen)]` off-canvas drawer for the primary rail on mobile, or `none` to compose an `fold-nav-launcher`; `scroll` scroll·stage — the shell owns the content scroll by default so pages flow (`[foldScrollRegion]` opts a nested area back into its own scroll); built-in skip-link to a focusable `<main>`. Regions float per-surface via `foldElevated`, not a shell flag). |
293
330
  | `FoldMenuComponent` (+ `Item` / `Section` / `Separator`) | `fold-menu` | Collapsible nav rail — coloured sections, `tint="follow"`, depth `level`, collapse-toggle placement. Items are `a[fold-menu-item]`. |
294
331
  | `FoldNavLauncherComponent` (+ `FoldNavTileComponent`) | `fold-nav-launcher` | Full-screen mobile nav launcher — a centred tile grid over a blurred scrim (scrim / `Escape` / close dismissal, focus-trap, scroll-lock). `columns="auto"` scales tiles to the count. Pairs with `fold-app-shell mobileNav="none"`. Tiles are `a[fold-nav-tile]` — `variant="surface"`·`filled`. |
295
332
  | `FoldPageLayoutComponent` | `fold-page-layout` | Page scaffold — gutter + header + body rhythm; fills its container (width is a content concern). Tokens `--fold-page-gutter` / `--fold-page-gap`; sections can `bleed` edge-to-edge. |
@@ -304,21 +341,26 @@ the package root.
304
341
  | `FoldFieldListComponent` / `…Field` | `fold-field-list` | Read-only `dl/dt/dd` recap — label/value pairs (`[empty]` placeholder). The _display_ half of a record; `fold-input` is the _edit_ half. |
305
342
  | `FoldInputComponent` | `fold-input` | Text-input control (`value: string`) — Signal Forms (`[formField]`) or standalone `[(value)]`; size × align × variant, `label` / `required` / `hint`. The _edit_ half of a record (`fold-field` reads). |
306
343
  | `FoldNumberInputComponent` | `fold-number-input` | Numeric sibling of `fold-input` (`value: number \| null`, empty ⇒ `null`); owns `min` / `max` / `step` + `label` / `required` / `hint`. Split so each control keeps its true type. |
344
+ | `FoldTextareaComponent` | `fold-textarea` | Multiline sibling of `fold-input` (`value: string`). **No resize handle** — fixed `rows` height, wraps + scrolls overflow. Shares the box + `label`/`required`/`hint`/`error` chrome. Signal Forms or `[(value)]`. |
345
+ | `FoldSelectComponent` | `fold-select` | Native `<select>` wrapper (options projected as `<option>`); shares `fold-input`'s box chrome. Signal Forms (`FormValueControl<string>`) or `[(value)]`. For custom rows use `fold-listbox`. |
346
+ | `FoldDateComponent` | `fold-date` | Native calendar-date wrapper (`type` = date · datetime-local · month · week) — keeps the OS picker, hands back a typed `[(value)]` string (`YYYY-MM-DD`). `min`/`max`/`step` pass through. Time-of-day → `fold-time`. |
347
+ | `FoldTimeComponent` | `fold-time` | Native time-of-day wrapper (`<input type="time">`) — typed `[(value)]` string (`HH:mm`), `min`/`max`/`step`. The sibling of `fold-date`. |
307
348
  | `FoldCheckboxComponent` | `fold-checkbox` | Boolean control — a native `<input type="checkbox">` (keyboard, `indeterminate`, forms) restyled to tokens. Signal Forms (`[formField]`, a `FormCheckboxControl`) or standalone `[(checked)]`; `indeterminate`, `label`/`ariaLabel`, `hint`/`errors`, `size`. |
308
349
  | `FoldPasswordFieldComponent` | `fold-password-field` | Password input + a live requirements checklist (a dot/tick per rule). Rules injected via `FoldPasswordRule` (`{ label, test }` — regex/zod/anything); `revealable` eye (a `fold-input` capability); `marker` dot/check; `[rules]` slot to redesign the list; `validChange`; Signal Forms. |
309
350
  | `FoldViewToggleComponent` | `fold-view-toggle` | Segmented single-select (Cards/Table, density, chart-mode…). Generic `options` (`{ value, icon?, label?, ariaLabel?, disabled? }`) + `[(value)]`; a real `role="radiogroup"` — roving tabindex, arrow keys, Home/End, disabled-skip; `size`, `iconOnly`, `activeStyle` (raised / accent). |
310
351
  | `FoldSearchComponent` | `fold-search` | Debounced search box — an `fold-input` that emits `searchChange` once typing settles (`delayMs`), trimmed + de-duplicated. |
311
352
  | `FoldListboxComponent` / `FoldOptionComponent` | `fold-listbox` / `fold-option` | Styleable single-select — the richer sibling of `fold-select` (native `<select>`) for options that need custom rows (icon, second line, status). On `fold-popover`; `role="listbox"` + `aria-activedescendant`, full keyboard (↑/↓, `Home`/`End`, type-ahead, `Enter`). Signal Forms (`FormValueControl<string>`, `[formField]`/`[(value)]`); shares `fold-input`'s box chrome. |
312
353
  | `FoldMultiselectComponent` | `fold-multiselect` | Multi-select sibling of `fold-listbox` (same popover + `fold-option` rows). Value is a set (`readonly string[]`); activating a row toggles it and the panel stays open. Separate component — not a `multiple` flag — so the Signal-Forms value type stays honest. `role="listbox"` + `aria-multiselectable`; the trigger summarises the picks. |
354
+ | `FoldOptgroupComponent` | `fold-optgroup` | Labelled group of `<fold-option>`s inside a `fold-listbox` / `fold-multiselect` — the styleable `<optgroup>`. Presentational: `role="group"` + `aria-labelledby` header (no `role="option"`, so keyboard nav skips it); the owner discovers grouped options in document order, so roving crosses groups seamlessly. |
313
355
  | `FoldSliderComponent` | `fold-slider` | Single-value range slider — a styled native `<input type="range">` (design-system track/fill/thumb). Signal Forms (`[formField]`, a `FormValueControl<number>`) or `[(value)]`; real `<label for>`, `aria-valuetext`, `hint`/`errors`, focus ring. |
314
356
  | `FoldRangeSliderComponent` | `fold-range-slider` | Dual-thumb range slider selecting a `{ min, max }` window (shares the slider track/thumb). Two-way `[(value)]`; a labelled `role="group"`, per-thumb i18n aria (`minLabel`/`maxLabel`) + formatted `aria-valuetext`, `disabled`. |
315
- | `FoldRangeSliderComponent` | `fold-range-slider` | Dual-thumb slider selecting a `{ min, max }` window over `[min, max]`; shares the slider track/fill/thumb. |
316
357
  | `FoldFileDropzoneComponent` | `fold-file-dropzone` | File-picker dropzone — drag-over visuals, keyboard activation, hidden `<input type=file>` plumbing; emits the picked `File[]` (presentational — never uploads). |
317
358
  | `FoldLinkComponent` | `fold-link` | Inline text link / link-button (icons, accent · muted). |
318
359
  | `FoldButtonComponent` | `button[foldButton]` · `a[foldButton]` | Action button — applied to a real `<button>` **or** `<a>` (link that looks like a button, gets `href`/`routerLink`); orthogonal `emphasis` (solid·soft·outline) × `intent` (primary·neutral·warning·danger) × 3 sizes × shape/`block`; `icon`/`iconTrailing` shorthand (auto-sized) or project content; `loading` (spinner + `aria-busy`). Use native `(click)`. |
319
360
  | `FoldButtonIconComponent` | `fold-button-icon` | Icon-only **momentary** button — shape × size × tone; a one-shot action (no pressed state). For a text button use `fold-button`; for on/off use `fold-toggle-icon`. |
320
361
  | `FoldToggleIconComponent` | `fold-toggle-icon` | Icon-only **toggle** — the same surface as `fold-button-icon`, plus `[(active)]` + `aria-pressed` (true/false) and a pressed state. Emits `toggled`. |
321
362
  | `FoldInlineConfirmComponent` | `fold-inline-confirm` | In-place “are you sure?” guard — the projected trigger swaps to a confirm/cancel row (no modal). Simple (`confirmed` emits `""`), type-to-confirm (`[match]`), or secret (`password`, masked, emits the value). `confirmIcon` + a chosen `cancelIcon`; `Escape` cancels; `message` announced via `aria-describedby`; focus in-then-back; controlled `[(open)]` + `keepOpenOnConfirm` for async pending; i18n via `provideFoldInlineConfirmLabels`. |
363
+ | `FoldDangerZoneComponent` | `fold-danger-zone` | Framed destructive-action block for “delete X” settings. `appearance="filled"` (alert-tinted) or `"section"` (a danger section: alert border + normal-background body for ordinary content). The confirm **reveals on click** — an `actionLabel` button opens an in-place `fold-inline-confirm` (plain “are you sure?”, or type-to-confirm when `confirmPhrase` is set); `(confirmed)` emits the typed text. Omit `actionLabel` for a section with no action. `role="group"` + `aria-labelledby`. |
322
364
  | `FoldDataTableComponent` | `fold-data-table` | Controlled roster table — sortable sticky header, tone rows, controlled selection (checkbox column), roving-keyboard nav, `mobileLayout` (scroll / auto-cards / custom `foldRowCard`), an optional `foldToolbar` bar, sticky-first, density. |
323
365
  | `FoldPaginatorComponent` | `fold-paginator` | Server-side paginator (size selector + range + page nav). |
324
366
  | `FoldTimelineComponent` | `fold-timeline` | Connected rail of nodes (dot + optional date + label) — `vertical` navigable history or `horizontal` step progress; nodes optionally clickable. |
@@ -326,8 +368,10 @@ the package root.
326
368
  | `FoldStatusBadgeComponent` | `fold-status-badge` | Status→colour badge (maps a domain status key to a tone). |
327
369
  | `FoldChoiceRowComponent` | `fold-choice-row` | Segmented / chip selector. |
328
370
  | `FoldViewNavComponent` | `fold-view-nav` | Navigation bar styled as tabs. Items carry a `link` (routerLink → a real `<a>`: cmd-click, deep-links, active state auto), an `href`, or nothing (a button); `aria-current="page"` on the active one. `direction="auto"` follows a wrapping `fold-nav-layout`; `collapsed` for an icon rail. For in-page panel switching use `fold-tabs` instead. |
371
+ | `FoldBreadcrumbComponent` | `fold-breadcrumb` | Hierarchical link trail. Data-driven `[items]` where each crumb links by `routerLink` **or** `href` (works without the router; `RouterLink` only instantiates on a `routerLink` crumb). Last item is the current page (`aria-current="page"`), not a link. `navigation` landmark, decorative chevron separators. Needs `@angular/router` only when a crumb uses `routerLink` (optional peer). |
372
+ | `FoldBackLinkComponent` | `fold-back-link` | The “← Back” affordance for a detail page. Three modes by input: `routerLink` (in-app), `href` (external / non-router), or neither → a history-back `<button>` (`Location.back()`). Router-coupled but degradable — the history mode needs no router. `label` + leading `icon` (default `chevron-left`). |
329
373
  | `FoldTabsComponent` + `FoldTabPanelComponent` | `fold-tabs` + `fold-tab-panel` | The in-page ARIA Tabs widget: `role="tablist"` + roving arrow-key keyboard, `aria-selected`/`aria-orientation`, each tab wired to its `fold-tab-panel` (`aria-controls` ↔ `aria-labelledby`). Panels take the bar by ref (`[tabs]="t"`) so they coordinate across `fold-nav-layout` slots. |
330
- | `FoldIconComponent` | `fold-icon` | SVG icon (114-icon built-in set + `FoldIconRegistry`). |
374
+ | `FoldIconComponent` | `fold-icon` | SVG icon (~135 built-in glyphs across 7 categories incl. `commerce` + `FoldIconRegistry`). |
331
375
  | `FoldSpinnerComponent` | `fold-spinner` | Indeterminate loading arc (`currentColor`, icon-sized, reduced-motion aware). Decorative by default; `label` → `role="status"`. Powers `loading` on the buttons. |
332
376
  | `FoldAvatarComponent` / `…Detail` | `fold-avatar` | Initials/image avatar (square, muted, status ring) + identity cell. |
333
377
  | `FoldAvatarListComponent` | `fold-avatar-list` | Overlapping avatar cluster (per-face variant, `limit` + a `+N` overflow chip). |
@@ -338,6 +382,7 @@ the package root.
338
382
  | `FoldDisclosureComponent` | `fold-disclosure` | One summary toggling one collapsible panel — the accordion primitive (open-state is the consumer's to bind); keeps content mounted, unlike native `<details>`. |
339
383
  | `FoldPanelHostComponent` | `fold-panel-host` | Side-panel / overlay host (+ `FoldPanelHostService` / `FoldPanelRef` / `FoldPanelToggle`). Modal: accessible name, `inert` background barrier, top-most focus trap, scroll-lock. Localise the close label once via `provideFoldPanelLabels({ close })`. |
340
384
  | `FoldPanelHeaderComponent` | `fold-panel-header` | Standard panel header (title/eyebrow, self-closing). Names its dialog (`aria-labelledby`) and reads the app-wide close label. |
385
+ | `FoldPanelFooterComponent` | `fold-panel-footer` | Panel/dialog action bar pairing with `fold-panel-header`: tokenised top border + padding + `align="end" \| "between" \| "start"`. Projects the buttons; sits `flex: none` so it stays pinned to the panel bottom while the body scrolls (no `position: sticky`). |
341
386
  | `FoldPopoverComponent` | `fold-popover` | Anchored floating layer — projected content in the native top layer (escapes `overflow`/`z-index`), positioned by a dependency-free **flip → size → shift** engine (`computePlacement`): a tall panel gets a `max-height` and scrolls inside the viewport. `[(open)]`; `autoUpdate` (ResizeObserver); optional `arrow`; native CSS enter/exit (`@starting-style` + `allow-discrete`); outside-click + `Escape` dismissal, focus-return, auto-wired `aria-haspopup`/`expanded`/`controls`. |
342
387
  | `FoldDropdownComponent` | `fold-dropdown` | Actions menu on `fold-popover` — `role="menu"` with `<fold-dropdown-item>`s, ↑/↓ roving, `Home`/`End`, type-ahead; opens onto the first enabled item, closes returning focus to the trigger. Give the trigger `foldPopoverTrigger="menu"`. |
343
388
 
@@ -397,7 +442,11 @@ an inset, rounded, shadowed card — the per-surface "floating" mechanism, drive
397
442
  by `--fold-surface-inset`/`-radius`/`-shadow`), **`foldStickyColumn`** (turn an
398
443
  `<aside>` into a sticky side column — `sticky="top·center·bottom"` + `stickyOffset`,
399
444
  the rest tunable via `--fold-sticky-column-*`; un-stick with
400
- `--fold-sticky-column-position: static` at the page's stacking breakpoint), and
445
+ `--fold-sticky-column-position: static` at the page's stacking breakpoint),
446
+ **`foldScrollRegion`** (turn any element into a bounded, coordinated scroll box —
447
+ `overflow` + `min-*: 0` + `overscroll-behavior` + the house scrollbar, on a
448
+ `block`·`inline`·`both` axis; the one opt-in of the shell scroll system, it
449
+ registers with the shell so an overlay freezes it while a modal is open), and
401
450
  **`foldRepeatPress`** (press-and-hold auto-repeat for a stepper button — fires
402
451
  once on press then on a tunable cadence while held, and stops the instant
403
452
  `foldRepeatPressDisabled` goes true mid-hold).
@@ -0,0 +1,4 @@
1
+ {
2
+ "module": "../fesm2022/fold-ng-src-devtools.mjs",
3
+ "typings": "../types/fold-ng-src-devtools.d.ts"
4
+ }