@hyalo/angular 0.1.0 → 0.3.1

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 (198) hide show
  1. package/CHANGELOG.md +95 -41
  2. package/README.md +46 -32
  3. package/fesm2022/hyalo-angular-accordion.mjs +6 -6
  4. package/fesm2022/hyalo-angular-accordion.mjs.map +1 -1
  5. package/fesm2022/hyalo-angular-alert.mjs +171 -0
  6. package/fesm2022/hyalo-angular-alert.mjs.map +1 -0
  7. package/fesm2022/hyalo-angular-aspect-ratio.mjs +64 -0
  8. package/fesm2022/hyalo-angular-aspect-ratio.mjs.map +1 -0
  9. package/fesm2022/hyalo-angular-avatar.mjs +169 -0
  10. package/fesm2022/hyalo-angular-avatar.mjs.map +1 -0
  11. package/fesm2022/hyalo-angular-badge.mjs +268 -0
  12. package/fesm2022/hyalo-angular-badge.mjs.map +1 -0
  13. package/fesm2022/hyalo-angular-bento.mjs +132 -0
  14. package/fesm2022/hyalo-angular-bento.mjs.map +1 -0
  15. package/fesm2022/hyalo-angular-blocks.mjs +985 -0
  16. package/fesm2022/hyalo-angular-blocks.mjs.map +1 -0
  17. package/fesm2022/hyalo-angular-border-beam.mjs +73 -0
  18. package/fesm2022/hyalo-angular-border-beam.mjs.map +1 -0
  19. package/fesm2022/hyalo-angular-breadcrumb.mjs +77 -0
  20. package/fesm2022/hyalo-angular-breadcrumb.mjs.map +1 -0
  21. package/fesm2022/hyalo-angular-button-group.mjs +90 -0
  22. package/fesm2022/hyalo-angular-button-group.mjs.map +1 -0
  23. package/fesm2022/hyalo-angular-button.mjs +4 -3
  24. package/fesm2022/hyalo-angular-button.mjs.map +1 -1
  25. package/fesm2022/hyalo-angular-card.mjs +2 -2
  26. package/fesm2022/hyalo-angular-card.mjs.map +1 -1
  27. package/fesm2022/hyalo-angular-carousel.mjs +709 -0
  28. package/fesm2022/hyalo-angular-carousel.mjs.map +1 -0
  29. package/fesm2022/hyalo-angular-chapter-nav.mjs +336 -0
  30. package/fesm2022/hyalo-angular-chapter-nav.mjs.map +1 -0
  31. package/fesm2022/hyalo-angular-chart-engine-DSJop6He.mjs +5 -0
  32. package/fesm2022/hyalo-angular-chart-engine-DSJop6He.mjs.map +1 -0
  33. package/fesm2022/hyalo-angular-chart.mjs +391 -0
  34. package/fesm2022/hyalo-angular-chart.mjs.map +1 -0
  35. package/fesm2022/hyalo-angular-choice.mjs +2 -1
  36. package/fesm2022/hyalo-angular-choice.mjs.map +1 -1
  37. package/fesm2022/hyalo-angular-code-block-highlight-B20d5C76.mjs +40 -0
  38. package/fesm2022/hyalo-angular-code-block-highlight-B20d5C76.mjs.map +1 -0
  39. package/fesm2022/hyalo-angular-code-block.mjs +161 -0
  40. package/fesm2022/hyalo-angular-code-block.mjs.map +1 -0
  41. package/fesm2022/hyalo-angular-command.mjs +193 -0
  42. package/fesm2022/hyalo-angular-command.mjs.map +1 -0
  43. package/fesm2022/hyalo-angular-copy.mjs +138 -0
  44. package/fesm2022/hyalo-angular-copy.mjs.map +1 -0
  45. package/fesm2022/hyalo-angular-core.mjs +424 -0
  46. package/fesm2022/hyalo-angular-core.mjs.map +1 -0
  47. package/fesm2022/hyalo-angular-description-list.mjs +57 -0
  48. package/fesm2022/hyalo-angular-description-list.mjs.map +1 -0
  49. package/fesm2022/hyalo-angular-dialog.mjs +51 -22
  50. package/fesm2022/hyalo-angular-dialog.mjs.map +1 -1
  51. package/fesm2022/hyalo-angular-divider.mjs +66 -0
  52. package/fesm2022/hyalo-angular-divider.mjs.map +1 -0
  53. package/fesm2022/hyalo-angular-empty-state.mjs +123 -0
  54. package/fesm2022/hyalo-angular-empty-state.mjs.map +1 -0
  55. package/fesm2022/hyalo-angular-field.mjs +41 -9
  56. package/fesm2022/hyalo-angular-field.mjs.map +1 -1
  57. package/fesm2022/hyalo-angular-filter.mjs +309 -0
  58. package/fesm2022/hyalo-angular-filter.mjs.map +1 -0
  59. package/fesm2022/hyalo-angular-hotspot.mjs +225 -0
  60. package/fesm2022/hyalo-angular-hotspot.mjs.map +1 -0
  61. package/fesm2022/hyalo-angular-hover-card.mjs +233 -0
  62. package/fesm2022/hyalo-angular-hover-card.mjs.map +1 -0
  63. package/fesm2022/hyalo-angular-icon.mjs +42 -0
  64. package/fesm2022/hyalo-angular-icon.mjs.map +1 -1
  65. package/fesm2022/hyalo-angular-image-compare.mjs +168 -0
  66. package/fesm2022/hyalo-angular-image-compare.mjs.map +1 -0
  67. package/fesm2022/hyalo-angular-interaction.mjs +32 -41
  68. package/fesm2022/hyalo-angular-interaction.mjs.map +1 -1
  69. package/fesm2022/hyalo-angular-kbd.mjs +41 -0
  70. package/fesm2022/hyalo-angular-kbd.mjs.map +1 -0
  71. package/fesm2022/hyalo-angular-lightbox.mjs +228 -0
  72. package/fesm2022/hyalo-angular-lightbox.mjs.map +1 -0
  73. package/fesm2022/hyalo-angular-link.mjs +42 -0
  74. package/fesm2022/hyalo-angular-link.mjs.map +1 -0
  75. package/fesm2022/hyalo-angular-list.mjs +131 -0
  76. package/fesm2022/hyalo-angular-list.mjs.map +1 -0
  77. package/fesm2022/hyalo-angular-marquee.mjs +197 -0
  78. package/fesm2022/hyalo-angular-marquee.mjs.map +1 -0
  79. package/fesm2022/hyalo-angular-menu.mjs +423 -0
  80. package/fesm2022/hyalo-angular-menu.mjs.map +1 -0
  81. package/fesm2022/hyalo-angular-mesh.mjs +126 -0
  82. package/fesm2022/hyalo-angular-mesh.mjs.map +1 -0
  83. package/fesm2022/hyalo-angular-motion.mjs +620 -0
  84. package/fesm2022/hyalo-angular-motion.mjs.map +1 -0
  85. package/fesm2022/hyalo-angular-navbar.mjs +1 -1
  86. package/fesm2022/hyalo-angular-navbar.mjs.map +1 -1
  87. package/fesm2022/hyalo-angular-navigation-menu.mjs +222 -0
  88. package/fesm2022/hyalo-angular-navigation-menu.mjs.map +1 -0
  89. package/fesm2022/hyalo-angular-number-ticker.mjs +174 -0
  90. package/fesm2022/hyalo-angular-number-ticker.mjs.map +1 -0
  91. package/fesm2022/hyalo-angular-overlay.mjs +71 -2
  92. package/fesm2022/hyalo-angular-overlay.mjs.map +1 -1
  93. package/fesm2022/hyalo-angular-pagination.mjs +187 -0
  94. package/fesm2022/hyalo-angular-pagination.mjs.map +1 -0
  95. package/fesm2022/hyalo-angular-popover.mjs +34 -5
  96. package/fesm2022/hyalo-angular-popover.mjs.map +1 -1
  97. package/fesm2022/hyalo-angular-progress.mjs +209 -0
  98. package/fesm2022/hyalo-angular-progress.mjs.map +1 -0
  99. package/fesm2022/hyalo-angular-progressive-blur.mjs +73 -0
  100. package/fesm2022/hyalo-angular-progressive-blur.mjs.map +1 -0
  101. package/fesm2022/hyalo-angular-refraction-renderer-5ZA-kfsA.mjs +186 -0
  102. package/fesm2022/hyalo-angular-refraction-renderer-5ZA-kfsA.mjs.map +1 -0
  103. package/fesm2022/hyalo-angular-refraction.mjs +178 -0
  104. package/fesm2022/hyalo-angular-refraction.mjs.map +1 -0
  105. package/fesm2022/hyalo-angular-scroll-area.mjs +93 -0
  106. package/fesm2022/hyalo-angular-scroll-area.mjs.map +1 -0
  107. package/fesm2022/hyalo-angular-scroll-to-top.mjs +127 -0
  108. package/fesm2022/hyalo-angular-scroll-to-top.mjs.map +1 -0
  109. package/fesm2022/hyalo-angular-segmented.mjs +1 -1
  110. package/fesm2022/hyalo-angular-segmented.mjs.map +1 -1
  111. package/fesm2022/hyalo-angular-select.mjs +112 -15
  112. package/fesm2022/hyalo-angular-select.mjs.map +1 -1
  113. package/fesm2022/hyalo-angular-sidebar.mjs +241 -0
  114. package/fesm2022/hyalo-angular-sidebar.mjs.map +1 -0
  115. package/fesm2022/hyalo-angular-skeleton.mjs +131 -0
  116. package/fesm2022/hyalo-angular-skeleton.mjs.map +1 -0
  117. package/fesm2022/hyalo-angular-sparkline.mjs +110 -0
  118. package/fesm2022/hyalo-angular-sparkline.mjs.map +1 -0
  119. package/fesm2022/hyalo-angular-split-text.mjs +126 -0
  120. package/fesm2022/hyalo-angular-split-text.mjs.map +1 -0
  121. package/fesm2022/hyalo-angular-stat.mjs +193 -0
  122. package/fesm2022/hyalo-angular-stat.mjs.map +1 -0
  123. package/fesm2022/hyalo-angular-stepper.mjs +254 -0
  124. package/fesm2022/hyalo-angular-stepper.mjs.map +1 -0
  125. package/fesm2022/hyalo-angular-table.mjs +335 -0
  126. package/fesm2022/hyalo-angular-table.mjs.map +1 -0
  127. package/fesm2022/hyalo-angular-tabs.mjs +1 -1
  128. package/fesm2022/hyalo-angular-tabs.mjs.map +1 -1
  129. package/fesm2022/hyalo-angular-theme.mjs +1 -1
  130. package/fesm2022/hyalo-angular-theme.mjs.map +1 -1
  131. package/fesm2022/hyalo-angular-timeline.mjs +157 -0
  132. package/fesm2022/hyalo-angular-timeline.mjs.map +1 -0
  133. package/fesm2022/hyalo-angular-toast.mjs +2 -1
  134. package/fesm2022/hyalo-angular-toast.mjs.map +1 -1
  135. package/fesm2022/hyalo-angular.mjs +2 -473
  136. package/package.json +198 -2
  137. package/styles/parts/field.css +44 -36
  138. package/styles/parts/glass.css +2 -0
  139. package/styles/parts/motion.css +19 -0
  140. package/styles/parts/typography.css +17 -0
  141. package/types/hyalo-angular-accordion.d.ts +1 -1
  142. package/types/hyalo-angular-alert.d.ts +47 -0
  143. package/types/hyalo-angular-aspect-ratio.d.ts +13 -0
  144. package/types/hyalo-angular-avatar.d.ts +39 -0
  145. package/types/hyalo-angular-badge.d.ts +82 -0
  146. package/types/hyalo-angular-bento.d.ts +25 -0
  147. package/types/hyalo-angular-blocks.d.ts +272 -0
  148. package/types/hyalo-angular-border-beam.d.ts +15 -0
  149. package/types/hyalo-angular-breadcrumb.d.ts +22 -0
  150. package/types/hyalo-angular-button-group.d.ts +19 -0
  151. package/types/hyalo-angular-button.d.ts +4 -3
  152. package/types/hyalo-angular-card.d.ts +1 -1
  153. package/types/hyalo-angular-carousel.d.ts +147 -0
  154. package/types/hyalo-angular-chapter-nav.d.ts +79 -0
  155. package/types/hyalo-angular-chart.d.ts +90 -0
  156. package/types/hyalo-angular-code-block.d.ts +46 -0
  157. package/types/hyalo-angular-command.d.ts +41 -0
  158. package/types/hyalo-angular-copy.d.ts +39 -0
  159. package/types/hyalo-angular-core.d.ts +210 -0
  160. package/types/hyalo-angular-description-list.d.ts +16 -0
  161. package/types/hyalo-angular-dialog.d.ts +2 -1
  162. package/types/hyalo-angular-divider.d.ts +12 -0
  163. package/types/hyalo-angular-empty-state.d.ts +30 -0
  164. package/types/hyalo-angular-field.d.ts +10 -4
  165. package/types/hyalo-angular-filter.d.ts +122 -0
  166. package/types/hyalo-angular-hotspot.d.ts +70 -0
  167. package/types/hyalo-angular-hover-card.d.ts +47 -0
  168. package/types/hyalo-angular-image-compare.d.ts +55 -0
  169. package/types/hyalo-angular-kbd.d.ts +5 -0
  170. package/types/hyalo-angular-lightbox.d.ts +67 -0
  171. package/types/hyalo-angular-link.d.ts +5 -0
  172. package/types/hyalo-angular-list.d.ts +30 -0
  173. package/types/hyalo-angular-marquee.d.ts +48 -0
  174. package/types/hyalo-angular-menu.d.ts +105 -0
  175. package/types/hyalo-angular-mesh.d.ts +36 -0
  176. package/types/hyalo-angular-motion.d.ts +218 -0
  177. package/types/hyalo-angular-navigation-menu.d.ts +47 -0
  178. package/types/hyalo-angular-number-ticker.d.ts +52 -0
  179. package/types/hyalo-angular-overlay.d.ts +9 -0
  180. package/types/hyalo-angular-pagination.d.ts +59 -0
  181. package/types/hyalo-angular-popover.d.ts +10 -0
  182. package/types/hyalo-angular-progress.d.ts +46 -0
  183. package/types/hyalo-angular-progressive-blur.d.ts +14 -0
  184. package/types/hyalo-angular-refraction.d.ts +46 -0
  185. package/types/hyalo-angular-scroll-area.d.ts +18 -0
  186. package/types/hyalo-angular-scroll-to-top.d.ts +31 -0
  187. package/types/hyalo-angular-segmented.d.ts +1 -1
  188. package/types/hyalo-angular-select.d.ts +49 -3
  189. package/types/hyalo-angular-sidebar.d.ts +57 -0
  190. package/types/hyalo-angular-skeleton.d.ts +34 -0
  191. package/types/hyalo-angular-sparkline.d.ts +19 -0
  192. package/types/hyalo-angular-split-text.d.ts +24 -0
  193. package/types/hyalo-angular-stat.d.ts +56 -0
  194. package/types/hyalo-angular-stepper.d.ts +63 -0
  195. package/types/hyalo-angular-table.d.ts +125 -0
  196. package/types/hyalo-angular-timeline.d.ts +36 -0
  197. package/types/hyalo-angular.d.ts +2 -345
  198. package/fesm2022/hyalo-angular.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"hyalo-angular-tabs.mjs","names":["trackIndicator"],"sources":["../../../projects/hyalo/tabs/tabs.ts","../../../projects/hyalo/tabs/tabs.html"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n booleanAttribute,\r\n computed,\r\n contentChildren,\r\n inject,\r\n input,\r\n model,\r\n viewChild,\r\n viewChildren,\r\n} from '@angular/core';\r\nimport { Tab, TabList, TabPanel, Tabs } from '@angular/aria/tabs';\r\nimport { HyIcon } from '@hyalo/angular/icon';\r\n\r\nimport { ɵtrackIndicator as trackIndicator } from '@hyalo/angular';\r\n\r\n/**\r\n * One tab and its panel. The panel content is rendered up front and hidden\r\n * with `inert` while another tab is selected.\r\n */\r\n@Component({\r\n selector: 'hy-tab',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n hostDirectives: [{ directive: TabPanel, inputs: ['value'] }],\r\n host: { class: 'hy-tab-panel' },\r\n template: `<ng-content />`,\r\n styleUrl: './tab.css',\r\n})\r\nexport class HyTab {\r\n readonly panel = inject(TabPanel);\r\n /** Text of the tab button. */\r\n readonly label = input.required<string>();\r\n /** Optional icon name shown before the label. */\r\n readonly icon = input<string>();\r\n /** Disables this tab. Arrow keys skip it. */\r\n readonly disabled = input(false, { transform: booleanAttribute });\r\n}\r\n\r\n/**\r\n * Tabs with a sliding glass indicator, built on Angular Aria. Arrow keys\r\n * move between tabs (mirrored in RTL), Home and End jump to the ends.\r\n *\r\n * ```html\r\n * <hy-tabs label=\"Watch details\" [(selected)]=\"tab\">\r\n * <hy-tab value=\"overview\" label=\"Overview\">...</hy-tab>\r\n * <hy-tab value=\"specs\" label=\"Specifications\">...</hy-tab>\r\n * </hy-tabs>\r\n * ```\r\n *\r\n * With `selectionMode=\"follow\"` (the default) a tab opens as soon as it is\r\n * focused. Use `explicit` when panels are expensive, so users press Enter or\r\n * Space to open one.\r\n */\r\n@Component({\r\n selector: 'hy-tabs',\r\n imports: [TabList, Tab, HyIcon],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n hostDirectives: [Tabs],\r\n host: { class: 'hy-tabs', '[attr.data-stretch]': 'stretch() || null' },\r\n templateUrl: './tabs.html',\r\n styleUrl: './tabs.css',\r\n})\r\nexport class HyTabs {\r\n protected readonly tabs = contentChildren(HyTab);\r\n private readonly list = viewChild<ElementRef<HTMLElement>>('list');\r\n private readonly tabEls = viewChildren('tabEl', { read: ElementRef<HTMLElement> });\r\n\r\n /** Accessible name for the tab list. */\r\n readonly label = input<string>();\r\n /** Value of the selected tab. Two-way bindable. Defaults to the first tab. */\r\n readonly selected = model<string | undefined>(undefined);\r\n /** `follow` opens a tab on focus, `explicit` waits for Enter or Space. */\r\n readonly selectionMode = input<'follow' | 'explicit'>('follow');\r\n /** Stretch tabs to fill the row. */\r\n readonly stretch = input(false, { transform: booleanAttribute });\r\n\r\n /** The selected value, falling back to the first enabled tab. */\r\n protected readonly current = computed(\r\n () =>\r\n this.selected() ??\r\n this.tabs()\r\n .find((t) => !t.disabled())\r\n ?.panel.value(),\r\n );\r\n\r\n protected readonly ready = trackIndicator(this.list, () => {\r\n const index = this.tabs().findIndex((t) => t.panel.value() === this.current());\r\n return this.tabEls()[index]?.nativeElement ?? null;\r\n });\r\n}\r\n\r\n/** Everything needed to build tabs. */\r\nexport const HY_TABS = [HyTabs, HyTab] as const;\r\n","<div\r\n #list\r\n ngTabList\r\n class=\"hy-tabs__list\"\r\n [attr.data-ready]=\"ready() || null\"\r\n [attr.aria-label]=\"label() || null\"\r\n [selectedTab]=\"current()\"\r\n (selectedTabChange)=\"selected.set($event)\"\r\n [selectionMode]=\"selectionMode()\"\r\n [focusMode]=\"'roving'\"\r\n>\r\n <span class=\"hy-tabs__indicator\" aria-hidden=\"true\"></span>\r\n @for (tab of tabs(); track tab.panel.value()) {\r\n <button\r\n #tabEl\r\n type=\"button\"\r\n ngTab\r\n class=\"hy-tabs__tab\"\r\n [value]=\"tab.panel.value()\"\r\n [disabled]=\"tab.disabled()\"\r\n >\r\n @if (tab.icon(); as icon) {\r\n <hy-icon [name]=\"icon\" />\r\n }\r\n {{ tab.label() }}\r\n </button>\r\n }\r\n</div>\r\n<div class=\"hy-tabs__panels\"><ng-content /></div>\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"hyalo-angular-tabs.mjs","names":["trackIndicator"],"sources":["../../../projects/hyalo/tabs/tabs.ts","../../../projects/hyalo/tabs/tabs.html"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n booleanAttribute,\r\n computed,\r\n contentChildren,\r\n inject,\r\n input,\r\n model,\r\n viewChild,\r\n viewChildren,\r\n} from '@angular/core';\r\nimport { Tab, TabList, TabPanel, Tabs } from '@angular/aria/tabs';\r\nimport { HyIcon } from '@hyalo/angular/icon';\r\n\r\nimport { ɵtrackIndicator as trackIndicator } from '@hyalo/angular/core';\r\n\r\n/**\r\n * One tab and its panel. The panel content is rendered up front and hidden\r\n * with `inert` while another tab is selected.\r\n */\r\n@Component({\r\n selector: 'hy-tab',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n hostDirectives: [{ directive: TabPanel, inputs: ['value'] }],\r\n host: { class: 'hy-tab-panel' },\r\n template: `<ng-content />`,\r\n styleUrl: './tab.css',\r\n})\r\nexport class HyTab {\r\n readonly panel = inject(TabPanel);\r\n /** Text of the tab button. */\r\n readonly label = input.required<string>();\r\n /** Optional icon name shown before the label. */\r\n readonly icon = input<string>();\r\n /** Disables this tab. Arrow keys skip it. */\r\n readonly disabled = input(false, { transform: booleanAttribute });\r\n}\r\n\r\n/**\r\n * Tabs with a sliding glass indicator, built on Angular Aria. Arrow keys\r\n * move between tabs (mirrored in RTL), Home and End jump to the ends.\r\n *\r\n * ```html\r\n * <hy-tabs label=\"Watch details\" [(selected)]=\"tab\">\r\n * <hy-tab value=\"overview\" label=\"Overview\">...</hy-tab>\r\n * <hy-tab value=\"specs\" label=\"Specifications\">...</hy-tab>\r\n * </hy-tabs>\r\n * ```\r\n *\r\n * With `selectionMode=\"follow\"` (the default) a tab opens as soon as it is\r\n * focused. Use `explicit` when panels are expensive, so users press Enter or\r\n * Space to open one.\r\n */\r\n@Component({\r\n selector: 'hy-tabs',\r\n imports: [TabList, Tab, HyIcon],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n hostDirectives: [Tabs],\r\n host: { class: 'hy-tabs', '[attr.data-stretch]': 'stretch() || null' },\r\n templateUrl: './tabs.html',\r\n styleUrl: './tabs.css',\r\n})\r\nexport class HyTabs {\r\n protected readonly tabs = contentChildren(HyTab);\r\n private readonly list = viewChild<ElementRef<HTMLElement>>('list');\r\n private readonly tabEls = viewChildren('tabEl', { read: ElementRef<HTMLElement> });\r\n\r\n /** Accessible name for the tab list. */\r\n readonly label = input<string>();\r\n /** Value of the selected tab. Two-way bindable. Defaults to the first tab. */\r\n readonly selected = model<string | undefined>(undefined);\r\n /** `follow` opens a tab on focus, `explicit` waits for Enter or Space. */\r\n readonly selectionMode = input<'follow' | 'explicit'>('follow');\r\n /** Stretch tabs to fill the row. */\r\n readonly stretch = input(false, { transform: booleanAttribute });\r\n\r\n /** The selected value, falling back to the first enabled tab. */\r\n protected readonly current = computed(\r\n () =>\r\n this.selected() ??\r\n this.tabs()\r\n .find((t) => !t.disabled())\r\n ?.panel.value(),\r\n );\r\n\r\n protected readonly ready = trackIndicator(this.list, () => {\r\n const index = this.tabs().findIndex((t) => t.panel.value() === this.current());\r\n return this.tabEls()[index]?.nativeElement ?? null;\r\n });\r\n}\r\n\r\n/** Everything needed to build tabs. */\r\nexport const HY_TABS = [HyTabs, HyTab] as const;\r\n","<div\r\n #list\r\n ngTabList\r\n class=\"hy-tabs__list\"\r\n [attr.data-ready]=\"ready() || null\"\r\n [attr.aria-label]=\"label() || null\"\r\n [selectedTab]=\"current()\"\r\n (selectedTabChange)=\"selected.set($event)\"\r\n [selectionMode]=\"selectionMode()\"\r\n [focusMode]=\"'roving'\"\r\n>\r\n <span class=\"hy-tabs__indicator\" aria-hidden=\"true\"></span>\r\n @for (tab of tabs(); track tab.panel.value()) {\r\n <button\r\n #tabEl\r\n type=\"button\"\r\n ngTab\r\n class=\"hy-tabs__tab\"\r\n [value]=\"tab.panel.value()\"\r\n [disabled]=\"tab.disabled()\"\r\n >\r\n @if (tab.icon(); as icon) {\r\n <hy-icon [name]=\"icon\" />\r\n }\r\n {{ tab.label() }}\r\n </button>\r\n }\r\n</div>\r\n<div class=\"hy-tabs__panels\"><ng-content /></div>\r\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
