forty-cdk 0.29.0 → 0.30.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.
Files changed (164) hide show
  1. package/README.md +11 -8
  2. package/breadcrumbs/README.md +1 -1
  3. package/breakpoints/README.md +1 -1
  4. package/calendar/README.md +1 -1
  5. package/date-adapter/README.md +48 -0
  6. package/date-field/README.md +1 -1
  7. package/date-picker/README.md +6 -3
  8. package/defaults/README.md +100 -0
  9. package/drawer/README.md +1 -1
  10. package/fesm2022/forty-cdk-avatar.mjs +4 -26
  11. package/fesm2022/forty-cdk-avatar.mjs.map +1 -1
  12. package/fesm2022/forty-cdk-breadcrumbs.mjs +4 -26
  13. package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -1
  14. package/fesm2022/forty-cdk-breakpoints.mjs +5 -52
  15. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -1
  16. package/fesm2022/forty-cdk-calendar.mjs +6 -167
  17. package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
  18. package/fesm2022/forty-cdk-carousel.mjs +4 -37
  19. package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
  20. package/fesm2022/forty-cdk-combobox.mjs +4 -35
  21. package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
  22. package/fesm2022/forty-cdk-context-menu.mjs +4 -30
  23. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
  24. package/fesm2022/forty-cdk-core-overlay.mjs +1 -109
  25. package/fesm2022/forty-cdk-core-overlay.mjs.map +1 -1
  26. package/fesm2022/forty-cdk-core.mjs +3 -114
  27. package/fesm2022/forty-cdk-core.mjs.map +1 -1
  28. package/fesm2022/forty-cdk-date-adapter.mjs +188 -0
  29. package/fesm2022/forty-cdk-date-adapter.mjs.map +1 -0
  30. package/fesm2022/forty-cdk-date-field.mjs +4 -87
  31. package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
  32. package/fesm2022/forty-cdk-date-picker.mjs +16 -70
  33. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
  34. package/fesm2022/forty-cdk-defaults.mjs +1506 -0
  35. package/fesm2022/forty-cdk-defaults.mjs.map +1 -0
  36. package/fesm2022/forty-cdk-dialog.mjs +4 -34
  37. package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
  38. package/fesm2022/forty-cdk-drag-drop.mjs +4 -32
  39. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
  40. package/fesm2022/forty-cdk-drawer.mjs +4 -46
  41. package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
  42. package/fesm2022/forty-cdk-dropdown-menu.mjs +4 -30
  43. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
  44. package/fesm2022/forty-cdk-file-upload.mjs +14 -3
  45. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
  46. package/fesm2022/forty-cdk-hover-card.mjs +6 -56
  47. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
  48. package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
  49. package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
  50. package/fesm2022/forty-cdk-listbox.mjs +4 -36
  51. package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
  52. package/fesm2022/forty-cdk-menu.mjs +4 -33
  53. package/fesm2022/forty-cdk-menu.mjs.map +1 -1
  54. package/fesm2022/forty-cdk-menubar.mjs +4 -30
  55. package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
  56. package/fesm2022/forty-cdk-navigation-menu.mjs +5 -29
  57. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
  58. package/fesm2022/forty-cdk-number-input.mjs +4 -26
  59. package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
  60. package/fesm2022/forty-cdk-pagination.mjs +4 -27
  61. package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
  62. package/fesm2022/forty-cdk-popover.mjs +4 -30
  63. package/fesm2022/forty-cdk-popover.mjs.map +1 -1
  64. package/fesm2022/forty-cdk-progress.mjs +4 -27
  65. package/fesm2022/forty-cdk-progress.mjs.map +1 -1
  66. package/fesm2022/forty-cdk-radio-group.mjs +4 -26
  67. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
  68. package/fesm2022/forty-cdk-scroll-area.mjs +4 -29
  69. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -1
  70. package/fesm2022/forty-cdk-search.mjs +4 -26
  71. package/fesm2022/forty-cdk-search.mjs.map +1 -1
  72. package/fesm2022/forty-cdk-select.mjs +4 -29
  73. package/fesm2022/forty-cdk-select.mjs.map +1 -1
  74. package/fesm2022/forty-cdk-shared.mjs +2 -1
  75. package/fesm2022/forty-cdk-shared.mjs.map +1 -1
  76. package/fesm2022/forty-cdk-slider.mjs +4 -26
  77. package/fesm2022/forty-cdk-slider.mjs.map +1 -1
  78. package/fesm2022/forty-cdk-stepper.mjs +4 -29
  79. package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
  80. package/fesm2022/forty-cdk-table-virtualization.mjs +6 -0
  81. package/fesm2022/forty-cdk-table-virtualization.mjs.map +1 -1
  82. package/fesm2022/forty-cdk-table.mjs +166 -83
  83. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  84. package/fesm2022/forty-cdk-tabs.mjs +4 -27
  85. package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
  86. package/fesm2022/forty-cdk-testing.mjs +381 -0
  87. package/fesm2022/forty-cdk-testing.mjs.map +1 -0
  88. package/fesm2022/forty-cdk-time-field.mjs +4 -81
  89. package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
  90. package/fesm2022/forty-cdk-time-picker.mjs +14 -36
  91. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
  92. package/fesm2022/forty-cdk-toast.mjs +4 -36
  93. package/fesm2022/forty-cdk-toast.mjs.map +1 -1
  94. package/fesm2022/forty-cdk-toggle.mjs +4 -27
  95. package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
  96. package/fesm2022/forty-cdk-toolbar.mjs +4 -26
  97. package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
  98. package/fesm2022/forty-cdk-tooltip.mjs +6 -80
  99. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
  100. package/fesm2022/forty-cdk-tree.mjs +71 -63
  101. package/fesm2022/forty-cdk-tree.mjs.map +1 -1
  102. package/file-upload/README.md +20 -12
  103. package/hover-card/README.md +1 -1
  104. package/internationalized-date/README.md +1 -1
  105. package/listbox/README.md +1 -1
  106. package/menu/README.md +2 -2
  107. package/package.json +13 -1
  108. package/popover/README.md +1 -1
  109. package/select/README.md +1 -1
  110. package/shared/README.md +14 -14
  111. package/table/README.md +120 -30
  112. package/table-virtualization/README.md +24 -3
  113. package/testing/README.md +97 -0
  114. package/time-field/README.md +1 -1
  115. package/time-picker/README.md +19 -19
  116. package/toast/README.md +1 -1
  117. package/tooltip/README.md +1 -1
  118. package/tree/README.md +44 -23
  119. package/types/forty-cdk-avatar.d.ts +4 -31
  120. package/types/forty-cdk-breadcrumbs.d.ts +2 -31
  121. package/types/forty-cdk-breakpoints.d.ts +5 -56
  122. package/types/forty-cdk-calendar.d.ts +7 -109
  123. package/types/forty-cdk-carousel.d.ts +6 -92
  124. package/types/forty-cdk-combobox.d.ts +6 -96
  125. package/types/forty-cdk-context-menu.d.ts +7 -58
  126. package/types/forty-cdk-core-overlay.d.ts +2 -94
  127. package/types/forty-cdk-core.d.ts +13 -268
  128. package/types/forty-cdk-date-adapter.d.ts +296 -0
  129. package/types/forty-cdk-date-field.d.ts +6 -130
  130. package/types/forty-cdk-date-picker.d.ts +18 -104
  131. package/types/forty-cdk-defaults.d.ts +2125 -0
  132. package/types/forty-cdk-dialog.d.ts +3 -69
  133. package/types/forty-cdk-drag-drop.d.ts +5 -62
  134. package/types/forty-cdk-drawer.d.ts +6 -127
  135. package/types/forty-cdk-dropdown-menu.d.ts +6 -56
  136. package/types/forty-cdk-file-upload.d.ts +30 -20
  137. package/types/forty-cdk-hover-card.d.ts +8 -80
  138. package/types/forty-cdk-internationalized-date.d.ts +1 -1
  139. package/types/forty-cdk-listbox.d.ts +4 -51
  140. package/types/forty-cdk-menu.d.ts +5 -88
  141. package/types/forty-cdk-menubar.d.ts +7 -55
  142. package/types/forty-cdk-navigation-menu.d.ts +4 -33
  143. package/types/forty-cdk-number-input.d.ts +4 -29
  144. package/types/forty-cdk-pagination.d.ts +4 -27
  145. package/types/forty-cdk-popover.d.ts +7 -55
  146. package/types/forty-cdk-progress.d.ts +4 -38
  147. package/types/forty-cdk-radio-group.d.ts +4 -29
  148. package/types/forty-cdk-scroll-area.d.ts +5 -53
  149. package/types/forty-cdk-search.d.ts +5 -31
  150. package/types/forty-cdk-select.d.ts +7 -50
  151. package/types/forty-cdk-shared.d.ts +2 -1
  152. package/types/forty-cdk-slider.d.ts +4 -29
  153. package/types/forty-cdk-stepper.d.ts +5 -50
  154. package/types/forty-cdk-table-virtualization.d.ts +3 -1
  155. package/types/forty-cdk-table.d.ts +96 -20
  156. package/types/forty-cdk-tabs.d.ts +5 -37
  157. package/types/forty-cdk-testing.d.ts +200 -0
  158. package/types/forty-cdk-time-field.d.ts +6 -130
  159. package/types/forty-cdk-time-picker.d.ts +14 -57
  160. package/types/forty-cdk-toast.d.ts +6 -86
  161. package/types/forty-cdk-toggle.d.ts +4 -34
  162. package/types/forty-cdk-toolbar.d.ts +4 -29
  163. package/types/forty-cdk-tooltip.d.ts +8 -117
  164. package/types/forty-cdk-tree.d.ts +57 -65
package/README.md CHANGED
@@ -28,7 +28,7 @@ Optional — install only if you use the matching entry point / primitives:
28
28
  | Peer | Needed by |
29
29
  | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
30
  | `@angular/forms` `^22.0.1` | Form-control primitives (`Switch`, `Checkbox`, `RadioGroup`, `Listbox`, `Select`, `Slider`, `Combobox`, …). They implement `FormValueControl` / `FormCheckboxControl` from `@angular/forms/signals` for `[formField]` auto-wiring. The contract is type-only, so the published bundle never references the package — consumers using only non-form primitives can skip it. |
