forty-cdk 0.2.0 → 0.3.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 (220) hide show
  1. package/accordion/README.md +122 -0
  2. package/aspect-ratio/README.md +76 -0
  3. package/avatar/README.md +100 -0
  4. package/breadcrumbs/README.md +49 -0
  5. package/breakpoints/README.md +81 -0
  6. package/button/README.md +49 -0
  7. package/calendar/README.md +458 -0
  8. package/carousel/README.md +358 -0
  9. package/checkbox/README.md +146 -0
  10. package/combobox/README.md +535 -0
  11. package/context-menu/README.md +139 -0
  12. package/date-field/README.md +184 -0
  13. package/date-picker/README.md +338 -0
  14. package/dialog/README.md +388 -0
  15. package/disclosure/README.md +114 -0
  16. package/drag-drop/README.md +359 -0
  17. package/drawer/README.md +560 -0
  18. package/dropdown-menu/README.md +176 -0
  19. package/fesm2022/forty-cdk-accordion.mjs +348 -0
  20. package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
  21. package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
  22. package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
  23. package/fesm2022/forty-cdk-avatar.mjs +308 -0
  24. package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
  25. package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
  26. package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
  27. package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
  28. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
  29. package/fesm2022/forty-cdk-button.mjs +134 -0
  30. package/fesm2022/forty-cdk-button.mjs.map +1 -0
  31. package/fesm2022/forty-cdk-calendar.mjs +2034 -0
  32. package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
  33. package/fesm2022/forty-cdk-carousel.mjs +968 -0
  34. package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
  35. package/fesm2022/forty-cdk-checkbox.mjs +226 -0
  36. package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
  37. package/fesm2022/forty-cdk-combobox.mjs +2596 -0
  38. package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
  39. package/fesm2022/forty-cdk-context-menu.mjs +413 -0
  40. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
  41. package/fesm2022/forty-cdk-core.mjs +9022 -0
  42. package/fesm2022/forty-cdk-core.mjs.map +1 -0
  43. package/fesm2022/forty-cdk-date-field.mjs +744 -0
  44. package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
  45. package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
  46. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
  47. package/fesm2022/forty-cdk-dialog.mjs +707 -0
  48. package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
  49. package/fesm2022/forty-cdk-disclosure.mjs +190 -0
  50. package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
  51. package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
  52. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
  53. package/fesm2022/forty-cdk-drawer.mjs +1641 -0
  54. package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
  55. package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
  56. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
  57. package/fesm2022/forty-cdk-field.mjs +425 -0
  58. package/fesm2022/forty-cdk-field.mjs.map +1 -0
  59. package/fesm2022/forty-cdk-fieldset.mjs +164 -0
  60. package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
  61. package/fesm2022/forty-cdk-file-upload.mjs +221 -0
  62. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
  63. package/fesm2022/forty-cdk-hover-card.mjs +496 -0
  64. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
  65. package/fesm2022/forty-cdk-input.mjs +274 -0
  66. package/fesm2022/forty-cdk-input.mjs.map +1 -0
  67. package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
  68. package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
  69. package/fesm2022/forty-cdk-listbox.mjs +1279 -0
  70. package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
  71. package/fesm2022/forty-cdk-menu.mjs +1439 -0
  72. package/fesm2022/forty-cdk-menu.mjs.map +1 -0
  73. package/fesm2022/forty-cdk-menubar.mjs +787 -0
  74. package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
  75. package/fesm2022/forty-cdk-meter.mjs +211 -0
  76. package/fesm2022/forty-cdk-meter.mjs.map +1 -0
  77. package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
  78. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
  79. package/fesm2022/forty-cdk-number-input.mjs +559 -0
  80. package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
  81. package/fesm2022/forty-cdk-otp-input.mjs +527 -0
  82. package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
  83. package/fesm2022/forty-cdk-pagination.mjs +323 -0
  84. package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
  85. package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
  86. package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
  87. package/fesm2022/forty-cdk-popover.mjs +698 -0
  88. package/fesm2022/forty-cdk-popover.mjs.map +1 -0
  89. package/fesm2022/forty-cdk-progress.mjs +226 -0
  90. package/fesm2022/forty-cdk-progress.mjs.map +1 -0
  91. package/fesm2022/forty-cdk-radio-group.mjs +378 -0
  92. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
  93. package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
  94. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
  95. package/fesm2022/forty-cdk-search.mjs +205 -0
  96. package/fesm2022/forty-cdk-search.mjs.map +1 -0
  97. package/fesm2022/forty-cdk-select.mjs +1661 -0
  98. package/fesm2022/forty-cdk-select.mjs.map +1 -0
  99. package/fesm2022/forty-cdk-separator.mjs +82 -0
  100. package/fesm2022/forty-cdk-separator.mjs.map +1 -0
  101. package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
  102. package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
  103. package/fesm2022/forty-cdk-slider.mjs +803 -0
  104. package/fesm2022/forty-cdk-slider.mjs.map +1 -0
  105. package/fesm2022/forty-cdk-stepper.mjs +886 -0
  106. package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
  107. package/fesm2022/forty-cdk-switch.mjs +137 -0
  108. package/fesm2022/forty-cdk-switch.mjs.map +1 -0
  109. package/fesm2022/forty-cdk-table.mjs +1518 -0
  110. package/fesm2022/forty-cdk-table.mjs.map +1 -0
  111. package/fesm2022/forty-cdk-tabs.mjs +400 -0
  112. package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
  113. package/fesm2022/forty-cdk-time-field.mjs +593 -0
  114. package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
  115. package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
  116. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
  117. package/fesm2022/forty-cdk-toast.mjs +1153 -0
  118. package/fesm2022/forty-cdk-toast.mjs.map +1 -0
  119. package/fesm2022/forty-cdk-toggle.mjs +516 -0
  120. package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
  121. package/fesm2022/forty-cdk-toolbar.mjs +374 -0
  122. package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
  123. package/fesm2022/forty-cdk-tooltip.mjs +672 -0
  124. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
  125. package/fesm2022/forty-cdk-tree.mjs +2007 -0
  126. package/fesm2022/forty-cdk-tree.mjs.map +1 -0
  127. package/fesm2022/forty-cdk-virtualization.mjs +1 -1
  128. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  129. package/fesm2022/forty-cdk.mjs +0 -43310
  130. package/fesm2022/forty-cdk.mjs.map +1 -1
  131. package/field/README.md +97 -0
  132. package/fieldset/README.md +86 -0
  133. package/file-upload/README.md +73 -0
  134. package/hover-card/README.md +171 -0
  135. package/input/README.md +156 -0
  136. package/listbox/README.md +424 -0
  137. package/menu/README.md +181 -0
  138. package/menubar/README.md +140 -0
  139. package/meter/README.md +128 -0
  140. package/navigation-menu/README.md +253 -0
  141. package/number-input/README.md +171 -0
  142. package/otp-input/README.md +198 -0
  143. package/package.json +213 -1
  144. package/pagination/README.md +61 -0
  145. package/pane-resizer/README.md +136 -0
  146. package/popover/README.md +262 -0
  147. package/progress/README.md +115 -0
  148. package/radio-group/README.md +129 -0
  149. package/scroll-area/README.md +184 -0
  150. package/search/README.md +42 -0
  151. package/select/README.md +488 -0
  152. package/separator/README.md +84 -0
  153. package/signal-forms/README.md +72 -0
  154. package/slider/README.md +152 -0
  155. package/stepper/README.md +292 -0
  156. package/switch/README.md +116 -0
  157. package/table/README.md +769 -0
  158. package/tabs/README.md +130 -0
  159. package/time-field/README.md +157 -0
  160. package/time-picker/README.md +172 -0
  161. package/toast/README.md +398 -0
  162. package/toggle/README.md +224 -0
  163. package/toolbar/README.md +109 -0
  164. package/tooltip/README.md +274 -0
  165. package/tree/README.md +708 -0
  166. package/types/forty-cdk-accordion.d.ts +242 -0
  167. package/types/forty-cdk-aspect-ratio.d.ts +59 -0
  168. package/types/forty-cdk-avatar.d.ts +133 -0
  169. package/types/forty-cdk-breadcrumbs.d.ts +92 -0
  170. package/types/forty-cdk-breakpoints.d.ts +141 -0
  171. package/types/forty-cdk-button.d.ts +80 -0
  172. package/types/forty-cdk-calendar.d.ts +914 -0
  173. package/types/forty-cdk-carousel.d.ts +530 -0
  174. package/types/forty-cdk-checkbox.d.ts +141 -0
  175. package/types/forty-cdk-combobox.d.ts +1259 -0
  176. package/types/forty-cdk-context-menu.d.ts +313 -0
  177. package/types/forty-cdk-core.d.ts +5774 -0
  178. package/types/forty-cdk-date-field.d.ts +307 -0
  179. package/types/forty-cdk-date-picker.d.ts +622 -0
  180. package/types/forty-cdk-dialog.d.ts +546 -0
  181. package/types/forty-cdk-disclosure.d.ts +127 -0
  182. package/types/forty-cdk-drag-drop.d.ts +456 -0
  183. package/types/forty-cdk-drawer.d.ts +871 -0
  184. package/types/forty-cdk-dropdown-menu.d.ts +242 -0
  185. package/types/forty-cdk-field.d.ts +236 -0
  186. package/types/forty-cdk-fieldset.d.ts +119 -0
  187. package/types/forty-cdk-file-upload.d.ts +124 -0
  188. package/types/forty-cdk-hover-card.d.ts +320 -0
  189. package/types/forty-cdk-input.d.ts +169 -0
  190. package/types/forty-cdk-internationalized-date.d.ts +1 -1
  191. package/types/forty-cdk-listbox.d.ts +513 -0
  192. package/types/forty-cdk-menu.d.ts +629 -0
  193. package/types/forty-cdk-menubar.d.ts +451 -0
  194. package/types/forty-cdk-meter.d.ts +122 -0
  195. package/types/forty-cdk-navigation-menu.d.ts +514 -0
  196. package/types/forty-cdk-number-input.d.ts +319 -0
  197. package/types/forty-cdk-otp-input.d.ts +248 -0
  198. package/types/forty-cdk-pagination.d.ts +214 -0
  199. package/types/forty-cdk-pane-resizer.d.ts +145 -0
  200. package/types/forty-cdk-popover.d.ts +509 -0
  201. package/types/forty-cdk-progress.d.ts +143 -0
  202. package/types/forty-cdk-radio-group.d.ts +222 -0
  203. package/types/forty-cdk-scroll-area.d.ts +258 -0
  204. package/types/forty-cdk-search.d.ts +142 -0
  205. package/types/forty-cdk-select.d.ts +899 -0
  206. package/types/forty-cdk-separator.d.ts +59 -0
  207. package/types/forty-cdk-signal-forms.d.ts +58 -0
  208. package/types/forty-cdk-slider.d.ts +379 -0
  209. package/types/forty-cdk-stepper.d.ts +650 -0
  210. package/types/forty-cdk-switch.d.ts +87 -0
  211. package/types/forty-cdk-table.d.ts +723 -0
  212. package/types/forty-cdk-tabs.d.ts +235 -0
  213. package/types/forty-cdk-time-field.d.ts +307 -0
  214. package/types/forty-cdk-time-picker.d.ts +578 -0
  215. package/types/forty-cdk-toast.d.ts +598 -0
  216. package/types/forty-cdk-toggle.d.ts +310 -0
  217. package/types/forty-cdk-toolbar.d.ts +217 -0
  218. package/types/forty-cdk-tooltip.d.ts +436 -0
  219. package/types/forty-cdk-tree.d.ts +688 -0
  220. package/types/forty-cdk.d.ts +1 -19743
