forty-cdk 0.17.0 → 0.19.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 +8 -5
- package/context-menu/README.md +18 -0
- package/date-picker/README.md +8 -4
- package/dropdown-menu/README.md +16 -0
- package/fesm2022/forty-cdk-combobox.mjs +199 -130
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-context-menu.mjs +102 -42
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +1109 -258
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-field.mjs +2 -12
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs +44 -8
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-range-field.mjs +2 -7
- package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
- package/fesm2022/forty-cdk-dropdown-menu.mjs +90 -21
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-hover-card.mjs +7 -1
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +52 -31
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-menu.mjs +330 -33
- package/fesm2022/forty-cdk-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-menubar.mjs +63 -12
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs +10 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-otp-input.mjs +6 -4
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs +7 -1
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +95 -77
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-table.mjs +501 -62
- package/fesm2022/forty-cdk-table.mjs.map +1 -1
- package/fesm2022/forty-cdk-tabs.mjs +34 -12
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +27 -4
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +7 -1
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-tree.mjs +34 -19
- package/fesm2022/forty-cdk-tree.mjs.map +1 -1
- package/hover-card/README.md +2 -1
- package/listbox/README.md +5 -4
- package/menu/README.md +96 -4
- package/menubar/README.md +17 -17
- package/navigation-menu/README.md +2 -1
- package/otp-input/README.md +1 -0
- package/package.json +1 -5
- package/popover/README.md +2 -1
- package/select/README.md +8 -3
- package/shared/README.md +59 -0
- package/table/README.md +11 -0
- package/time-picker/README.md +23 -1
- package/tooltip/README.md +19 -10
- package/types/forty-cdk-combobox.d.ts +52 -30
- package/types/forty-cdk-context-menu.d.ts +70 -14
- package/types/forty-cdk-core.d.ts +579 -186
- package/types/forty-cdk-date-picker.d.ts +23 -5
- package/types/forty-cdk-dropdown-menu.d.ts +59 -9
- package/types/forty-cdk-listbox.d.ts +8 -3
- package/types/forty-cdk-menu.d.ts +264 -18
- package/types/forty-cdk-menubar.d.ts +57 -12
- package/types/forty-cdk-otp-input.d.ts +4 -3
- package/types/forty-cdk-select.d.ts +13 -6
- package/types/forty-cdk-shared.d.ts +1 -1
- package/types/forty-cdk-table.d.ts +209 -28
- package/types/forty-cdk-tabs.d.ts +19 -5
- package/types/forty-cdk-time-picker.d.ts +11 -1
- package/types/forty-cdk-tree.d.ts +3 -2
- package/fesm2022/forty-cdk-signal-forms.mjs +0 -134
- package/fesm2022/forty-cdk-signal-forms.mjs.map +0 -1
- package/signal-forms/README.md +0 -72
- package/types/forty-cdk-signal-forms.d.ts +0 -69
package/combobox/README.md
CHANGED
|
@@ -20,11 +20,11 @@ Two anatomies share the same core:
|
|
|
20
20
|
The editable (default) anatomy — an `<input>` that filters a portaled listbox in place:
|
|
21
21
|
|
|
22
22
|
```html
|
|
23
|
-
<div forCombobox [(query)]="query" [(value)]="value">
|
|
23
|
+
<div forCombobox #combobox="forCombobox" [(query)]="query" [(value)]="value">
|
|
24
24
|
<input forComboboxInput placeholder="Search…" />
|
|
25
25
|
<button forComboboxClear>×</button>
|
|
26
26
|
|
|
27
|
-
<!-- @if (open()) { -->
|
|
27
|
+
<!-- @if (combobox.open()) { -->
|
|
28
28
|
<div forComboboxContent>
|
|
29
29
|
<div forComboboxOption [value]="item.id" [label]="item.label">
|
|
30
30
|
<span forComboboxIndicator>✓</span>
|
|
@@ -106,7 +106,7 @@ Static and `@for`-rendered options share the same registry, navigation order (DO
|
|
|
106
106
|
|
|
107
107
|
For a legacy `<form action="…">` flow, set `[name]` — the directive mirrors `[(value)]` into N `<input type="hidden">` siblings (one per array entry; zero when empty). String values land verbatim in the hidden input; object values default to `JSON.stringify` (override via `[itemToFormValue]`, see below).
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
A single-select field is modeled as the same `readonly T[]`, kept at length ≤ 1, and bound with `[formField]` directly — single mode needs no adapter. A `FieldTree<T | null>` cannot bind here; map to that shape at the edge that needs it. See [the selection value-type contract](../../../docs/selection-value-type-contract.md).
|
|
110
110
|
|
|
111
111
|
## API
|
|
112
112
|
|
|
@@ -118,6 +118,7 @@ Input tables are not yet tabulated for this primitive. See the feature sections
|
|
|
118
118
|
| ------------------------ | ------------------ | ------------------------------------------------------------------- |
|
|
119
119
|
| `[forCombobox]` | `data-state` | `open` \| `closed` |
|
|
120
120
|
| `[forCombobox]` | `data-disabled` | present / absent |
|
|
121
|
+
| `[forCombobox]` | `data-readonly` | present / absent |
|
|
121
122
|
| `[forComboboxInput]` | `data-state` | `open` \| `closed` |
|
|
122
123
|
| `[forComboboxInput]` | `data-disabled` | present / absent |
|
|
123
124
|
| `[forComboboxContent]` | `data-state` | `open` \| `closed` |
|
|
@@ -190,6 +191,8 @@ Why the list part is required, not optional: a `role="listbox"` may only own `op
|
|
|
190
191
|
|
|
191
192
|
**Focus hand-off.** Registering a trigger opts the combobox into the standard trigger-anchored focus model: on open, focus moves into the input (the search field inside the panel); on close, focus returns to the trigger. Both moves are vetoable via `(autoFocusOnOpen)` / `(autoFocusOnClose)` on `[forCombobox]`, and the return is gated by `[returnFocus]` (default `true`). Escape stays owned by the input. See [Focus & the `(autoFocusOnOpen)` / `(autoFocusOnClose)` hooks](#focus--the-autofocusonopen--autofocusonclose-hooks).
|
|
192
193
|
|
|
194
|
+
**A trigger that registers late still owns the hand-off.** The trigger does not have to be declared before `[forComboboxContent]`, and does not have to exist when the panel first mounts — project it through `<ng-content>` from a wrapper, put it under a `@defer`, or gate it on an `@if` over loaded data. One caveat: focus moving _into_ the panel is a mount-time event, so a trigger that arrives while the panel is **already open** does not retroactively pull focus out of wherever you left it. From that moment on it does own the return focus, `(autoFocusOnClose)` and the Escape fallback, and the next open moves focus into the input as usual.
|
|
195
|
+
|
|
193
196
|
**Trigger keyboard.** Click / Enter / Space toggle (open moves focus into the input). ArrowDown opens with the first enabled option highlighted; ArrowUp opens with the last.
|
|
194
197
|
|
|
195
198
|
**Anchor preference.** With a trigger present the panel anchors to it by default. An explicit `[forComboboxAnchor]` still wins (explicit anchor → trigger → input), so you can wrap a decorated trigger box and anchor against it.
|
|
@@ -414,7 +417,7 @@ The `autocompleteMode` input mirrors the WAI-ARIA `aria-autocomplete` property:
|
|
|
414
417
|
|
|
415
418
|
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.
|
|
416
419
|
|
|
417
|
-
> **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'`
|
|
420
|
+
> **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'` — it opens the popup, so the options render and the cache warms. Leaving `[forComboboxContent]` permanently mounted instead is not a supported shape and warns in dev mode: mount **is** the open state for this surface, so it never runs `animate.enter` / `animate.leave`, and its dismissible layer stays active while closed.
|
|
418
421
|
|
|
419
422
|
## Dismiss events
|
|
420
423
|
|
|
@@ -517,7 +520,7 @@ How navigation flows when virtualizing:
|
|
|
517
520
|
3. Your virtualizer scrolls; the directive's `@for` mounts the option for index 999.
|
|
518
521
|
4. As soon as that option registers (at the matching `posInSet`), the directive seeds `aria-activedescendant` to its id.
|
|
519
522
|
|
|
520
|
-
|
|
523
|
+
Inline autocomplete matches against the most recently rendered window overlaid with the position snapshot, so completion against off-screen labels still works. `selected().label` reads a separate, selection-keyed cache instead — bounded by the selection, so the label of a selected option survives close / re-open and any number of query rebuilds, but it is **not** resolved from the position snapshot: a value that enters the selection while its option is outside the rendered window (a `[(value)]` write restoring a saved selection, say) falls back to `[itemToStringLabel]` until that option renders once. Supply `[itemToStringLabel]` whenever the selection can be seeded from outside the list.
|
|
521
524
|
|
|
522
525
|
```html
|
|
523
526
|
<div
|
package/context-menu/README.md
CHANGED
|
@@ -95,6 +95,24 @@ Angular resolves `ng-template` DI at the template's **declaration** site, not wh
|
|
|
95
95
|
</ng-template>
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
### Sharing one menu with a second opener
|
|
99
|
+
|
|
100
|
+
`[forContextMenu]` is a **single-opener preset**: one root, one right-click region. When the same actions must also be reachable another way — the canonical case being a table row with a right-click region _and_ a kebab button — bind the trigger to a `[forMenu]` root instead, which drives one `[forMenuContent]` block from any number of openers. See [Shared openers](../menu/README.md#shared-openers-formenu).
|
|
101
|
+
|
|
102
|
+
The same explicit-reference input carries it, and here the binding is **required** rather than optional: the trigger resolves `FOR_CONTEXT_MENU_CONTEXT`, which `[forMenu]` deliberately does not provide (`forty-cdk/menu` must not depend on `forty-cdk/context-menu`).
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<tr forMenu #row="forMenu" [(open)]="open" ariaLabel="Row actions">
|
|
106
|
+
<td [forContextMenuTrigger]="row">…cells…</td>
|
|
107
|
+
<td>
|
|
108
|
+
<button [forDropdownMenuTrigger]="row" [menuPositioning]="{ sideOffset: 4 }">⋮</button>
|
|
109
|
+
</td>
|
|
110
|
+
<!-- one content block, no duplication -->
|
|
111
|
+
</tr>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Both triggers carry `[menuPositioning]`, a partial `{ side, align, sideOffset, alignOffset }` override applied only to the opens that trigger drives, with each omitted key falling back to the root's input. It exists because a shared root cannot pick offsets that suit heterogeneous openers: the region above keeps the root's `sideOffset` of `0` — flush at the cursor, which is what a pointer-anchored menu wants — while the sibling button opener asks for the 4px of clearance a menu button wants. Under a `[forContextMenu]` root it resolves the same way, where it is simply a per-trigger spelling of the root's inputs. See [Per-opener positioning](../menu/README.md#per-opener-positioning).
|
|
115
|
+
|
|
98
116
|
## API
|
|
99
117
|
|
|
100
118
|
### `ForContextMenu`
|
package/date-picker/README.md
CHANGED
|
@@ -18,18 +18,19 @@ All date math and formatting go through a `DateAdapter<D>`, shared with `ForCale
|
|
|
18
18
|
## Anatomy
|
|
19
19
|
|
|
20
20
|
```html
|
|
21
|
-
<div forDatePicker [(value)]="date" [(open)]="open" name="dob">
|
|
21
|
+
<div forDatePicker #picker="forDatePicker" [(value)]="date" [(open)]="open" name="dob">
|
|
22
22
|
<!-- optional: wrap a decorated field box in [forDatePickerAnchor] to position against it -->
|
|
23
23
|
<button forDatePickerTrigger>
|
|
24
24
|
<span forDatePickerValue placeholder="Pick a date"></span>
|
|
25
25
|
</button>
|
|
26
26
|
|
|
27
|
-
<!--
|
|
27
|
+
<!-- @if (picker.open()) { -->
|
|
28
28
|
<div forDatePickerContent>
|
|
29
29
|
<div forCalendar [(value)]="date">
|
|
30
30
|
<!-- …calendar header + grid… -->
|
|
31
31
|
</div>
|
|
32
32
|
</div>
|
|
33
|
+
<!-- } -->
|
|
33
34
|
</div>
|
|
34
35
|
```
|
|
35
36
|
|
|
@@ -171,8 +172,10 @@ Plus the shared `FormUiControl` inputs from the base (`disabled`, `readonly`, `r
|
|
|
171
172
|
| ------------------------ | ------------------ | ------------------ |
|
|
172
173
|
| `[forDatePicker]` | `data-state` | `open` \| `closed` |
|
|
173
174
|
| `[forDatePicker]` | `data-disabled` | present \| absent |
|
|
175
|
+
| `[forDatePicker]` | `data-readonly` | present \| absent |
|
|
174
176
|
| `[forDatePickerTrigger]` | `data-state` | `open` \| `closed` |
|
|
175
177
|
| `[forDatePickerTrigger]` | `data-disabled` | present \| absent |
|
|
178
|
+
| `[forDatePickerTrigger]` | `data-readonly` | present \| absent |
|
|
176
179
|
| `[forDatePickerContent]` | `data-state` | `open` \| `closed` |
|
|
177
180
|
| `[forDatePickerValue]` | `data-placeholder` | present \| absent |
|
|
178
181
|
|
|
@@ -333,9 +336,10 @@ Inside the surface, the projected `ForCalendar` owns the full grid keyboard map
|
|
|
333
336
|
|
|
334
337
|
Implements the [WAI-ARIA Date Picker Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/examples/datepicker-dialog/).
|
|
335
338
|
|
|
336
|
-
- **`
|
|
339
|
+
- **`role="combobox"`** on the trigger with **`aria-haspopup="dialog"`**, `aria-expanded` reflecting `open()`, and `aria-controls` pointing at the surface while open — the same shape `[forSelectTrigger]` / `[forTimePickerTrigger]` ship, with the `dialog` popup token ARIA 1.2 allows for a combobox surface. The role is also what makes the form-control ARIA below legal: `role="button"` supports neither `aria-readonly` nor `aria-required`.
|
|
337
340
|
- **`role="dialog"`** on the surface, named by `[ariaLabel]` (or `aria-labelledby` the trigger when no label is set). `aria-modal="true"` only in modal mode (truthy-only).
|
|
338
|
-
- **Form-control ARIA** (`aria-readonly` / `aria-required` / `aria-invalid` / `aria-busy`) is reflected on the focusable trigger so assistive tech announces validity on the element that takes focus. The disabled state is the exception: it reflects through the native `disabled` attribute alone (plus `data-disabled`), never `aria-disabled` — one channel per #561 D2.
|
|
341
|
+
- **Form-control ARIA** (`aria-readonly` / `aria-required` / `aria-invalid` / `aria-busy`) is reflected on the focusable trigger so assistive tech announces validity on the element that takes focus, alongside the `data-readonly` styling hook. The disabled state is the exception: it reflects through the native `disabled` attribute alone (plus `data-disabled`), never `aria-disabled` — one channel per #561 D2.
|
|
342
|
+
- **Inside a `[forField]` the labelled element is the trigger**, not the `[forDatePicker]` / `[forDateRangePicker]` wrapper: the field's `controlId` and its `aria-labelledby` / `aria-describedby` / `aria-errormessage` land on `[forDatePickerTrigger]`, so `[forLabel]`'s `for` points at the element that takes focus, clicking a non-`<label>` `[forLabel]` opens the surface, and Signal Forms' focus-on-error reaches the trigger. `role="combobox"` takes its name from the author, so this is the channel that names the control — the root's `[ariaLabel]` names the `role="dialog"` surface instead.
|
|
339
343
|
- **Focus management**: focus enters the surface on open (the calendar's roving cell in non-modal mode) and returns to the trigger on close, both vetoable via `(autoFocusOnOpen)` / `(autoFocusOnClose)`.
|
|
340
344
|
- **Dismissal**: Escape (`(escapeKeyDown)`) and outside-pointer (`(pointerDownOutside)` / `(interactOutside)`) close the surface, each vetoable.
|
|
341
345
|
|
package/dropdown-menu/README.md
CHANGED
|
@@ -113,6 +113,22 @@ Angular resolves `ng-template` DI at the template's **declaration** site, not wh
|
|
|
113
113
|
</ng-template>
|
|
114
114
|
```
|
|
115
115
|
|
|
116
|
+
### Sharing one menu with a second opener
|
|
117
|
+
|
|
118
|
+
`[forDropdownMenu]` is a **single-opener preset**: one root, one button trigger. When the same actions must also be reachable another way — the canonical case being a table row with a kebab button _and_ a right-click region over the whole row — bind the trigger to a `[forMenu]` root instead, which drives one `[forMenuContent]` block from any number of openers. See [Shared openers](../menu/README.md#shared-openers-formenu).
|
|
119
|
+
|
|
120
|
+
```html
|
|
121
|
+
<tr forMenu #row="forMenu" [(open)]="open" ariaLabel="Row actions">
|
|
122
|
+
<td [forContextMenuTrigger]="row">…cells…</td>
|
|
123
|
+
<td>
|
|
124
|
+
<button [forDropdownMenuTrigger]="row" [menuPositioning]="{ sideOffset: 4 }">⋮</button>
|
|
125
|
+
</td>
|
|
126
|
+
<!-- one content block, no duplication -->
|
|
127
|
+
</tr>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`[menuPositioning]` is the trigger's own placement override — a partial `{ side, align, sideOffset, alignOffset }`, each key falling back to the root's input when omitted. It exists because a shared root cannot pick offsets that suit heterogeneous openers: the `sideOffset: 4` above keeps the button-opened menu clear of the button while a sibling right-click region still opens flush at the cursor. Under a `[forDropdownMenu]` root it resolves the same way, where it is simply a per-trigger spelling of the root's inputs. See [Per-opener positioning](../menu/README.md#per-opener-positioning).
|
|
131
|
+
|
|
116
132
|
## API
|
|
117
133
|
|
|
118
134
|
### `ForDropdownMenu`
|