fold-ng 0.4.0 → 0.5.2

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,341 @@ All notable changes to **fold-ng** are documented here. The format follows
8
8
 
9
9
  _Nothing yet._
10
10
 
11
+ ## [0.5.2] - 2026-07-27
12
+
13
+ ### Added
14
+
15
+ - **`fold-listbox` gains a `selectionChange: T` output** — fires when the user
16
+ picks an option, carrying the chosen value and **never `null`**. Use it for
17
+ the common "do X on selection" case to skip the `T | null` narrowing that
18
+ `[(value)]` / `valueChange` force on every handler. Clearing the value (the ×
19
+ affordance) does not fire it — observe `value` / `valueChange` for that.
20
+ - **`fold-range-slider` gains a `rangeChange: FoldRangeValue` output** — the same
21
+ ergonomics for ranges: fires on a thumb drag with the resolved `{ min, max }`,
22
+ never `undefined`.
23
+
24
+ ### Changed
25
+
26
+ - **`fold-card` `separators` / `raisedBands` accept a boolean shorthand.** On top
27
+ of the `FoldCardBandChrome` enum (`none`/`header`/`footer`/`both`), a bare
28
+ attribute (`<fold-card separators>`) or `[separators]="true"` now means `both`,
29
+ and `false` means `none` — the idiomatic Angular boolean-attribute ergonomics,
30
+ while the enum stays for per-band control. Exposes `foldCardBandChrome` (the
31
+ coercion fn) and `FoldCardBandChromeInput`.
32
+ - **`pnpm release` gates the full suite (incl. `test:e2e`) locally before
33
+ tagging.** Release tags are protected/immutable, so a tag pushed for a build
34
+ that then fails CI burns that version number (how `0.5.0` was lost). The
35
+ release script now runs `lint · tsc · strictTemplates · vitest · test:e2e`
36
+ (installing Chromium first) up front — a red build aborts with nothing bumped
37
+ or tagged.
38
+
39
+ ## [0.5.1] - 2026-07-27
40
+
41
+ First published 0.5.x — same tree as the (unpublished, e2e-gated) v0.5.0 tag with the release e2e locator scoped to the first control.
42
+
43
+ ## [0.5.0] - 2026-07-27
44
+
45
+ ### Added
46
+
47
+ - **`fold-view-toggle` — a segmented single-select.** A compact Cards / Table
48
+ (or density, chart-mode…) switch, generic and zero-domain: pass `options`
49
+ (`{ value, icon?, label?, ariaLabel?, disabled? }`) and bind `[(value)]`. It's a
50
+ real `role="radiogroup"` of `role="radio"` segments — roving tabindex, arrow-key
51
+ selection, `Home`/`End`, disabled-skip — not two independent toggles. `size`
52
+ (`sm`/`md`), `iconOnly`, `activeStyle` (`raised` chip or `accent` brand tint),
53
+ `forced-colors`-aware. New gallery `/view-toggle` page.
54
+
55
+ - **`fold-password-field` + `revealable` on `fold-input`.** A password input with
56
+ a **live requirements checklist** — a dot per rule that turns on as the value
57
+ satisfies it — built in two layers. `fold-input` gains a reusable `revealable`
58
+ (a show/hide eye toggle on a `type="password"` input); `fold-password-field`
59
+ composes it with the checklist. Rules are **injected**, not hard-coded: a
60
+ `FoldPasswordRule` is `{ label, test }`, so a `RegExp` (`foldRegexRule`), a
61
+ `zod` `safeParse`, a length check or anything drops in — `rules` defaults to a
62
+ sensible policy (`foldDefaultPasswordRules`). `validChange` emits when every
63
+ rule passes; Signal-Forms-native; the checklist is an `aria-live` list that
64
+ labels each row met / not met. The checklist is **redesignable**: project into
65
+ the `[rules]` slot and drive your own markup off the exported live state
66
+ (`#pw="foldPasswordField"` → `pw.checklist()`), with the default list as the
67
+ fallback. The built-in row marker is a `dot` (filling in) or a `check` tick
68
+ (`marker="check"`). New gallery `/password` page.
69
+
70
+ - **`fold-popover` + `fold-dropdown` — anchored floating layer & actions menu.**
71
+ fold's first floating primitive. `fold-popover` renders projected content in
72
+ the native **top layer** (the `popover` attribute — escapes `overflow: hidden`
73
+ and every `z-index`), anchored to a projected `[foldPopoverTrigger]` by a
74
+ **dependency-free flip/shift engine** (`computePlacement`, exported and
75
+ unit-tested in isolation — no Floating UI). Controlled via `[(open)]`;
76
+ dismissal (outside-click + `Escape`) and focus-return are built in, and the
77
+ trigger gets `aria-haspopup`/`aria-expanded`/`aria-controls` wired
78
+ automatically. `fold-dropdown` (+ `fold-dropdown-item`) is the actions menu on
79
+ top: `role="menu"`, ↑/↓ roving tabindex, `Home`/`End`, type-ahead, opens onto
80
+ its first enabled item, closes returning focus to the trigger. New gallery
81
+ `/popover` page.
82
+
83
+ Hardened to a competitor benchmark (Floating UI / Radix): the placement engine
84
+ now does **flip → size → shift** — it picks the best-fitting side (preferred →
85
+ opposite → roomiest) and reports the available space so a tall panel gets a
86
+ `max-height` and **scrolls inside the viewport** instead of overflowing;
87
+ **autoUpdate** tracks the trigger + panel via `ResizeObserver` (not just
88
+ scroll/resize) so the anchor never drifts; an optional **`arrow`** points at
89
+ the trigger; **enter + exit transitions** are native CSS (`@starting-style` +
90
+ `transition-behavior: allow-discrete`, no JS timers); and the dropdown's
91
+ type-ahead is **multi-letter** (buffered); `fallbackPlacements` makes the flip
92
+ chain configurable; the `autoUpdate` helper is exported. Nested/sub-menus and
93
+ cursor-anchored context menus are tracked for a later pass.
94
+
95
+ Added a **Playwright interaction suite** (`pnpm test:e2e`, real Chromium) for
96
+ what jsdom can't reach — native top-layer open, box sizing, keyboard/focus.
97
+ It caught two focus bugs now fixed: the dropdown focused its first item
98
+ _before_ the popover had shown the panel (so keydowns missed the menu), and
99
+ focus-return targeted a non-focusable wrapper trigger (e.g. `fold-button-icon`)
100
+ instead of its inner control — the popover now resolves the focusable element
101
+ for both focus and the aria wiring.
102
+
103
+ - **`fold-listbox` (+ `fold-option`) — a styleable single-select.** The richer
104
+ sibling of `fold-select` (which wraps a native `<select>`): reach for it when
105
+ options need custom rendering the OS popup can't give — an icon, a second line,
106
+ a status dot. Built on `fold-popover`, so it inherits the native top layer,
107
+ flip/shift positioning, outside-click + `Escape` dismissal and focus return.
108
+ On top it implements the ARIA select pattern — a `role="listbox"` that holds
109
+ focus and drives `aria-activedescendant` — with full keyboard (↑/↓, `Home`/
110
+ `End`, multi-letter type-ahead, `Enter`), a disabled-row skip, and a pure-CSS
111
+ selected check. Signal-Forms-native (`FormValueControl<string>`, so `[formField]`
112
+ and `[(value)]` both work) and shares `fold-input`'s box chrome (sizes, `panel`
113
+ variant). Options are dumb + presentational — each derives its own selected /
114
+ active state from the parent by `computed`, so nothing is pushed in during
115
+ change detection. New gallery `/listbox` page and a Playwright suite. Option
116
+ groups and a filter/combobox variant are tracked for later.
117
+
118
+ - **`fold-multiselect` — the multi-select sibling.** Same styleable popover +
119
+ `fold-option` rows, but the value is a set (`readonly string[]`): activating a
120
+ row **toggles** its membership and the panel **stays open**. It's a separate
121
+ component, not a `multiple` flag, because the Signal-Forms value type genuinely
122
+ differs from single-select's `string` — keeping `[formField]` / `[(value)]`
123
+ honestly typed (no `any`). `role="listbox"` + `aria-multiselectable`, the same
124
+ keyboard core (`Enter`/`Space` toggle), each selected row keeps its check, and
125
+ the trigger summarises the picks. The keyboard/roving/type-ahead core and the
126
+ option↔owner contract are now **shared** between the two components (a
127
+ `FOLD_LISTBOX_OWNER` token instead of a concrete injection, which also removed
128
+ a circular import). New `/listbox` demo section + a Playwright suite.
129
+
130
+ - **`fold-inline-confirm` — in-place destructive-action guard.** Extracted from
131
+ SH3PHERD's shared inline-confirm (which replaced four ad-hoc patterns) and
132
+ rebuilt to fold conventions. The host projects a real focusable trigger
133
+ (`foldButton` / `fold-button-icon`); on activation it is swapped, in the same
134
+ slot, for a confirm/cancel row — no modal, no layout jump. Three families:
135
+ **simple** (`confirmed` emits `""`), **type-to-confirm** (`[match]` — the
136
+ button unlocks once the text matches, case-insensitive + trimmed), and
137
+ **secret** (`password` — a masked field that confirms when non-empty and emits
138
+ the typed value, since a password can only be verified server-side). `Escape`
139
+ cancels; `Enter` confirms. Fully i18n via `provideFoldInlineConfirmLabels()`
140
+ (English default) or a per-instance `labels` input. Composed of `fold-button`,
141
+ `fold-button-icon` and `fold-input`. New gallery `/inline-confirm` page.
142
+
143
+ Hardened against a competitor benchmark (Radix `AlertDialog` / React-Aria):
144
+ the warning `message` is wired to the confirm button via `aria-describedby`
145
+ (so it is announced on focus, not stranded on the group); focus reliably
146
+ returns to the trigger on close (the previous attempt read the trigger before
147
+ it re-rendered and no-op'd); the trigger no longer double-fires on `Enter`.
148
+ New API: `confirmIcon` (leading icon on the confirm button) and `cancelIcon`
149
+ now takes an **icon name of your choice** (was a fixed `×`); a two-way
150
+ `[(open)]` model plus `keepOpenOnConfirm` give a controlled async story —
151
+ keep the affordance open, show `loading`, close it when the request settles.
152
+ 16 spec blocks.
153
+
154
+ - **Slider hardcore pass — `fold-slider` + `fold-range-slider`.** `fold-slider`
155
+ now implements `FormValueControl<number>` (bind `[formField]`, or `[(value)]`);
156
+ the visible label is a real `<label for>` (else `ariaLabel`); a `valueText`
157
+ override is announced via `aria-valuetext`; `hint` + touched-gated `errors`.
158
+ `fold-range-slider` gets a `model()` value (`[(value)]` parity), `disabled`,
159
+ and i18n thumb labels (`minLabel` / `maxLabel`, English default) resolving the
160
+ hardcoded aria suffixes; it's a labelled `role="group"` with formatted
161
+ `aria-valuetext` per thumb (duration reads `mm:ss`). Both share a hardened
162
+ thumb — a focus-visible ring (was invisible on keyboard focus),
163
+ `prefers-reduced-motion`, `forced-colors`, and tokenised motion. New gallery
164
+ `/slider` page; README rows; specs 12 → 20.
165
+
166
+ - **`fold-paginator` hardcore pass.** Fully i18n — every string
167
+ (`Pagination`/prev/next/page/size/range/empty) is now overridable via
168
+ `provideFoldPaginatorLabels()` (English default) or a per-instance `labels`
169
+ input, resolving the last hardcoded-French portability blocker. Plus:
170
+ keyboard focus is preserved after a page change (moves to the active page, or
171
+ the prev/next arrow while it stays enabled — never dropped to `<body>`); the
172
+ visible range + active button clamp an out-of-range `currentPage` (a lagging
173
+ parent can't render a garbage range); the current `pageSize` is always in the
174
+ selector's options (the `<select>` can't show a phantom value); `disabled` is a
175
+ `booleanAttribute`; `siblingCount` is floored + zero-bounded. `@selector` + a
176
+ gallery `/paginator` page; specs cover DOM clicks, i18n, focus and the edges.
177
+
178
+ - **`fold-checkbox` — the boolean form control.** A native
179
+ `<input type="checkbox">` (keyboard, focus, the `checkbox` role, form
180
+ submission and `indeterminate` all native) visually replaced by a tokenised
181
+ box + check/dash mark. Signal-forms native via `FormCheckboxControl` (bind
182
+ `[formField]`), or standalone `[(checked)]`; plus `indeterminate`, `label` /
183
+ `ariaLabel`, `hint` + touched-gated `errors`, `required`, `size` (`sm`/`md`),
184
+ `disabled`. Accessible by construction (visible label wraps the control, or a
185
+ required `ariaLabel` — dev-warns when neither is set), with a focus-visible
186
+ ring, `prefers-reduced-motion` and `forced-colors` handled. `fold-data-table`'s
187
+ selection column now uses it.
188
+
189
+ - **`fold-data-table` — controlled row selection.** `selectable` renders a
190
+ checkbox column plus a header select-all with an indeterminate state over the
191
+ current rows; the parent owns the set via `selected` (a `Set` of row keys) and
192
+ `selectionChange` (emits the next set — the table never mutates). Selected rows
193
+ carry an accent tint + `aria-selected`, and a `selectionLabel` names each
194
+ checkbox. Toggling a checkbox never triggers `rowClick`.
195
+ - **`fold-data-table` — `mobileLayout` (parent owns the small-screen shape).**
196
+ `scroll` (default — stay tabular, scroll horizontally; the table imposes no
197
+ card), `auto-cards` (each row stacks into a label/value card), or `custom` —
198
+ the parent supplies `<ng-template foldRowCard let-row>` (new
199
+ `FoldDataTableRowCardDirective`) and the table renders _that_ per row on mobile
200
+ instead of an imposed card. The table owns the chrome, not the content.
201
+ - **`fold-data-table` — a `loading` state.** A fetching table now shows a
202
+ centred `fold-spinner` instead of the empty state, so an in-flight roster
203
+ reads as "loading", never as "no data".
204
+ - **`fold-data-table` — an optional toolbar/title bar.** Project content with
205
+ `[foldToolbar]` (a title, a live count, a bulk-action bar that appears once
206
+ rows are selected) and it renders as a visible band above the column header —
207
+ the same content-projection idiom as `fold-card`'s `[cardHeader]`, collapsing
208
+ to nothing when the parent projects nothing. It stays put while the body
209
+ scrolls (the table now has an inner scroll region). `toolbarSurface` lends the
210
+ table a level —
211
+ `default` / `sunken` / `raised` / `accent` — mapped only to fold surface
212
+ tokens (no hard-coded colour); `accent` reuses the shared
213
+ `[data-surface="accent"]` machinery, so the bar's content auto-inverts to the
214
+ on-accent palette per theme.
215
+ - **`fold-data-table` — an accessible `caption`.** A new `caption` input renders
216
+ a visually-hidden `<caption>` that names the table for assistive tech
217
+ (distinct from the visible `foldToolbar` title).
218
+ - **`fold-data-table` — `stickyFirst` + `density`.** `stickyFirst` pins the
219
+ checkbox + identity columns while the body scrolls horizontally (opaque-backed
220
+ so tints don't bleed); `density="compact"` tightens the row padding.
221
+ - **`fold-data-table` — column `align: "center"` and `truncate`.** `center`
222
+ joins `right`; `truncate` clips a column to one ellipsised line (pair with
223
+ `width`).
224
+ - **`data-table` gallery page.** Added `/data-table` to the demo (live sort, row
225
+ select, keyboard nav, the loading + empty states, a custom mobile card, and a
226
+ playground for every flag).
227
+ - **Select family — generic value + a data-driven `[options]` API.**
228
+ `fold-listbox` / `fold-multiselect` / `fold-option` are now generic over the
229
+ option value `T` (was `string`-only): `value` is `T | null` (single) /
230
+ `readonly T[]` (multi), and a `compareWith` input (default `Object.is`) matches
231
+ **object** values by identity — string/number/enum need nothing. On top of
232
+ projected `<fold-option>`, a `[options]="FoldSelectOption<T>[]"` array API links
233
+ the value type to the options at compile time (rich rows via a projected
234
+ `<ng-template #option let-o>`). The type stays honest end-to-end (`T` public,
235
+ the owner token erased to `unknown`, no `any`/`as`).
236
+ - **`fold-data-table` — i18n label token.** `provideFoldDataTableLabels` +
237
+ `FoldDataTableLabels` + a `labels` input (same shape as the paginator), so the
238
+ select-all/select-row/sort/loading accessible strings are overridable per
239
+ locale instead of hardcoded English.
240
+ - **`/changelog` — the CHANGELOG as a designed timeline.** A new gallery page
241
+ renders `CHANGELOG.md` as a vertical `fold-timeline` (one card per release,
242
+ category-count badges, breaking flagged) — parsed at build into a typed,
243
+ SSR-safe data file (runs pre-tokenised, no runtime markdown, no `innerHTML`).
244
+ - **`/lab` — an "in dev" index.** A dedicated menu of exactly the components not
245
+ yet on npm, each linking to its page with the version it ships in. Both the
246
+ list and the `dev` rail badges are derived from each nav item's `since` vs the
247
+ published version, so they clear themselves the moment a release is cut.
248
+ - **`pnpm eta` — a read-only release preview.** Prints the next version, the
249
+ derived bump level, and the reasons, straight from the CHANGELOG's
250
+ `[Unreleased]` section — no side effects.
251
+
252
+ ### Fixed
253
+
254
+ - **0.5 review-hardening pass** (multi-agent review of the release). `fold-data-table`
255
+ accessible strings are now i18n-overridable (were hardcoded); `fold-password-field`'s
256
+ requirements checklist actually **announces** rule flips (the `aria-live` region
257
+ now carries the met/not-met word as text, not just an attribute) and forwards
258
+ the reveal labels; a `forced-colors` + `prefers-reduced-motion` sweep across
259
+ `fold-data-table`, `fold-paginator`, the `fold-input` reveal button, `fold-view-toggle`
260
+ and the select-family trigger; `fold-view-toggle`'s roving tab stop never lands
261
+ on a disabled segment (+ a dev warning for a missing `ariaLabel`);
262
+ `fold-inline-confirm`'s Escape is guarded while `loading`; `fold-dropdown`
263
+ excludes Space from type-ahead; slider spacing tokenised.
264
+
265
+ - **Select family — a review-driven hardening pass** (`fold-listbox` /
266
+ `fold-multiselect` / `fold-popover`). Closing focus no longer traps: the
267
+ popover only pulls focus back to the trigger when it's still inside the closing
268
+ panel (Escape / pick) or nowhere — a `Tab` out now **advances** to the next
269
+ field like a native `<select>`. Dismissing the popup (Escape, outside-click,
270
+ Tab, or a pick) now marks the field **touched**, so a `required` select that's
271
+ opened and abandoned surfaces its error (blur parity). The trigger's
272
+ `aria-controls` points at the real `role="listbox"` (new `ariaControls` on
273
+ `fold-popover`), the active-row highlight and selected check get a
274
+ `forced-colors` treatment, and `fold-multiselect` membership is a `Set`
275
+ (O(1), not O(n²) across a long list) with the trigger summary collapsing to
276
+ "…, +N". Dev-mode now warns when a control holds a value with no matching
277
+ `<fold-option>`. New: `allowClear` on `fold-listbox` (a clear × once a value is
278
+ picked) and closed-trigger type-ahead (type to pick without opening).
279
+
280
+ - **`fold-popover` panels are now opaque.** The panel used `surface-raised` — a
281
+ ~5% tint meant to _sit on_ an opaque surface — so on the top layer the page
282
+ bled through (visible while scrolling a long `fold-listbox`). It now composites
283
+ that tint over an opaque `surface-card` base. The popover also publishes its
284
+ trigger width as `--fold-popover-anchor-width`, and `fold-listbox` reads it for
285
+ a `min-width` — so the panel is never narrower than the trigger (a coherent
286
+ select look).
287
+
288
+ - **Heading inputs no longer leak a native `title` tooltip.** A static
289
+ `title="…"` on a component that has a `title` input both seeds the input _and_
290
+ stays on the host as a real HTML attribute — so `fold-page-layout`,
291
+ `fold-page-section`, `fold-element-title`, `fold-context-card`,
292
+ `fold-empty-state` and `fold-panel-header` rendered their heading a second time
293
+ as a browser tooltip on hover. The reflected attribute is now stripped
294
+ (`host: { '[attr.title]': 'null' }`); `fold-icon` keeps its `title` on purpose
295
+ (it maps to `aria-label`).
296
+
297
+ ### Changed
298
+
299
+ - **Form-control box metrics are single-sourced** (`_field-box.scss`). The size
300
+ and `panel` dimensions shared by the native inputs (`input-shell`) and the
301
+ select-family triggers (`_listbox-shell`) now live in one Sass mixin, so a
302
+ metric redesign lands in both instead of drifting.
303
+
304
+ - **CI + release gates now run the Playwright browser tier.** The `test:e2e`
305
+ suite (native popover top layer, focus, positioning — behaviour jsdom can't
306
+ reach) is wired into both `ci.yml` (inside the single required `ci` job) and
307
+ `release.yml`, so a PR or a tag can't go green on a broken overlay.
308
+
309
+ - **The release bump is derived from the CHANGELOG.** `pnpm release` with no
310
+ argument now reads `[Unreleased]` and derives patch/minor/major (0.x-aware:
311
+ breaking → minor, features → minor, else patch) — the changelog you curate
312
+ defines the version. An explicit level still overrides. Parser + derivation
313
+ live in a shared `scripts/lib/changelog.mjs`, reused by the release flow, the
314
+ `pnpm eta` preview, and the `/changelog` page.
315
+
316
+ - **`fold-data-table` — clickable rows are a roving-tabindex group.** They now
317
+ answer Space as well as Enter (page-scroll suppressed) and Arrow Up/Down +
318
+ Home/End move focus between rows with a single tab stop — the ARIA grid
319
+ keyboard pattern, not a wall of tab stops.
320
+ - **`fold-data-table` — sort indicators are now `fold-icon`s** (`expand-all` when
321
+ idle, `chevron-up` / `chevron-down` when active) rather than text glyphs, so
322
+ the direction cue is theme-aware, pixel-aligned, and consistent with the rest
323
+ of the system. The icon is decorative — `aria-sort` on the `<th>` stays the
324
+ accessible carrier.
325
+ - Transitions now respect `prefers-reduced-motion`.
326
+ - **`fold-data-table` hardening (hardcore-review follow-ups).** Row keydown now
327
+ fires only when the row itself is focused — keys bubbling from an inner control
328
+ (a link, a button, the selection checkbox) are no longer stolen or
329
+ double-handled; the roving tab stop survives the focused row being removed
330
+ (never strands the group with zero tab stops); the `custom` mobile layout no
331
+ longer instantiates its card list on desktop; `truncate` warns in dev when its
332
+ column lacks a `width` to clip against; `aria-colcount` + a "Sort by …" label
333
+ on the sort control; the checkbox-column width is a single source of truth.
334
+
335
+ - **BREAKING (`fold-data-table`): the primary column renders as
336
+ `<th scope="row">`** (was a `<td>`) so screen readers announce each row by its
337
+ identity cell. Consumers that target the first cell with a `td`-specific
338
+ selector should switch to the `.folddt-cell` class (present on both the row
339
+ header and the data cells).
340
+ - **BREAKING (`fold-data-table`): `mobileCards` (boolean) is replaced by
341
+ `mobileLayout`, and the default flips from cards to scroll.** A narrow-screen
342
+ table now stays tabular (scrolls) unless asked to stack. `[mobileCards]="true"`
343
+ (or unset) → `mobileLayout="auto-cards"` to keep the stacked cards;
344
+ `[mobileCards]="false"` → drop it (`scroll` is the default).
345
+
11
346
  ## [0.4.0] - 2026-07-25
12
347
 
13
348
  ### Added
@@ -198,7 +533,10 @@ design-token stylesheet.
198
533
  `currentColor`; `prefers-reduced-motion` + `forced-colors` are respected;
199
534
  strings localise via inputs / providers (`provideFoldPanelLabels`).
200
535
 
201
- [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.4.0...HEAD
536
+ [unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.5.2...HEAD
537
+ [0.5.2]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.5.2
538
+ [0.5.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.5.1
539
+ [0.5.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.5.0
202
540
  [0.4.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.4.0
203
541
  [0.3.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.3.0
204
542
  [0.2.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.2.1
package/README.md CHANGED
@@ -304,15 +304,22 @@ the package root.
304
304
  | `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
305
  | `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
306
  | `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. |
307
+ | `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
+ | `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
+ | `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). |
307
310
  | `FoldSearchComponent` | `fold-search` | Debounced search box — an `fold-input` that emits `searchChange` once typing settles (`delayMs`), trimmed + de-duplicated. |
308
- | `FoldSliderComponent` | `fold-slider` | Single-value range slidera styled native `<input type="range">` (design-system track/fill/thumb), two-way `[(value)]`. |
311
+ | `FoldListboxComponent` / `FoldOptionComponent` | `fold-listbox` / `fold-option` | Styleable single-selectthe 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
+ | `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. |
313
+ | `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
+ | `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`. |
309
315
  | `FoldRangeSliderComponent` | `fold-range-slider` | Dual-thumb slider selecting a `{ min, max }` window over `[min, max]`; shares the slider track/fill/thumb. |
310
316
  | `FoldFileDropzoneComponent` | `fold-file-dropzone` | File-picker dropzone — drag-over visuals, keyboard activation, hidden `<input type=file>` plumbing; emits the picked `File[]` (presentational — never uploads). |
311
317
  | `FoldLinkComponent` | `fold-link` | Inline text link / link-button (icons, accent · muted). |
312
318
  | `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)`. |
313
319
  | `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`. |
314
320
  | `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`. |
315
- | `FoldDataTableComponent` | `fold-data-table` | Controlled roster table (sortable, tone rows, mobile cards). |
321
+ | `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`. |
322
+ | `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. |
316
323
  | `FoldPaginatorComponent` | `fold-paginator` | Server-side paginator (size selector + range + page nav). |
317
324
  | `FoldTimelineComponent` | `fold-timeline` | Connected rail of nodes (dot + optional date + label) — `vertical` navigable history or `horizontal` step progress; nodes optionally clickable. |
318
325
  | `FoldBadgeComponent` | `fold-badge` | Status / count pill (accent/info/warning/alert/success). |
@@ -331,6 +338,8 @@ the package root.
331
338
  | `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>`. |
332
339
  | `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 })`. |
333
340
  | `FoldPanelHeaderComponent` | `fold-panel-header` | Standard panel header (title/eyebrow, self-closing). Names its dialog (`aria-labelledby`) and reads the app-wide close label. |
341
+ | `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
+ | `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"`. |
334
343
 
335
344
  Directives worth knowing: **`foldSurface`** (`page`·`chrome` — the seam a mixed
336
345
  theme re-colours across), **`foldElevated`** (raise any bg-owning element into