@@ -0,0 +1,74 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, numberAttribute, computed, Directive } from '@angular/core';
3
+ import { createDefaults } from 'forty-cdk/core';
4
+
5
+ /**
6
+ * Headless aspect-ratio container. Locks the host element's box to a fixed
7
+ * `width / height` ratio via the native CSS `aspect-ratio` property — useful
8
+ * for media (video, image, iframe) and skeletons that must reserve space
9
+ * before content loads.
10
+ *
11
+ * The directive only writes the `aspect-ratio` style; sizing rules (width,
12
+ * max-width, background, etc.) stay with the consumer.
13
+ *
14
+ * @example
15
+ * ```html
16
+ * <div forAspectRatio [ratio]="16 / 9">
17
+ * <img src="cover.jpg" alt="" />
18
+ * </div>
19
+ *
20
+ * <div forAspectRatio ratio="1"> <!-- square -->
21
+ * <video src="clip.mp4" controls></video>
22
+ * </div>
23
+ * ```
24
+ */
25
+ class ForAspectRatio {
26
+ /**
27
+ * Width / height ratio, e.g. `16 / 9`, `4 / 3`, `1`. Accepts numeric inputs
28
+ * (`[ratio]="16 / 9"`) and string attributes (`ratio="1.5"`). Defaults to
29
+ * `1` (square). Non-positive or non-finite values fall back to `1` so the
30
+ * host never emits an invalid `aspect-ratio`.
31
+ */
32
+ ratio = input(1, { ...(ngDevMode ? { debugName: "ratio" } : /* istanbul ignore next */ {}), transform: numberAttribute });
33
+ /**
34
+ * The host `aspect-ratio` style value, guarded so a `0`, negative, or
35
+ * non-finite `ratio` never produces invalid CSS — it falls back to `1`.
36
+ */
37
+ resolvedRatio = computed(() => {
38
+ const value = this.ratio();
39
+ return String(Number.isFinite(value) && value > 0 ? value : 1);
40
+ }, /* @ts-ignore */
41
+ ...(ngDevMode ? [{ debugName: "resolvedRatio" }] : /* istanbul ignore next */ []));
42
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAspectRatio, deps: [], target: i0.ɵɵFactoryTarget.Directive });
43
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForAspectRatio, isStandalone: true, selector: "[forAspectRatio]", inputs: { ratio: { classPropertyName: "ratio", publicName: "ratio", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "style.aspect-ratio": "resolvedRatio()" } }, exportAs: ["forAspectRatio"], ngImport: i0 });
44
+ }
45
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAspectRatio, decorators: [{
46
+ type: Directive,
47
+ args: [{
48
+ selector: '[forAspectRatio]',
49
+ exportAs: 'forAspectRatio',
50
+ host: {
51
+ '[style.aspect-ratio]': 'resolvedRatio()',
52
+ },
53
+ }]
54
+ }], propDecorators: { ratio: [{ type: i0.Input, args: [{ isSignal: true, alias: "ratio", required: false }] }] } });
55
+
56
+ const FALLBACK = {};
57
+ const { token, provideDefaults } = createDefaults('FOR_ASPECT_RATIO_DEFAULTS', FALLBACK);
58
+ /** Token holding the resolved aspect-ratio defaults for the current scope. */
59
+ const FOR_ASPECT_RATIO_DEFAULTS = token;
60
+ /**
61
+ * Configures forty-cdk aspect-ratio defaults for this injector scope. Partial
62
+ * overrides inherit unspecified keys from the parent scope (or library
63
+ * defaults at the root).
64
+ */
65
+ function provideForAspectRatioDefaults(defaults = {}) {
66
+ return provideDefaults(defaults);
67
+ }
68
+
69
+ /**
70
+ * Generated bundle index. Do not edit.
71
+ */
72
+
73
+ export { FOR_ASPECT_RATIO_DEFAULTS, ForAspectRatio, provideForAspectRatioDefaults };
74
+ //# sourceMappingURL=forty-cdk-aspect-ratio.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forty-cdk-aspect-ratio.mjs","sources":["../../../projects/forty-cdk/aspect-ratio/src/aspect-ratio.ts","../../../projects/forty-cdk/aspect-ratio/src/aspect-ratio-defaults.ts","../../../projects/forty-cdk/aspect-ratio/src/forty-cdk-aspect-ratio.ts"],"sourcesContent":["import { Directive, computed, input, numberAttribute } from '@angular/core';\n\n/**\n * Headless aspect-ratio container. Locks the host element's box to a fixed\n * `width / height` ratio via the native CSS `aspect-ratio` property — useful\n * for media (video, image, iframe) and skeletons that must reserve space\n * before content loads.\n *\n * The directive only writes the `aspect-ratio` style; sizing rules (width,\n * max-width, background, etc.) stay with the consumer.\n *\n * @example\n * ```html\n * <div forAspectRatio [ratio]=\"16 / 9\">\n * <img src=\"cover.jpg\" alt=\"\" />\n * </div>\n *\n * <div forAspectRatio ratio=\"1\"> <!-- square -->\n * <video src=\"clip.mp4\" controls></video>\n * </div>\n * ```\n */\n@Directive({\n selector: '[forAspectRatio]',\n exportAs: 'forAspectRatio',\n host: {\n '[style.aspect-ratio]': 'resolvedRatio()',\n },\n})\nexport class ForAspectRatio {\n /**\n * Width / height ratio, e.g. `16 / 9`, `4 / 3`, `1`. Accepts numeric inputs\n * (`[ratio]=\"16 / 9\"`) and string attributes (`ratio=\"1.5\"`). Defaults to\n * `1` (square). Non-positive or non-finite values fall back to `1` so the\n * host never emits an invalid `aspect-ratio`.\n */\n readonly ratio = input(1, { transform: numberAttribute });\n\n /**\n * The host `aspect-ratio` style value, guarded so a `0`, negative, or\n * non-finite `ratio` never produces invalid CSS — it falls back to `1`.\n */\n protected readonly resolvedRatio = computed(() => {\n const value = this.ratio();\n return String(Number.isFinite(value) && value > 0 ? value : 1);\n });\n}\n","import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant aspect-ratio containers in the surrounding\n * injector scope. Configure with `provideForAspectRatioDefaults`. The shape\n * is a stub today — present so future per-scope tuning can land without\n * churning the public surface.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ForAspectRatioDefaults {}\n\nconst FALLBACK: ForAspectRatioDefaults = {};\n\nconst { token, provideDefaults } = createDefaults<ForAspectRatioDefaults>(\n 'FOR_ASPECT_RATIO_DEFAULTS',\n FALLBACK,\n);\n\n/** Token holding the resolved aspect-ratio defaults for the current scope. */\nexport const FOR_ASPECT_RATIO_DEFAULTS = token;\n\n/**\n * Configures forty-cdk aspect-ratio defaults for this injector scope. Partial\n * overrides inherit unspecified keys from the parent scope (or library\n * defaults at the root).\n */\nexport function provideForAspectRatioDefaults(\n defaults: Partial<ForAspectRatioDefaults> = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAEA;;;;;;;;;;;;;;;;;;;AAmBG;MAQU,cAAc,CAAA;AACzB;;;;;AAKG;IACM,KAAK,GAAG,KAAK,CAAC,CAAC,6EAAI,SAAS,EAAE,eAAe,EAAA,CAAG;AAEzD;;;AAGG;AACgB,IAAA,aAAa,GAAG,QAAQ,CAAC,MAAK;AAC/C,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE;QAC1B,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IAChE,CAAC;sFAAC;uGAhBS,cAAc,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAd,cAAc,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,oBAAA,EAAA,iBAAA,EAAA,EAAA,EAAA,QAAA,EAAA,CAAA,gBAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAAd,cAAc,EAAA,UAAA,EAAA,CAAA;kBAP1B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,kBAAkB;AAC5B,oBAAA,QAAQ,EAAE,gBAAgB;AAC1B,oBAAA,IAAI,EAAE;AACJ,wBAAA,sBAAsB,EAAE,iBAAiB;AAC1C,qBAAA;AACF,iBAAA;;;ACfD,MAAM,QAAQ,GAA2B,EAAE;AAE3C,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,GAAG,cAAc,CAC/C,2BAA2B,EAC3B,QAAQ,CACT;AAED;AACO,MAAM,yBAAyB,GAAG;AAEzC;;;;AAIG;AACG,SAAU,6BAA6B,CAC3C,QAAA,GAA4C,EAAE,EAAA;AAE9C,IAAA,OAAO,eAAe,CAAC,QAAQ,CAAC;AAClC;;AChCA;;AAEG;;;;"}
@@ -0,0 +1,308 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, input, numberAttribute, signal, linkedSignal, computed, effect, DestroyRef, Directive, ElementRef, output, afterNextRender, PLATFORM_ID } from '@angular/core';
3
+ import { createDefaults } from 'forty-cdk/core';
4
+ import { isPlatformBrowser } from '@angular/common';
5
+
6
+ const FOR_AVATAR_CONTEXT = new InjectionToken('FOR_AVATAR_CONTEXT');
7
+ /**
8
+ * Injects the nearest {@link ForAvatarContext}, throwing a descriptive error
9
+ * when used outside a `[forAvatar]` element.
10
+ *
11
+ * @param piece Name of the calling directive, used in the error message.
12
+ */
13
+ function injectAvatarContext(piece) {
14
+ const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });
15
+ if (!ctx) {
16
+ throw new Error(`[forty-cdk/avatar] ${piece} must be used inside a [forAvatar] element.`);
17
+ }
18
+ return ctx;
19
+ }
20
+
21
+ /**
22
+ * Library fallback for avatar defaults, read at the root injector when no
23
+ * consumer has called `provideForAvatarDefaults`. Exported for the shared defaults
24
+ * contract spec; not re-exported from the primitive's public entry.
25
+ */
26
+ const FOR_AVATAR_FALLBACK_DEFAULTS = {
27
+ fallbackDelayMs: 0,
28
+ };
29
+ const { token, provideDefaults } = createDefaults('FOR_AVATAR_DEFAULTS', FOR_AVATAR_FALLBACK_DEFAULTS);
30
+ /** Token holding the resolved avatar defaults for the current scope. */
31
+ const FOR_AVATAR_DEFAULTS = token;
32
+ /**
33
+ * Configures forty-cdk avatar defaults for this injector scope. Partial
34
+ * overrides inherit unspecified keys from the parent scope (or library
35
+ * defaults at the root).
36
+ */
37
+ function provideForAvatarDefaults(defaults = {}) {
38
+ return provideDefaults(defaults);
39
+ }
40
+
41
+ /**
42
+ * Headless avatar root. Tracks the load lifecycle of an inner
43
+ * `[forAvatarImage]` and exposes `status` + `shouldShowFallback` so the
44
+ * consumer can swap the image for fallback content (initials, generic icon)
45
+ * via `@if`.
46
+ *
47
+ * No ARIA role is imposed — avatars are typically presentational. If the
48
+ * surrounding component conveys identity (a person's name in the header,
49
+ * a chat bubble), the consumer should provide an `aria-label` on the
50
+ * relevant ancestor or pair the avatar with visible text.
51
+ *
52
+ * @example
53
+ * ```html
54
+ * <span forAvatar #a="forAvatar" fallbackDelayMs="500">
55
+ * <img forAvatarImage [src]="user.avatarUrl" [alt]="user.name" />
56
+ * @if (a.shouldShowFallback()) {
57
+ * <span forAvatarFallback>{{ user.initials }}</span>
58
+ * }
59
+ * </span>
60
+ * ```
61
+ */
62
+ class ForAvatar {
63
+ #defaults = inject(FOR_AVATAR_DEFAULTS);
64
+ /**
65
+ * Milliseconds to defer the fallback for fast loads, avoiding a brief
66
+ * "initials → image" flicker. Set to `0` (default) to show immediately
67
+ * during `idle` / `loading`, or to e.g. `500` to skip rendering during
68
+ * quick cached loads. `error` always shows the fallback immediately.
69
+ * The default is read from `provideForAvatarDefaults` for the
70
+ * surrounding scope.
71
+ */
72
+ fallbackDelayMs = input(this.#defaults.fallbackDelayMs, { ...(ngDevMode ? { debugName: "fallbackDelayMs" } : /* istanbul ignore next */ {}), transform: numberAttribute });
73
+ #status = signal('idle', /* @ts-ignore */
74
+ ...(ngDevMode ? [{ debugName: "#status" }] : /* istanbul ignore next */ []));
75
+ status = this.#status.asReadonly();
76
+ // Whether the deferred-fallback timer has fired for the current status/delay.
77
+ // `linkedSignal` resets it to `false` whenever the source changes (the
78
+ // canonical replacement for resetting state inside an effect); the timer
79
+ // callback in the constructor flips it to `true`.
80
+ #timerElapsed = linkedSignal({ ...(ngDevMode ? { debugName: "#timerElapsed" } : /* istanbul ignore next */ {}), source: () => ({ status: this.#status(), delay: this.fallbackDelayMs() }),
81
+ computation: () => false });
82
+ #timer = null;
83
+ shouldShowFallback = computed(() => {
84
+ const status = this.#status();
85
+ if (status === 'loaded')
86
+ return false;
87
+ if (status === 'error')
88
+ return true;
89
+ // idle | loading: an immediate (delay <= 0) gate shows the fallback right
90
+ // away; a deferred gate waits for the timer armed in the effect below.
91
+ if (this.fallbackDelayMs() <= 0)
92
+ return true;
93
+ return this.#timerElapsed();
94
+ }, /* @ts-ignore */
95
+ ...(ngDevMode ? [{ debugName: "shouldShowFallback" }] : /* istanbul ignore next */ []));
96
+ /** @internal */
97
+ reportStatus(status) {
98
+ this.#status.set(status);
99
+ }
100
+ constructor() {
101
+ // Imperative timer integration: the effect's only job is to (re)arm a
102
+ // setTimeout for the deferred-fallback case (idle | loading with a positive
103
+ // delay). The synchronous branches (loaded / error / delay <= 0) are pure
104
+ // derivations and live in `shouldShowFallback`. The signal write happens
105
+ // inside the timer callback, which runs outside the effect's reactive
106
+ // scope, so this does NOT create a self-cycle in the reactive graph.
107
+ effect(() => {
108
+ const status = this.#status();
109
+ const delay = this.fallbackDelayMs();
110
+ this.#cancelTimer();
111
+ if ((status === 'idle' || status === 'loading') && delay > 0) {
112
+ this.#timer = setTimeout(() => {
113
+ this.#timer = null;
114
+ this.#timerElapsed.set(true);
115
+ }, delay);
116
+ }
117
+ });
118
+ inject(DestroyRef).onDestroy(() => this.#cancelTimer());
119
+ }
120
+ #cancelTimer() {
121
+ if (this.#timer !== null) {
122
+ clearTimeout(this.#timer);
123
+ this.#timer = null;
124
+ }
125
+ }
126
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAvatar, deps: [], target: i0.ɵɵFactoryTarget.Directive });
127
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForAvatar, isStandalone: true, selector: "[forAvatar]", inputs: { fallbackDelayMs: { classPropertyName: "fallbackDelayMs", publicName: "fallbackDelayMs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-status": "status()" } }, providers: [{ provide: FOR_AVATAR_CONTEXT, useExisting: ForAvatar }], exportAs: ["forAvatar"], ngImport: i0 });
128
+ }
129
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAvatar, decorators: [{
130
+ type: Directive,
131
+ args: [{
132
+ selector: '[forAvatar]',
133
+ exportAs: 'forAvatar',
134
+ host: {
135
+ '[attr.data-status]': 'status()',
136
+ },
137
+ providers: [{ provide: FOR_AVATAR_CONTEXT, useExisting: ForAvatar }],
138
+ }]
139
+ }], ctorParameters: () => [], propDecorators: { fallbackDelayMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fallbackDelayMs", required: false }] }] } });
140
+
141
+ /**
142
+ * Wraps an `<img>` and reports its load lifecycle to the surrounding
143
+ * `[forAvatar]`. The native `src` / `alt` bindings are owned by the
144
+ * consumer — this directive only observes.
145
+ *
146
+ * Reflects `data-status` on the image element so the consumer can hide it
147
+ * via CSS while loading or after an error (e.g. `img[data-status="error"]
148
+ * { display: none }`).
149
+ */
150
+ class ForAvatarImage {
151
+ #host = inject(ElementRef).nativeElement;
152
+ #parent = inject(FOR_AVATAR_CONTEXT, { optional: true });
153
+ /**
154
+ * Emits whenever the image lifecycle transitions to a new state. Useful
155
+ * if the consumer wants to log analytics, swap to a different src on
156
+ * error, etc.
157
+ */
158
+ loadStatusChanged = output();
159
+ constructor() {
160
+ if (!this.#parent) {
161
+ throw new Error('[forty-cdk/avatar] ForAvatarImage must be used inside a [forAvatar] element.');
162
+ }
163
+ const parent = this.#parent;
164
+ const host = this.#host;
165
+ afterNextRender(() => {
166
+ this.#syncFromAttr();
167
+ });
168
+ if (isPlatformBrowser(inject(PLATFORM_ID)) && typeof MutationObserver !== 'undefined') {
169
+ const observer = new MutationObserver(() => {
170
+ this.#syncFromAttr();
171
+ });
172
+ observer.observe(host, { attributes: true, attributeFilter: ['src'] });
173
+ inject(DestroyRef).onDestroy(() => observer.disconnect());
174
+ }
175
+ // Re-emit upstream whenever the parent's status changes due to our writes.
176
+ // De-dupe on the (request, status) pair rather than status alone: a new
177
+ // `src` opens a fresh request token, so an identical status (e.g. a second
178
+ // image that also `loaded`) still re-reports for the new request, while a
179
+ // repeated status within the same request is suppressed.
180
+ let last = null;
181
+ let lastReportedToken = -1;
182
+ const emit = (status) => {
183
+ if (last === status && lastReportedToken === this.#requestToken)
184
+ return;
185
+ last = status;
186
+ lastReportedToken = this.#requestToken;
187
+ parent.reportStatus(status);
188
+ this.loadStatusChanged.emit(status);
189
+ };
190
+ this.#emit = emit;
191
+ }
192
+ status() {
193
+ return this.#parent.status();
194
+ }
195
+ onLoad() {
196
+ // Ignore a `load` that fires for a request the host has already moved past
197
+ // (the native event can arrive after `src` was reassigned).
198
+ if (!this.#isCurrentRequest())
199
+ return;
200
+ this.#emit('loaded');
201
+ }
202
+ onError() {
203
+ if (!this.#isCurrentRequest())
204
+ return;
205
+ this.#emit('error');
206
+ }
207
+ /**
208
+ * True when the `src` the host currently carries is the one the latest
209
+ * `#syncFromAttr` observed, so a native load/error event belongs to the
210
+ * in-flight request rather than a superseded one.
211
+ */
212
+ #isCurrentRequest() {
213
+ return this.#host.getAttribute('src') === this.#requestSrc;
214
+ }
215
+ #syncFromAttr() {
216
+ const src = this.#host.getAttribute('src');
217
+ this.#requestToken++;
218
+ this.#requestSrc = src;
219
+ if (!src) {
220
+ this.#emit('idle');
221
+ return;
222
+ }
223
+ if (this.#host.complete) {
224
+ // Cached image: load/error events may not fire — derive from naturalWidth.
225
+ if (this.#host.naturalWidth > 0) {
226
+ this.#emit('loaded');
227
+ return;
228
+ }
229
+ // A complete image with zero intrinsic width is ambiguous: it may be a
230
+ // broken image OR a valid SVG with no intrinsic dimensions. Don't
231
+ // pessimistically flag `error` and hide a valid image — verify with
232
+ // `decode()`, which resolves for a decodable (even zero-size) image and
233
+ // rejects for a broken one. Stay `loading` until it settles.
234
+ this.#emit('loading');
235
+ this.#verifyCachedZeroSize(src);
236
+ return;
237
+ }
238
+ this.#emit('loading');
239
+ }
240
+ #verifyCachedZeroSize(src) {
241
+ const decode = this.#host.decode?.bind(this.#host);
242
+ if (!decode) {
243
+ // No `decode()` (older engines / jsdom): keep the legacy heuristic.
244
+ this.#emit('error');
245
+ return;
246
+ }
247
+ decode().then(() => {
248
+ if (this.#host.getAttribute('src') === src) {
249
+ this.#emit('loaded');
250
+ }
251
+ }, () => {
252
+ if (this.#host.getAttribute('src') === src) {
253
+ this.#emit('error');
254
+ }
255
+ });
256
+ }
257
+ // Initialized in constructor.
258
+ #emit;
259
+ /** Monotonic id bumped on every `src` change so stale events are ignored. */
260
+ #requestToken = 0;
261
+ /** The `src` the latest `#syncFromAttr` observed, used to match native events. */
262
+ #requestSrc = null;
263
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAvatarImage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
264
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForAvatarImage, isStandalone: true, selector: "img[forAvatarImage]", outputs: { loadStatusChanged: "loadStatusChanged" }, host: { listeners: { "load": "onLoad()", "error": "onError()" }, properties: { "attr.data-status": "status()" } }, exportAs: ["forAvatarImage"], ngImport: i0 });
265
+ }
266
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAvatarImage, decorators: [{
267
+ type: Directive,
268
+ args: [{
269
+ selector: 'img[forAvatarImage]',
270
+ exportAs: 'forAvatarImage',
271
+ host: {
272
+ '[attr.data-status]': 'status()',
273
+ '(load)': 'onLoad()',
274
+ '(error)': 'onError()',
275
+ },
276
+ }]
277
+ }], ctorParameters: () => [], propDecorators: { loadStatusChanged: [{ type: i0.Output, args: ["loadStatusChanged"] }] } });
278
+
279
+ /**
280
+ * Marker for the avatar's fallback content (initials, generic icon, …).
281
+ * Reflects `data-status` from the parent `[forAvatar]` so the consumer can
282
+ * style for the `error` state separately from `idle` / `loading` if needed.
283
+ *
284
+ * The decision of *whether* to render the fallback lives on the root —
285
+ * drive `@if` with `forAvatar.shouldShowFallback()`.
286
+ */
287
+ class ForAvatarFallback {
288
+ parent = injectAvatarContext('ForAvatarFallback');
289
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAvatarFallback, deps: [], target: i0.ɵɵFactoryTarget.Directive });
290
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForAvatarFallback, isStandalone: true, selector: "[forAvatarFallback]", host: { properties: { "attr.data-status": "parent.status()" } }, exportAs: ["forAvatarFallback"], ngImport: i0 });
291
+ }
292
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForAvatarFallback, decorators: [{
293
+ type: Directive,
294
+ args: [{
295
+ selector: '[forAvatarFallback]',
296
+ exportAs: 'forAvatarFallback',
297
+ host: {
298
+ '[attr.data-status]': 'parent.status()',
299
+ },
300
+ }]
301
+ }] });
302
+
303
+ /**
304
+ * Generated bundle index. Do not edit.
305
+ */
306
+
307
+ export { FOR_AVATAR_CONTEXT, FOR_AVATAR_DEFAULTS, ForAvatar, ForAvatarFallback, ForAvatarImage, provideForAvatarDefaults };
308
+ //# sourceMappingURL=forty-cdk-avatar.mjs.map
@@ -0,0 +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\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\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 * Internal — reported by `[forAvatarImage]` so the root can transition\n * its state machine. Consumers should not call this.\n */\n reportStatus(status: ForAvatarStatus): void;\n}\n\nexport const FOR_AVATAR_CONTEXT = new InjectionToken<ForAvatarContext>('FOR_AVATAR_CONTEXT');\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): ForAvatarContext {\n const ctx = inject(FOR_AVATAR_CONTEXT, { optional: true });\n if (!ctx) {\n throw new Error(`[forty-cdk/avatar] ${piece} must be used inside a [forAvatar] element.`);\n }\n return ctx;\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 */\nexport function provideForAvatarDefaults(defaults: Partial<ForAvatarDefaults> = {}): Provider[] {\n return provideDefaults(defaults);\n}\n","import {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n input,\n linkedSignal,\n numberAttribute,\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: numberAttribute,\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\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 /** @internal */\n 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 ((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 { FOR_AVATAR_CONTEXT, type ForAvatarStatus } 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 = inject(FOR_AVATAR_CONTEXT, { optional: true });\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 loadStatusChanged = output<ForAvatarStatus>();\n\n constructor() {\n if (!this.#parent) {\n throw new Error(\n '[forty-cdk/avatar] ForAvatarImage must be used inside a [forAvatar] element.',\n );\n }\n const parent = this.#parent;\n const host = this.#host;\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 (last === status && lastReportedToken === this.#requestToken) return;\n last = status;\n lastReportedToken = this.#requestToken;\n parent.reportStatus(status);\n this.loadStatusChanged.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 this.#emit('loaded');\n }\n\n protected onError(): void {\n if (!this.#isCurrentRequest()) return;\n this.#emit('error');\n }\n\n /**\n * True when the `src` the host currently carries is the one the latest\n * `#syncFromAttr` observed, so a native load/error event belongs to the\n * in-flight request rather than a superseded one.\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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"}
@@ -0,0 +1,125 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, Directive, booleanAttribute } from '@angular/core';
3
+ import { createDefaults } from 'forty-cdk/core';
4
+
5
+ /**
6
+ * Headless breadcrumb trail implementing the
7
+ * [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/):
8
+ * a `navigation` landmark wrapping a set of links that reveal the page's
9
+ * position within the site hierarchy.
10
+ *
11
+ * The directive contributes only the landmark role + accessible name; the
12
+ * consumer owns the markup structure (an `<ol>` of `<li>` links is the APG
13
+ * recommendation) and applies their own styles. Each link decorated with
14
+ * `[forBreadcrumbItem]` can mark itself the current page via `current`, and
15
+ * visual dividers decorated with `[forBreadcrumbSeparator]` are hidden from
16
+ * assistive technology.
17
+ *
18
+ * There is no keyboard interaction or focus management beyond what the native
19
+ * links provide — breadcrumb links are ordinary tab stops.
20
+ *
21
+ * @example
22
+ * ```html
23
+ * <nav forBreadcrumbs>
24
+ * <ol>
25
+ * <li><a forBreadcrumbItem href="/">Home</a></li>
26
+ * <li forBreadcrumbSeparator aria-hidden="true">/</li>
27
+ * <li><a forBreadcrumbItem href="/library">Library</a></li>
28
+ * <li forBreadcrumbSeparator aria-hidden="true">/</li>
29
+ * <li><a forBreadcrumbItem href="/library/data" current>Data</a></li>
30
+ * </ol>
31
+ * </nav>
32
+ * ```
33
+ */
34
+ class ForBreadcrumbs {
35
+ /**
36
+ * Accessible label for the navigation landmark. Defaults to `'Breadcrumb'`
37
+ * so a bare `<nav forBreadcrumbs>` is already a correctly labelled landmark —
38
+ * the conventional name for this pattern. Override it (or point a native
39
+ * `aria-labelledby` at a visible heading) when the page hosts more than one
40
+ * breadcrumb trail and they need to be told apart.
41
+ */
42
+ ariaLabel = input(null, /* @ts-ignore */
43
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
44
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbs, deps: [], target: i0.ɵɵFactoryTarget.Directive });
45
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForBreadcrumbs, isStandalone: true, selector: "[forBreadcrumbs]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "navigation" }, properties: { "attr.aria-label": "ariaLabel() || 'Breadcrumb'" } }, exportAs: ["forBreadcrumbs"], ngImport: i0 });
46
+ }
47
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbs, decorators: [{
48
+ type: Directive,
49
+ args: [{
50
+ selector: '[forBreadcrumbs]',
51
+ exportAs: 'forBreadcrumbs',
52
+ host: {
53
+ role: 'navigation',
54
+ '[attr.aria-label]': "ariaLabel() || 'Breadcrumb'",
55
+ },
56
+ }]
57
+ }], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
58
+
59
+ /**
60
+ * A single link within a breadcrumb trail. Apply on an `<a>` so navigation is
61
+ * native. Set `current` on the link that points to the page the user is on —
62
+ * it reflects [`aria-current="page"`](https://www.w3.org/TR/wai-aria-1.2/#aria-current)
63
+ * so assistive technology announces it as the current location.
64
+ */
65
+ class ForBreadcrumbItem {
66
+ /**
67
+ * Whether this item represents the page the user is currently on. The
68
+ * current item reflects `aria-current="page"`; all others emit no
69
+ * `aria-current` attribute.
70
+ */
71
+ current = input(false, { ...(ngDevMode ? { debugName: "current" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
72
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
73
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForBreadcrumbItem, isStandalone: true, selector: "[forBreadcrumbItem]", inputs: { current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-current": "current() ? \"page\" : null" } }, exportAs: ["forBreadcrumbItem"], ngImport: i0 });
74
+ }
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbItem, decorators: [{
76
+ type: Directive,
77
+ args: [{
78
+ selector: '[forBreadcrumbItem]',
79
+ exportAs: 'forBreadcrumbItem',
80
+ host: {
81
+ '[attr.aria-current]': 'current() ? "page" : null',
82
+ },
83
+ }]
84
+ }], propDecorators: { current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }] } });
85
+
86
+ /**
87
+ * Decorative divider rendered between breadcrumb items (a `/`, `>`, chevron,
88
+ * etc.). Reflects `aria-hidden="true"` so assistive technology skips it — the
89
+ * trail's structure is already conveyed by the list and the links, making the
90
+ * visual separator redundant in the accessibility tree.
91
+ */
92
+ class ForBreadcrumbSeparator {
93
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
94
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForBreadcrumbSeparator, isStandalone: true, selector: "[forBreadcrumbSeparator]", host: { attributes: { "aria-hidden": "true" } }, exportAs: ["forBreadcrumbSeparator"], ngImport: i0 });
95
+ }
96
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForBreadcrumbSeparator, decorators: [{
97
+ type: Directive,
98
+ args: [{
99
+ selector: '[forBreadcrumbSeparator]',
100
+ exportAs: 'forBreadcrumbSeparator',
101
+ host: {
102
+ 'aria-hidden': 'true',
103
+ },
104
+ }]
105
+ }] });
106
+
107
+ const FALLBACK = {};
108
+ const { token, provideDefaults } = createDefaults('FOR_BREADCRUMBS_DEFAULTS', FALLBACK);
109
+ /** Token holding the resolved breadcrumbs defaults for the current scope. */
110
+ const FOR_BREADCRUMBS_DEFAULTS = token;
111
+ /**
112
+ * Configures forty-cdk breadcrumbs defaults for this injector scope. Partial
113
+ * overrides inherit unspecified keys from the parent scope (or library
114
+ * defaults at the root).
115
+ */
116
+ function provideForBreadcrumbsDefaults(defaults = {}) {
117
+ return provideDefaults(defaults);
118
+ }
119
+
120
+ /**
121
+ * Generated bundle index. Do not edit.
122
+ */
123
+
124
+ export { FOR_BREADCRUMBS_DEFAULTS, ForBreadcrumbItem, ForBreadcrumbSeparator, ForBreadcrumbs, provideForBreadcrumbsDefaults };
125
+ //# sourceMappingURL=forty-cdk-breadcrumbs.mjs.map
@@ -0,0 +1 @@
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/breadcrumbs-defaults.ts","../../../projects/forty-cdk/breadcrumbs/src/forty-cdk-breadcrumbs.ts"],"sourcesContent":["import { Directive, input } from '@angular/core';\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]': \"ariaLabel() || 'Breadcrumb'\",\n },\n})\nexport class ForBreadcrumbs {\n /**\n * Accessible label for the navigation landmark. Defaults to `'Breadcrumb'`\n * so a bare `<nav forBreadcrumbs>` is already a correctly labelled landmark —\n * the conventional name for this pattern. Override it (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.\n */\n readonly ariaLabel = input<string | null>(null);\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","import { type Provider } from '@angular/core';\n\nimport { createDefaults } from 'forty-cdk/core';\n\n/**\n * Defaults inherited by descendant breadcrumbs in the surrounding injector\n * scope. Configure with `provideForBreadcrumbsDefaults`. The shape is a stub\n * today — present so future per-scope tuning can land without churning the\n * public surface.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ForBreadcrumbsDefaults {}\n\nconst FALLBACK: ForBreadcrumbsDefaults = {};\n\nconst { token, provideDefaults } = createDefaults<ForBreadcrumbsDefaults>(\n 'FOR_BREADCRUMBS_DEFAULTS',\n FALLBACK,\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 */\nexport function provideForBreadcrumbsDefaults(\n defaults: Partial<ForBreadcrumbsDefaults> = {},\n): Provider[] {\n return provideDefaults(defaults);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}