2
  import { DOCUMENT, Injectable, inject, signal } from "@angular/core";
3
- import { HY_CONFIG, ɵinjectIsBrowser } from "@hyalo/angular";
3
+ import { HY_CONFIG, ɵinjectIsBrowser } from "@hyalo/angular/core";
4
4
  const ATTR = "data-hy-palette";
5
5
  const VALID_NAME = /^[a-z0-9-]+$/;
6
6
  function defaultPaletteHref(name) {
@@ -1 +1 @@
1
- {"version":3,"file":"hyalo-angular-theme.mjs","names":["injectIsBrowser"],"sources":["../../../projects/hyalo/theme/palette.ts"],"sourcesContent":["import { DOCUMENT, Injectable, inject, signal } from '@angular/core';\r\nimport { HY_CONFIG, ɵinjectIsBrowser as injectIsBrowser } from '@hyalo/angular';\r\n\r\n/** The palettes in `styles/palettes/`. Apps can add their own names too. */\r\nexport type HyPaletteName = 'aurora' | 'ember' | 'orchid' | 'ocean' | 'rose' | 'graphite';\r\n\r\nconst ATTR = 'data-hy-palette';\r\nconst VALID_NAME = /^[a-z0-9-]+$/;\r\n\r\n/** Where a palette stylesheet is served from when no `paletteHref` is configured. */\r\nexport function defaultPaletteHref(name: string): string {\r\n return `palette-${name}.css`;\r\n}\r\n\r\n/**\r\n * Switches palettes, loading each stylesheet the first time it is needed.\r\n *\r\n * Palettes are separate files in `styles/palettes/`, so they never add to\r\n * the initial CSS. Their rules only match while `data-hy-palette` is on\r\n * `<html>`, which means a loaded palette costs nothing until it is picked.\r\n * The attribute is set only after the file has loaded, so the page never\r\n * shows half a palette, and switching back to a palette is instant.\r\n *\r\n * To restore a saved palette before the app boots, an inline script can add\r\n * `<link rel=\"stylesheet\" data-hy-palette=\"ocean\" href=\"...\">` to the head\r\n * and the attribute to `<html>`. HyPalette picks up both.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class HyPalette {\r\n private readonly document = inject(DOCUMENT);\r\n private readonly config = inject(HY_CONFIG);\r\n private readonly isBrowser = injectIsBrowser();\r\n private readonly loads = new Map<string, Promise<void>>();\r\n private readonly _current = signal(this.document.documentElement.getAttribute(ATTR));\r\n private readonly _selected = signal(this._current());\r\n\r\n /** The palette on screen, or null for the built-in one. */\r\n readonly current = this._current.asReadonly();\r\n\r\n /**\r\n * The palette last asked for. It changes straight away, while `current`\r\n * waits for the stylesheet, so a picker can show the choice immediately.\r\n */\r\n readonly selected = this._selected.asReadonly();\r\n\r\n /**\r\n * Loads a palette and applies it, or goes back to the built-in palette for\r\n * null. When several calls overlap, the last one wins. Rejects when the\r\n * stylesheet fails to load, and leaves the current palette in place. The\r\n * exception is a palette restored before boot whose file never loaded: the\r\n * page goes back to the built-in palette instead of claiming it.\r\n */\r\n async use(name: HyPaletteName | (string & {}) | null): Promise<void> {\r\n if (name !== null) assertName(name);\r\n this._selected.set(name);\r\n try {\r\n if (name !== null && this.isBrowser) await this.load(name);\r\n } catch (error) {\r\n if (this._current() === name) {\r\n // The palette the page claims to show never loaded, so drop the claim.\r\n this.document.documentElement.removeAttribute(ATTR);\r\n this._current.set(null);\r\n }\r\n if (this._selected() === name) this._selected.set(this._current());\r\n throw error;\r\n }\r\n if (this._selected() !== name) return;\r\n const root = this.document.documentElement;\r\n if (name === null) root.removeAttribute(ATTR);\r\n else root.setAttribute(ATTR, name);\r\n this._current.set(name);\r\n }\r\n\r\n /** Starts loading a palette without applying it, for example on hover. */\r\n preload(name: HyPaletteName | (string & {})): void {\r\n assertName(name);\r\n if (this.isBrowser) this.load(name).catch(() => undefined);\r\n }\r\n\r\n private load(name: string): Promise<void> {\r\n const cached = this.loads.get(name);\r\n if (cached) return cached;\r\n const head = this.document.head;\r\n const existing = head.querySelector<HTMLLinkElement>(`link[${ATTR}=\"${name}\"]`);\r\n // A link without a sheet may have failed before anyone listened, as one\r\n // added by a pre-paint script can, and would then never settle. Replace\r\n // it; if it was still loading, the new one comes from the cache.\r\n if (existing && !existing.sheet) existing.remove();\r\n const pending = existing?.sheet\r\n ? Promise.resolve()\r\n : new Promise<void>((resolve, reject) => {\r\n const link = this.document.createElement('link');\r\n link.addEventListener('load', () => resolve(), { once: true });\r\n link.addEventListener(\r\n 'error',\r\n () => {\r\n link.remove();\r\n this.loads.delete(name);\r\n reject(new Error(`Hyalo: palette \"${name}\" failed to load from ${link.href}`));\r\n },\r\n { once: true },\r\n );\r\n link.rel = 'stylesheet';\r\n link.href = (this.config.paletteHref ?? defaultPaletteHref)(name);\r\n link.setAttribute(ATTR, name);\r\n head.appendChild(link);\r\n });\r\n this.loads.set(name, pending);\r\n return pending;\r\n }\r\n}\r\n\r\n/** Names end up in a selector and a URL, so only simple ones are allowed. */\r\nfunction assertName(name: string): void {\r\n if (!VALID_NAME.test(name)) {\r\n throw new Error(`Hyalo: \"${name}\" is not a valid palette name. Use a-z, 0-9 and dashes.`);\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"hyalo-angular-theme.mjs","names":["injectIsBrowser"],"sources":["../../../projects/hyalo/theme/palette.ts"],"sourcesContent":["import { DOCUMENT, Injectable, inject, signal } from '@angular/core';\r\nimport { HY_CONFIG, ɵinjectIsBrowser as injectIsBrowser } from '@hyalo/angular/core';\r\n\r\n/** The palettes in `styles/palettes/`. Apps can add their own names too. */\r\nexport type HyPaletteName = 'aurora' | 'ember' | 'orchid' | 'ocean' | 'rose' | 'graphite';\r\n\r\nconst ATTR = 'data-hy-palette';\r\nconst VALID_NAME = /^[a-z0-9-]+$/;\r\n\r\n/** Where a palette stylesheet is served from when no `paletteHref` is configured. */\r\nexport function defaultPaletteHref(name: string): string {\r\n return `palette-${name}.css`;\r\n}\r\n\r\n/**\r\n * Switches palettes, loading each stylesheet the first time it is needed.\r\n *\r\n * Palettes are separate files in `styles/palettes/`, so they never add to\r\n * the initial CSS. Their rules only match while `data-hy-palette` is on\r\n * `<html>`, which means a loaded palette costs nothing until it is picked.\r\n * The attribute is set only after the file has loaded, so the page never\r\n * shows half a palette, and switching back to a palette is instant.\r\n *\r\n * To restore a saved palette before the app boots, an inline script can add\r\n * `<link rel=\"stylesheet\" data-hy-palette=\"ocean\" href=\"...\">` to the head\r\n * and the attribute to `<html>`. HyPalette picks up both.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class HyPalette {\r\n private readonly document = inject(DOCUMENT);\r\n private readonly config = inject(HY_CONFIG);\r\n private readonly isBrowser = injectIsBrowser();\r\n private readonly loads = new Map<string, Promise<void>>();\r\n private readonly _current = signal(this.document.documentElement.getAttribute(ATTR));\r\n private readonly _selected = signal(this._current());\r\n\r\n /** The palette on screen, or null for the built-in one. */\r\n readonly current = this._current.asReadonly();\r\n\r\n /**\r\n * The palette last asked for. It changes straight away, while `current`\r\n * waits for the stylesheet, so a picker can show the choice immediately.\r\n */\r\n readonly selected = this._selected.asReadonly();\r\n\r\n /**\r\n * Loads a palette and applies it, or goes back to the built-in palette for\r\n * null. When several calls overlap, the last one wins. Rejects when the\r\n * stylesheet fails to load, and leaves the current palette in place. The\r\n * exception is a palette restored before boot whose file never loaded: the\r\n * page goes back to the built-in palette instead of claiming it.\r\n */\r\n async use(name: HyPaletteName | (string & {}) | null): Promise<void> {\r\n if (name !== null) assertName(name);\r\n this._selected.set(name);\r\n try {\r\n if (name !== null && this.isBrowser) await this.load(name);\r\n } catch (error) {\r\n if (this._current() === name) {\r\n // The palette the page claims to show never loaded, so drop the claim.\r\n this.document.documentElement.removeAttribute(ATTR);\r\n this._current.set(null);\r\n }\r\n if (this._selected() === name) this._selected.set(this._current());\r\n throw error;\r\n }\r\n if (this._selected() !== name) return;\r\n const root = this.document.documentElement;\r\n if (name === null) root.removeAttribute(ATTR);\r\n else root.setAttribute(ATTR, name);\r\n this._current.set(name);\r\n }\r\n\r\n /** Starts loading a palette without applying it, for example on hover. */\r\n preload(name: HyPaletteName | (string & {})): void {\r\n assertName(name);\r\n if (this.isBrowser) this.load(name).catch(() => undefined);\r\n }\r\n\r\n private load(name: string): Promise<void> {\r\n const cached = this.loads.get(name);\r\n if (cached) return cached;\r\n const head = this.document.head;\r\n const existing = head.querySelector<HTMLLinkElement>(`link[${ATTR}=\"${name}\"]`);\r\n // A link without a sheet may have failed before anyone listened, as one\r\n // added by a pre-paint script can, and would then never settle. Replace\r\n // it; if it was still loading, the new one comes from the cache.\r\n if (existing && !existing.sheet) existing.remove();\r\n const pending = existing?.sheet\r\n ? Promise.resolve()\r\n : new Promise<void>((resolve, reject) => {\r\n const link = this.document.createElement('link');\r\n link.addEventListener('load', () => resolve(), { once: true });\r\n link.addEventListener(\r\n 'error',\r\n () => {\r\n link.remove();\r\n this.loads.delete(name);\r\n reject(new Error(`Hyalo: palette \"${name}\" failed to load from ${link.href}`));\r\n },\r\n { once: true },\r\n );\r\n link.rel = 'stylesheet';\r\n link.href = (this.config.paletteHref ?? defaultPaletteHref)(name);\r\n link.setAttribute(ATTR, name);\r\n head.appendChild(link);\r\n });\r\n this.loads.set(name, pending);\r\n return pending;\r\n }\r\n}\r\n\r\n/** Names end up in a selector and a URL, so only simple ones are allowed. */\r\nfunction assertName(name: string): void {\r\n if (!VALID_NAME.test(name)) {\r\n throw new Error(`Hyalo: \"${name}\" is not a valid palette name. Use a-z, 0-9 and dashes.`);\r\n }\r\n}\r\n"],"mappings":";;;AAMA,MAAM,OAAO;AACb,MAAM,aAAa;AAGnB,SAAgB,mBAAmB,MAAY;CAC7C,OAAO,WAAW,KAAI;AACxB;AAgBA,IAAa,YAAb,MAAa,UAAS;CACH,WAAW,OAAO,QAAQ;CAC1B,SAAS,OAAO,SAAS;CACzB,YAAYA,iBAAe;CAC3B,wBAAQ,IAAI,IAAG;CACf,WAAW,OAAO,KAAK,SAAS,gBAAgB,aAAa,IAAI,GAAE,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CACnE,YAAY,OAAO,KAAK,SAAQ,GAAG,GAAA,YAAA,CAAA,EAAA,WAAA,YAAA,CAAA,+BAAA,CAAA,CAAA;CAG3C,UAAU,KAAK,SAAS,WAAU;CAMlC,WAAW,KAAK,UAAU,WAAU;CAS7C,MAAM,IAAI,MAA0C;EAClD,IAAI,SAAS,MAAM,WAAW,IAAI;EAClC,KAAK,UAAU,IAAI,IAAI;EACvB,IAAI;GACF,IAAI,SAAS,QAAQ,KAAK,WAAW,MAAM,KAAK,KAAK,IAAI;EAC3D,SAAS,OAAO;GACd,IAAI,KAAK,SAAQ,MAAO,MAAM;IAE5B,KAAK,SAAS,gBAAgB,gBAAgB,IAAI;IAClD,KAAK,SAAS,IAAI,IAAI;GACxB;GACA,IAAI,KAAK,UAAS,MAAO,MAAM,KAAK,UAAU,IAAI,KAAK,SAAQ,CAAE;GACjE,MAAM;EACR;EACA,IAAI,KAAK,UAAS,MAAO,MAAM;EAC/B,MAAM,OAAO,KAAK,SAAS;EAC3B,IAAI,SAAS,MAAM,KAAK,gBAAgB,IAAI;OACvC,KAAK,aAAa,MAAM,IAAI;EACjC,KAAK,SAAS,IAAI,IAAI;CACxB;CAGA,QAAQ,MAAmC;EACzC,WAAW,IAAI;EACf,IAAI,KAAK,WAAW,KAAK,KAAK,IAAI,CAAC,CAAC,YAAY,MAAS;CAC3D;CAEQ,KAAK,MAAY;EACvB,MAAM,SAAS,KAAK,MAAM,IAAI,IAAI;EAClC,IAAI,QAAQ,OAAO;EACnB,MAAM,OAAO,KAAK,SAAS;EAC3B,MAAM,WAAW,KAAK,cAA+B,QAAQ,KAAI,IAAK,KAAI,GAAI;EAI9E,IAAI,YAAY,CAAC,SAAS,OAAO,SAAS,OAAM;EAChD,MAAM,UAAU,UAAU,QACtB,QAAQ,QAAO,IACf,IAAI,SAAe,SAAS,WAAU;GACpC,MAAM,OAAO,KAAK,SAAS,cAAc,MAAM;GAC/C,KAAK,iBAAiB,cAAc,QAAO,GAAI,EAAE,MAAM,KAAI,CAAE;GAC7D,KAAK,iBACH,eACK;IACH,KAAK,OAAM;IACX,KAAK,MAAM,OAAO,IAAI;IACtB,uBAAO,IAAI,MAAM,mBAAmB,KAAI,wBAAyB,KAAK,MAAM,CAAC;GAC/E,GACA,EAAE,MAAM,KAAI,CAAE;GAEhB,KAAK,MAAM;GACX,KAAK,QAAQ,KAAK,OAAO,eAAe,mBAAA,CAAoB,IAAI;GAChE,KAAK,aAAa,MAAM,IAAI;GAC5B,KAAK,YAAY,IAAI;EACvB,CAAC;EACL,KAAK,MAAM,IAAI,MAAM,OAAO;EAC5B,OAAO;CACT;CAjFW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAS,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAT,OAAA,QAAA,GAAA,sBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAS,YADI;CAAM,CAAA;;AACnB,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAS,YAAA,CAAA;EADrB,MAAA;EAAW,MAAA,CAAA,EAAE,YAAY,OAAM,CAAE;;;AAsFlC,SAAS,WAAW,MAAY;CAC9B,IAAI,CAAC,WAAW,KAAK,IAAI,GACvB,MAAM,IAAI,MAAM,WAAW,KAAI,wDAAyD;AAE5F"}
@@ -0,0 +1,157 @@
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, Component, input } from "@angular/core";
3
+ var HyTimeline = class HyTimeline {
4
+ label = input("Timeline", ...ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []);
5
+ static ɵfac = i0.ɵɵngDeclareFactory({
6
+ minVersion: "12.0.0",
7
+ version: "22.2.3",
8
+ ngImport: i0,
9
+ type: HyTimeline,
10
+ deps: [],
11
+ target: i0.ɵɵFactoryTarget.Component
12
+ });
13
+ static ɵcmp = i0.ɵɵngDeclareComponent({
14
+ minVersion: "17.1.0",
15
+ version: "22.2.3",
16
+ type: HyTimeline,
17
+ isStandalone: true,
18
+ selector: "hy-timeline",
19
+ inputs: { label: {
20
+ classPropertyName: "label",
21
+ publicName: "label",
22
+ isSignal: true,
23
+ isRequired: false,
24
+ transformFunction: null
25
+ } },
26
+ host: {
27
+ attributes: { "role": "list" },
28
+ properties: { "attr.aria-label": "label()" }
29
+ },
30
+ ngImport: i0,
31
+ template: "<ng-content />",
32
+ isInline: true,
33
+ styles: [":host{display:block}\n"],
34
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
35
+ });
36
+ };
37
+ i0.ɵɵngDeclareClassMetadata({
38
+ minVersion: "12.0.0",
39
+ version: "22.2.3",
40
+ ngImport: i0,
41
+ type: HyTimeline,
42
+ decorators: [{
43
+ type: Component,
44
+ args: [{
45
+ selector: "hy-timeline",
46
+ changeDetection: ChangeDetectionStrategy.OnPush,
47
+ host: {
48
+ role: "list",
49
+ "[attr.aria-label]": "label()"
50
+ },
51
+ template: "<ng-content />",
52
+ styles: [":host{display:block}\n"]
53
+ }]
54
+ }],
55
+ propDecorators: { label: [{
56
+ type: i0.Input,
57
+ args: [{
58
+ isSignal: true,
59
+ alias: "label",
60
+ required: false
61
+ }]
62
+ }] }
63
+ });
64
+ var HyTimelineItem = class HyTimelineItem {
65
+ heading = input.required(...ngDevMode ? [{ debugName: "heading" }] : /* istanbul ignore next */ []);
66
+ date = input("", ...ngDevMode ? [{ debugName: "date" }] : /* istanbul ignore next */ []);
67
+ dateTime = input(null, ...ngDevMode ? [{ debugName: "dateTime" }] : /* istanbul ignore next */ []);
68
+ static ɵfac = i0.ɵɵngDeclareFactory({
69
+ minVersion: "12.0.0",
70
+ version: "22.2.3",
71
+ ngImport: i0,
72
+ type: HyTimelineItem,
73
+ deps: [],
74
+ target: i0.ɵɵFactoryTarget.Component
75
+ });
76
+ static ɵcmp = i0.ɵɵngDeclareComponent({
77
+ minVersion: "17.0.0",
78
+ version: "22.2.3",
79
+ type: HyTimelineItem,
80
+ isStandalone: true,
81
+ selector: "hy-timeline-item",
82
+ inputs: {
83
+ heading: {
84
+ classPropertyName: "heading",
85
+ publicName: "heading",
86
+ isSignal: true,
87
+ isRequired: true,
88
+ transformFunction: null
89
+ },
90
+ date: {
91
+ classPropertyName: "date",
92
+ publicName: "date",
93
+ isSignal: true,
94
+ isRequired: false,
95
+ transformFunction: null
96
+ },
97
+ dateTime: {
98
+ classPropertyName: "dateTime",
99
+ publicName: "dateTime",
100
+ isSignal: true,
101
+ isRequired: false,
102
+ transformFunction: null
103
+ }
104
+ },
105
+ host: { attributes: { "role": "listitem" } },
106
+ ngImport: i0,
107
+ template: "<span class=\"dot\" aria-hidden=\"true\"></span>\r\n@if (date()) {\r\n <time [attr.datetime]=\"dateTime()\">{{ date() }}</time>\r\n}\r\n<strong>{{ heading() }}</strong>\r\n<div><ng-content /></div>\r\n",
108
+ styles: [":host{display:block;position:relative;padding-block:0 var(--hy-space-8);padding-inline:var(--hy-space-8) 0;border-inline-start:1px solid var(--hy-line-strong);margin-inline-start:.5rem;color:var(--hy-ink-2)}:host(:last-child){border-color:transparent}.dot{position:absolute;inset-inline-start:-.35rem;inset-block-start:.4rem;inline-size:.65rem;block-size:.65rem;border-radius:50%;background:var(--hy-primary);box-shadow:0 0 0 4px var(--hy-bg)}time,strong{display:block}time{font-size:var(--hy-step--1);color:var(--hy-ink-3)}strong{color:var(--hy-ink);margin-block-end:var(--hy-space-2)}\n"],
109
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
110
+ });
111
+ };
112
+ i0.ɵɵngDeclareClassMetadata({
113
+ minVersion: "12.0.0",
114
+ version: "22.2.3",
115
+ ngImport: i0,
116
+ type: HyTimelineItem,
117
+ decorators: [{
118
+ type: Component,
119
+ args: [{
120
+ selector: "hy-timeline-item",
121
+ changeDetection: ChangeDetectionStrategy.OnPush,
122
+ host: { role: "listitem" },
123
+ template: "<span class=\"dot\" aria-hidden=\"true\"></span>\r\n@if (date()) {\r\n <time [attr.datetime]=\"dateTime()\">{{ date() }}</time>\r\n}\r\n<strong>{{ heading() }}</strong>\r\n<div><ng-content /></div>\r\n",
124
+ styles: [":host{display:block;position:relative;padding-block:0 var(--hy-space-8);padding-inline:var(--hy-space-8) 0;border-inline-start:1px solid var(--hy-line-strong);margin-inline-start:.5rem;color:var(--hy-ink-2)}:host(:last-child){border-color:transparent}.dot{position:absolute;inset-inline-start:-.35rem;inset-block-start:.4rem;inline-size:.65rem;block-size:.65rem;border-radius:50%;background:var(--hy-primary);box-shadow:0 0 0 4px var(--hy-bg)}time,strong{display:block}time{font-size:var(--hy-step--1);color:var(--hy-ink-3)}strong{color:var(--hy-ink);margin-block-end:var(--hy-space-2)}\n"]
125
+ }]
126
+ }],
127
+ propDecorators: {
128
+ heading: [{
129
+ type: i0.Input,
130
+ args: [{
131
+ isSignal: true,
132
+ alias: "heading",
133
+ required: true
134
+ }]
135
+ }],
136
+ date: [{
137
+ type: i0.Input,
138
+ args: [{
139
+ isSignal: true,
140
+ alias: "date",
141
+ required: false
142
+ }]
143
+ }],
144
+ dateTime: [{
145
+ type: i0.Input,
146
+ args: [{
147
+ isSignal: true,
148
+ alias: "dateTime",
149
+ required: false
150
+ }]
151
+ }]
152
+ }
153
+ });
154
+ const HY_TIMELINE = [HyTimeline, HyTimelineItem];
155
+ export { HY_TIMELINE, HyTimeline, HyTimelineItem };
156
+
157
+ //# sourceMappingURL=hyalo-angular-timeline.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hyalo-angular-timeline.mjs","names":[],"sources":["../../../projects/hyalo/timeline/timeline.ts","../../../projects/hyalo/timeline/timeline.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, input } from '@angular/core';\r\n@Component({\r\n selector: 'hy-timeline',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: { role: 'list', '[attr.aria-label]': 'label()' },\r\n template: '<ng-content />',\r\n styleUrl: './timeline.css',\r\n})\r\nexport class HyTimeline {\r\n readonly label = input('Timeline');\r\n}\r\n@Component({\r\n selector: 'hy-timeline-item',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: { role: 'listitem' },\r\n templateUrl: './timeline.html',\r\n styleUrl: './timeline-item.css',\r\n})\r\nexport class HyTimelineItem {\r\n readonly heading = input.required<string>();\r\n readonly date = input('');\r\n readonly dateTime = input<string | null>(null);\r\n}\r\nexport const HY_TIMELINE = [HyTimeline, HyTimelineItem] as const;\r\n","<span class=\"dot\" aria-hidden=\"true\"></span>\r\n@if (date()) {\r\n <time [attr.datetime]=\"dateTime()\">{{ date() }}</time>\r\n}\r\n<strong>{{ heading() }}</strong>\r\n<div><ng-content /></div>\r\n"],"mappings":";;AAQA,IAAa,aAAb,MAAa,WAAU;CACZ,QAAQ,MAAM,YAAW,GAAA,YAAA,CAAA,EAAA,WAAA,QAAA,CAAA,+BAAA,CAAA,CAAA;CADvB,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAU,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAV,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAU,cAAA;EAAA,UAAA;EAAA,QAAA,EAAA,OAAA;GAAA,mBAAA;GAAA,YAAA;GAAA,UAAA;GAAA,YAAA;GAAA,mBAAA;EAAA,EAAA;EAAA,MAAA;GAAA,YAAA,EAAA,QAAA,OAAA;GAAA,YAAA,EAAA,mBAAA,UAAA;EAAA;EAAA,UAAA;EAAA,UAHX;EAAgB,UAAA;EAAA,QAAA,CAAA,wBAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAGf,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAU,YAAA,CAAA;EAPtB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAa,iBACN,wBAAwB;GAAM,MACzC;IAAE,MAAM;IAAQ,qBAAqB;GAAS;GAAE,UAC5C;GAAgB,QAAA,CAAA,wBAAA;EAAA,CAAA;;;;;;;;;;;AAa5B,IAAa,iBAAb,MAAa,eAAc;CAChB,UAAU,MAAM,SAAkB,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAClC,OAAO,MAAM,IAAG,GAAA,YAAA,CAAA,EAAA,WAAA,OAAA,CAAA,+BAAA,CAAA,CAAA;CAChB,WAAW,MAAqB,MAAK,GAAA,YAAA,CAAA,EAAA,WAAA,WAAA,CAAA,+BAAA,CAAA,CAAA;CAHnC,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAc,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAd,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAc,cAAA;EAAA,UAAA;EAAA,QAAA;GAAA,SAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,MAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;GAAA,UAAA;IAAA,mBAAA;IAAA,YAAA;IAAA,UAAA;IAAA,YAAA;IAAA,mBAAA;GAAA;EAAA;EAAA,MAAA,EAAA,YAAA,EAAA,QAAA,WAAA,EAAA;EAAA,UAAA;EAAA,UClB3B;EAMA,QAAA,CAAA,8kBAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;ADYa,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAc,YAAA,CAAA;EAP1B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAkB,iBACX,wBAAwB;GAAM,MACzC,EAAE,MAAM,WAAU;GAAE,UAAA;GAAA,QAAA,CAAA,8kBAAA;EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAS5B,MAAa,cAAc,CAAC,YAAY,cAAc"}
@@ -3,7 +3,8 @@ import { createGlobalPositionStrategy, createOverlayRef } from "@angular/cdk/ove
3
3
  import { ComponentPortal } from "@angular/cdk/portal";
4
4
  import * as i0 from "@angular/core";
5
5
  import { ChangeDetectionStrategy, Component, ElementRef, Injectable, Injector, afterRenderEffect, computed, inject, signal } from "@angular/core";
6
- import { HyFidelity, HyMotion, HySpring, HyVelocityTracker, MOTION, SPRINGS, project, ɵinjectIsBrowser } from "@hyalo/angular";
6
+ import { HyFidelity, ɵinjectIsBrowser } from "@hyalo/angular/core";
7
+ import { HyMotion, HySpring, HyVelocityTracker, MOTION, SPRINGS, project } from "@hyalo/angular/motion";
7
8
  import { HyIcon } from "@hyalo/angular/icon";
8
9
  const SWIPE_DISTANCE = 80;
9
10
  const SLOP = 8;
@@ -1 +1 @@
1
- {"version":3,"file":"hyalo-angular-toast.mjs","names":["injectIsBrowser"],"sources":["../../../projects/hyalo/toast/toast-stack.ts","../../../projects/hyalo/toast/toast-stack.html","../../../projects/hyalo/toast/toaster.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n afterRenderEffect,\r\n computed,\r\n inject,\r\n} from '@angular/core';\r\nimport { HyIcon } from '@hyalo/angular/icon';\r\nimport {\r\n HyFidelity,\r\n HyMotion,\r\n HySpring,\r\n HyVelocityTracker,\r\n MOTION,\r\n project,\r\n SPRINGS,\r\n} from '@hyalo/angular';\r\nimport { HyToaster, type HyToastItem } from './toaster';\r\n\r\n/** How far a swipe has to be headed before it dismisses, in px. */\r\nconst SWIPE_DISTANCE = 80;\r\n/** Movement before a press turns into a swipe, in px. */\r\nconst SLOP = 8;\r\n\r\ninterface Swipe {\r\n readonly el: HTMLElement;\r\n readonly id: number;\r\n readonly spring: HySpring;\r\n readonly tracker: HyVelocityTracker;\r\n startX: number;\r\n readonly startY: number;\r\n dragging: boolean;\r\n}\r\n\r\n/** @internal The visible stack of toasts. Created by HyToaster. */\r\n@Component({\r\n selector: 'hy-toast-stack',\r\n imports: [HyIcon],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {\r\n class: 'hy-toast-stack',\r\n role: 'region',\r\n 'aria-label': 'Notifications',\r\n '(pointerenter)': \"toaster.pause('hover')\",\r\n '(pointerleave)': \"toaster.resume('hover')\",\r\n '(focusin)': \"toaster.pause('focus')\",\r\n '(focusout)': 'onFocusOut($event)',\r\n },\r\n templateUrl: './toast-stack.html',\r\n})\r\nexport class HyToastStack {\r\n protected readonly toaster = inject(HyToaster);\r\n private readonly fidelity = inject(HyFidelity);\r\n private readonly motion = inject(HyMotion);\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\r\n private readonly springs = new WeakMap<HTMLElement, HySpring>();\r\n private swipe: Swipe | null = null;\r\n /** Layout position of each toast after the last render, for the slide. */\r\n private tops = new Map<number, number>();\r\n\r\n protected readonly material = computed(() =>\r\n this.fidelity.tier() === 'low' ? 'solid' : 'frosted',\r\n );\r\n\r\n constructor() {\r\n afterRenderEffect(() => {\r\n this.toaster.items();\r\n // Removing the focused toast drops focus without a reliable focusout.\r\n if (!this.host.contains(this.host.ownerDocument.activeElement)) {\r\n this.toaster.resume('focus');\r\n }\r\n this.slideIntoPlace();\r\n });\r\n }\r\n\r\n protected onFocusOut(event: FocusEvent): void {\r\n // Moving between buttons inside the stack keeps the timers paused.\r\n if (!this.host.contains(event.relatedTarget as Node | null)) this.toaster.resume('focus');\r\n }\r\n\r\n protected onDown(event: PointerEvent): void {\r\n if (event.button !== 0 || (event.target as Element).closest('button')) return;\r\n const el = event.currentTarget as HTMLElement;\r\n const spring = this.springFor(el);\r\n // A toast still springing back is caught where it is.\r\n const caught = spring.running;\r\n spring.stop();\r\n this.swipe = {\r\n el,\r\n id: event.pointerId,\r\n spring,\r\n tracker: new HyVelocityTracker(),\r\n startX: event.clientX - spring.value,\r\n startY: event.clientY,\r\n dragging: false,\r\n };\r\n if (caught) this.startDrag(this.swipe, event);\r\n }\r\n\r\n protected onMove(event: PointerEvent): void {\r\n const s = this.swipe;\r\n if (!s || event.pointerId !== s.id) return;\r\n const dx = event.clientX - s.startX;\r\n if (!s.dragging) {\r\n const dy = Math.abs(event.clientY - s.startY);\r\n const moved = Math.abs(dx - s.spring.value);\r\n if (dy > SLOP && dy > moved) {\r\n this.swipe = null;\r\n return;\r\n }\r\n if (moved < SLOP) return;\r\n this.startDrag(s, event);\r\n return;\r\n }\r\n s.tracker.add(dx);\r\n s.spring.set(dx);\r\n }\r\n\r\n protected onUp(event: PointerEvent, toast: HyToastItem | null): void {\r\n const s = this.swipe;\r\n if (!s || event.pointerId !== s.id) return;\r\n this.swipe = null;\r\n if (!s.dragging) return;\r\n const x = s.spring.value;\r\n const velocity = toast ? s.tracker.velocity() : 0;\r\n const landing = x + project(velocity);\r\n // A flick against the drag direction takes the toast back.\r\n const out =\r\n toast && Math.abs(landing) > SWIPE_DISTANCE && Math.sign(landing) === Math.sign(x || 1);\r\n if (out) {\r\n const away = Math.sign(landing) * (s.el.offsetWidth + 48);\r\n s.spring.to(away, {\r\n ...SPRINGS.settle,\r\n response: 0.3,\r\n velocity,\r\n onRest: () => this.toaster.discard(toast.id),\r\n });\r\n } else {\r\n // The flick carried speed, so the return may overshoot a little.\r\n s.spring.to(0, { ...SPRINGS.momentum, velocity });\r\n }\r\n }\r\n\r\n private startDrag(s: Swipe, event: PointerEvent): void {\r\n s.dragging = true;\r\n s.startX = event.clientX - s.spring.value;\r\n s.tracker.add(s.spring.value);\r\n try {\r\n s.el.setPointerCapture(event.pointerId);\r\n } catch {\r\n // The pointer is already gone; the swipe still ends on pointerup.\r\n }\r\n }\r\n\r\n private springFor(el: HTMLElement): HySpring {\r\n let spring = this.springs.get(el);\r\n if (!spring) {\r\n spring = new HySpring(0, (x) => {\r\n el.style.translate = x ? `${x}px 0` : '';\r\n el.style.opacity = x ? String(Math.max(0, 1 - Math.abs(x) / el.offsetWidth)) : '';\r\n });\r\n this.springs.set(el, spring);\r\n }\r\n return spring;\r\n }\r\n\r\n /**\r\n * When a toast arrives or leaves, the others glide to their new place\r\n * instead of jumping. It starts from where each one is on screen, so a\r\n * slide that is interrupted by another change carries on smoothly.\r\n */\r\n private slideIntoPlace(): void {\r\n const tops = new Map<number, number>();\r\n for (const el of this.host.querySelectorAll<HTMLElement>('.hy-toast[data-id]')) {\r\n const id = Number(el.dataset['id']);\r\n const top = el.offsetTop;\r\n tops.set(id, top);\r\n const before = this.tops.get(id);\r\n if (before === undefined || before === top) continue;\r\n if (this.motion.reduced() || typeof el.animate !== 'function') continue;\r\n const current = new DOMMatrixReadOnly(getComputedStyle(el).transform).m42;\r\n for (const running of el.getAnimations()) {\r\n if (running.id === 'hy-toast-slide') running.cancel();\r\n }\r\n el.animate(\r\n [{ transform: `translateY(${before + current - top}px)` }, { transform: 'none' }],\r\n {\r\n id: 'hy-toast-slide',\r\n duration: MOTION.settle.duration * 1000,\r\n easing: MOTION.settle.css,\r\n },\r\n );\r\n }\r\n this.tops = tops;\r\n }\r\n}\r\n","<ol class=\"hy-toast-list\">\r\n @for (toast of toaster.items(); track toast.id) {\r\n <li\r\n class=\"hy-toast hy-glass\"\r\n [attr.data-id]=\"toast.id\"\r\n [attr.data-material]=\"material()\"\r\n [attr.data-tone]=\"toast.tone\"\r\n [attr.data-leaving]=\"toast.leaving || null\"\r\n (pointerdown)=\"onDown($event)\"\r\n (pointermove)=\"onMove($event)\"\r\n (pointerup)=\"onUp($event, toast)\"\r\n (pointercancel)=\"onUp($event, null)\"\r\n >\r\n @if (toast.tone !== 'neutral') {\r\n <hy-icon class=\"hy-toast__icon\" [name]=\"toast.tone\" />\r\n }\r\n <div class=\"hy-toast__text\">\r\n @if (toast.title) {\r\n <p class=\"hy-toast__title\">{{ toast.title }}</p>\r\n }\r\n <p class=\"hy-toast__message\">{{ toast.message }}</p>\r\n </div>\r\n @if (toast.action; as action) {\r\n <button\r\n type=\"button\"\r\n class=\"hy-toast__action\"\r\n (click)=\"action.run(); toaster.dismiss(toast.id)\"\r\n >\r\n {{ action.label }}\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"hy-toast__close\"\r\n aria-label=\"Dismiss\"\r\n (click)=\"toaster.dismiss(toast.id)\"\r\n >\r\n <hy-icon name=\"x\" />\r\n </button>\r\n </li>\r\n }\r\n</ol>\r\n","import { LiveAnnouncer } from '@angular/cdk/a11y';\r\nimport {\r\n createGlobalPositionStrategy,\r\n createOverlayRef,\r\n type OverlayRef,\r\n} from '@angular/cdk/overlay';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { Injectable, Injector, inject, signal } from '@angular/core';\r\nimport { MOTION, ɵinjectIsBrowser as injectIsBrowser } from '@hyalo/angular';\r\nimport { HyToastStack } from './toast-stack';\r\n\r\n/** Colour and icon of a toast. */\r\nexport type HyToastTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\r\n\r\n/** What to show. */\r\nexport interface HyToastOptions {\r\n readonly message: string;\r\n readonly title?: string;\r\n readonly tone?: HyToastTone;\r\n /**\r\n * Milliseconds before it hides. 0 keeps it until dismissed. Defaults to\r\n * 5000, or 0 when there is an action, so nobody has to race a timer.\r\n */\r\n readonly duration?: number;\r\n /** One optional action, such as Undo. */\r\n readonly action?: { readonly label: string; readonly run: () => void };\r\n}\r\n\r\n/** @internal One toast in the stack. */\r\nexport interface HyToastItem extends HyToastOptions {\r\n readonly id: number;\r\n readonly leaving: boolean;\r\n}\r\n\r\n/** Handle to a shown toast. */\r\nexport interface HyToastRef {\r\n dismiss(): void;\r\n}\r\n\r\nconst MAX_VISIBLE = 4;\r\n\r\n/** @internal Why the timers are on hold. Hover and focus are tracked apart. */\r\nexport type HyToastHold = 'hover' | 'focus';\r\n\r\n/**\r\n * Shows toasts: short messages about something that just happened.\r\n *\r\n * ```ts\r\n * toaster.show({ message: 'Address saved', tone: 'success' });\r\n * toaster.show({ message: 'Draft deleted', action: { label: 'Undo', run: () => restore() } });\r\n * ```\r\n *\r\n * Every toast is announced through the live announcer (`danger` toasts\r\n * assertively, the rest politely). Timers pause while the pointer or focus\r\n * is in the stack, and toasts can be swiped away. The stack sits at the\r\n * bottom inline-end corner and is a labelled region, so keyboard and screen\r\n * reader users can reach the actions.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class HyToaster {\r\n private readonly injector = inject(Injector);\r\n private readonly announcer = inject(LiveAnnouncer);\r\n private readonly isBrowser = injectIsBrowser();\r\n private readonly timers = new Map<\r\n number,\r\n { left: number; started: number; handle?: ReturnType<typeof setTimeout> }\r\n >();\r\n private overlayRef: OverlayRef | null = null;\r\n private nextId = 1;\r\n private readonly holds = new Set<HyToastHold>();\r\n\r\n /** @internal The current stack, newest last. */\r\n readonly items = signal<readonly HyToastItem[]>([]);\r\n\r\n /** Shows a toast. Does nothing during server rendering. */\r\n show(options: HyToastOptions): HyToastRef {\r\n const id = this.nextId++;\r\n const ref = { dismiss: () => this.dismiss(id) };\r\n if (!this.isBrowser) return ref;\r\n this.ensureStack();\r\n\r\n const item: HyToastItem = { tone: 'neutral', ...options, id, leaving: false };\r\n this.items.update((list) => [...list, item]);\r\n // Toasts already on their way out do not count, and are not picked again.\r\n const staying = this.items().filter((t) => !t.leaving);\r\n const overflow = staying.length - MAX_VISIBLE;\r\n for (const old of staying.slice(0, Math.max(0, overflow))) this.dismiss(old.id);\r\n\r\n const text = options.title ? `${options.title}. ${options.message}` : options.message;\r\n void this.announcer.announce(text, item.tone === 'danger' ? 'assertive' : 'polite');\r\n\r\n const duration = options.duration ?? (options.action ? 0 : 5000);\r\n if (duration > 0) {\r\n this.timers.set(id, { left: duration, started: Date.now() });\r\n if (this.holds.size === 0) this.startTimer(id);\r\n }\r\n return ref;\r\n }\r\n\r\n success(message: string, title?: string): HyToastRef {\r\n return this.show({ message, title, tone: 'success' });\r\n }\r\n\r\n error(message: string, title?: string): HyToastRef {\r\n return this.show({ message, title, tone: 'danger' });\r\n }\r\n\r\n /** Hides one toast with its exit animation, a fade with reduced motion. */\r\n dismiss(id: number): void {\r\n this.clearTimer(id);\r\n if (!this.items().some((t) => t.id === id && !t.leaving)) return;\r\n this.items.update((list) => list.map((t) => (t.id === id ? { ...t, leaving: true } : t)));\r\n setTimeout(() => this.remove(id), MOTION.quick.duration * 1000 + 40);\r\n }\r\n\r\n /** @internal Removes a toast that already left the screen, as after a swipe. */\r\n discard(id: number): void {\r\n this.clearTimer(id);\r\n this.remove(id);\r\n }\r\n\r\n private clearTimer(id: number): void {\r\n clearTimeout(this.timers.get(id)?.handle);\r\n this.timers.delete(id);\r\n }\r\n\r\n /** Hides every toast. */\r\n dismissAll(): void {\r\n for (const t of this.items()) this.dismiss(t.id);\r\n }\r\n\r\n /**\r\n * @internal Pauses all timers while the stack is hovered or focused. Each\r\n * reason is held separately, so the pointer leaving does not restart the\r\n * timers while focus is still on a toast.\r\n */\r\n pause(reason: HyToastHold): void {\r\n const wasPaused = this.holds.size > 0;\r\n this.holds.add(reason);\r\n if (wasPaused) return;\r\n const now = Date.now();\r\n for (const timer of this.timers.values()) {\r\n clearTimeout(timer.handle);\r\n timer.left -= now - timer.started;\r\n }\r\n }\r\n\r\n /** @internal Releases one hold. Timers restart once no hold is left. */\r\n resume(reason: HyToastHold): void {\r\n if (!this.holds.delete(reason) || this.holds.size > 0) return;\r\n for (const id of this.timers.keys()) this.startTimer(id);\r\n }\r\n\r\n private startTimer(id: number): void {\r\n const timer = this.timers.get(id);\r\n if (!timer) return;\r\n timer.started = Date.now();\r\n timer.handle = setTimeout(() => this.dismiss(id), Math.max(0, timer.left));\r\n }\r\n\r\n private remove(id: number): void {\r\n this.items.update((list) => list.filter((t) => t.id !== id));\r\n }\r\n\r\n private ensureStack(): void {\r\n if (this.overlayRef) return;\r\n this.overlayRef = createOverlayRef(this.injector, {\r\n positionStrategy: createGlobalPositionStrategy(this.injector).bottom('0').end('0'),\r\n panelClass: 'hy-toast-pane',\r\n });\r\n this.overlayRef.attach(new ComponentPortal(HyToastStack, null, this.injector));\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"hyalo-angular-toast.mjs","names":["injectIsBrowser"],"sources":["../../../projects/hyalo/toast/toast-stack.ts","../../../projects/hyalo/toast/toast-stack.html","../../../projects/hyalo/toast/toaster.ts"],"sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n ElementRef,\r\n afterRenderEffect,\r\n computed,\r\n inject,\r\n} from '@angular/core';\r\nimport { HyIcon } from '@hyalo/angular/icon';\r\nimport { HyFidelity } from '@hyalo/angular/core';\r\nimport {\r\n HyMotion,\r\n HySpring,\r\n HyVelocityTracker,\r\n MOTION,\r\n project,\r\n SPRINGS,\r\n} from '@hyalo/angular/motion';\r\nimport { HyToaster, type HyToastItem } from './toaster';\r\n\r\n/** How far a swipe has to be headed before it dismisses, in px. */\r\nconst SWIPE_DISTANCE = 80;\r\n/** Movement before a press turns into a swipe, in px. */\r\nconst SLOP = 8;\r\n\r\ninterface Swipe {\r\n readonly el: HTMLElement;\r\n readonly id: number;\r\n readonly spring: HySpring;\r\n readonly tracker: HyVelocityTracker;\r\n startX: number;\r\n readonly startY: number;\r\n dragging: boolean;\r\n}\r\n\r\n/** @internal The visible stack of toasts. Created by HyToaster. */\r\n@Component({\r\n selector: 'hy-toast-stack',\r\n imports: [HyIcon],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n host: {\r\n class: 'hy-toast-stack',\r\n role: 'region',\r\n 'aria-label': 'Notifications',\r\n '(pointerenter)': \"toaster.pause('hover')\",\r\n '(pointerleave)': \"toaster.resume('hover')\",\r\n '(focusin)': \"toaster.pause('focus')\",\r\n '(focusout)': 'onFocusOut($event)',\r\n },\r\n templateUrl: './toast-stack.html',\r\n})\r\nexport class HyToastStack {\r\n protected readonly toaster = inject(HyToaster);\r\n private readonly fidelity = inject(HyFidelity);\r\n private readonly motion = inject(HyMotion);\r\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\r\n private readonly springs = new WeakMap<HTMLElement, HySpring>();\r\n private swipe: Swipe | null = null;\r\n /** Layout position of each toast after the last render, for the slide. */\r\n private tops = new Map<number, number>();\r\n\r\n protected readonly material = computed(() =>\r\n this.fidelity.tier() === 'low' ? 'solid' : 'frosted',\r\n );\r\n\r\n constructor() {\r\n afterRenderEffect(() => {\r\n this.toaster.items();\r\n // Removing the focused toast drops focus without a reliable focusout.\r\n if (!this.host.contains(this.host.ownerDocument.activeElement)) {\r\n this.toaster.resume('focus');\r\n }\r\n this.slideIntoPlace();\r\n });\r\n }\r\n\r\n protected onFocusOut(event: FocusEvent): void {\r\n // Moving between buttons inside the stack keeps the timers paused.\r\n if (!this.host.contains(event.relatedTarget as Node | null)) this.toaster.resume('focus');\r\n }\r\n\r\n protected onDown(event: PointerEvent): void {\r\n if (event.button !== 0 || (event.target as Element).closest('button')) return;\r\n const el = event.currentTarget as HTMLElement;\r\n const spring = this.springFor(el);\r\n // A toast still springing back is caught where it is.\r\n const caught = spring.running;\r\n spring.stop();\r\n this.swipe = {\r\n el,\r\n id: event.pointerId,\r\n spring,\r\n tracker: new HyVelocityTracker(),\r\n startX: event.clientX - spring.value,\r\n startY: event.clientY,\r\n dragging: false,\r\n };\r\n if (caught) this.startDrag(this.swipe, event);\r\n }\r\n\r\n protected onMove(event: PointerEvent): void {\r\n const s = this.swipe;\r\n if (!s || event.pointerId !== s.id) return;\r\n const dx = event.clientX - s.startX;\r\n if (!s.dragging) {\r\n const dy = Math.abs(event.clientY - s.startY);\r\n const moved = Math.abs(dx - s.spring.value);\r\n if (dy > SLOP && dy > moved) {\r\n this.swipe = null;\r\n return;\r\n }\r\n if (moved < SLOP) return;\r\n this.startDrag(s, event);\r\n return;\r\n }\r\n s.tracker.add(dx);\r\n s.spring.set(dx);\r\n }\r\n\r\n protected onUp(event: PointerEvent, toast: HyToastItem | null): void {\r\n const s = this.swipe;\r\n if (!s || event.pointerId !== s.id) return;\r\n this.swipe = null;\r\n if (!s.dragging) return;\r\n const x = s.spring.value;\r\n const velocity = toast ? s.tracker.velocity() : 0;\r\n const landing = x + project(velocity);\r\n // A flick against the drag direction takes the toast back.\r\n const out =\r\n toast && Math.abs(landing) > SWIPE_DISTANCE && Math.sign(landing) === Math.sign(x || 1);\r\n if (out) {\r\n const away = Math.sign(landing) * (s.el.offsetWidth + 48);\r\n s.spring.to(away, {\r\n ...SPRINGS.settle,\r\n response: 0.3,\r\n velocity,\r\n onRest: () => this.toaster.discard(toast.id),\r\n });\r\n } else {\r\n // The flick carried speed, so the return may overshoot a little.\r\n s.spring.to(0, { ...SPRINGS.momentum, velocity });\r\n }\r\n }\r\n\r\n private startDrag(s: Swipe, event: PointerEvent): void {\r\n s.dragging = true;\r\n s.startX = event.clientX - s.spring.value;\r\n s.tracker.add(s.spring.value);\r\n try {\r\n s.el.setPointerCapture(event.pointerId);\r\n } catch {\r\n // The pointer is already gone; the swipe still ends on pointerup.\r\n }\r\n }\r\n\r\n private springFor(el: HTMLElement): HySpring {\r\n let spring = this.springs.get(el);\r\n if (!spring) {\r\n spring = new HySpring(0, (x) => {\r\n el.style.translate = x ? `${x}px 0` : '';\r\n el.style.opacity = x ? String(Math.max(0, 1 - Math.abs(x) / el.offsetWidth)) : '';\r\n });\r\n this.springs.set(el, spring);\r\n }\r\n return spring;\r\n }\r\n\r\n /**\r\n * When a toast arrives or leaves, the others glide to their new place\r\n * instead of jumping. It starts from where each one is on screen, so a\r\n * slide that is interrupted by another change carries on smoothly.\r\n */\r\n private slideIntoPlace(): void {\r\n const tops = new Map<number, number>();\r\n for (const el of this.host.querySelectorAll<HTMLElement>('.hy-toast[data-id]')) {\r\n const id = Number(el.dataset['id']);\r\n const top = el.offsetTop;\r\n tops.set(id, top);\r\n const before = this.tops.get(id);\r\n if (before === undefined || before === top) continue;\r\n if (this.motion.reduced() || typeof el.animate !== 'function') continue;\r\n const current = new DOMMatrixReadOnly(getComputedStyle(el).transform).m42;\r\n for (const running of el.getAnimations()) {\r\n if (running.id === 'hy-toast-slide') running.cancel();\r\n }\r\n el.animate(\r\n [{ transform: `translateY(${before + current - top}px)` }, { transform: 'none' }],\r\n {\r\n id: 'hy-toast-slide',\r\n duration: MOTION.settle.duration * 1000,\r\n easing: MOTION.settle.css,\r\n },\r\n );\r\n }\r\n this.tops = tops;\r\n }\r\n}\r\n","<ol class=\"hy-toast-list\">\r\n @for (toast of toaster.items(); track toast.id) {\r\n <li\r\n class=\"hy-toast hy-glass\"\r\n [attr.data-id]=\"toast.id\"\r\n [attr.data-material]=\"material()\"\r\n [attr.data-tone]=\"toast.tone\"\r\n [attr.data-leaving]=\"toast.leaving || null\"\r\n (pointerdown)=\"onDown($event)\"\r\n (pointermove)=\"onMove($event)\"\r\n (pointerup)=\"onUp($event, toast)\"\r\n (pointercancel)=\"onUp($event, null)\"\r\n >\r\n @if (toast.tone !== 'neutral') {\r\n <hy-icon class=\"hy-toast__icon\" [name]=\"toast.tone\" />\r\n }\r\n <div class=\"hy-toast__text\">\r\n @if (toast.title) {\r\n <p class=\"hy-toast__title\">{{ toast.title }}</p>\r\n }\r\n <p class=\"hy-toast__message\">{{ toast.message }}</p>\r\n </div>\r\n @if (toast.action; as action) {\r\n <button\r\n type=\"button\"\r\n class=\"hy-toast__action\"\r\n (click)=\"action.run(); toaster.dismiss(toast.id)\"\r\n >\r\n {{ action.label }}\r\n </button>\r\n }\r\n <button\r\n type=\"button\"\r\n class=\"hy-toast__close\"\r\n aria-label=\"Dismiss\"\r\n (click)=\"toaster.dismiss(toast.id)\"\r\n >\r\n <hy-icon name=\"x\" />\r\n </button>\r\n </li>\r\n }\r\n</ol>\r\n","import { LiveAnnouncer } from '@angular/cdk/a11y';\r\nimport {\r\n createGlobalPositionStrategy,\r\n createOverlayRef,\r\n type OverlayRef,\r\n} from '@angular/cdk/overlay';\r\nimport { ComponentPortal } from '@angular/cdk/portal';\r\nimport { Injectable, Injector, inject, signal } from '@angular/core';\r\nimport { ɵinjectIsBrowser as injectIsBrowser } from '@hyalo/angular/core';\r\nimport { MOTION } from '@hyalo/angular/motion';\r\nimport { HyToastStack } from './toast-stack';\r\n\r\n/** Colour and icon of a toast. */\r\nexport type HyToastTone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';\r\n\r\n/** What to show. */\r\nexport interface HyToastOptions {\r\n readonly message: string;\r\n readonly title?: string;\r\n readonly tone?: HyToastTone;\r\n /**\r\n * Milliseconds before it hides. 0 keeps it until dismissed. Defaults to\r\n * 5000, or 0 when there is an action, so nobody has to race a timer.\r\n */\r\n readonly duration?: number;\r\n /** One optional action, such as Undo. */\r\n readonly action?: { readonly label: string; readonly run: () => void };\r\n}\r\n\r\n/** @internal One toast in the stack. */\r\nexport interface HyToastItem extends HyToastOptions {\r\n readonly id: number;\r\n readonly leaving: boolean;\r\n}\r\n\r\n/** Handle to a shown toast. */\r\nexport interface HyToastRef {\r\n dismiss(): void;\r\n}\r\n\r\nconst MAX_VISIBLE = 4;\r\n\r\n/** @internal Why the timers are on hold. Hover and focus are tracked apart. */\r\nexport type HyToastHold = 'hover' | 'focus';\r\n\r\n/**\r\n * Shows toasts: short messages about something that just happened.\r\n *\r\n * ```ts\r\n * toaster.show({ message: 'Address saved', tone: 'success' });\r\n * toaster.show({ message: 'Draft deleted', action: { label: 'Undo', run: () => restore() } });\r\n * ```\r\n *\r\n * Every toast is announced through the live announcer (`danger` toasts\r\n * assertively, the rest politely). Timers pause while the pointer or focus\r\n * is in the stack, and toasts can be swiped away. The stack sits at the\r\n * bottom inline-end corner and is a labelled region, so keyboard and screen\r\n * reader users can reach the actions.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class HyToaster {\r\n private readonly injector = inject(Injector);\r\n private readonly announcer = inject(LiveAnnouncer);\r\n private readonly isBrowser = injectIsBrowser();\r\n private readonly timers = new Map<\r\n number,\r\n { left: number; started: number; handle?: ReturnType<typeof setTimeout> }\r\n >();\r\n private overlayRef: OverlayRef | null = null;\r\n private nextId = 1;\r\n private readonly holds = new Set<HyToastHold>();\r\n\r\n /** @internal The current stack, newest last. */\r\n readonly items = signal<readonly HyToastItem[]>([]);\r\n\r\n /** Shows a toast. Does nothing during server rendering. */\r\n show(options: HyToastOptions): HyToastRef {\r\n const id = this.nextId++;\r\n const ref = { dismiss: () => this.dismiss(id) };\r\n if (!this.isBrowser) return ref;\r\n this.ensureStack();\r\n\r\n const item: HyToastItem = { tone: 'neutral', ...options, id, leaving: false };\r\n this.items.update((list) => [...list, item]);\r\n // Toasts already on their way out do not count, and are not picked again.\r\n const staying = this.items().filter((t) => !t.leaving);\r\n const overflow = staying.length - MAX_VISIBLE;\r\n for (const old of staying.slice(0, Math.max(0, overflow))) this.dismiss(old.id);\r\n\r\n const text = options.title ? `${options.title}. ${options.message}` : options.message;\r\n void this.announcer.announce(text, item.tone === 'danger' ? 'assertive' : 'polite');\r\n\r\n const duration = options.duration ?? (options.action ? 0 : 5000);\r\n if (duration > 0) {\r\n this.timers.set(id, { left: duration, started: Date.now() });\r\n if (this.holds.size === 0) this.startTimer(id);\r\n }\r\n return ref;\r\n }\r\n\r\n success(message: string, title?: string): HyToastRef {\r\n return this.show({ message, title, tone: 'success' });\r\n }\r\n\r\n error(message: string, title?: string): HyToastRef {\r\n return this.show({ message, title, tone: 'danger' });\r\n }\r\n\r\n /** Hides one toast with its exit animation, a fade with reduced motion. */\r\n dismiss(id: number): void {\r\n this.clearTimer(id);\r\n if (!this.items().some((t) => t.id === id && !t.leaving)) return;\r\n this.items.update((list) => list.map((t) => (t.id === id ? { ...t, leaving: true } : t)));\r\n setTimeout(() => this.remove(id), MOTION.quick.duration * 1000 + 40);\r\n }\r\n\r\n /** @internal Removes a toast that already left the screen, as after a swipe. */\r\n discard(id: number): void {\r\n this.clearTimer(id);\r\n this.remove(id);\r\n }\r\n\r\n private clearTimer(id: number): void {\r\n clearTimeout(this.timers.get(id)?.handle);\r\n this.timers.delete(id);\r\n }\r\n\r\n /** Hides every toast. */\r\n dismissAll(): void {\r\n for (const t of this.items()) this.dismiss(t.id);\r\n }\r\n\r\n /**\r\n * @internal Pauses all timers while the stack is hovered or focused. Each\r\n * reason is held separately, so the pointer leaving does not restart the\r\n * timers while focus is still on a toast.\r\n */\r\n pause(reason: HyToastHold): void {\r\n const wasPaused = this.holds.size > 0;\r\n this.holds.add(reason);\r\n if (wasPaused) return;\r\n const now = Date.now();\r\n for (const timer of this.timers.values()) {\r\n clearTimeout(timer.handle);\r\n timer.left -= now - timer.started;\r\n }\r\n }\r\n\r\n /** @internal Releases one hold. Timers restart once no hold is left. */\r\n resume(reason: HyToastHold): void {\r\n if (!this.holds.delete(reason) || this.holds.size > 0) return;\r\n for (const id of this.timers.keys()) this.startTimer(id);\r\n }\r\n\r\n private startTimer(id: number): void {\r\n const timer = this.timers.get(id);\r\n if (!timer) return;\r\n timer.started = Date.now();\r\n timer.handle = setTimeout(() => this.dismiss(id), Math.max(0, timer.left));\r\n }\r\n\r\n private remove(id: number): void {\r\n this.items.update((list) => list.filter((t) => t.id !== id));\r\n }\r\n\r\n private ensureStack(): void {\r\n if (this.overlayRef) return;\r\n this.overlayRef = createOverlayRef(this.injector, {\r\n positionStrategy: createGlobalPositionStrategy(this.injector).bottom('0').end('0'),\r\n panelClass: 'hy-toast-pane',\r\n });\r\n this.overlayRef.attach(new ComponentPortal(HyToastStack, null, this.injector));\r\n }\r\n}\r\n"],"mappings":"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"}