fold-ng 0.9.0 → 0.10.1
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 +133 -1
- package/README.md +2 -0
- package/fesm2022/fold-ng.mjs +5844 -5599
- package/fesm2022/fold-ng.mjs.map +1 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/tmp-esm2022/tsconfig.lib.tsbuildinfo +1 -1
- package/types/fold-ng.d.ts +189 -26
- package/types/fold-ng.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,136 @@ All notable changes to **fold-ng** are documented here. The format follows
|
|
|
8
8
|
|
|
9
9
|
_Nothing yet._
|
|
10
10
|
|
|
11
|
+
## [0.10.1] - 2026-08-12
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **A `side="auto"` panel now docks to the bottom on a narrow host.** It got the
|
|
16
|
+
bottom-sheet _shape_ (content height, rounded top, slide-up, grabber) but not
|
|
17
|
+
its _alignment_: the rule that pinned it, `align-items: flex-end`, selected
|
|
18
|
+
`.panel-dock` from **inside** the dock's own `@container` query — and an
|
|
19
|
+
element can never match its own container query (`container-type` makes it a
|
|
20
|
+
container for its _descendants_). The sheet kept the dock's default `stretch`
|
|
21
|
+
and filled the whole region, which reads as a sheet stuck to the top of the
|
|
22
|
+
screen. The alignment now rides on the panel (`align-self`), a real
|
|
23
|
+
descendant. Regression test in `e2e/panel.spec.ts` — a **wide viewport with a
|
|
24
|
+
narrow stage**, because shrinking the viewport instead lets some ancestor
|
|
25
|
+
container match ≤640 and answer the query in the dock's place, hiding the bug.
|
|
26
|
+
|
|
27
|
+
- **A `background="surface"` tab bar is opaque.** `fold-view-nav` / `fold-tabs`
|
|
28
|
+
painted the rail-3 role, which is a 3% white **overlay** — so a bar a consumer
|
|
29
|
+
pinned with `position: sticky` had page content scrolling visibly through it.
|
|
30
|
+
The overlay is now layered over the page fill. On the page background — where
|
|
31
|
+
a filled bar belongs — the paint is unchanged; anywhere else it is opaque,
|
|
32
|
+
which is what "carries its own surface" always claimed.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **Tighter insets below 640px.** Three defaults step one notch down the spacing
|
|
37
|
+
scale on a phone, where a horizontal inset is charged twice against a reading
|
|
38
|
+
width that has none to spare: the nav↔content gap (`fold-nav-layout` and a
|
|
39
|
+
standalone `fold-view-nav`, 16 → 8px), the `fold-card` body and chrome padding
|
|
40
|
+
(16 → 12px, 12/16 → 8/12px), and the panel host's template-panel body
|
|
41
|
+
(16 → 12px, keyed to the dock's width, not the viewport). Every one keeps its
|
|
42
|
+
override: `--fold-nav-layout-gap`, `--fold-card-padding` and the card's
|
|
43
|
+
`padding` input (`p-sm` / `p-lg` / `p-none`) still win at every width — an
|
|
44
|
+
author who asked for a generous card meant it.
|
|
45
|
+
|
|
46
|
+
## [0.10.0] - 2026-08-11
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- **BREAKING — `fold-file-dropzone` defaults to English copy.** `label` was
|
|
51
|
+
`"Glissez un fichier ou parcourez"` and `busyLabel` `"Téléversement en
|
|
52
|
+
cours…"`; they are now `"Drag a file or browse"` and `"Uploading…"`. Both were
|
|
53
|
+
always inputs, so the fix for a French app is to pass the old strings — but a
|
|
54
|
+
library that defaults to one natural language makes every other consumer
|
|
55
|
+
override it just to be readable, which is the portability promise leaking. It
|
|
56
|
+
was the last of the seven release blockers in `docs/RELEASE-READINESS.md`.
|
|
57
|
+
|
|
58
|
+
- **BREAKING — identical toasts collapse instead of stacking.** A `show()` whose
|
|
59
|
+
message _and_ variant match a toast already on screen now folds into it as a
|
|
60
|
+
`×N` tally and restarts its countdown, rather than queueing a copy: a retry
|
|
61
|
+
loop used to bury the screen under the same sentence. Opt out with
|
|
62
|
+
`provideFoldToasts({ dedupe: false })`. `FoldToast` gained a required
|
|
63
|
+
`repeats` field, so anything constructing that type by hand must supply it.
|
|
64
|
+
|
|
65
|
+
- **`FoldToastService.show()` returns the toast's id** (it returned `void`). The
|
|
66
|
+
id was generated and thrown away, which made a sticky "Uploading…" impossible
|
|
67
|
+
to close from the code that opened it. A deduped call returns the id of the
|
|
68
|
+
toast it folded into. Ids now come from `FoldIdService`'s counter rather than
|
|
69
|
+
`crypto.randomUUID()`, which **throws outright** in a non-secure context
|
|
70
|
+
(plain http off localhost) and whose randomness bought nothing for an id that
|
|
71
|
+
only has to be unique within one queue.
|
|
72
|
+
|
|
73
|
+
### Added
|
|
74
|
+
|
|
75
|
+
- **`fold-info` — the `i` that answers "what is this?", now a primitive.** The
|
|
76
|
+
affordance shipped inlined in the field label row; it is extracted so the same
|
|
77
|
+
bubble works where there is no field at all — a dashboard card's corner, a
|
|
78
|
+
table header — which is exactly where the second consumer had hand-rolled it
|
|
79
|
+
(LaFolieDouce's admin: ~30 sites across the growth dashboard and the booking
|
|
80
|
+
rules). `text` · `label` (accessible name, English default) · `placement`.
|
|
81
|
+
`fold-input-base` now composes it instead of carrying its own copy, so the
|
|
82
|
+
`info` input on every labelled field renders the same component. A click, not
|
|
83
|
+
a hover: hover-only help is unreachable on a touch screen.
|
|
84
|
+
|
|
85
|
+
- **`maxVisible` bounds the toast stack.** Beyond the cap the oldest toast is
|
|
86
|
+
evicted — it has been on screen longest, so it is the one already read. Unset
|
|
87
|
+
(the default) stays unbounded. Deliberately eviction and not a waiting queue:
|
|
88
|
+
a queue only drains when a visible toast leaves, and a sticky one — an
|
|
89
|
+
`error`, by default — never does, so the backlog would sit behind it forever.
|
|
90
|
+
|
|
91
|
+
- **`fold-toast` pauses its countdown while it is being read.** Hovering the
|
|
92
|
+
toast, or moving the keyboard focus into it, freezes the auto-dismiss timer;
|
|
93
|
+
leaving resumes it with **the time that was left**, not a fresh `duration` —
|
|
94
|
+
a toast that renewed itself on every mouse pass would never leave. Focus
|
|
95
|
+
moving between the message and the close button counts as staying inside.
|
|
96
|
+
This is WCAG 2.2.1 (Timing Adjustable): the previous timer ran regardless, so
|
|
97
|
+
a message could expire mid-sentence, or while the pointer was travelling to
|
|
98
|
+
its ✕. The paused state is exposed as `data-paused` for styling.
|
|
99
|
+
|
|
100
|
+
- **`info` on every labelled input — the explanation a `hint` can't carry.** An
|
|
101
|
+
`i` button at the end of the label line reveals a sentence or two in a
|
|
102
|
+
`fold-popover`. Lives in `fold-input-base`, so it lands on the whole family at
|
|
103
|
+
once: `fold-input`, `fold-number-input`, `fold-select`, `fold-textarea`,
|
|
104
|
+
`fold-date`, `fold-time`, `fold-listbox`, `fold-multiselect` (`[info]`, plus
|
|
105
|
+
`[infoLabel]` for the button's accessible name, default `More information`).
|
|
106
|
+
Composes with `hint` rather than replacing it — short line under the control,
|
|
107
|
+
the _why_ behind the `i`. The panel rides the popover's native top layer, so a
|
|
108
|
+
long explanation never pushes the next control down, and it escapes any
|
|
109
|
+
`overflow: hidden`. Consumers were hand-rolling this: LaFolieDouce B2B had a
|
|
110
|
+
bespoke absolutely-positioned bubble anchored to each field wrapper.
|
|
111
|
+
|
|
112
|
+
### Fixed
|
|
113
|
+
|
|
114
|
+
- **Opening a panel no longer shoves the page sideways.** The main content
|
|
115
|
+
jumped left by the panel's width and eased back — a long-standing glitch whose
|
|
116
|
+
cause turned out to be neither of the two suspects (an inner scrollbar, a
|
|
117
|
+
`backdrop-filter` repaint). Measured frame by frame: a panel begins its enter
|
|
118
|
+
animation parked off-edge at `translateX(100%)`, the dock did not clip, so the
|
|
119
|
+
parked panel joined the positioned host's **scrollable overflow**
|
|
120
|
+
(`scrollWidth` 960 → 1221 on the gallery); the focus trap then moved focus into
|
|
121
|
+
it and the browser scrolled the host sideways to reveal it
|
|
122
|
+
(`scrollLeft` 0 → 261); the slide-in shrank the overflow again and the scroll
|
|
123
|
+
was clamped back frame by frame, hence the eased return. Fixed with
|
|
124
|
+
`overflow: clip` on the dock — a parked panel has no business in anyone's
|
|
125
|
+
scrollable overflow — plus `focus({ preventScroll: true })` when a focus trap
|
|
126
|
+
activates, since entering an overlay must never scroll the page behind it.
|
|
127
|
+
`e2e/panel.spec.ts` samples the whole animation and fails on any sideways
|
|
128
|
+
scroll; jsdom cannot see this class of bug at all.
|
|
129
|
+
|
|
130
|
+
- **A horizontal tab bar that doesn't fit now scrolls instead of clipping its
|
|
131
|
+
tail.** `fold-view-nav` and `fold-tabs` share a bar whose host clipped its
|
|
132
|
+
overflow while its items never wrap — so on a phone, the last entries of a
|
|
133
|
+
four-item `size="comfortable"` bar simply vanished, and with them any way of
|
|
134
|
+
reaching those pages. The host now scrolls on the inline axis (thin house
|
|
135
|
+
scrollbar, `overscroll-behavior-x: contain`) and the bar keeps its natural
|
|
136
|
+
width. Vertical rails are untouched — the page scroll already covers them, and
|
|
137
|
+
an inner scroll box there would trap the wheel — and so is `collapsed`, the
|
|
138
|
+
icon accordion, which is designed to fit any width and needs its overflow
|
|
139
|
+
visible for its tooltips.
|
|
140
|
+
|
|
11
141
|
## [0.9.0] - 2026-08-04
|
|
12
142
|
|
|
13
143
|
### Changed
|
|
@@ -1263,7 +1393,9 @@ design-token stylesheet.
|
|
|
1263
1393
|
`currentColor`; `prefers-reduced-motion` + `forced-colors` are respected;
|
|
1264
1394
|
strings localise via inputs / providers (`provideFoldPanelLabels`).
|
|
1265
1395
|
|
|
1266
|
-
[unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.
|
|
1396
|
+
[unreleased]: https://github.com/hugoheynard/fold-ng/compare/v0.10.1...HEAD
|
|
1397
|
+
[0.10.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.10.1
|
|
1398
|
+
[0.10.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.10.0
|
|
1267
1399
|
[0.9.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.9.0
|
|
1268
1400
|
[0.8.1]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.8.1
|
|
1269
1401
|
[0.8.0]: https://github.com/hugoheynard/fold-ng/releases/tag/v0.8.0
|
package/README.md
CHANGED
|
@@ -310,6 +310,7 @@ ships it.
|
|
|
310
310
|
| a **"delete X" danger block** (type-to-confirm) | `fold-danger-zone` (alert frame + blast-radius text + retype-to-arm) |
|
|
311
311
|
| a **status / count pill** · status→colour | `fold-badge` · `fold-status-badge` |
|
|
312
312
|
| a **tinted message / alert** row | `fold-callout` (`inset` for in-flow) |
|
|
313
|
+
| a **"what is this?"** help bubble | `fold-info` (the `i` → popover; also the `info` input on every labelled field) |
|
|
313
314
|
| a transient **toast** | `fold-toast` + `FoldToastService` |
|
|
314
315
|
| a **card** · titled info card · page **splash** | `fold-card` · `fold-context-card` · `fold-hero-section` (bordered → `fold-hero-card`) |
|
|
315
316
|
| in-page **tabs** · a routed **nav bar** · a segmented control | `fold-tabs` · `fold-view-nav` · `fold-view-toggle` / `fold-choice-row` |
|
|
@@ -385,6 +386,7 @@ ships it.
|
|
|
385
386
|
| `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`). |
|
|
386
387
|
| `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`. |
|
|
387
388
|
| `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"`. |
|
|
389
|
+
| `FoldInfoComponent` | `fold-info` | The small `i` that answers "what is this?" — a quiet round trigger revealing a sentence or two in a `fold-popover` (native top layer, so it escapes `overflow: hidden` and every `z-index`). A **click**, not a hover: hover-only help is unreachable by touch. `text` · `label` (accessible name) · `placement`. It is what the `info` input on every labelled field renders, and it stands alone where there is no field — a card corner, a table header. |
|
|
388
390
|
|
|
389
391
|
| `FoldCalendarMonthComponent` | `fold-calendar-month` | Month grid where events **span** the days they cover: a date-axis `role="grid"` with one roving tab stop, and over it a lane-packed layer of bands (one per week crossed, with open edges). `+N` chips sit on the crowded day, not at the end of the row. |
|
|
390
392
|
| `FoldCalendarWeekComponent` | `fold-calendar-week` | Seven day columns of stacked chips — nothing spans, so nothing is clipped and every chip is a real button in the tab order. Container-queried: labels and icons drop out before they truncate. |
|