31
- | `@internationalized/date` `^3.0.0` | The `forty-cdk/internationalized-date` entry point (`InternationalizedDateAdapter`, `InternationalizedDateTimeAdapter`). The date/time primitives themselves depend only on the abstract `DateAdapter` contract from `forty-cdk/shared` — install this peer only when you import that entry point. |
31
+ | `@internationalized/date` `^3.0.0` | The `forty-cdk/internationalized-date` entry point (`InternationalizedDateAdapter`, `InternationalizedDateTimeAdapter`). The date/time primitives themselves depend only on the abstract `DateAdapter` contract from `forty-cdk/date-adapter` — install this peer only when you import that entry point. |
32
32
 
33
33
  ### Regular dependencies
34
34
 
@@ -41,15 +41,18 @@ Two packages are regular dependencies, installed automatically and never declare
41
41
 
42
42
  ## Entry points
43
43
 
44
- **The package name itself exports nothing.** `import { … } from 'forty-cdk'` resolves to no symbol, and your editor will not auto-import anything under the bare package name — by design, so that every symbol has exactly one import path. There are three specifiers you do import from:
44
+ **The package name itself exports nothing.** `import { … } from 'forty-cdk'` resolves to no symbol, and your editor will not auto-import anything under the bare package name — by design, so that the bare name never becomes a second import path for a symbol. There are six specifiers you do import from:
45
45
 
46
- | Specifier | What it exports |
47
- | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
48
- | `forty-cdk/<primitive>` | The primitive's directives, components, context tokens and defaults provider — `ForDialog` from `forty-cdk/dialog`, `ForAccordion` from `forty-cdk/accordion`, and so on for every entry in the tables below. |
49
- | [`forty-cdk/shared`](shared) | The cross-primitive contract types a primitive's public API references — `WritingDirection`, `VetoableEvent`, `DateAdapter`, `FloatingSide`, … — declared once and published once. Eight ship from their own primitive instead; that README names them. |
50
- | `forty-cdk/internationalized-date` | The `@internationalized/date` adapters, kept apart so that optional peer stays genuinely optional. |
46
+ | Specifier | What it exports |
47
+ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
48
+ | `forty-cdk/<primitive>` | The primitive's directives, components, context tokens and defaults provider — `ForDialog` from `forty-cdk/dialog`, `ForAccordion` from `forty-cdk/accordion`, and so on for every entry in the tables below. |
49
+ | [`forty-cdk/shared`](shared) | The cross-primitive contract types a primitive's public API references — `WritingDirection`, `VetoableEvent`, `DateAdapter`, `FloatingSide`, … — declared once and published once. Eight ship from their own primitive instead; that README names them. |
50
+ | `forty-cdk/internationalized-date` | The `@internationalized/date` adapters, kept apart so that optional peer stays genuinely optional. |
51
+ | [`forty-cdk/defaults`](defaults) | Every `provideFor<Primitive>Defaults` with its token and interface. Each primitive re-exports its own pair too, but a root provider imported from the primitive loads the primitive at startup; imported from here, it does not. |
52
+ | [`forty-cdk/date-adapter`](date-adapter) | The `DateAdapter` contract, the `FOR_DATE_ADAPTER` token and `provideNativeDateAdapter`. `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. |
53
+ | [`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. |
51
54
 
52
- `forty-cdk/core` and `forty-cdk/core-overlay` resolve too, but neither 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 two rather than one for a bundling reason you get for free: a published module is a bundler's chunk-splitting unit, so keeping the positioning engine (`@floating-ui/dom` and the overlay shells) in its own module means a lazy route that renders no overlay does not load it. Measured on a seven-lazy-route app, that 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 three specifiers above, it is internal by design — [open an issue](https://github.com/tutkli/forty-cdk/issues) rather than importing from either.
55
+ `forty-cdk/core` and `forty-cdk/core-overlay` resolve too, but neither 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 two rather than one for a bundling reason you get for free: a published module is a bundler's chunk-splitting unit, so keeping the positioning engine (`@floating-ui/dom` and the overlay shells) in its own module means a lazy route that renders no overlay does not load it. Measured on a seven-lazy-route app, that 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 six specifiers above, it is internal by design — [open an issue](https://github.com/tutkli/forty-cdk/issues) rather than importing from either.
53
56
 
54
57
  ## Errors
55
58
 
@@ -93,7 +93,7 @@ The primitive renders whatever items you give it, so collapsing a deep path is a
93
93
  <!-- snippet: fragment -->
94
94
 
95
95
  ```ts
96
- import { provideForBreadcrumbsDefaults } from 'forty-cdk/breadcrumbs';
96
+ import { provideForBreadcrumbsDefaults } from 'forty-cdk/defaults';
97
97
 
98
98
  bootstrapApplication(App, {
99
99
  providers: [provideForBreadcrumbsDefaults({ label: 'Ruta de navegación' })],
@@ -16,7 +16,7 @@ Configuring is optional: without a provider the Tailwind scale (`sm` 640, `md` 7
16
16
 
17
17
  ```ts
18
18
  import { ApplicationConfig } from '@angular/core';
19
- import { provideForBreakpointsDefaults } from 'forty-cdk/breakpoints';
19
+ import { provideForBreakpointsDefaults } from 'forty-cdk/defaults';
20
20
 
