forty-cdk 0.40.0 → 0.41.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/README.md +1 -1
- package/breadcrumbs/README.md +1 -1
- package/breakpoints/README.md +12 -2
- package/calendar/README.md +2 -2
- package/dialog/README.md +22 -9
- package/drawer/README.md +18 -9
- package/fesm2022/forty-cdk-calendar.mjs +14 -4
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
- package/fesm2022/forty-cdk-combobox.mjs +49 -40
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
- package/fesm2022/forty-cdk-context-menu.mjs +42 -38
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-core-anchored.mjs +2076 -0
- package/fesm2022/forty-cdk-core-anchored.mjs.map +1 -0
- package/fesm2022/forty-cdk-core-overlay.mjs +145 -2202
- package/fesm2022/forty-cdk-core-overlay.mjs.map +1 -1
- package/fesm2022/forty-cdk-core.mjs +60 -28
- package/fesm2022/forty-cdk-core.mjs.map +1 -1
- package/fesm2022/forty-cdk-date-picker.mjs +80 -57
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-defaults.mjs.map +1 -1
- package/fesm2022/forty-cdk-dialog.mjs +2 -1
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
- package/fesm2022/forty-cdk-drawer.mjs +2 -1
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
- package/fesm2022/forty-cdk-dropdown-menu.mjs +48 -33
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-hover-card.mjs +52 -35
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
- package/fesm2022/forty-cdk-input.mjs +19 -13
- package/fesm2022/forty-cdk-input.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +56 -43
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
- package/fesm2022/forty-cdk-menu.mjs +32 -6
- package/fesm2022/forty-cdk-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-menubar.mjs +13 -10
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs +8 -1
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
- package/fesm2022/forty-cdk-popover.mjs +74 -34
- package/fesm2022/forty-cdk-popover.mjs.map +1 -1
- package/fesm2022/forty-cdk-select.mjs +50 -28
- package/fesm2022/forty-cdk-select.mjs.map +1 -1
- package/fesm2022/forty-cdk-shared.mjs +1 -1
- package/fesm2022/forty-cdk-table-virtualization.mjs +8 -8
- package/fesm2022/forty-cdk-table-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk-time-picker.mjs +53 -23
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
- package/fesm2022/forty-cdk-tooltip.mjs +61 -38
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs +14 -12
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/hover-card/README.md +0 -1
- package/listbox/README.md +3 -3
- package/package.json +5 -1
- package/shared/README.md +5 -5
- package/table/README.md +8 -8
- package/table-virtualization/README.md +1 -1
- package/types/forty-cdk-calendar.d.ts +26 -9
- package/types/forty-cdk-combobox.d.ts +12 -13
- package/types/forty-cdk-context-menu.d.ts +11 -13
- package/types/forty-cdk-core-anchored.d.ts +2855 -0
- package/types/forty-cdk-core-overlay.d.ts +222 -2984
- package/types/forty-cdk-core.d.ts +22 -8
- package/types/forty-cdk-date-picker.d.ts +29 -20
- package/types/forty-cdk-defaults.d.ts +2 -1
- package/types/forty-cdk-dialog.d.ts +51 -15
- package/types/forty-cdk-drawer.d.ts +51 -15
- package/types/forty-cdk-dropdown-menu.d.ts +3 -2
- package/types/forty-cdk-hover-card.d.ts +12 -9
- package/types/forty-cdk-listbox.d.ts +15 -14
- package/types/forty-cdk-menu.d.ts +5 -3
- package/types/forty-cdk-menubar.d.ts +2 -3
- package/types/forty-cdk-popover.d.ts +26 -13
- package/types/forty-cdk-select.d.ts +10 -5
- package/types/forty-cdk-shared.d.ts +1 -1
- package/types/forty-cdk-table-virtualization.d.ts +1 -1
- package/types/forty-cdk-time-picker.d.ts +8 -3
- package/types/forty-cdk-tooltip.d.ts +25 -15
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ Two packages are regular dependencies, installed automatically and never declare
|
|
|
54
54
|
| [`forty-cdk/id-salt`](id-salt) | `FOR_ID_SALT` and `provideForIdSalt`, the per-app salt for the ids the primitives generate when several forty-cdk apps share a page. `forty-cdk/shared` re-exports them, but a root provider imported from there loads the code every primitive shares at startup; imported from here, it does not. |
|
|
55
55
|
| [`forty-cdk/testing`](testing) | Spec helpers: keyboard and mouse events dispatched the way a browser dispatches them, stubs for the browser APIs jsdom lacks, and dialog and drawer refs for content mounted outside its manager. |
|
|
56
56
|
|
|
57
|
-
`forty-cdk/core` and `forty-cdk/core-
|
|
57
|
+
`forty-cdk/core`, `forty-cdk/core-overlay` and `forty-cdk/core-anchored` resolve too, but none is **public**: together they hold the engines and DI singletons the library refactors freely, and they exist so every primitive resolves that shared implementation to one compiled module. They are three rather than one for a bundling reason you get for free: a published module is a bundler's chunk-splitting unit, so keeping the overlay machinery in modules of its own means a lazy route that renders no overlay does not load it, and keeping the positioning engine (`@floating-ui/dom` and the anchored overlay shell) apart from the modal shell means an app that opens a dialog from its initial bundle does not load the engine with it. Measured on a seven-lazy-route app, the first split is **41.7 kB raw / 12.1 kB transfer** a non-overlay route no longer pays. If a symbol you need is not exported by the seven specifiers above, it is internal by design — [open an issue](https://github.com/tutkli/forty-cdk/issues) rather than importing from any of them.
|
|
58
58
|
|
|
59
59
|
## Errors
|
|
60
60
|
|
package/breadcrumbs/README.md
CHANGED
|
@@ -84,7 +84,7 @@ export class BreadcrumbsDefaultExample {
|
|
|
84
84
|
|
|
85
85
|
### Collapsing a long trail
|
|
86
86
|
|
|
87
|
-
The primitive renders whatever items you give it, so collapsing a deep path is a consumer decision. Here the middle is folded into an expandable ellipsis button that reveals the hidden crumbs. The trail stays a single accessible navigation landmark either way.
|
|
87
|
+
The primitive renders whatever items you give it, so collapsing a deep path is a consumer decision. Here the middle is folded into an expandable ellipsis button that reveals the hidden crumbs. Expanding removes the button, so focus moves to the first crumb it revealed instead of falling back to the page. The trail stays a single accessible navigation landmark either way.
|
|
88
88
|
|
|
89
89
|
## API
|
|
90
90
|
|
package/breakpoints/README.md
CHANGED
|
@@ -38,12 +38,14 @@ Resize the browser window and watch the matched breakpoint change: the breakpoin
|
|
|
38
38
|
```ts
|
|
39
39
|
import { ChangeDetectionStrategy, Component, computed } from '@angular/core';
|
|
40
40
|
import { forBreakpointsTailwind, injectBreakpoints } from 'forty-cdk/breakpoints';
|
|
41
|
+
import { ForVisuallyHidden } from 'forty-cdk/visually-hidden';
|
|
41
42
|
|
|
42
43
|
type TailwindName = keyof typeof forBreakpointsTailwind;
|
|
43
44
|
|
|
44
45
|
@Component({
|
|
45
46
|
selector: 'app-breakpoints-active-example',
|
|
46
47
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
48
|
+
imports: [ForVisuallyHidden],
|
|
47
49
|
template: `
|
|
48
50
|
<div class="bp-demo">
|
|
49
51
|
<div class="bp-readout">
|
|
@@ -58,8 +60,16 @@ type TailwindName = keyof typeof forBreakpointsTailwind;
|
|
|
58
60
|
<span class="bp-name">{{ row.name }}</span>
|
|
59
61
|
<span class="bp-min">≥ {{ row.min }}px</span>
|
|
60
62
|
<span class="bp-flags">
|
|
61
|
-
<span class="bp-flag" [class.bp-flag--on]="row.up()">
|
|
62
|
-
|
|
63
|
+
<span class="bp-flag" [class.bp-flag--on]="row.up()">
|
|
64
|
+
<span aria-hidden="true">{{ row.up() ? '✓' : '✕' }}</span>
|
|
65
|
+
up
|
|
66
|
+
<span forVisuallyHidden>{{ row.up() ? 'matches' : 'does not match' }}</span>
|
|
67
|
+
</span>
|
|
68
|
+
<span class="bp-flag" [class.bp-flag--on]="row.only()">
|
|
69
|
+
<span aria-hidden="true">{{ row.only() ? '✓' : '✕' }}</span>
|
|
70
|
+
only
|
|
71
|
+
<span forVisuallyHidden>{{ row.only() ? 'matches' : 'does not match' }}</span>
|
|
72
|
+
</span>
|
|
63
73
|
</span>
|
|
64
74
|
</li>
|
|
65
75
|
}
|
package/calendar/README.md
CHANGED
|
@@ -393,7 +393,7 @@ The recommended path is `[forCalendarMonthSelect]` and `[forCalendarYearSelect]`
|
|
|
393
393
|
|
|
394
394
|
The default window is **anchored to the current year** (not the visible year), so navigating far away never drops the current year off the list. Out-of-`[min, max]` entries have `disabled: true`, matching the `CalendarYearOption` shape. Both directives set the native `disabled` attribute on the `<select>` itself when the calendar is disabled.
|
|
395
395
|
|
|
396
|
-
**Keep `[forCalendarHeading]` in the DOM.** The grid's `aria-labelledby` points at the heading's id. When dropdowns replace the visible heading, keep a visually-hidden `[forCalendarHeading]` so the grid stays named.
|
|
396
|
+
**Keep `[forCalendarHeading]` in the DOM.** The grid's `aria-labelledby` points at the heading's id while a heading is mounted. When dropdowns replace the visible heading, keep a visually-hidden `[forCalendarHeading]` so the grid stays named. Without a heading the grids emit no `aria-labelledby` at all, so they are unnamed unless you name them: a static `aria-labelledby` pointing at your own title, or an `aria-label`, written on each grid.
|
|
397
397
|
|
|
398
398
|
**Name each `<select>` yourself.** Neither directive names the element it sits on, and the heading names the grid, not the dropdowns. Without a name a screen reader announces only the value each one holds, so give each an `aria-label` as the snippet above does, or a `<label for>`.
|
|
399
399
|
|
|
@@ -582,7 +582,7 @@ Focus that crosses a month boundary re-pages the visible grid and keeps the focu
|
|
|
582
582
|
Implements the [WAI-ARIA Grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/grid/).
|
|
583
583
|
|
|
584
584
|
- **`role="grid"`** on the table, `columnheader` weekday headers and `gridcell` days follow the APG Date Picker Dialog technique over a real `<table>`.
|
|
585
|
-
- **`aria-labelledby`** wires the grid to the heading so it names the visible period. Paging the month is announced through a dedicated off-screen `aria-live="polite"` region (owned by `[forCalendar]`), so the period is read on navigation without the heading double-announcing as both a live region and the grid's label.
|
|
585
|
+
- **`aria-labelledby`** wires the grid to the heading so it names the visible period. It is emitted only while a `[forCalendarHeading]` is mounted, and a static `aria-labelledby` you write on the grid replaces it. Paging the month is announced through a dedicated off-screen `aria-live="polite"` region (owned by `[forCalendar]`), so the period is read on navigation without the heading double-announcing as both a live region and the grid's label.
|
|
586
586
|
- **`aria-label`** on every cell carries the full localized date (e.g. `"Monday, June 15, 2026"`) so screen readers announce the whole date, not the bare day number that stays the cell's visible content. Outside-month padding days are suffixed (`" (outside month)"`) so they are distinguishable. Override the format via `ForCalendar`'s `dateLabel` input.
|
|
587
587
|
- **`aria-selected`** is always emitted (`"true"` / `"false"`); **`aria-current="date"`** marks today; **`aria-disabled`** marks unavailable dates (truthy-only).
|
|
588
588
|
- **`aria-multiselectable="true"`** is set on the day grid in `selectionMode="range"`, where a committed range selects several cells, and is absent in single mode.
|
package/dialog/README.md
CHANGED
|
@@ -221,7 +221,19 @@ The auto-focus pair is bound as **function references** (input callbacks), not a
|
|
|
221
221
|
|
|
222
222
|
```ts
|
|
223
223
|
import { Component, inject } from '@angular/core';
|
|
224
|
-
import {
|
|
224
|
+
import {
|
|
225
|
+
FOR_DIALOG_CONTRACT,
|
|
226
|
+
type ForDialogContract,
|
|
227
|
+
ForDialogManager,
|
|
228
|
+
ForDialogRef,
|
|
229
|
+
injectDialogData,
|
|
230
|
+
} from 'forty-cdk/dialog';
|
|
231
|
+
|
|
232
|
+
interface ConfirmData {
|
|
233
|
+
message: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
type ConfirmResult = 'confirm' | 'cancel';
|
|
225
237
|
|
|
226
238
|
@Component({
|
|
227
239
|
template: `
|
|
@@ -231,8 +243,9 @@ import { ForDialogManager, ForDialogRef, injectDialogData } from 'forty-cdk/dial
|
|
|
231
243
|
`,
|
|
232
244
|
})
|
|
233
245
|
class ConfirmDialog {
|
|
234
|
-
readonly
|
|
235
|
-
readonly
|
|
246
|
+
declare readonly [FOR_DIALOG_CONTRACT]: ForDialogContract<ConfirmData, ConfirmResult>;
|
|
247
|
+
readonly data = injectDialogData<ConfirmDialog>();
|
|
248
|
+
readonly ref = inject(ForDialogRef<ConfirmResult>);
|
|
236
249
|
}
|
|
237
250
|
|
|
238
251
|
@Component({
|
|
@@ -243,10 +256,7 @@ export class DemoHost {
|
|
|
243
256
|
readonly dialogs = inject(ForDialogManager);
|
|
244
257
|
|
|
245
258
|
async askToDelete() {
|
|
246
|
-
const ref = this.dialogs.open
|
|
247
|
-
ConfirmDialog,
|
|
248
|
-
{ data: { message: 'Are you sure?' } },
|
|
249
|
-
);
|
|
259
|
+
const ref = this.dialogs.open(ConfirmDialog, { data: { message: 'Are you sure?' } });
|
|
250
260
|
const { result } = await ref.closed; // result: 'confirm' | 'cancel' | undefined
|
|
251
261
|
if (result === 'confirm') {
|
|
252
262
|
/* ... */
|
|
@@ -255,7 +265,9 @@ export class DemoHost {
|
|
|
255
265
|
}
|
|
256
266
|
```
|
|
257
267
|
|
|
258
|
-
`injectDialogData<
|
|
268
|
+
**Typing the payload and the result.** The `[FOR_DIALOG_CONTRACT]` member states, once, the data a dialog component reads and the result it closes with. Declare it with `declare` and no initializer, and keep it public. `open()` then infers both from the component: a `data` of any other shape fails to compile, and the returned ref is a `ForDialogRef<ConfirmResult>`. `injectDialogData<ConfirmDialog>()` reads the payload at the type the contract names, so the component and every caller share one definition. A component without the member keeps an unchecked `data` and an `unknown` result, and `injectDialogData<T>()` with a plain type still types the payload as `T`. Explicit type arguments (`open<ConfirmDialog, ConfirmResult>`) override what the contract supplies.
|
|
269
|
+
|
|
270
|
+
`injectDialogData()` returns `null` too: the manager provides `null` when `open()` is called without `data`, so guard (`data?.message`) before dereferencing the payload.
|
|
259
271
|
|
|
260
272
|
**Styling the programmatic overlay root.** Declaratively you write the surface yourself (`<div forDialog class="my-dialog">`), so the class lands on the same element that carries `data-state` / `role`. The manager creates that host for you and it is class-less, so pass `class` / `classList` to style it.
|
|
261
273
|
|
|
@@ -320,7 +332,8 @@ class ScopedHost {
|
|
|
320
332
|
| `ForDialogManager` | Injectable. `open(component, config?)` returns a `ForDialogRef<R>`. `closeAll(result?)` closes every open dialog, topmost first, with reason `'programmatic'`; exit animations still play and focus ends where the bottom dialog returns it. `openCount` is a `Signal<number>` of the dialogs still open, counting a closed one until its exit animation finishes. |
|
|
321
333
|
| `ForDialogRef<R>` | `close(result?)`, `closed: Promise<{ reason: ForDialogCloseReason; result: R \| undefined }>`, `result: Signal<R \| undefined>`, `isClosed: Signal<boolean>`. |
|
|
322
334
|
| `FOR_DIALOG_DATA` | Token for the `data` payload. Inject in the opened component. |
|
|
323
|
-
| `injectDialogData<T>()` | Typed accessor for `FOR_DIALOG_DATA`. Returns `T \| null` (`null` when `open()` got no `data`)
|
|
335
|
+
| `injectDialogData<T>()` | Typed accessor for `FOR_DIALOG_DATA`. Returns `T \| null` (`null` when `open()` got no `data`), or the data type the contract names when `T` is a component declaring `FOR_DIALOG_CONTRACT`. |
|
|
336
|
+
| `FOR_DIALOG_CONTRACT` | Key of the member a dialog component declares as `ForDialogContract<D, R>` so `open()` checks `data` against `D` and returns a `ForDialogRef<R>`. `ForDialogDataOf<C>` / `ForDialogResultOf<C>` read the two types off a component. |
|
|
324
337
|
|
|
325
338
|
### `ForDialogOpenConfig`
|
|
326
339
|
|
package/drawer/README.md
CHANGED
|
@@ -100,9 +100,17 @@ import {
|
|
|
100
100
|
ForDrawerManager,
|
|
101
101
|
ForDrawerRef,
|
|
102
102
|
ForDrawerTitle,
|
|
103
|
+
FOR_DRAWER_CONTRACT,
|
|
104
|
+
type ForDrawerContract,
|
|
103
105
|
injectDrawerData,
|
|
104
106
|
} from 'forty-cdk/drawer';
|
|
105
107
|
|
|
108
|
+
interface ConfirmData {
|
|
109
|
+
message: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type ConfirmResult = 'confirm' | 'cancel';
|
|
113
|
+
|
|
106
114
|
@Component({
|
|
107
115
|
imports: [
|
|
108
116
|
ForDrawerBackdrop,
|
|
@@ -126,8 +134,9 @@ import {
|
|
|
126
134
|
`,
|
|
127
135
|
})
|
|
128
136
|
class ConfirmDrawer {
|
|
129
|
-
readonly
|
|
130
|
-
readonly
|
|
137
|
+
declare readonly [FOR_DRAWER_CONTRACT]: ForDrawerContract<ConfirmData, ConfirmResult>;
|
|
138
|
+
readonly data = injectDrawerData<ConfirmDrawer>();
|
|
139
|
+
readonly ref = inject(ForDrawerRef<ConfirmResult>);
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
@Component({
|
|
@@ -138,7 +147,7 @@ class DemoHost {
|
|
|
138
147
|
readonly #drawers = inject(ForDrawerManager);
|
|
139
148
|
|
|
140
149
|
async askToDelete(): Promise<void> {
|
|
141
|
-
const ref = this.#drawers.open
|
|
150
|
+
const ref = this.#drawers.open(ConfirmDrawer, {
|
|
142
151
|
data: { message: 'This action cannot be undone.' },
|
|
143
152
|
side: 'bottom',
|
|
144
153
|
snapPoints: ['148px', 1],
|
|
@@ -151,7 +160,7 @@ class DemoHost {
|
|
|
151
160
|
}
|
|
152
161
|
```
|
|
153
162
|
|
|
154
|
-
`injectDrawerData<
|
|
163
|
+
The `[FOR_DRAWER_CONTRACT]` member states, once, the data a drawer component reads and the result it closes with: `open()` infers both from the component, so a `data` of any other shape fails to compile and the ref is a `ForDrawerRef<ConfirmResult>`, and `injectDrawerData<ConfirmDrawer>()` reads the payload at that type. Declare it with `declare`, no initializer, and keep it public. Without the member `data` is unchecked, the result is `unknown`, and `injectDrawerData<T>()` with a plain type still types the payload as `T`; explicit type arguments override the contract. `injectDrawerData()` returns `null` too, because the manager provides `null` when `open()` is called without `data`, so guard (`data?.message`) before dereferencing the payload. `await ref.closed` resolves `{ reason, result }`, whose `reason` (a `ForDrawerCloseReason`) tells apart an imperative `close()` (`'programmatic'`) from Escape / backdrop / outside / swipe / close-button dismissals.
|
|
155
164
|
|
|
156
165
|
Drawers opened by the manager share the LIFO dismissible layer with declarative ones, so Escape closes the topmost drawer first whichever API opened it. Nesting is resolved from where a drawer is declared, not from the order drawers open in: a declarative `[forDrawer]` inside a programmatic drawer's component is its child (`data-depth="1"`, and the programmatic parent reflects `data-state-nested`), but a drawer the manager opens over a declarative one is a root, so it reflects `data-depth="0"` and its declarative neighbour gets no `data-state-nested`.
|
|
157
166
|
|
|
@@ -262,7 +271,7 @@ Declaratively the same recipe is the four vetoable outputs on `[forDrawer]`: `(i
|
|
|
262
271
|
|
|
263
272
|
## Examples
|
|
264
273
|
|
|
265
|
-
Open the drawer, drag it by its edge and let go past the threshold. `
|
|
274
|
+
Open the drawer, drag it by its edge and let go past the threshold. `animate.enter` / `animate.leave` drive the enter and exit, and `data-dragging` marks the gesture, so a release short of the threshold springs back through the surface's `translate` transition.
|
|
266
275
|
|
|
267
276
|
```ts
|
|
268
277
|
import { ChangeDetectionStrategy, Component, computed, signal } from '@angular/core';
|
|
@@ -454,13 +463,13 @@ Pass them in **strictly increasing** order (closest-to-edge first); in dev mode
|
|
|
454
463
|
<div
|
|
455
464
|
forDrawer
|
|
456
465
|
class="sheet"
|
|
457
|
-
[snapPoints]="['
|
|
466
|
+
[snapPoints]="['25%', '50%', 1]"
|
|
458
467
|
[(activeSnapPoint)]="snap"
|
|
459
468
|
[fadeFromIndex]="1"
|
|
460
469
|
></div>
|
|
461
470
|
```
|
|
462
471
|
|
|
463
|
-
The three snaps read peek → mid → full; `snap` is the current one, written on every release that changes the snap; the backdrop fades once the active snap reaches the second index.
|
|
472
|
+
The three snaps read peek → mid → full; `snap` is the current one, written on every release that changes the snap; the backdrop fades once the active snap reaches the second index. All three are fractions of the sheet, so their order holds at any viewport height. A `'148px'` peek beside `'50%'` would only stay below the mid snap while the sheet is taller than 296px, so the `80vh` sheet below would throw `FORCDK-DRAWER-010` on a viewport shorter than 370px.
|
|
464
473
|
|
|
465
474
|
The `model<>()` change emitter (`(activeSnapPointChange)`) fires on internal transitions (the mount-time default and every release that changes the snap), and stays silent on consumer writes through `[(activeSnapPoint)]`.
|
|
466
475
|
|
|
@@ -486,8 +495,8 @@ For a seamless release, transition **both** `translate` and your snap-position p
|
|
|
486
495
|
.sheet[data-active-snap-point][data-dragging] {
|
|
487
496
|
transition: none; /* the drag follows the pointer 1:1 */
|
|
488
497
|
}
|
|
489
|
-
.sheet[data-active-snap-point='
|
|
490
|
-
bottom:
|
|
498
|
+
.sheet[data-active-snap-point='25%'] {
|
|
499
|
+
bottom: -60vh;
|
|
491
500
|
}
|
|
492
501
|
.sheet[data-active-snap-point='50%'] {
|
|
493
502
|
bottom: -40vh;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { Directive, ElementRef, InjectionToken, Injector, afterNextRender, booleanAttribute, computed, effect, inject, input, linkedSignal, model, numberAttribute, signal } from "@angular/core";
|
|
2
|
+
import { DestroyRef, Directive, ElementRef, InjectionToken, Injector, afterNextRender, booleanAttribute, computed, effect, inject, input, linkedSignal, model, numberAttribute, signal } from "@angular/core";
|
|
3
3
|
import { Collection, FOR_PICKER_SOURCE_HOST, IdGenerator, LiveAnnouncer, adoptHostId, assertInputBound, assertRootContext, clampToBounds, compareDateOf, hostAriaLabel, hostButtonType, hostLabelledBy, injectDateAdapter, injectTextDirection, isUnset, orphanContextError, registerHandle, unsetInput } from "forty-cdk/core";
|
|
4
4
|
import { FOR_CALENDAR_DEFAULTS, FOR_CALENDAR_DEFAULTS as FOR_CALENDAR_DEFAULTS$1, provideForCalendarDefaults } from "forty-cdk/defaults";
|
|
5
5
|
import { NativeDateAdapter, provideNativeDateAdapter } from "forty-cdk/date-adapter";
|
|
@@ -766,6 +766,14 @@ var ForCalendar = class ForCalendar {
|
|
|
766
766
|
}, ...ngDevMode ? [{ debugName: "visibleMonth" }] : /* istanbul ignore next */ []);
|
|
767
767
|
#headingId = signal(this.#idGen.next("for-calendar-heading"), ...ngDevMode ? [{ debugName: "#headingId" }] : /* istanbul ignore next */ []);
|
|
768
768
|
headingId = this.#headingId.asReadonly();
|
|
769
|
+
#headingCount = signal(0, ...ngDevMode ? [{ debugName: "#headingCount" }] : /* istanbul ignore next */ []);
|
|
770
|
+
hasHeading = computed(() => this.#headingCount() > 0, ...ngDevMode ? [{ debugName: "hasHeading" }] : /* istanbul ignore next */ []);
|
|
771
|
+
registerHeading() {
|
|
772
|
+
this.#headingCount.update((count) => count + 1);
|
|
773
|
+
}
|
|
774
|
+
unregisterHeading() {
|
|
775
|
+
this.#headingCount.update((count) => count - 1);
|
|
776
|
+
}
|
|
769
777
|
adoptHeadingId(el) {
|
|
770
778
|
adoptHostId(el, this.#headingId);
|
|
771
779
|
}
|
|
@@ -1251,7 +1259,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1251
1259
|
});
|
|
1252
1260
|
var ForCalendarGrid = class ForCalendarGrid {
|
|
1253
1261
|
ctx = injectCalendarContext("ForCalendarGrid");
|
|
1254
|
-
labelledBy = hostLabelledBy(() => this.ctx.headingId());
|
|
1262
|
+
labelledBy = hostLabelledBy(() => this.ctx.hasHeading() ? this.ctx.headingId() : null);
|
|
1255
1263
|
weekDays = this.ctx.weekDays;
|
|
1256
1264
|
weeks = this.ctx.weeks;
|
|
1257
1265
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
@@ -1471,6 +1479,8 @@ var ForCalendarHeading = class ForCalendarHeading {
|
|
|
1471
1479
|
label = this.ctx.viewTriggerLabel;
|
|
1472
1480
|
constructor() {
|
|
1473
1481
|
this.ctx.adoptHeadingId(inject(ElementRef).nativeElement);
|
|
1482
|
+
this.ctx.registerHeading();
|
|
1483
|
+
inject(DestroyRef).onDestroy(() => this.ctx.unregisterHeading());
|
|
1474
1484
|
}
|
|
1475
1485
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1476
1486
|
minVersion: "12.0.0",
|
|
@@ -1652,7 +1662,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1652
1662
|
});
|
|
1653
1663
|
var ForCalendarMonthGrid = class ForCalendarMonthGrid {
|
|
1654
1664
|
ctx = injectCalendarContext("ForCalendarMonthGrid");
|
|
1655
|
-
labelledBy = hostLabelledBy(() => this.ctx.headingId());
|
|
1665
|
+
labelledBy = hostLabelledBy(() => this.ctx.hasHeading() ? this.ctx.headingId() : null);
|
|
1656
1666
|
rows = this.ctx.monthRows;
|
|
1657
1667
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1658
1668
|
minVersion: "12.0.0",
|
|
@@ -1804,7 +1814,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1804
1814
|
});
|
|
1805
1815
|
var ForCalendarYearGrid = class ForCalendarYearGrid {
|
|
1806
1816
|
ctx = injectCalendarContext("ForCalendarYearGrid");
|
|
1807
|
-
labelledBy = hostLabelledBy(() => this.ctx.headingId());
|
|
1817
|
+
labelledBy = hostLabelledBy(() => this.ctx.hasHeading() ? this.ctx.headingId() : null);
|
|
1808
1818
|
rows = this.ctx.yearRows;
|
|
1809
1819
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
1810
1820
|
minVersion: "12.0.0",
|