forty-cdk 0.12.0 → 0.13.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/combobox/README.md +33 -6
- package/date-field/README.md +9 -8
- package/date-range-field/README.md +9 -8
- package/drawer/README.md +14 -0
- package/fesm2022/forty-cdk-calendar.mjs +2 -11
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-carousel.mjs +6 -16
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +150 -54
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +1477 -733
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs +18 -58
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-range-field.mjs +26 -13
- package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-dialog.mjs +51 -237
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +133 -229
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-hover-card.mjs +116 -105
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs +3 -21
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +45 -161
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs +7 -99
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +114 -180
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-stepper.mjs +30 -19
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +2 -2
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-field.mjs +19 -60
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +13 -15
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-range-field.mjs +39 -14
- package/fesm2022/forty-cdk-time-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-toast.mjs +43 -21
- package/fesm2022/forty-cdk-toast.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +13 -96
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +12 -3
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/hover-card/README.md +56 -8
- package/package.json +1 -1
- package/select/README.md +9 -7
- package/stepper/README.md +27 -1
- package/table/README.md +3 -3
- package/time-field/README.md +9 -8
- package/time-range-field/README.md +12 -11
- package/toast/README.md +4 -1
- package/tooltip/README.md +15 -15
- package/types/forty-cdk-combobox.d.ts +62 -15
- package/types/forty-cdk-core.d.ts +926 -231
- package/types/forty-cdk-date-field.d.ts +16 -24
- package/types/forty-cdk-date-picker.d.ts +1 -1
- package/types/forty-cdk-date-range-field.d.ts +18 -11
- package/types/forty-cdk-dialog.d.ts +31 -122
- package/types/forty-cdk-drawer.d.ts +34 -77
- package/types/forty-cdk-hover-card.d.ts +55 -72
- package/types/forty-cdk-popover.d.ts +6 -90
- package/types/forty-cdk-select.d.ts +29 -7
- package/types/forty-cdk-stepper.d.ts +34 -13
- package/types/forty-cdk-time-field.d.ts +17 -26
- package/types/forty-cdk-time-picker.d.ts +12 -5
- package/types/forty-cdk-time-range-field.d.ts +31 -12
- package/types/forty-cdk-toast.d.ts +8 -1
- package/types/forty-cdk-tooltip.d.ts +17 -86
- package/types/forty-cdk-virtualization.d.ts +9 -2
package/combobox/README.md
CHANGED
|
@@ -36,6 +36,21 @@ The editable (default) anatomy — an `<input>` that filters a portaled listbox
|
|
|
36
36
|
</div>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
**Editable + list (no trigger).** Wrapping the options in a `[forComboboxList]` without adding a `[forComboboxTrigger]` is a supported shape, and the a11y-clean way to add non-option pieces (`[forComboboxEmpty]`, `[forComboboxStatus]`, `[forComboboxAction]`) to the editable anatomy. Because content carries `role="listbox"` (which may only own `option` / `group` children), moving the options into `[forComboboxList]` makes those pieces siblings of the listbox instead of invalid listbox children — content becomes role-less and the list owns the listbox role. The role split keys off `hasList`, the focus model off `trigger()`, so focus still stays on the input the whole time:
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<div forComboboxContent>
|
|
43
|
+
<div forComboboxList>
|
|
44
|
+
<div forComboboxOption [value]="item.id" [label]="item.label">{{ item.label }}</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div forComboboxEmpty>No matches.</div>
|
|
47
|
+
</div>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A `[forComboboxAction]` **requires** this shape — see [Action items](#action-items).
|
|
51
|
+
|
|
52
|
+
> **Editable-anatomy caveat.** For the common case of options plus only a `[forComboboxEmpty]` / `[forComboboxStatus]` message (the bare anatomy above, no `[forComboboxList]`), the message sits directly inside `[forComboboxContent]`. This is still supported and does not throw, but it leaves the `role="status"` message as an owned child of `role="listbox"`, a minor `aria-required-owned` compromise; wrap the options in a `[forComboboxList]` (the "editable + list" shape) when you want the strictly-clean tree.
|
|
53
|
+
|
|
39
54
|
The picker anatomy adds a `[forComboboxTrigger]` `<button>` showing the committed selection, with the search input and a `[forComboboxList]` (`role="listbox"`) nested inside the popup — see [Picker anatomy](#picker-anatomy). Multi mode wraps the chips + input in `[forComboboxChips]` — see [Multi mode](#multi-mode). Optional `[forComboboxAnchor]`, `[forComboboxStatus]`, `[forComboboxGroup]` / `[forComboboxGroupLabel]`, and `[forComboboxSeparator]` pieces are covered in their own sections below.
|
|
40
55
|
|
|
41
56
|
## Examples
|
|
@@ -204,15 +219,24 @@ A combobox popup often needs an entry that is an **action**, not a value —
|
|
|
204
219
|
`role="button"` actions, not `role="option"` selections, so `[forComboboxAction]`
|
|
205
220
|
renders one that stays out of the option/value collection entirely.
|
|
206
221
|
|
|
222
|
+
`[forComboboxAction]` **requires a `[forComboboxList]`**: content carries
|
|
223
|
+
`role="listbox"` in the editable anatomy, so a `role="button"` placed directly
|
|
224
|
+
inside it would be an invalid listbox child (`aria-required-owned`). Wrap the
|
|
225
|
+
options in a `[forComboboxList]` so the action becomes a sibling of the listbox.
|
|
226
|
+
An action rendered without a `[forComboboxList]` throws `[forty-cdk/combobox]` at
|
|
227
|
+
runtime. This is the "editable + list" shape (no `[forComboboxTrigger]` needed).
|
|
228
|
+
|
|
207
229
|
```html
|
|
208
230
|
<div forCombobox #combobox="forCombobox" [(query)]="query" [(value)]="value">
|
|
209
231
|
<input forComboboxInput placeholder="Search…" />
|
|
210
232
|
@if (combobox.open()) {
|
|
211
233
|
<div forComboboxContent>
|
|
212
234
|
<button forComboboxAction (action)="createNew(query())">Create "{{ query() }}"</button>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
235
|
+
<div forComboboxList>
|
|
236
|
+
@for (it of filtered; track it.id) {
|
|
237
|
+
<div forComboboxOption [value]="it.id" [label]="it.label">{{ it.label }}</div>
|
|
238
|
+
}
|
|
239
|
+
</div>
|
|
216
240
|
</div>
|
|
217
241
|
}
|
|
218
242
|
</div>
|
|
@@ -249,9 +273,9 @@ returns focus to the input (editable anatomy) or the `[forComboboxTrigger]`
|
|
|
249
273
|
only. With no action registered, Tab keeps its default "close and let Tab flow on"
|
|
250
274
|
behaviour, so existing comboboxes are unchanged.
|
|
251
275
|
|
|
252
|
-
Actions live inside `[forComboboxContent]`
|
|
253
|
-
|
|
254
|
-
dismissal checks, exactly like the input.
|
|
276
|
+
Actions live inside `[forComboboxContent]` and beside `[forComboboxList]` (never
|
|
277
|
+
inside it, in either anatomy), so they are naturally "inside" the outside-pointer /
|
|
278
|
+
outside-focus dismissal checks, exactly like the input.
|
|
255
279
|
|
|
256
280
|
### API
|
|
257
281
|
|
|
@@ -382,6 +406,8 @@ The `autocompleteMode` input mirrors the WAI-ARIA `aria-autocomplete` property:
|
|
|
382
406
|
|
|
383
407
|
Inline completion preserves the user's typed prefix as unselected and selects the appended remainder, so the next keystroke replaces the selection (matching native browser autofill behavior). Backspace deletes the selection without re-completing, so the user can always shorten the query.
|
|
384
408
|
|
|
409
|
+
> **Pure `'inline'` needs a warm cache.** `'inline'` never opens the popup (per APG — `aria-autocomplete="inline"` has no listbox), so in the default `@if (open())` anatomy no `[forComboboxOption]` ever renders and the label cache starts cold. A first keystroke into a combobox that has never been opened completes against nothing; inline completion only works once the options have rendered at least once (the user opened the popup via ArrowDown or `[openOnFocus]`, warming the cache). If completion must work from the very first keystroke, use `'both'` (which opens the popup) or keep the options mounted rather than gating them behind `@if (open())`.
|
|
410
|
+
|
|
385
411
|
## Dismiss events
|
|
386
412
|
|
|
387
413
|
Each dismiss reason emits a vetoable event from `[forCombobox]` — call `preventDefault()` on the event to keep the listbox open.
|
|
@@ -585,6 +611,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
|
|
|
585
611
|
- `[forComboboxSeparator]` is decorative and never registers with the listbox's option collection — keyboard navigation skips it automatically.
|
|
586
612
|
- `[forComboboxGroup]` is purely advisory grouping — options inside still register flatly with the root, so navigation flows through groups without interruption.
|
|
587
613
|
- `[forComboboxEmpty]` carries `role="status"` + `aria-live="polite"` so the empty-state message is announced when filtering removes all matches.
|
|
614
|
+
- Non-option pieces (`[forComboboxAction]`, and ideally `[forComboboxEmpty]` / `[forComboboxStatus]`) belong inside `[forComboboxContent]` but **outside** `[forComboboxList]` — `role="listbox"` may only own `option` / `group` children (`aria-required-owned-elements`). Wrapping the options in a `[forComboboxList]` (the "editable + list" shape) makes those pieces siblings of the listbox. `[forComboboxAction]` **requires** a `[forComboboxList]` and throws `[forty-cdk/combobox]` without one; `[forComboboxEmpty]` / `[forComboboxStatus]` stay lenient in the bare editable anatomy (documented compromise) — see the [editable-anatomy caveat](#anatomy).
|
|
588
615
|
- The input element is exempt from the listbox's outside-pointer dismissal layer, so a click on the input while the listbox is open routes through `(click)` (toggle / focus open) instead of double-firing as an outside dismissal.
|
|
589
616
|
|
|
590
617
|
## Styling
|
package/date-field/README.md
CHANGED
|
@@ -146,7 +146,7 @@ Plus the shared `FormUiControl` members from `@angular/forms/signals`: `disabled
|
|
|
146
146
|
| `[forDateFieldSegment]` | `data-disabled` | present \| absent |
|
|
147
147
|
| `[forDateFieldSegment]` | `data-readonly` | present \| absent |
|
|
148
148
|
|
|
149
|
-
`data-empty` marks the
|
|
149
|
+
`data-empty` marks the field only while **every** editable segment is empty (nothing has been entered); a partially-typed field is **not** empty. `data-placeholder` marks each individual segment that is still empty. `data-highlighted` is the current roving-tabindex segment — the only focus hook the consumer gets, shared with the other roving primitives. `[forDateFieldLiteral]` carries no data-\* attributes (it is `aria-hidden` and out of the tab order).
|
|
150
150
|
|
|
151
151
|
## Date-time field
|
|
152
152
|
|
|
@@ -192,13 +192,14 @@ providers: [
|
|
|
192
192
|
|
|
193
193
|
Key behavior applies per segment. Horizontal arrows mirror under `dir="rtl"`.
|
|
194
194
|
|
|
195
|
-
| Key | Behavior
|
|
196
|
-
| -------------------------- |
|
|
197
|
-
| **0–9** | Type the value; auto-advances to the next segment when full.
|
|
198
|
-
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today.
|
|
199
|
-
| **ArrowLeft / ArrowRight** | Move to the previous / next segment (no wrap).
|
|
200
|
-
| **Home / End** | Jump to the segment minimum / maximum.
|
|
201
|
-
| **Backspace
|
|
195
|
+
| Key | Behavior |
|
|
196
|
+
| -------------------------- | --------------------------------------------------------------------------------------- |
|
|
197
|
+
| **0–9** | Type the value; auto-advances to the next segment when full. |
|
|
198
|
+
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today. |
|
|
199
|
+
| **ArrowLeft / ArrowRight** | Move to the previous / next segment (no wrap). |
|
|
200
|
+
| **Home / End** | Jump to the segment minimum / maximum. |
|
|
201
|
+
| **Backspace** | Delete the last entered digit; the value becomes `null` when the last digit is removed. |
|
|
202
|
+
| **Delete** | Clear the whole segment (the value becomes `null` until refilled). |
|
|
202
203
|
|
|
203
204
|
The day clamps to the current month's length (e.g. 31 → 28 in February), and a composed value is clamped into `[minDate, maxDate]`.
|
|
204
205
|
|
|
@@ -189,7 +189,7 @@ Plus the shared `FormUiControl` members from `@angular/forms/signals`: `disabled
|
|
|
189
189
|
| `[forDateRangeFieldSegment]` | `data-disabled` | present \| absent |
|
|
190
190
|
| `[forDateRangeFieldSegment]` | `data-readonly` | present \| absent |
|
|
191
191
|
|
|
192
|
-
`data-empty` marks the
|
|
192
|
+
`data-empty` marks the field only while **both** endpoints are entirely empty; a partially-filled or complete-but-disordered range is **not** empty (`data-range-error` covers the disorder). `data-range-error` marks the specific case of two complete but out-of-order endpoints; `data-placeholder` marks each individual segment that is still empty. `data-highlighted` is the current roving-tabindex segment — the only focus hook the consumer gets, shared with the other roving primitives.
|
|
193
193
|
|
|
194
194
|
## Ordering
|
|
195
195
|
|
|
@@ -222,13 +222,14 @@ providers: [
|
|
|
222
222
|
|
|
223
223
|
Key behavior applies per segment. Each endpoint is its own tab stop, so `Tab` moves from the start group to the end group to the next control; arrows move between segments **within** an endpoint. Horizontal arrows mirror under `dir="rtl"`.
|
|
224
224
|
|
|
225
|
-
| Key | Behavior
|
|
226
|
-
| -------------------------- |
|
|
227
|
-
| **0–9** | Type the value; auto-advances to the next segment when full.
|
|
228
|
-
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today.
|
|
229
|
-
| **ArrowLeft / ArrowRight** | Move to the previous / next segment in the same endpoint (no wrap).
|
|
230
|
-
| **Home / End** | Jump to the segment minimum / maximum.
|
|
231
|
-
| **Backspace
|
|
225
|
+
| Key | Behavior |
|
|
226
|
+
| -------------------------- | --------------------------------------------------------------------------------------- |
|
|
227
|
+
| **0–9** | Type the value; auto-advances to the next segment when full. |
|
|
228
|
+
| **ArrowUp / ArrowDown** | Step the value. Day and month wrap; year clamps. Empty seeds from today. |
|
|
229
|
+
| **ArrowLeft / ArrowRight** | Move to the previous / next segment in the same endpoint (no wrap). |
|
|
230
|
+
| **Home / End** | Jump to the segment minimum / maximum. |
|
|
231
|
+
| **Backspace** | Delete the last entered digit; the range becomes `null` when the last digit is removed. |
|
|
232
|
+
| **Delete** | Clear the whole segment (the range becomes `null` until refilled). |
|
|
232
233
|
|
|
233
234
|
The day clamps to the current month's length (e.g. 31 → 28 in February), and each composed endpoint is clamped into `[minDate, maxDate]`.
|
|
234
235
|
|
package/drawer/README.md
CHANGED
|
@@ -205,6 +205,20 @@ this.#drawers.open(ConfirmDrawer, {
|
|
|
205
205
|
|
|
206
206
|
`activeSnapPointChange` fires with the landed snap on the mount-time default and every drag release — the read-back the declarative API exposes through `[(activeSnapPoint)]`. All three subscriptions are released automatically when the drawer closes.
|
|
207
207
|
|
|
208
|
+
**Driving the active snap point.** `ForDrawerRef.setActiveSnapPoint(snap)` moves a snap-point drawer to a new snap after open — the programmatic equivalent of _writing_ `[(activeSnapPoint)]` on the declarative `[forDrawer]`. `ref.activeSnapPoint()` is the matching reactive read (it also reflects the drawer's own internal transitions — the mount-time default and every drag release):
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const ref = this.#drawers.open(ConfirmDrawer, {
|
|
212
|
+
data,
|
|
213
|
+
snapPoints: ['148px', '50%', 1],
|
|
214
|
+
defaultSnapPoint: '148px',
|
|
215
|
+
});
|
|
216
|
+
ref.setActiveSnapPoint('50%'); // slide to the mid snap
|
|
217
|
+
ref.activeSnapPoint(); // => '50%'
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Like the declarative model it does not validate the argument against `snapPoints` (the drag engine tolerates a non-member), it is a no-op once the drawer has closed, and it is meaningful only when `snapPoints` are configured. The surface movement is the consumer's CSS keyed off the reflected `data-active-snap-point` attribute (see [Positioning the snaps](#positioning-the-snaps-css-contract)); `setActiveSnapPoint` only sets the state. As with `[(activeSnapPoint)]`, driving the snap this way does **not** re-fire the `activeSnapPointChange` callback (that fires only on the drawer's own internal transitions).
|
|
221
|
+
|
|
208
222
|
### Per-channel dismissal (Escape-only drawers)
|
|
209
223
|
|
|
210
224
|
`dismissible` is **not** all-or-nothing. The four dismiss channels — Escape, pointer-down-outside, focus-outside, and the composite outside-interaction — are independently vetoable on both APIs, so you can keep some live and suppress others (e.g. a non-modal floater that closes on Escape but stays put on an outside click). Programmatically the channels are callbacks on the open config, mirroring the `autoFocusOn*` shape:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { computed, InjectionToken, inject, signal, Injector, model, input, booleanAttribute, numberAttribute, linkedSignal, effect, afterNextRender, Directive, ElementRef, Injectable } from '@angular/core';
|
|
3
|
-
import { compareDateOf, clampToBounds, Collection, createDefaults, IdGenerator, LiveAnnouncer, injectDateAdapter, injectTextDirection, adoptHostId, registerHandle, FOR_DATE_ADAPTER } from 'forty-cdk/core';
|
|
3
|
+
import { compareDateOf, clampToBounds, Collection, createDefaults, IdGenerator, LiveAnnouncer, injectDateAdapter, injectTextDirection, adoptHostId, registerHandle, createFormatterCache, FOR_DATE_ADAPTER } from 'forty-cdk/core';
|
|
4
4
|
export { FOR_DATE_ADAPTER, assertTimeCapable, injectDateAdapter } from 'forty-cdk/core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -1963,16 +1963,7 @@ function clampDay(year, month, day, hours = 0, minutes = 0, seconds = 0, millise
|
|
|
1963
1963
|
* calendar today; non-Gregorian calendar systems are deferred to #354.
|
|
1964
1964
|
*/
|
|
1965
1965
|
class NativeDateAdapter {
|
|
1966
|
-
#
|
|
1967
|
-
#formatter(locale, options) {
|
|
1968
|
-
const key = `${locale ?? ''}${JSON.stringify(options)}`;
|
|
1969
|
-
let formatter = this.#formatters.get(key);
|
|
1970
|
-
if (formatter === undefined) {
|
|
1971
|
-
formatter = new Intl.DateTimeFormat(locale, options);
|
|
1972
|
-
this.#formatters.set(key, formatter);
|
|
1973
|
-
}
|
|
1974
|
-
return formatter;
|
|
1975
|
-
}
|
|
1966
|
+
#formatter = createFormatterCache();
|
|
1976
1967
|
/**
|
|
1977
1968
|
* Today at local midnight in the runtime time zone. Subject to the
|
|
1978
1969
|
* SSR/hydration caveat on {@link DateAdapter.today}.
|