21
21
  export const appConfig: ApplicationConfig = {
22
22
  providers: [
@@ -535,7 +535,7 @@ Auto-disabled when the entire previous / next page would be outside `[min, max]`
535
535
  ## Scoped defaults
536
536
 
537
537
  ```ts
538
- import { provideForCalendarDefaults } from 'forty-cdk/calendar';
538
+ import { provideForCalendarDefaults } from 'forty-cdk/defaults';
539
539
 
540
540
  // app config or a component's providers — Monday-first weeks for this scope
541
541
  providers: [
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: Date adapter
3
+ group: utilities
4
+ archetype: [headless-utility]
5
+ ---
6
+
7
+ # Date adapter
8
+
9
+ The `DateAdapter` contract, the `FOR_DATE_ADAPTER` token that provides one, and the zero-dependency `NativeDateAdapter`, imported from `forty-cdk/date-adapter` so that providing an adapter at the application root does not load the date and time primitives, or the code they share, before bootstrap.
10
+
11
+ Every date and time primitive (Calendar, Date Field, Date Picker, Time Field, Time Picker) delegates its arithmetic and formatting to the adapter in scope, and none ships a default. Provide one where the [date adapters guide](../../../docs/date-adapters.md) says to, usually the application config:
12
+
13
+ ```ts
14
+ import { type ApplicationConfig } from '@angular/core';
15
+ import { provideNativeDateAdapter } from 'forty-cdk/date-adapter';
16
+
17
+ export const appConfig: ApplicationConfig = {
18
+ providers: [provideNativeDateAdapter()],
19
+ };
20
+ ```
21
+
22
+ `forty-cdk/shared` re-exports the same objects, and `forty-cdk/calendar` re-exports the native pair, so an import from either keeps compiling and binds the same token. Only the import from this entry point keeps the root provider cheap.
23
+
24
+ ## Why this exists
25
+
26
+ A bundler splits code into chunks one module at a time, and each forty-cdk entry point is published as a single module. A root `providers` array is in the static graph of your `main` bundle, so the module it imports from is loaded before the application bootstraps, on every route. This entry point imports nothing but `@angular/core`, so the code the primitives share stays in the lazy chunks of the routes that render them. `provideInternationalizedDateAdapter()` and `provideInternationalizedDateTimeAdapter()` read the token and the formatter cache from here too.
27
+
28
+ Measured on a production build of an app with four lazy routes (Tooltip, Calendar, Combobox, Menu) and `provideForTooltipDefaults`, `provideForCalendarDefaults`, `provideForComboboxDefaults` and `provideForMenuDefaults` from [`forty-cdk/defaults`](../defaults) at the root:
29
+
30
+ | Also at the root | `main` size (raw / gzip) | Shared chunks the lazy routes load |
31
+ | -------------------------------------------------------------------------- | ------------------------ | ---------------------------------- |
32
+ | No date adapter | 209,552 B / 63,580 B | two |
33
+ | A `NativeDateAdapter` subclass, imported from `forty-cdk/date-adapter` | 212,012 B / 64,263 B | two |
34
+ | A hand-written adapter on `FOR_DATE_ADAPTER` from `forty-cdk/date-adapter` | 209,723 B / 63,610 B | two |
35
+ | The same subclass, imported from `forty-cdk/shared` | 246,013 B / 74,209 B | none: they moved into `main` |
36
+
37
+ ## API
38
+
39
+ | Export | Kind | Contract |
40
+ | ---------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | `DateAdapter<D>` | interface | The date-library seam every date and time primitive calls. Its members, and the Gregorian and locale limits, are in the [date adapters guide](../../../docs/date-adapters.md#writing-your-own-adapter). |
42
+ | `TimeCapableDateAdapter<D>` | type | A `DateAdapter<D>` that implements the optional `getHours` / `getMinutes` / `getSeconds` / `setTime`. The time primitives require one. |
43
+ | `FOR_DATE_ADAPTER` | token | Holds the active adapter. A date or time primitive with no adapter in scope throws `FORCDK-CORE-002`. |
44
+ | `NativeDateAdapter` | class | A time-capable adapter over the built-in `Date`. Subclass it and override `locale()` to format in the app's language. |
45
+ | `provideNativeDateAdapter()` | provider | Binds `NativeDateAdapter` to `FOR_DATE_ADAPTER`. |
46
+ | `createFormatterCache()` | function | Returns a function that hands back one `Intl.DateTimeFormat` per distinct `(locale, options)` pair, for an adapter of your own to format through. |
47
+
48
+ `injectDateAdapter` and `assertTimeCapable` are not here: they throw through the library's error helpers, which live with the primitives, and ship from [`forty-cdk/shared`](../shared).
@@ -205,7 +205,7 @@ On the AM/PM segment, `a` / `p` set the period and ArrowUp / ArrowDown toggle it
205
205
  ## Scoped defaults
206
206
 
207
207
  ```ts
208
- import { provideForDateFieldDefaults } from 'forty-cdk/date-field';
208
+ import { provideForDateFieldDefaults } from 'forty-cdk/defaults';
209
209
 
210
210
  // app config or a component's providers — localize segment labels, the
211
211
  // empty-segment announcement and the placeholders for every nested [forDateField].
@@ -207,7 +207,8 @@ Open the picker and click a first day: the trigger keeps its placeholder, becaus
207
207
  | `closeOnSelect` | `input<boolean>` | Close the surface after a date is picked. Honoured at `granularity="day"`, and at any granularity with `anatomy="field"`.<br>**Default:** `true` |
208
208
  | `anatomy` | `input<'trigger' \| 'field'>` | Which piece is the control. `'field'` makes a projected `[forDateField]` the control and the trigger a plain button; see [Field anatomy](#field-anatomy).<br>**Default:** `'trigger'` |
209
209
  | `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision. `'day'` (default) is a pure date picker; coarser-than-day off composes a time field.<br>**Default:** `'day'` |
210
- | `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the value display (and typically the projected `[forTimeField]`).<br>**Default:** `null` → the scope's `hourCycle` (`provideForDatePickerDefaults`), then the locale |
210
+ | `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the value display; a projected time field takes `resolvedHourCycle()`.<br>**Default:** `null` → the scope's `hourCycle` (`provideForDatePickerDefaults`), then the locale |
211
+ | `resolvedHourCycle` | `Signal<12 \| 24 \| null>` | The effective cycle: `hourCycle`, then the scope's, or `null` for the locale. Bind it to a projected time field's `[hourCycle]`.<br>**Default:** — |
211
212
  | `modal` | `input<boolean>` | Trap focus + inert background + scroll lock (centered dialog) instead of an anchored popover.<br>**Default:** `false` |
212
213
  | `dismissible` | `input<boolean>` | Escape / outside-pointer dismiss the surface.<br>**Default:** `true` |
213
214
  | `returnFocus` | `input<boolean>` | Return focus to the trigger on close.<br>**Default:** `true` |
@@ -314,7 +315,7 @@ The content is a field boundary. Inside a [`[forField]`](../field/README.md#how-
314
315
  <div
315
316
  forTimeField
316
317
  [value]="picker.value()"
317
- [hourCycle]="picker.hourCycle()"
318
+ [hourCycle]="picker.resolvedHourCycle()"
318
319
  #field="forTimeField"
319
320
  >
320
321
  @for (seg of field.segments(); track seg.id) { @if (seg.isLiteral) {
@@ -328,7 +329,9 @@ The content is a field boundary. Inside a [`[forField]`](../field/README.md#how-
328
329
  </div>
329
330
  ```
330
331
 
331
- The value display (`[forDatePickerValue]`) automatically appends the time to its formatting when `granularity > 'day'` and you haven't set time fields in `formatOptions`.
332
+ The value display (`[forDatePickerValue]`) automatically appends a two-digit hour and minute to its formatting when `granularity > 'day'` and you haven't set time fields in `formatOptions`, so it matches the projected `[forTimeField]`. The resolved `hourCycle` applies to that default and to time fields you set yourself, unless `formatOptions` sets `hour12` or `hourCycle`.
333
+
334
+ Bind the time field's `[hourCycle]` to `picker.resolvedHourCycle()`, not `picker.hourCycle()`. The raw input is `null` when the cycle comes from `provideForDatePickerDefaults`, and the time field would then fall back to its own defaults and the locale, so an `en-US` browser would show `14:30` on the trigger and `2:30 PM` in the panel. The same applies to a projected `[forTimePicker]`.
332
335
 
333
336
  ## Field anatomy
334
337
 
@@ -0,0 +1,100 @@
1
+ ---
2
+ title: Defaults
3
+ group: utilities
4
+ archetype: [headless-utility]
5
+ ---
6
+
7
+ # Defaults
8
+
9
+ Every `provideFor<Primitive>Defaults` provider, its `FOR_<PRIMITIVE>_DEFAULTS` token and its `For<Primitive>Defaults` interface, imported from `forty-cdk/defaults` so that configuring a primitive at the application root does not load the primitive itself.
10
+
11
+ Each primitive's entry point still re-exports its own pair, so `import { provideForTooltipDefaults } from 'forty-cdk/tooltip'` keeps compiling. Both paths name the same token object, so a provider from one and a primitive reading the other share one scope.
12
+
13
+ ```ts
14
+ import { type ApplicationConfig } from '@angular/core';
15
+ import { provideForCalendarDefaults, provideForTooltipDefaults } from 'forty-cdk/defaults';
16
+
17
+ export const appConfig: ApplicationConfig = {
18
+ providers: [
19
+ provideForTooltipDefaults({ openDelay: 500 }),
20
+ provideForCalendarDefaults({ firstDayOfWeek: 1 }),
21
+ ],
22
+ };
23
+ ```
24
+
25
+ ## Why this exists
26
+
27
+ A bundler splits code into chunks one module at a time, and each forty-cdk entry point is published as a single module. A root `providers` array is in the static graph of your `main` bundle, so whatever module it imports is loaded before the application bootstraps, on every route, including the ones that never render that primitive.
28
+
29
+ Importing a provider from the primitive's own entry point therefore puts that primitive's directives in `main`. The re-export does not change it, because the bundler still has to load the module the re-export lives in. `forty-cdk/defaults` holds only the pairs and imports nothing but `@angular/core`, so the directives stay in the lazy chunks of the routes that render them.
30
+
31
+ Measured on a production build of an app with four lazy routes and these providers at the root, `provideForTooltipDefaults`, `provideForCalendarDefaults`, `provideForComboboxDefaults` and `provideForMenuDefaults`:
32
+
33
+ | Imported from | `main` holds | `main` size (raw / gzip) |
34
+ | ---------------------- | ----------------------------------------------------- | ------------------------ |
35
+ | Each primitive's entry | the Tooltip, Calendar, Combobox and Menu directives | 281,035 B / 83,020 B |
36
+ | `forty-cdk/defaults` | the four defaults pairs and the tooltip's coordinator | 209,552 B / 63,578 B |
37
+ | No root providers | nothing from forty-cdk | 206,303 B / 62,301 B |
38
+
39
+ A pair you do not import is not bundled: every token is a top-level `new InjectionToken(...)`, which a bundler drops when nothing reads it.
40
+
41
+ A date adapter follows the same rule from [`forty-cdk/date-adapter`](../date-adapter): `NativeDateAdapter`, `provideNativeDateAdapter` and the `FOR_DATE_ADAPTER` token they provide import nothing but `@angular/core`, so providing one next to these pairs keeps the primitives and the code they share out of `main`.
42
+
43
+ ## API
44
+
45
+ Each pair resolves the same way: a scope's overrides merge key by key over its parent scope's, and the library fallback fills the rest. A key set to `undefined` is treated as omitted. Pass a function instead of an object to build the overrides where `inject()` is available; it runs once per injector that resolves the token. The primitive's README documents its keys.
46
+
47
+ | Configures | Provider | Token | Also exported |
48
+ | --------------- | ----------------------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------- |
49
+ | avatar | `provideForAvatarDefaults` | `FOR_AVATAR_DEFAULTS` | `ForAvatarDefaults` |
50
+ | breadcrumbs | `provideForBreadcrumbsDefaults` | `FOR_BREADCRUMBS_DEFAULTS` | `ForBreadcrumbsDefaults` |
51
+ | breakpoints | `provideForBreakpointsDefaults` | `FOR_BREAKPOINTS_DEFAULTS` | `BreakpointMap`, `ForBreakpointsDefaults`, `forBreakpointsTailwind`, `TailwindBreakpointName` |
52
+ | calendar | `provideForCalendarDefaults` | `FOR_CALENDAR_DEFAULTS` | `ForCalendarDefaults` |
53
+ | carousel | `provideForCarouselDefaults` | `FOR_CAROUSEL_DEFAULTS` | `CarouselAlign`, `ForCarouselDefaults` |
54
+ | combobox | `provideForComboboxDefaults` | `FOR_COMBOBOX_DEFAULTS` | `ForComboboxDefaults`, `ForComboboxOpenHighlight` |
55
+ | context-menu | `provideForContextMenuDefaults` | `FOR_CONTEXT_MENU_DEFAULTS` | `ForContextMenuDefaults` |
56
+ | date-field | `provideForDateFieldDefaults` | `FOR_DATE_FIELD_DEFAULTS` | `DEFAULT_DATE_FIELD_SEGMENT_LABELS`, `ForDateFieldDefaults`, `ForDateFieldSegmentLabels` |
57
+ | date-picker | `provideForDatePickerDefaults` | `FOR_DATE_PICKER_DEFAULTS` | `ForDatePickerDefaults` |
58
+ | date-field | `provideForDateRangeFieldDefaults` | `FOR_DATE_RANGE_FIELD_DEFAULTS` | `DEFAULT_DATE_RANGE_FIELD_SEGMENT_LABELS`, `ForDateRangeFieldDefaults`, `ForDateRangeFieldSegmentLabels` |
59
+ | date-picker | `provideForDateRangePickerDefaults` | `FOR_DATE_RANGE_PICKER_DEFAULTS` | `ForDateRangePickerDefaults` |
60
+ | dialog | `provideForDialogDefaults` | `FOR_DIALOG_DEFAULTS` | `ForDialogDefaults` |
61
+ | drag-drop | `provideForDragDropDefaults` | `FOR_DRAG_DROP_DEFAULTS` | `ForDragDropDefaults` |
62
+ | drawer | `provideForDrawerDefaults` | `FOR_DRAWER_DEFAULTS` | `ForDrawerDefaults` |
63
+ | dropdown-menu | `provideForDropdownMenuDefaults` | `FOR_DROPDOWN_MENU_DEFAULTS` | `ForDropdownMenuDefaults` |
64
+ | hover-card | `provideForHoverCardDefaults` | `FOR_HOVER_CARD_DEFAULTS` | `ForHoverCardDefaults`, `HoverCardCoordinator`, `HoverCardDefaultsOptions` |
65
+ | listbox | `provideForListboxDefaults` | `FOR_LISTBOX_DEFAULTS` | `ForListboxDefaults` |
66
+ | menubar | `provideForMenubarDefaults` | `FOR_MENUBAR_DEFAULTS` | `ForMenubarDefaults` |
67
+ | menu | `provideForMenuDefaults` | `FOR_MENU_DEFAULTS` | `ForMenuDefaults` |
68
+ | navigation-menu | `provideForNavigationMenuDefaults` | `FOR_NAVIGATION_MENU_DEFAULTS` | `ForNavigationMenuDefaults` |
69
+ | number-input | `provideForNumberInputDefaults` | `FOR_NUMBER_INPUT_DEFAULTS` | `ForNumberInputDefaults` |
70
+ | pagination | `provideForPaginationDefaults` | `FOR_PAGINATION_DEFAULTS` | `ForPaginationDefaults` |
71
+ | popover | `provideForPopoverDefaults` | `FOR_POPOVER_DEFAULTS` | `ForPopoverDefaults` |
72
+ | progress | `provideForProgressDefaults` | `FOR_PROGRESS_DEFAULTS` | `ForProgressDefaults` |
73
+ | radio-group | `provideForRadioGroupDefaults` | `FOR_RADIO_GROUP_DEFAULTS` | `ForRadioGroupDefaults` |
74
+ | scroll-area | `provideForScrollAreaDefaults` | `FOR_SCROLL_AREA_DEFAULTS` | `ForScrollAreaDefaults`, `ForScrollAreaTrackPress` |
75
+ | search | `provideForSearchDefaults` | `FOR_SEARCH_DEFAULTS` | `ForSearchDefaults` |
76
+ | select | `provideForSelectDefaults` | `FOR_SELECT_DEFAULTS` | `ForSelectDefaults` |
77
+ | slider | `provideForSliderDefaults` | `FOR_SLIDER_DEFAULTS` | `ForSliderDefaults` |
78
+ | stepper | `provideForStepperDefaults` | `FOR_STEPPER_DEFAULTS` | `ForStepperDefaults`, `StepperActivationMode` |
79
+ | tabs | `provideForTabsDefaults` | `FOR_TABS_DEFAULTS` | `ForTabsDefaults`, `TabsActivationMode` |
80
+ | time-field | `provideForTimeFieldDefaults` | `FOR_TIME_FIELD_DEFAULTS` | `DEFAULT_TIME_FIELD_SEGMENT_LABELS`, `ForTimeFieldDefaults`, `ForTimeFieldSegmentLabels` |
81
+ | time-picker | `provideForTimePickerDefaults` | `FOR_TIME_PICKER_DEFAULTS` | `ForTimePickerDefaults` |
82
+ | time-field | `provideForTimeRangeFieldDefaults` | `FOR_TIME_RANGE_FIELD_DEFAULTS` | `DEFAULT_TIME_RANGE_FIELD_SEGMENT_LABELS`, `ForTimeRangeFieldDefaults`, `ForTimeRangeFieldSegmentLabels` |
83
+ | toast | `provideForToastDefaults` | `FOR_TOAST_DEFAULTS` | `ForToastDefaults`, `ForToastStackShift` |
84
+ | toggle | `provideForToggleDefaults` | `FOR_TOGGLE_DEFAULTS` | `ForToggleDefaults` |
85
+ | toolbar | `provideForToolbarDefaults` | `FOR_TOOLBAR_DEFAULTS` | `ForToolbarDefaults` |
86
+ | tooltip | `provideForTooltipDefaults` | `FOR_TOOLTIP_DEFAULTS` | `ForTooltipDefaults`, `TooltipCoordinator`, `TooltipDefaultsOptions` |
87
+ | tree | `provideForTreeDefaults` | `FOR_TREE_DEFAULTS` | `ForTreeDefaults` |
88
+
89
+ `forBreakpointsTailwind` is the default breakpoint map, the Tailwind CSS scale value for value. `provideForBreakpointsDefaults` takes a whole map and replaces it rather than merging key by key.
90
+
91
+ ### Skip-delay windows
92
+
93
+ Tooltips and hover cards share a skip-delay window per scope: right after one closes, the next one in the same window opens without its open delay. `provideForTooltipDefaults` and `provideForHoverCardDefaults` take a second `options` argument whose `skipDelayScope` decides whether the scope starts its own window (`'own'`) or shares its parent's (`'inherit'`).
94
+
95
+ | Export | What it is |
96
+ | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
97
+ | `TooltipCoordinator` | The window of a tooltip scope. Inject it to read `skipDelay()` or to call `startSkipDelay()` / `cancelSkipDelay()` from your own code. |
98
+ | `HoverCardCoordinator` | The same window for hover cards. It is never shared with a tooltip scope. |
99
+ | `createSkipDelayWindow(duration)` | A standalone window, owned by its caller: `active` reads `true` from `start()` until `duration()` ms later, or until `cancel()`. |
100
+ | `SkipDelayWindow` | The handle `createSkipDelayWindow` returns. |
package/drawer/README.md CHANGED
@@ -589,7 +589,7 @@ Always nest the child's `@if` inside the parent's `@if`. That guarantees Angular
589
589
  <!-- snippet: fragment -->
590
590
 
591
591
  ```ts
592
- import { provideForDrawerDefaults } from 'forty-cdk/drawer';
592
+ import { provideForDrawerDefaults } from 'forty-cdk/defaults';
593
593
 
594
594
  bootstrapApplication(App, {
595
595
  providers: [
@@ -1,7 +1,9 @@
1
1
  import { isPlatformBrowser } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
3
  import { InjectionToken, inject, input, numberAttribute, signal, linkedSignal, PLATFORM_ID, computed, effect, DestroyRef, Directive, ElementRef, output, afterNextRender } from '@angular/core';
4
- import { orphanContextError, assertRootContext, createDefaults } from 'forty-cdk/core';
4
+ import { orphanContextError, assertRootContext } from 'forty-cdk/core';
5
+ import { FOR_AVATAR_DEFAULTS } from 'forty-cdk/defaults';
6
+ export { FOR_AVATAR_DEFAULTS, provideForAvatarDefaults } from 'forty-cdk/defaults';
5
7
 
6
8
 
7
9
 
@@ -77,30 +79,6 @@ function injectAvatarImageContext() {
77
79
 
78
80
 
79
81
 
80
- const FOR_AVATAR_FALLBACK_DEFAULTS = {
81
- fallbackDelayMs: 0,
82
- };
83
- const { token, provideDefaults } = createDefaults('FOR_AVATAR_DEFAULTS', FOR_AVATAR_FALLBACK_DEFAULTS);
84
-
85
- const FOR_AVATAR_DEFAULTS = token;
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
- function provideForAvatarDefaults(defaults = {}) {
96
- return provideDefaults(defaults);
97
- }
98
-
99
-
100
-
101
-
102
-
103
-
104
82
 
105
83
 
106
84
 
@@ -373,5 +351,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
373
351
 
374
352
 
375
353
 
376
- export { FOR_AVATAR_CONTEXT, FOR_AVATAR_DEFAULTS, ForAvatar, ForAvatarFallback, ForAvatarImage, provideForAvatarDefaults };
354
+ export { FOR_AVATAR_CONTEXT, ForAvatar, ForAvatarFallback, ForAvatarImage };
377
355
  //# sourceMappingURL=forty-cdk-avatar.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"forty-cdk-avatar.mjs","sources":["../../../projects/forty-cdk/avatar/src/avatar-context.ts","../../../projects/forty-cdk/avatar/src/avatar-defaults.ts","../../../projects/forty-cdk/avatar/src/avatar.ts","../../../projects/forty-cdk/avatar/src/avatar-image.ts","../../../projects/forty-cdk/avatar/src/avatar-fallback.ts","../../../projects/forty-cdk/avatar/src/forty-cdk-avatar.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\nimport { assertRootContext, orphanContextError } from 'forty-cdk/core';\n\n/**\n * Lifecycle stages of the avatar's image:\n * - `idle`: no `src` set yet.\n * - `loading`: `src` set, waiting for the network.\n * - `loaded`: image rendered successfully.\n * - `error`: image failed to load.\n */\nexport type ForAvatarStatus = 'idle' | 'loading' | 'loaded' | 'error';\n\n/** Read surface `[forAvatar]` publishes through {@link FOR_AVATAR_CONTEXT}. */\nexport interface ForAvatarContext {\n /** Current status of the avatar's image. */\n readonly status: Signal<ForAvatarStatus>;\n /**\n * `true` when the consumer should render the fallback. Drives `@if` in\n * the consumer's template. Honors `fallbackDelayMs` for `idle` / `loading`\n * states; `error` flips it to `true` immediately.\n */\n readonly shouldShowFallback: Signal<boolean>;\n}\n\n/**\n * The avatar's piece-coordination surface: the one call `[forAvatarImage]`\n * makes so the root can transition its state machine.\n *\n * **Not** part of {@link ForAvatarContext} and never exported from\n * `public-api.ts` — a consumer drives the status by binding `[src]`, never by\n * reporting a transition into the root.\n */\nexport interface AvatarPieceContext {\n /** Reported by `[forAvatarImage]` so the root can transition its state machine. */\n reportStatus(status: ForAvatarStatus): void;\n}\n\n/**\n * The avatar's internal coordination surface: everything\n * {@link ForAvatarContext} publishes plus the {@link AvatarPieceContext} call.\n *\n * Never exported from `public-api.ts`. It is the type the pieces read\n * {@link FOR_AVATAR_CONTEXT} at, so a consumer who injects that token gets the\n * read surface while `[forAvatarImage]` gets the transition channel. `ForAvatar`\n * declares `reportStatus` TS-`private`, which keeps it out of the emitted\n * `.d.ts` while `useExisting` still satisfies this contract at runtime.\n */\nexport interface AvatarContext extends ForAvatarContext, AvatarPieceContext {}\n\n/**\n * DI token for the avatar's coordination surface, provided by `[forAvatar]`.\n *\n * Publicly typed as the read surface {@link ForAvatarContext}, which is the whole of\n * what the token promises a consumer. `[forAvatarImage]` reads the same token at an\n * internal type that adds the status-report channel, so a wrapper re-providing it must\n * alias it to the root: `{ provide: FOR_AVATAR_CONTEXT, useExisting: MyAvatar }`, where\n * `MyAvatar` extends `ForAvatar`. A value that merely satisfies the declared type\n * resolves too, and is rejected in dev mode by the first piece to reach the channel.\n */\nexport const FOR_AVATAR_CONTEXT = new InjectionToken<ForAvatarContext>('FOR_AVATAR_CONTEXT');\n\n/**\n * The constant half of both resolvers' {@link assertRootContext} calls, so the\n * fallback and the image state the same requirement.\n */\nconst ROOT_ASSERTION = {\n entryPoint: 'avatar',\n token: 'FOR_AVATAR_CONTEXT',\n root: '[forAvatar]',\n};\n\n/**\n * Injects the nearest {@link ForAvatarContext}, throwing a descriptive error\n * when used outside a `[forAvatar]` element.\n *\n * @param piece Name of the calling directive, used in the error message.\n */\nexport function injectAvatarContext(piece: string): AvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw orphanContextError({\n code: 'FORCDK-AVATAR-001',\n piece,\n root: '[forAvatar]',\n token: 'FOR_AVATAR_CONTEXT',\n });\n }\n const widened = ctx as AvatarContext;\n assertRootContext({\n ...ROOT_ASSERTION,\n piece,\n probe: () => widened.reportStatus,\n });\n return widened;\n}\n\n/**\n * `[forAvatarImage]`'s own resolver. It reports a distinct failure from the rest\n * of the anatomy — the image is the piece that drives the state machine, so an\n * orphaned one silently never transitions — and therefore carries its own code\n * rather than routing through {@link injectAvatarContext}.\n */\nexport function injectAvatarImageContext(): AvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw orphanContextError({\n code: 'FORCDK-AVATAR-002',\n piece: 'ForAvatarImage',\n root: '[forAvatar]',\n token: 'FOR_AVATAR_CONTEXT',\n });\n }\n const widened = ctx as AvatarContext;\n assertRootContext({\n ...ROOT_ASSERTION,\n piece: 'ForAvatarImage',\n probe: () => widened.reportStatus,\n });\n return widened;\n}\n","import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant avatars in the surrounding injector\n * scope. Configure with `provideForAvatarDefaults` either at the\n * application root or in any component's `providers` array; partial\n * overrides merge with the parent scope.\n */\nexport interface ForAvatarDefaults {\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` to show immediately during\n * `idle` / `loading`, or to e.g. `500` to skip rendering during quick\n * cached loads. `error` always shows the fallback immediately.\n */\n fallbackDelayMs: number;\n}\n\n/**\n * Library fallback for avatar defaults, read at the root injector when no\n * consumer has called `provideForAvatarDefaults`. Exported for the shared defaults\n * contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_AVATAR_FALLBACK_DEFAULTS: ForAvatarDefaults = {\n fallbackDelayMs: 0,\n};\n\nconst { token, provideDefaults } = createDefaults<ForAvatarDefaults>(\n 'FOR_AVATAR_DEFAULTS',\n FOR_AVATAR_FALLBACK_DEFAULTS,\n);\n\n/** Token holding the resolved avatar defaults for the current scope. */\nexport const FOR_AVATAR_DEFAULTS = token;\n\n/**\n * Configures forty-cdk avatar defaults for this injector scope. Partial\n * overrides inherit unspecified keys from the parent scope (or library\n * defaults at the root).\n *\n * Pass a function instead of an object to build the overrides where\n * `inject()` is available; it runs once per injector that resolves the\n * defaults.\n */\nexport function provideForAvatarDefaults(\n defaults: Partial<ForAvatarDefaults> | (() => Partial<ForAvatarDefaults>) = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n input,\n linkedSignal,\n numberAttribute,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\n\nimport { FOR_AVATAR_CONTEXT, type ForAvatarContext, type ForAvatarStatus } from './avatar-context';\nimport { FOR_AVATAR_DEFAULTS } from './avatar-defaults';\n\n/**\n * Headless avatar root. Tracks the load lifecycle of an inner\n * `[forAvatarImage]` and exposes `status` + `shouldShowFallback` so the\n * consumer can swap the image for fallback content (initials, generic icon)\n * via `@if`.\n *\n * No ARIA role is imposed — avatars are typically presentational. If the\n * surrounding component conveys identity (a person's name in the header,\n * a chat bubble), the consumer should provide an `aria-label` on the\n * relevant ancestor or pair the avatar with visible text.\n *\n * @example\n * ```html\n * <span forAvatar #a=\"forAvatar\" fallbackDelayMs=\"500\">\n * <img forAvatarImage [src]=\"user.avatarUrl\" [alt]=\"user.name\" />\n * @if (a.shouldShowFallback()) {\n * <span forAvatarFallback>{{ user.initials }}</span>\n * }\n * </span>\n * ```\n */\n@Directive({\n selector: '[forAvatar]',\n exportAs: 'forAvatar',\n host: {\n '[attr.data-status]': 'status()',\n },\n providers: [{ provide: FOR_AVATAR_CONTEXT, useExisting: ForAvatar }],\n})\nexport class ForAvatar implements ForAvatarContext {\n readonly #defaults = inject(FOR_AVATAR_DEFAULTS);\n\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` (default) to show immediately\n * during `idle` / `loading`, or to e.g. `500` to skip rendering during\n * quick cached loads. `error` always shows the fallback immediately.\n * The default is read from `provideForAvatarDefaults` for the\n * surrounding scope.\n */\n readonly fallbackDelayMs = input(this.#defaults.fallbackDelayMs, {\n transform: (value: unknown) => {\n const parsed = numberAttribute(value);\n return Number.isFinite(parsed) ? parsed : this.#defaults.fallbackDelayMs;\n },\n });\n\n readonly #status = signal<ForAvatarStatus>('idle');\n readonly status = this.#status.asReadonly();\n\n // Whether the deferred-fallback timer has fired for the current status/delay.\n // `linkedSignal` resets it to `false` whenever the source changes (the\n // canonical replacement for resetting state inside an effect); the timer\n // callback in the constructor flips it to `true`.\n readonly #timerElapsed = linkedSignal<{ status: ForAvatarStatus; delay: number }, boolean>({\n source: () => ({ status: this.#status(), delay: this.fallbackDelayMs() }),\n computation: () => false,\n });\n #timer: ReturnType<typeof setTimeout> | null = null;\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n readonly shouldShowFallback = computed(() => {\n const status = this.#status();\n if (status === 'loaded') return false;\n if (status === 'error') return true;\n // idle | loading: an immediate (delay <= 0) gate shows the fallback right\n // away; a deferred gate waits for the timer armed in the effect below.\n if (this.fallbackDelayMs() <= 0) return true;\n return this.#timerElapsed();\n });\n\n private reportStatus(status: ForAvatarStatus): void {\n this.#status.set(status);\n }\n\n constructor() {\n // Imperative timer integration: the effect's only job is to (re)arm a\n // setTimeout for the deferred-fallback case (idle | loading with a positive\n // delay). The synchronous branches (loaded / error / delay <= 0) are pure\n // derivations and live in `shouldShowFallback`. The signal write happens\n // inside the timer callback, which runs outside the effect's reactive\n // scope, so this does NOT create a self-cycle in the reactive graph.\n effect(() => {\n const status = this.#status();\n const delay = this.fallbackDelayMs();\n this.#cancelTimer();\n if (this.#isBrowser && (status === 'idle' || status === 'loading') && delay > 0) {\n this.#timer = setTimeout(() => {\n this.#timer = null;\n this.#timerElapsed.set(true);\n }, delay);\n }\n });\n\n inject(DestroyRef).onDestroy(() => this.#cancelTimer());\n }\n\n #cancelTimer(): void {\n if (this.#timer !== null) {\n clearTimeout(this.#timer);\n this.#timer = null;\n }\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n output,\n PLATFORM_ID,\n} from '@angular/core';\n\nimport { type ForAvatarStatus, injectAvatarImageContext } from './avatar-context';\n\n/**\n * Wraps an `<img>` and reports its load lifecycle to the surrounding\n * `[forAvatar]`. The native `src` / `alt` bindings are owned by the\n * consumer — this directive only observes.\n *\n * Reflects `data-status` on the image element so the consumer can hide it\n * via CSS while loading or after an error (e.g. `img[data-status=\"error\"]\n * { display: none }`).\n */\n@Directive({\n selector: 'img[forAvatarImage]',\n exportAs: 'forAvatarImage',\n host: {\n '[attr.data-status]': 'status()',\n '(load)': 'onLoad()',\n '(error)': 'onError()',\n },\n})\nexport class ForAvatarImage {\n readonly #host = inject<ElementRef<HTMLImageElement>>(ElementRef).nativeElement;\n readonly #parent = injectAvatarImageContext();\n\n /**\n * Emits whenever the image lifecycle transitions to a new state. Useful\n * if the consumer wants to log analytics, swap to a different src on\n * error, etc.\n */\n readonly loadStatusChange = output<ForAvatarStatus>();\n\n constructor() {\n const parent = this.#parent;\n const host = this.#host;\n\n let destroyed = false;\n inject(DestroyRef).onDestroy(() => (destroyed = true));\n\n afterNextRender(() => {\n this.#syncFromAttr();\n });\n\n if (isPlatformBrowser(inject(PLATFORM_ID)) && typeof MutationObserver !== 'undefined') {\n const observer = new MutationObserver(() => {\n this.#syncFromAttr();\n });\n observer.observe(host, { attributes: true, attributeFilter: ['src'] });\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n // Re-emit upstream whenever the parent's status changes due to our writes.\n // De-dupe on the (request, status) pair rather than status alone: a new\n // `src` opens a fresh request token, so an identical status (e.g. a second\n // image that also `loaded`) still re-reports for the new request, while a\n // repeated status within the same request is suppressed.\n let last: ForAvatarStatus | null = null;\n let lastReportedToken = -1;\n const emit = (status: ForAvatarStatus): void => {\n if (destroyed) return;\n if (last === status && lastReportedToken === this.#requestToken) return;\n last = status;\n lastReportedToken = this.#requestToken;\n parent.reportStatus(status);\n this.loadStatusChange.emit(status);\n };\n this.#emit = emit;\n }\n\n protected status(): ForAvatarStatus {\n return this.#parent.status();\n }\n\n protected onLoad(): void {\n // Ignore a `load` that fires for a request the host has already moved past\n // (the native event can arrive after `src` was reassigned).\n if (!this.#isCurrentRequest()) return;\n if (!this.#host.complete) return;\n this.#emit('loaded');\n }\n\n protected onError(): void {\n if (!this.#isCurrentRequest()) return;\n if (!this.#host.complete) return;\n this.#emit('error');\n }\n\n /**\n * Cheap string-identity fast-path: true when the `src` the host currently\n * carries is the one the latest `#syncFromAttr` observed, so a native\n * load/error event has not drifted away from the in-flight request. This\n * proves src-attribute stability only; request *completeness* (a late event\n * from a superseded resource that happens to share the current src) is\n * verified separately in onLoad/onError via `this.#host.complete`.\n */\n #isCurrentRequest(): boolean {\n return this.#host.getAttribute('src') === this.#requestSrc;\n }\n\n #syncFromAttr(): void {\n const src = this.#host.getAttribute('src');\n this.#requestToken++;\n this.#requestSrc = src;\n if (!src) {\n this.#emit('idle');\n return;\n }\n if (this.#host.complete) {\n // Cached image: load/error events may not fire — derive from naturalWidth.\n if (this.#host.naturalWidth > 0) {\n this.#emit('loaded');\n return;\n }\n // A complete image with zero intrinsic width is ambiguous: it may be a\n // broken image OR a valid SVG with no intrinsic dimensions. Don't\n // pessimistically flag `error` and hide a valid image — verify with\n // `decode()`, which resolves for a decodable (even zero-size) image and\n // rejects for a broken one. Stay `loading` until it settles.\n this.#emit('loading');\n this.#verifyCachedZeroSize(src);\n return;\n }\n this.#emit('loading');\n }\n\n #verifyCachedZeroSize(src: string): void {\n const decode = this.#host.decode?.bind(this.#host);\n if (!decode) {\n // No `decode()` (older engines / jsdom): keep the legacy heuristic.\n this.#emit('error');\n return;\n }\n decode().then(\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('loaded');\n }\n },\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('error');\n }\n },\n );\n }\n\n // Initialized in constructor.\n #emit!: (status: ForAvatarStatus) => void;\n\n /** Monotonic id bumped on every `src` change so stale events are ignored. */\n #requestToken = 0;\n /** The `src` the latest `#syncFromAttr` observed, used to match native events. */\n #requestSrc: string | null = null;\n}\n","import { Directive } from '@angular/core';\n\nimport { injectAvatarContext } from './avatar-context';\n\n/**\n * Marker for the avatar's fallback content (initials, generic icon, …).\n * Reflects `data-status` from the parent `[forAvatar]` so the consumer can\n * style for the `error` state separately from `idle` / `loading` if needed.\n *\n * The decision of *whether* to render the fallback lives on the root —\n * drive `@if` with `forAvatar.shouldShowFallback()`.\n */\n@Directive({\n selector: '[forAvatarFallback]',\n exportAs: 'forAvatarFallback',\n host: {\n '[attr.data-status]': 'parent.status()',\n },\n})\nexport class ForAvatarFallback {\n protected readonly parent = injectAvatarContext('ForAvatarFallback');\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forty-cdk-avatar.mjs","sources":["../../../projects/forty-cdk/avatar/src/avatar-context.ts","../../../projects/forty-cdk/avatar/src/avatar.ts","../../../projects/forty-cdk/avatar/src/avatar-image.ts","../../../projects/forty-cdk/avatar/src/avatar-fallback.ts","../../../projects/forty-cdk/avatar/src/forty-cdk-avatar.ts"],"sourcesContent":["import { inject, InjectionToken, type Signal } from '@angular/core';\n\nimport { assertRootContext, orphanContextError } from 'forty-cdk/core';\n\n/**\n * Lifecycle stages of the avatar's image:\n * - `idle`: no `src` set yet.\n * - `loading`: `src` set, waiting for the network.\n * - `loaded`: image rendered successfully.\n * - `error`: image failed to load.\n */\nexport type ForAvatarStatus = 'idle' | 'loading' | 'loaded' | 'error';\n\n/** Read surface `[forAvatar]` publishes through {@link FOR_AVATAR_CONTEXT}. */\nexport interface ForAvatarContext {\n /** Current status of the avatar's image. */\n readonly status: Signal<ForAvatarStatus>;\n /**\n * `true` when the consumer should render the fallback. Drives `@if` in\n * the consumer's template. Honors `fallbackDelayMs` for `idle` / `loading`\n * states; `error` flips it to `true` immediately.\n */\n readonly shouldShowFallback: Signal<boolean>;\n}\n\n/**\n * The avatar's piece-coordination surface: the one call `[forAvatarImage]`\n * makes so the root can transition its state machine.\n *\n * **Not** part of {@link ForAvatarContext} and never exported from\n * `public-api.ts` — a consumer drives the status by binding `[src]`, never by\n * reporting a transition into the root.\n */\nexport interface AvatarPieceContext {\n /** Reported by `[forAvatarImage]` so the root can transition its state machine. */\n reportStatus(status: ForAvatarStatus): void;\n}\n\n/**\n * The avatar's internal coordination surface: everything\n * {@link ForAvatarContext} publishes plus the {@link AvatarPieceContext} call.\n *\n * Never exported from `public-api.ts`. It is the type the pieces read\n * {@link FOR_AVATAR_CONTEXT} at, so a consumer who injects that token gets the\n * read surface while `[forAvatarImage]` gets the transition channel. `ForAvatar`\n * declares `reportStatus` TS-`private`, which keeps it out of the emitted\n * `.d.ts` while `useExisting` still satisfies this contract at runtime.\n */\nexport interface AvatarContext extends ForAvatarContext, AvatarPieceContext {}\n\n/**\n * DI token for the avatar's coordination surface, provided by `[forAvatar]`.\n *\n * Publicly typed as the read surface {@link ForAvatarContext}, which is the whole of\n * what the token promises a consumer. `[forAvatarImage]` reads the same token at an\n * internal type that adds the status-report channel, so a wrapper re-providing it must\n * alias it to the root: `{ provide: FOR_AVATAR_CONTEXT, useExisting: MyAvatar }`, where\n * `MyAvatar` extends `ForAvatar`. A value that merely satisfies the declared type\n * resolves too, and is rejected in dev mode by the first piece to reach the channel.\n */\nexport const FOR_AVATAR_CONTEXT = new InjectionToken<ForAvatarContext>('FOR_AVATAR_CONTEXT');\n\n/**\n * The constant half of both resolvers' {@link assertRootContext} calls, so the\n * fallback and the image state the same requirement.\n */\nconst ROOT_ASSERTION = {\n entryPoint: 'avatar',\n token: 'FOR_AVATAR_CONTEXT',\n root: '[forAvatar]',\n};\n\n/**\n * Injects the nearest {@link ForAvatarContext}, throwing a descriptive error\n * when used outside a `[forAvatar]` element.\n *\n * @param piece Name of the calling directive, used in the error message.\n */\nexport function injectAvatarContext(piece: string): AvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw orphanContextError({\n code: 'FORCDK-AVATAR-001',\n piece,\n root: '[forAvatar]',\n token: 'FOR_AVATAR_CONTEXT',\n });\n }\n const widened = ctx as AvatarContext;\n assertRootContext({\n ...ROOT_ASSERTION,\n piece,\n probe: () => widened.reportStatus,\n });\n return widened;\n}\n\n/**\n * `[forAvatarImage]`'s own resolver. It reports a distinct failure from the rest\n * of the anatomy — the image is the piece that drives the state machine, so an\n * orphaned one silently never transitions — and therefore carries its own code\n * rather than routing through {@link injectAvatarContext}.\n */\nexport function injectAvatarImageContext(): AvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw orphanContextError({\n code: 'FORCDK-AVATAR-002',\n piece: 'ForAvatarImage',\n root: '[forAvatar]',\n token: 'FOR_AVATAR_CONTEXT',\n });\n }\n const widened = ctx as AvatarContext;\n assertRootContext({\n ...ROOT_ASSERTION,\n piece: 'ForAvatarImage',\n probe: () => widened.reportStatus,\n });\n return widened;\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n input,\n linkedSignal,\n numberAttribute,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\n\nimport { FOR_AVATAR_CONTEXT, type ForAvatarContext, type ForAvatarStatus } from './avatar-context';\nimport { FOR_AVATAR_DEFAULTS } from 'forty-cdk/defaults';\n\n/**\n * Headless avatar root. Tracks the load lifecycle of an inner\n * `[forAvatarImage]` and exposes `status` + `shouldShowFallback` so the\n * consumer can swap the image for fallback content (initials, generic icon)\n * via `@if`.\n *\n * No ARIA role is imposed — avatars are typically presentational. If the\n * surrounding component conveys identity (a person's name in the header,\n * a chat bubble), the consumer should provide an `aria-label` on the\n * relevant ancestor or pair the avatar with visible text.\n *\n * @example\n * ```html\n * <span forAvatar #a=\"forAvatar\" fallbackDelayMs=\"500\">\n * <img forAvatarImage [src]=\"user.avatarUrl\" [alt]=\"user.name\" />\n * @if (a.shouldShowFallback()) {\n * <span forAvatarFallback>{{ user.initials }}</span>\n * }\n * </span>\n * ```\n */\n@Directive({\n selector: '[forAvatar]',\n exportAs: 'forAvatar',\n host: {\n '[attr.data-status]': 'status()',\n },\n providers: [{ provide: FOR_AVATAR_CONTEXT, useExisting: ForAvatar }],\n})\nexport class ForAvatar implements ForAvatarContext {\n readonly #defaults = inject(FOR_AVATAR_DEFAULTS);\n\n /**\n * Milliseconds to defer the fallback for fast loads, avoiding a brief\n * \"initials → image\" flicker. Set to `0` (default) to show immediately\n * during `idle` / `loading`, or to e.g. `500` to skip rendering during\n * quick cached loads. `error` always shows the fallback immediately.\n * The default is read from `provideForAvatarDefaults` for the\n * surrounding scope.\n */\n readonly fallbackDelayMs = input(this.#defaults.fallbackDelayMs, {\n transform: (value: unknown) => {\n const parsed = numberAttribute(value);\n return Number.isFinite(parsed) ? parsed : this.#defaults.fallbackDelayMs;\n },\n });\n\n readonly #status = signal<ForAvatarStatus>('idle');\n readonly status = this.#status.asReadonly();\n\n // Whether the deferred-fallback timer has fired for the current status/delay.\n // `linkedSignal` resets it to `false` whenever the source changes (the\n // canonical replacement for resetting state inside an effect); the timer\n // callback in the constructor flips it to `true`.\n readonly #timerElapsed = linkedSignal<{ status: ForAvatarStatus; delay: number }, boolean>({\n source: () => ({ status: this.#status(), delay: this.fallbackDelayMs() }),\n computation: () => false,\n });\n #timer: ReturnType<typeof setTimeout> | null = null;\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n readonly shouldShowFallback = computed(() => {\n const status = this.#status();\n if (status === 'loaded') return false;\n if (status === 'error') return true;\n // idle | loading: an immediate (delay <= 0) gate shows the fallback right\n // away; a deferred gate waits for the timer armed in the effect below.\n if (this.fallbackDelayMs() <= 0) return true;\n return this.#timerElapsed();\n });\n\n private reportStatus(status: ForAvatarStatus): void {\n this.#status.set(status);\n }\n\n constructor() {\n // Imperative timer integration: the effect's only job is to (re)arm a\n // setTimeout for the deferred-fallback case (idle | loading with a positive\n // delay). The synchronous branches (loaded / error / delay <= 0) are pure\n // derivations and live in `shouldShowFallback`. The signal write happens\n // inside the timer callback, which runs outside the effect's reactive\n // scope, so this does NOT create a self-cycle in the reactive graph.\n effect(() => {\n const status = this.#status();\n const delay = this.fallbackDelayMs();\n this.#cancelTimer();\n if (this.#isBrowser && (status === 'idle' || status === 'loading') && delay > 0) {\n this.#timer = setTimeout(() => {\n this.#timer = null;\n this.#timerElapsed.set(true);\n }, delay);\n }\n });\n\n inject(DestroyRef).onDestroy(() => this.#cancelTimer());\n }\n\n #cancelTimer(): void {\n if (this.#timer !== null) {\n clearTimeout(this.#timer);\n this.#timer = null;\n }\n }\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n output,\n PLATFORM_ID,\n} from '@angular/core';\n\nimport { type ForAvatarStatus, injectAvatarImageContext } from './avatar-context';\n\n/**\n * Wraps an `<img>` and reports its load lifecycle to the surrounding\n * `[forAvatar]`. The native `src` / `alt` bindings are owned by the\n * consumer — this directive only observes.\n *\n * Reflects `data-status` on the image element so the consumer can hide it\n * via CSS while loading or after an error (e.g. `img[data-status=\"error\"]\n * { display: none }`).\n */\n@Directive({\n selector: 'img[forAvatarImage]',\n exportAs: 'forAvatarImage',\n host: {\n '[attr.data-status]': 'status()',\n '(load)': 'onLoad()',\n '(error)': 'onError()',\n },\n})\nexport class ForAvatarImage {\n readonly #host = inject<ElementRef<HTMLImageElement>>(ElementRef).nativeElement;\n readonly #parent = injectAvatarImageContext();\n\n /**\n * Emits whenever the image lifecycle transitions to a new state. Useful\n * if the consumer wants to log analytics, swap to a different src on\n * error, etc.\n */\n readonly loadStatusChange = output<ForAvatarStatus>();\n\n constructor() {\n const parent = this.#parent;\n const host = this.#host;\n\n let destroyed = false;\n inject(DestroyRef).onDestroy(() => (destroyed = true));\n\n afterNextRender(() => {\n this.#syncFromAttr();\n });\n\n if (isPlatformBrowser(inject(PLATFORM_ID)) && typeof MutationObserver !== 'undefined') {\n const observer = new MutationObserver(() => {\n this.#syncFromAttr();\n });\n observer.observe(host, { attributes: true, attributeFilter: ['src'] });\n inject(DestroyRef).onDestroy(() => observer.disconnect());\n }\n\n // Re-emit upstream whenever the parent's status changes due to our writes.\n // De-dupe on the (request, status) pair rather than status alone: a new\n // `src` opens a fresh request token, so an identical status (e.g. a second\n // image that also `loaded`) still re-reports for the new request, while a\n // repeated status within the same request is suppressed.\n let last: ForAvatarStatus | null = null;\n let lastReportedToken = -1;\n const emit = (status: ForAvatarStatus): void => {\n if (destroyed) return;\n if (last === status && lastReportedToken === this.#requestToken) return;\n last = status;\n lastReportedToken = this.#requestToken;\n parent.reportStatus(status);\n this.loadStatusChange.emit(status);\n };\n this.#emit = emit;\n }\n\n protected status(): ForAvatarStatus {\n return this.#parent.status();\n }\n\n protected onLoad(): void {\n // Ignore a `load` that fires for a request the host has already moved past\n // (the native event can arrive after `src` was reassigned).\n if (!this.#isCurrentRequest()) return;\n if (!this.#host.complete) return;\n this.#emit('loaded');\n }\n\n protected onError(): void {\n if (!this.#isCurrentRequest()) return;\n if (!this.#host.complete) return;\n this.#emit('error');\n }\n\n /**\n * Cheap string-identity fast-path: true when the `src` the host currently\n * carries is the one the latest `#syncFromAttr` observed, so a native\n * load/error event has not drifted away from the in-flight request. This\n * proves src-attribute stability only; request *completeness* (a late event\n * from a superseded resource that happens to share the current src) is\n * verified separately in onLoad/onError via `this.#host.complete`.\n */\n #isCurrentRequest(): boolean {\n return this.#host.getAttribute('src') === this.#requestSrc;\n }\n\n #syncFromAttr(): void {\n const src = this.#host.getAttribute('src');\n this.#requestToken++;\n this.#requestSrc = src;\n if (!src) {\n this.#emit('idle');\n return;\n }\n if (this.#host.complete) {\n // Cached image: load/error events may not fire — derive from naturalWidth.\n if (this.#host.naturalWidth > 0) {\n this.#emit('loaded');\n return;\n }\n // A complete image with zero intrinsic width is ambiguous: it may be a\n // broken image OR a valid SVG with no intrinsic dimensions. Don't\n // pessimistically flag `error` and hide a valid image — verify with\n // `decode()`, which resolves for a decodable (even zero-size) image and\n // rejects for a broken one. Stay `loading` until it settles.\n this.#emit('loading');\n this.#verifyCachedZeroSize(src);\n return;\n }\n this.#emit('loading');\n }\n\n #verifyCachedZeroSize(src: string): void {\n const decode = this.#host.decode?.bind(this.#host);\n if (!decode) {\n // No `decode()` (older engines / jsdom): keep the legacy heuristic.\n this.#emit('error');\n return;\n }\n decode().then(\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('loaded');\n }\n },\n () => {\n if (this.#host.getAttribute('src') === src) {\n this.#emit('error');\n }\n },\n );\n }\n\n // Initialized in constructor.\n #emit!: (status: ForAvatarStatus) => void;\n\n /** Monotonic id bumped on every `src` change so stale events are ignored. */\n #requestToken = 0;\n /** The `src` the latest `#syncFromAttr` observed, used to match native events. */\n #requestSrc: string | null = null;\n}\n","import { Directive } from '@angular/core';\n\nimport { injectAvatarContext } from './avatar-context';\n\n/**\n * Marker for the avatar's fallback content (initials, generic icon, …).\n * Reflects `data-status` from the parent `[forAvatar]` so the consumer can\n * style for the `error` state separately from `idle` / `loading` if needed.\n *\n * The decision of *whether* to render the fallback lives on the root —\n * drive `@if` with `forAvatar.shouldShowFallback()`.\n */\n@Directive({\n selector: '[forAvatarFallback]',\n exportAs: 'forAvatarFallback',\n host: {\n '[attr.data-status]': 'parent.status()',\n },\n})\nexport class ForAvatarFallback {\n protected readonly parent = injectAvatarContext('ForAvatarFallback');\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,30 +1,8 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, input, Directive, booleanAttribute } from '@angular/core';
3
- import { createDefaults, hostAriaLabel, resolveTextInput } from 'forty-cdk/core';
4
-
5
-
6
-
7
-
8
-
9
-
10
- const FOR_BREADCRUMBS_FALLBACK_DEFAULTS = {
11
- label: 'Breadcrumb',
12
- };
13
- const { token, provideDefaults } = createDefaults('FOR_BREADCRUMBS_DEFAULTS', FOR_BREADCRUMBS_FALLBACK_DEFAULTS);
14
-
15
- const FOR_BREADCRUMBS_DEFAULTS = token;
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
- function provideForBreadcrumbsDefaults(defaults = {}) {
26
- return provideDefaults(defaults);
27
- }
3
+ import { hostAriaLabel, resolveTextInput } from 'forty-cdk/core';
4
+ import { FOR_BREADCRUMBS_DEFAULTS } from 'forty-cdk/defaults';
5
+ export { FOR_BREADCRUMBS_DEFAULTS, provideForBreadcrumbsDefaults } from 'forty-cdk/defaults';
28
6
 
29
7
 
30
8
 
@@ -137,5 +115,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImpor
137
115
 
138
116
 
139
117
 
140
- export { FOR_BREADCRUMBS_DEFAULTS, ForBreadcrumbItem, ForBreadcrumbSeparator, ForBreadcrumbs, provideForBreadcrumbsDefaults };
118
+ export { ForBreadcrumbItem, ForBreadcrumbSeparator, ForBreadcrumbs };
141
119
  //# sourceMappingURL=forty-cdk-breadcrumbs.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"forty-cdk-breadcrumbs.mjs","sources":["../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs-defaults.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-item.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-separator.ts","../../../projects/forty-cdk/breadcrumbs/src/forty-cdk-breadcrumbs.ts"],"sourcesContent":["import { type Provider } from '@angular/core';\n\nimport { createDefaults, type LocalizableText } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant breadcrumb trails in the surrounding\n * injector scope. Configure with `provideForBreadcrumbsDefaults` either at the\n * application root or in any component's `providers` array; partial overrides\n * merge with the parent scope.\n */\nexport interface ForBreadcrumbsDefaults {\n /**\n * Accessible name for the breadcrumb `navigation` landmark, for trails that\n * don't set `[ariaLabel]` locally. Localize it here to translate every\n * breadcrumb landmark in the scope.\n */\n label: LocalizableText;\n}\n\n/**\n * Library fallback for breadcrumbs defaults, read at the root injector when no\n * consumer has called `provideForBreadcrumbsDefaults`. Exported for the shared\n * defaults contract spec; not re-exported from the primitive's public entry.\n */\nexport const FOR_BREADCRUMBS_FALLBACK_DEFAULTS: ForBreadcrumbsDefaults = {\n label: 'Breadcrumb',\n};\n\nconst { token, provideDefaults } = createDefaults<ForBreadcrumbsDefaults>(\n 'FOR_BREADCRUMBS_DEFAULTS',\n FOR_BREADCRUMBS_FALLBACK_DEFAULTS,\n);\n\n/** Token holding the resolved breadcrumbs defaults for the current scope. */\nexport const FOR_BREADCRUMBS_DEFAULTS = token;\n\n/**\n * Configures forty-cdk breadcrumbs defaults for this injector scope. Partial\n * overrides inherit unspecified keys from the parent scope (or library\n * defaults at the root).\n *\n * Pass a function instead of an object to build the overrides where\n * `inject()` is available; it runs once per injector that resolves the\n * defaults.\n */\nexport function provideForBreadcrumbsDefaults(\n defaults: Partial<ForBreadcrumbsDefaults> | (() => Partial<ForBreadcrumbsDefaults>) = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","import { Directive, inject, input } from '@angular/core';\n\nimport { hostAriaLabel, resolveTextInput } from 'forty-cdk/core';\nimport { FOR_BREADCRUMBS_DEFAULTS } from './breadcrumbs-defaults';\n\n/**\n * Headless breadcrumb trail implementing the\n * [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):\n * a `navigation` landmark wrapping a set of links that reveal the page's\n * position within the site hierarchy.\n *\n * The directive contributes only the landmark role + accessible name; the\n * consumer owns the markup structure (an `<ol>` of `<li>` links is the APG\n * recommendation) and applies their own styles. Each link decorated with\n * `[forBreadcrumbItem]` can mark itself the current page via `current`, and\n * visual dividers decorated with `[forBreadcrumbSeparator]` are hidden from\n * assistive technology.\n *\n * There is no keyboard interaction or focus management beyond what the native\n * links provide — breadcrumb links are ordinary tab stops.\n *\n * @example\n * ```html\n * <nav forBreadcrumbs>\n * <ol>\n * <li><a forBreadcrumbItem href=\"/\">Home</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library\">Library</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library/data\" current>Data</a></li>\n * </ol>\n * </nav>\n * ```\n */\n@Directive({\n selector: '[forBreadcrumbs]',\n exportAs: 'forBreadcrumbs',\n host: {\n role: 'navigation',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n },\n})\nexport class ForBreadcrumbs {\n readonly #defaults = inject(FOR_BREADCRUMBS_DEFAULTS);\n\n /**\n * Accessible label for the navigation landmark. Defaults to the scope's\n * `label` (`'Breadcrumb'` unless overridden via\n * `provideForBreadcrumbsDefaults`), so a bare `<nav forBreadcrumbs>` is\n * already a correctly labelled landmark — the conventional name for this\n * pattern. Override it per-instance via `[ariaLabel]` (or point a native\n * `aria-labelledby` at a visible heading) when the page hosts more than one\n * breadcrumb trail and they need to be told apart; set it to `null` to drop\n * the attribute and rely on `aria-labelledby`.\n */\n readonly ariaLabel = input<string | null>();\n\n protected readonly resolvedAriaLabel = hostAriaLabel(\n () => resolveTextInput(this.ariaLabel(), this.#defaults.label) || null,\n );\n}\n","import { booleanAttribute, Directive, input } from '@angular/core';\n\n/**\n * A single link within a breadcrumb trail. Apply on an `<a>` so navigation is\n * native. Set `current` on the link that points to the page the user is on —\n * it reflects [`aria-current=\"page\"`](https://www.w3.org/TR/wai-aria-1.2/#aria-current)\n * so assistive technology announces it as the current location.\n */\n@Directive({\n selector: '[forBreadcrumbItem]',\n exportAs: 'forBreadcrumbItem',\n host: {\n '[attr.aria-current]': 'current() ? \"page\" : null',\n },\n})\nexport class ForBreadcrumbItem {\n /**\n * Whether this item represents the page the user is currently on. The\n * current item reflects `aria-current=\"page\"`; all others emit no\n * `aria-current` attribute.\n */\n readonly current = input(false, { transform: booleanAttribute });\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Decorative divider rendered between breadcrumb items (a `/`, `>`, chevron,\n * etc.). Reflects `aria-hidden=\"true\"` so assistive technology skips it — the\n * trail's structure is already conveyed by the list and the links, making the\n * visual separator redundant in the accessibility tree.\n */\n@Directive({\n selector: '[forBreadcrumbSeparator]',\n exportAs: 'forBreadcrumbSeparator',\n host: {\n 'aria-hidden': 'true',\n },\n})\nexport class ForBreadcrumbSeparator {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forty-cdk-breadcrumbs.mjs","sources":["../../../projects/forty-cdk/breadcrumbs/src/breadcrumbs.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-item.ts","../../../projects/forty-cdk/breadcrumbs/src/breadcrumb-separator.ts","../../../projects/forty-cdk/breadcrumbs/src/forty-cdk-breadcrumbs.ts"],"sourcesContent":["import { Directive, inject, input } from '@angular/core';\n\nimport { hostAriaLabel, resolveTextInput } from 'forty-cdk/core';\nimport { FOR_BREADCRUMBS_DEFAULTS } from 'forty-cdk/defaults';\n\n/**\n * Headless breadcrumb trail implementing the\n * [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):\n * a `navigation` landmark wrapping a set of links that reveal the page's\n * position within the site hierarchy.\n *\n * The directive contributes only the landmark role + accessible name; the\n * consumer owns the markup structure (an `<ol>` of `<li>` links is the APG\n * recommendation) and applies their own styles. Each link decorated with\n * `[forBreadcrumbItem]` can mark itself the current page via `current`, and\n * visual dividers decorated with `[forBreadcrumbSeparator]` are hidden from\n * assistive technology.\n *\n * There is no keyboard interaction or focus management beyond what the native\n * links provide — breadcrumb links are ordinary tab stops.\n *\n * @example\n * ```html\n * <nav forBreadcrumbs>\n * <ol>\n * <li><a forBreadcrumbItem href=\"/\">Home</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library\">Library</a></li>\n * <li forBreadcrumbSeparator aria-hidden=\"true\">/</li>\n * <li><a forBreadcrumbItem href=\"/library/data\" current>Data</a></li>\n * </ol>\n * </nav>\n * ```\n */\n@Directive({\n selector: '[forBreadcrumbs]',\n exportAs: 'forBreadcrumbs',\n host: {\n role: 'navigation',\n '[attr.aria-label]': 'resolvedAriaLabel()',\n },\n})\nexport class ForBreadcrumbs {\n readonly #defaults = inject(FOR_BREADCRUMBS_DEFAULTS);\n\n /**\n * Accessible label for the navigation landmark. Defaults to the scope's\n * `label` (`'Breadcrumb'` unless overridden via\n * `provideForBreadcrumbsDefaults`), so a bare `<nav forBreadcrumbs>` is\n * already a correctly labelled landmark — the conventional name for this\n * pattern. Override it per-instance via `[ariaLabel]` (or point a native\n * `aria-labelledby` at a visible heading) when the page hosts more than one\n * breadcrumb trail and they need to be told apart; set it to `null` to drop\n * the attribute and rely on `aria-labelledby`.\n */\n readonly ariaLabel = input<string | null>();\n\n protected readonly resolvedAriaLabel = hostAriaLabel(\n () => resolveTextInput(this.ariaLabel(), this.#defaults.label) || null,\n );\n}\n","import { booleanAttribute, Directive, input } from '@angular/core';\n\n/**\n * A single link within a breadcrumb trail. Apply on an `<a>` so navigation is\n * native. Set `current` on the link that points to the page the user is on —\n * it reflects [`aria-current=\"page\"`](https://www.w3.org/TR/wai-aria-1.2/#aria-current)\n * so assistive technology announces it as the current location.\n */\n@Directive({\n selector: '[forBreadcrumbItem]',\n exportAs: 'forBreadcrumbItem',\n host: {\n '[attr.aria-current]': 'current() ? \"page\" : null',\n },\n})\nexport class ForBreadcrumbItem {\n /**\n * Whether this item represents the page the user is currently on. The\n * current item reflects `aria-current=\"page\"`; all others emit no\n * `aria-current` attribute.\n */\n readonly current = input(false, { transform: booleanAttribute });\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Decorative divider rendered between breadcrumb items (a `/`, `>`, chevron,\n * etc.). Reflects `aria-hidden=\"true\"` so assistive technology skips it — the\n * trail's structure is already conveyed by the list and the links, making the\n * visual separator redundant in the accessibility tree.\n */\n@Directive({\n selector: '[forBreadcrumbSeparator]',\n exportAs: 'forBreadcrumbSeparator',\n host: {\n 'aria-hidden': 'true',\n },\n})\nexport class ForBreadcrumbSeparator {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
@@ -1,55 +1,8 @@
1
- import { createDefaults, MediaQueryRegistry, fortyError } from 'forty-cdk/core';
2
- export { injectPrefersReducedMotion } from 'forty-cdk/core';
1
+ import { FOR_BREAKPOINTS_DEFAULTS } from 'forty-cdk/defaults';
2
+ export { FOR_BREAKPOINTS_DEFAULTS, forBreakpointsTailwind, provideForBreakpointsDefaults } from 'forty-cdk/defaults';
3
3
  import { inject, computed } from '@angular/core';
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
- const forBreakpointsTailwind = {
12
- sm: 640,
13
- md: 768,
14
- lg: 1024,
15
- xl: 1280,
16
- '2xl': 1536,
17
- };
18
-
19
-
20
-
21
-
22
-
23
- const FOR_BREAKPOINTS_FALLBACK_DEFAULTS = {
24
- breakpoints: forBreakpointsTailwind,
25
- };
26
- const { token, provideDefaults } = createDefaults('FOR_BREAKPOINTS_DEFAULTS', FOR_BREAKPOINTS_FALLBACK_DEFAULTS);
27
-
28
-
29
-
30
-
31
-
32
- const FOR_BREAKPOINTS_DEFAULTS = token;
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
- function provideForBreakpointsDefaults(breakpoints) {
51
- return provideDefaults(typeof breakpoints === 'function' ? () => ({ breakpoints: breakpoints() }) : { breakpoints });
52
- }
4
+ import { MediaQueryRegistry, fortyError } from 'forty-cdk/core';
5
+ export { injectPrefersReducedMotion } from 'forty-cdk/core';
53
6
 
54
7
 
55
8
 
@@ -126,5 +79,5 @@ function injectBreakpoints() {
126
79
 
127
80
 
128
81
 
129
- export { FOR_BREAKPOINTS_DEFAULTS, forBreakpointsTailwind, injectBreakpoints, provideForBreakpointsDefaults };
82
+ export { injectBreakpoints };
130
83
  //# sourceMappingURL=forty-cdk-breakpoints.mjs.map