@blotch/dolan 22.2.2 → 22.2.4

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 (259) hide show
  1. package/dist/commands/add.d.ts +6 -2
  2. package/dist/commands/add.js +41 -11
  3. package/dist/commands/add.js.map +1 -1
  4. package/dist/commands/add.test.js +162 -9
  5. package/dist/commands/add.test.js.map +1 -1
  6. package/dist/commands/diff.d.ts +2 -1
  7. package/dist/commands/diff.js +38 -25
  8. package/dist/commands/diff.js.map +1 -1
  9. package/dist/commands/diff.test.js +72 -11
  10. package/dist/commands/diff.test.js.map +1 -1
  11. package/dist/commands/doctor.js +228 -34
  12. package/dist/commands/doctor.js.map +1 -1
  13. package/dist/commands/doctor.test.js +367 -10
  14. package/dist/commands/doctor.test.js.map +1 -1
  15. package/dist/commands/eject.js +7 -0
  16. package/dist/commands/eject.js.map +1 -1
  17. package/dist/commands/eject.test.js +39 -0
  18. package/dist/commands/eject.test.js.map +1 -1
  19. package/dist/commands/info.js +3 -2
  20. package/dist/commands/info.js.map +1 -1
  21. package/dist/commands/init.d.ts +37 -1
  22. package/dist/commands/init.js +169 -40
  23. package/dist/commands/init.js.map +1 -1
  24. package/dist/commands/init.test.js +392 -27
  25. package/dist/commands/init.test.js.map +1 -1
  26. package/dist/commands/install-packages.d.ts +6 -0
  27. package/dist/commands/install-packages.js +20 -0
  28. package/dist/commands/install-packages.js.map +1 -0
  29. package/dist/commands/install-packages.test.d.ts +1 -0
  30. package/dist/commands/install-packages.test.js +55 -0
  31. package/dist/commands/install-packages.test.js.map +1 -0
  32. package/dist/commands/list.js +21 -14
  33. package/dist/commands/list.js.map +1 -1
  34. package/dist/commands/list.test.js +62 -5
  35. package/dist/commands/list.test.js.map +1 -1
  36. package/dist/commands/outdated.js +11 -10
  37. package/dist/commands/outdated.js.map +1 -1
  38. package/dist/commands/outdated.test.js +20 -5
  39. package/dist/commands/outdated.test.js.map +1 -1
  40. package/dist/commands/remove.d.ts +4 -0
  41. package/dist/commands/remove.js +17 -4
  42. package/dist/commands/remove.js.map +1 -1
  43. package/dist/commands/remove.test.js +53 -2
  44. package/dist/commands/remove.test.js.map +1 -1
  45. package/dist/commands/theme.js +1 -1
  46. package/dist/commands/theme.js.map +1 -1
  47. package/dist/commands/update.d.ts +11 -0
  48. package/dist/commands/update.js +174 -58
  49. package/dist/commands/update.js.map +1 -1
  50. package/dist/commands/update.test.js +587 -33
  51. package/dist/commands/update.test.js.map +1 -1
  52. package/dist/index.js +7 -0
  53. package/dist/index.js.map +1 -1
  54. package/dist/lockfile.d.ts +7 -0
  55. package/dist/lockfile.js +36 -0
  56. package/dist/lockfile.js.map +1 -1
  57. package/dist/lockfile.test.d.ts +1 -0
  58. package/dist/lockfile.test.js +25 -0
  59. package/dist/lockfile.test.js.map +1 -0
  60. package/dist/registry.d.ts +14 -0
  61. package/dist/registry.js +78 -24
  62. package/dist/registry.js.map +1 -1
  63. package/dist/registry.test.js +119 -38
  64. package/dist/registry.test.js.map +1 -1
  65. package/dist/test-utils.d.ts +5 -0
  66. package/dist/test-utils.js +6 -1
  67. package/dist/test-utils.js.map +1 -1
  68. package/dist/testing/fire-lynx-event.d.ts +14 -0
  69. package/dist/testing/fire-lynx-event.js +19 -0
  70. package/dist/testing/fire-lynx-event.js.map +1 -0
  71. package/dist/testing/fire-lynx-event.test.d.ts +1 -0
  72. package/dist/testing/fire-lynx-event.test.js +44 -0
  73. package/dist/testing/fire-lynx-event.test.js.map +1 -0
  74. package/dist/testing/lynx-main-thread.d.ts +22 -0
  75. package/dist/testing/lynx-main-thread.js +11 -0
  76. package/dist/testing/lynx-main-thread.js.map +1 -0
  77. package/dist/testing/lynx-main-thread.test.d.ts +1 -0
  78. package/dist/testing/lynx-main-thread.test.js +24 -0
  79. package/dist/testing/lynx-main-thread.test.js.map +1 -0
  80. package/dist/testing/set-input-signal.d.ts +14 -0
  81. package/dist/testing/set-input-signal.js +21 -0
  82. package/dist/testing/set-input-signal.js.map +1 -0
  83. package/dist/testing/set-input-signal.test.d.ts +1 -0
  84. package/dist/testing/set-input-signal.test.js +20 -0
  85. package/dist/testing/set-input-signal.test.js.map +1 -0
  86. package/dist/ui/components/accordion/accordion.test.ts +158 -144
  87. package/dist/ui/components/accordion/accordion.ts +19 -14
  88. package/dist/ui/components/action-sheet/action-sheet.test.ts +283 -151
  89. package/dist/ui/components/action-sheet/action-sheet.ts +77 -43
  90. package/dist/ui/components/alert/alert.test.ts +67 -19
  91. package/dist/ui/components/alert/alert.ts +14 -16
  92. package/dist/ui/components/alert-dialog/alert-dialog.test.ts +208 -94
  93. package/dist/ui/components/alert-dialog/alert-dialog.ts +75 -38
  94. package/dist/ui/components/area-chart/area-chart.test.ts +134 -119
  95. package/dist/ui/components/area-chart/area-chart.ts +100 -315
  96. package/dist/ui/components/area-chart/index.ts +2 -3
  97. package/dist/ui/components/avatar/avatar.test.ts +89 -45
  98. package/dist/ui/components/badge/badge.test.ts +57 -23
  99. package/dist/ui/components/badge/badge.ts +12 -14
  100. package/dist/ui/components/bar-chart/bar-chart.test.ts +172 -45
  101. package/dist/ui/components/bar-chart/bar-chart.ts +140 -294
  102. package/dist/ui/components/bar-chart/index.ts +3 -3
  103. package/dist/ui/components/bottom-sheet/bottom-sheet.test.ts +386 -128
  104. package/dist/ui/components/bottom-sheet/bottom-sheet.ts +170 -53
  105. package/dist/ui/components/button/button.test.ts +611 -2
  106. package/dist/ui/components/button/button.ts +434 -49
  107. package/dist/ui/components/button/index.ts +2 -0
  108. package/dist/ui/components/button-group/button-group.test.ts +484 -2
  109. package/dist/ui/components/button-group/button-group.ts +380 -68
  110. package/dist/ui/components/button-group/index.ts +3 -0
  111. package/dist/ui/components/candlestick-chart/candlestick-chart.test.ts +183 -58
  112. package/dist/ui/components/candlestick-chart/candlestick-chart.ts +150 -323
  113. package/dist/ui/components/candlestick-chart/index.ts +3 -4
  114. package/dist/ui/components/card/card.test.ts +82 -62
  115. package/dist/ui/components/chart/chart-focus.test.ts +132 -0
  116. package/dist/ui/components/chart/chart-focus.ts +115 -0
  117. package/dist/ui/components/chart/chart-presets.test.ts +335 -0
  118. package/dist/ui/components/chart/chart-presets.ts +278 -0
  119. package/dist/ui/components/chart/chart-scene.test.ts +589 -0
  120. package/dist/ui/components/chart/chart-scene.ts +483 -0
  121. package/dist/ui/components/chart/chart-theme.test.ts +85 -0
  122. package/dist/ui/components/chart/chart-theme.ts +210 -0
  123. package/dist/ui/components/chart/chart-zoom.test.ts +159 -0
  124. package/dist/ui/components/chart/chart-zoom.ts +162 -0
  125. package/dist/ui/components/chart/chart.test.ts +1017 -0
  126. package/dist/ui/components/chart/chart.ts +934 -0
  127. package/dist/ui/components/chart/index.ts +24 -0
  128. package/dist/ui/components/checkbox/checkbox.test.ts +124 -42
  129. package/dist/ui/components/checkbox/checkbox.ts +23 -23
  130. package/dist/ui/components/collapsible/collapsible.test.ts +158 -81
  131. package/dist/ui/components/collapsible/collapsible.ts +19 -14
  132. package/dist/ui/components/context-menu/context-menu.test.ts +224 -0
  133. package/dist/ui/components/context-menu/context-menu.ts +110 -0
  134. package/dist/ui/components/context-menu/index.ts +1 -0
  135. package/dist/ui/components/dialog/dialog.test.ts +213 -108
  136. package/dist/ui/components/dialog/dialog.ts +72 -35
  137. package/dist/ui/components/dock/dock-scroll.test.ts +130 -0
  138. package/dist/ui/components/dock/dock-scroll.ts +133 -0
  139. package/dist/ui/components/dock/dock.test.ts +749 -0
  140. package/dist/ui/components/dock/dock.ts +960 -0
  141. package/dist/ui/components/dock/index.ts +2 -0
  142. package/dist/ui/components/empty-state/empty-state.test.ts +28 -39
  143. package/dist/ui/components/glass/concentric-radius.test.ts +13 -0
  144. package/dist/ui/components/glass/concentric-radius.ts +17 -0
  145. package/dist/ui/components/glass/glass-design.test.ts +78 -0
  146. package/dist/ui/components/glass/glass-design.ts +119 -0
  147. package/dist/ui/components/glass/glass.test.ts +338 -0
  148. package/dist/ui/components/glass/glass.ts +360 -0
  149. package/dist/ui/components/glass/index.ts +18 -0
  150. package/dist/ui/components/icon/icon-theme.test.ts +169 -0
  151. package/dist/ui/components/icon/icon-theme.ts +227 -0
  152. package/dist/ui/components/icon/icon.test.ts +85 -25
  153. package/dist/ui/components/icon/icon.ts +38 -21
  154. package/dist/ui/components/input/input.test.ts +178 -101
  155. package/dist/ui/components/input/input.ts +15 -12
  156. package/dist/ui/components/label/label.test.ts +11 -24
  157. package/dist/ui/components/line-chart/index.ts +2 -3
  158. package/dist/ui/components/line-chart/line-chart.test.ts +149 -56
  159. package/dist/ui/components/line-chart/line-chart.ts +83 -241
  160. package/dist/ui/components/list/list.test.ts +119 -21
  161. package/dist/ui/components/list/list.ts +21 -15
  162. package/dist/ui/components/menu/index.ts +25 -0
  163. package/dist/ui/components/menu/menu-placement.test.ts +153 -0
  164. package/dist/ui/components/menu/menu-placement.ts +159 -0
  165. package/dist/ui/components/menu/menu-popup.test.ts +632 -0
  166. package/dist/ui/components/menu/menu-popup.ts +544 -0
  167. package/dist/ui/components/menu/menu.test.ts +429 -0
  168. package/dist/ui/components/menu/menu.ts +415 -0
  169. package/dist/ui/components/nav-drawer/nav-drawer.test.ts +435 -191
  170. package/dist/ui/components/nav-drawer/nav-drawer.ts +127 -56
  171. package/dist/ui/components/pagination/pagination.test.ts +177 -65
  172. package/dist/ui/components/pagination/pagination.ts +55 -8
  173. package/dist/ui/components/progress/progress.test.ts +102 -46
  174. package/dist/ui/components/progress/progress.ts +52 -54
  175. package/dist/ui/components/radio-group/radio-group.test.ts +128 -83
  176. package/dist/ui/components/radio-group/radio-group.ts +24 -24
  177. package/dist/ui/components/scatter-chart/index.ts +2 -3
  178. package/dist/ui/components/scatter-chart/scatter-chart.test.ts +131 -26
  179. package/dist/ui/components/scatter-chart/scatter-chart.ts +104 -250
  180. package/dist/ui/components/scroll-area/scroll-area.test.ts +95 -24
  181. package/dist/ui/components/scroll-area/scroll-area.ts +8 -8
  182. package/dist/ui/components/select/select.test.ts +138 -78
  183. package/dist/ui/components/select/select.ts +17 -11
  184. package/dist/ui/components/separator/separator.test.ts +18 -17
  185. package/dist/ui/components/sheet/sheet.test.ts +64 -11
  186. package/dist/ui/components/sheet/sheet.ts +9 -1
  187. package/dist/ui/components/skeleton/skeleton.test.ts +24 -7
  188. package/dist/ui/components/skeleton/skeleton.ts +10 -16
  189. package/dist/ui/components/spinner/spinner.test.ts +67 -22
  190. package/dist/ui/components/spinner/spinner.ts +48 -41
  191. package/dist/ui/components/switch/switch.test.ts +116 -46
  192. package/dist/ui/components/switch/switch.ts +8 -13
  193. package/dist/ui/components/tab-bar/index.ts +1 -0
  194. package/dist/ui/components/tab-bar/tab-bar.test.ts +280 -0
  195. package/dist/ui/components/tab-bar/tab-bar.ts +230 -0
  196. package/dist/ui/components/tabs/tabs.test.ts +215 -157
  197. package/dist/ui/components/tabs/tabs.ts +110 -118
  198. package/dist/ui/components/textarea/textarea.test.ts +206 -100
  199. package/dist/ui/components/textarea/textarea.ts +14 -12
  200. package/dist/ui/components/toast/toast.test.ts +436 -315
  201. package/dist/ui/components/toast/toast.ts +62 -55
  202. package/dist/ui/components/toggle/toggle.test.ts +247 -140
  203. package/dist/ui/theme/chart-colors.test.ts +35 -0
  204. package/dist/ui/theme/dark.css +46 -2
  205. package/dist/ui/theme/default.css +63 -7
  206. package/dist/ui/theme/icon-colors.test.ts +35 -0
  207. package/dist/ui/theme/rgb-channels.test.ts +84 -0
  208. package/dist/ui/theme/tailwind-build.test.ts +114 -0
  209. package/dist/ui/theme/tailwind-css.test.ts +83 -0
  210. package/dist/ui/theme/tailwind-plugin.test.ts +42 -2
  211. package/dist/ui/theme/tailwind-plugin.ts +98 -25
  212. package/dist/ui/theme/tailwind.css +113 -0
  213. package/dist/ui/utils/animate.test.ts +13 -1
  214. package/dist/utils/analyze.d.ts +14 -0
  215. package/dist/utils/analyze.js +15 -0
  216. package/dist/utils/analyze.js.map +1 -1
  217. package/dist/utils/analyze.test.js +14 -1
  218. package/dist/utils/analyze.test.js.map +1 -1
  219. package/dist/utils/installed.d.ts +29 -0
  220. package/dist/utils/installed.js +43 -0
  221. package/dist/utils/installed.js.map +1 -0
  222. package/dist/utils/installed.test.d.ts +1 -0
  223. package/dist/utils/installed.test.js +57 -0
  224. package/dist/utils/installed.test.js.map +1 -0
  225. package/dist/utils/interactive.d.ts +9 -0
  226. package/dist/utils/interactive.js +10 -0
  227. package/dist/utils/interactive.js.map +1 -0
  228. package/dist/utils/interactive.test.d.ts +1 -0
  229. package/dist/utils/interactive.test.js +24 -0
  230. package/dist/utils/interactive.test.js.map +1 -0
  231. package/dist/utils/packages.d.ts +36 -0
  232. package/dist/utils/packages.js +70 -0
  233. package/dist/utils/packages.js.map +1 -0
  234. package/dist/utils/packages.test.d.ts +1 -0
  235. package/dist/utils/packages.test.js +89 -0
  236. package/dist/utils/packages.test.js.map +1 -0
  237. package/dist/utils/resolve-paths.d.ts +17 -1
  238. package/dist/utils/resolve-paths.js +21 -3
  239. package/dist/utils/resolve-paths.js.map +1 -1
  240. package/dist/utils/resolve-paths.test.js +38 -2
  241. package/dist/utils/resolve-paths.test.js.map +1 -1
  242. package/dist/utils/tailwind.d.ts +25 -0
  243. package/dist/utils/tailwind.js +51 -0
  244. package/dist/utils/tailwind.js.map +1 -0
  245. package/dist/utils/tailwind.test.d.ts +1 -0
  246. package/dist/utils/tailwind.test.js +71 -0
  247. package/dist/utils/tailwind.test.js.map +1 -0
  248. package/package.json +13 -1
  249. package/dist/ui/components/area-chart/area-chart-render.test.ts +0 -121
  250. package/dist/ui/components/bar-chart/bar-chart-render.test.ts +0 -174
  251. package/dist/ui/components/button/button-render.test.ts +0 -113
  252. package/dist/ui/components/button-group/button-group-render.test.ts +0 -146
  253. package/dist/ui/components/candlestick-chart/candlestick-chart-render.test.ts +0 -174
  254. package/dist/ui/components/cartesian-chart/cartesian-chart-render.test.ts +0 -188
  255. package/dist/ui/components/cartesian-chart/cartesian-chart.test.ts +0 -525
  256. package/dist/ui/components/cartesian-chart/cartesian-chart.ts +0 -1140
  257. package/dist/ui/components/cartesian-chart/index.ts +0 -18
  258. package/dist/ui/components/line-chart/line-chart-render.test.ts +0 -121
  259. package/dist/ui/components/scatter-chart/scatter-chart-render.test.ts +0 -157
@@ -0,0 +1,130 @@
1
+ import { Component, signal } from '@angular/core';
2
+ import {
3
+ fireEvent,
4
+ render,
5
+ waitForUpdate,
6
+ } from '@blotch/angular-lynx-testing-library';
7
+ import { LYNX_ELEMENTS } from '@blotch/angular-lynx';
8
+ import { describe, expect, it } from 'vitest';
9
+ import { UiDockScroll, UiDockState } from './dock-scroll';
10
+
11
+ @Component({
12
+ imports: [LYNX_ELEMENTS, UiDockScroll],
13
+ template: `
14
+ @if (shown()) {
15
+ <scroll-view ui-dock-scroll scroll-orientation="vertical">
16
+ <view style="height: 2000px"></view>
17
+ </scroll-view>
18
+ }
19
+ `,
20
+ })
21
+ class ScrollHost {
22
+ readonly shown = signal(true);
23
+ }
24
+
25
+ const renderScroll = async () => {
26
+ const result = await render(ScrollHost);
27
+ const host = result.componentRef.instance as ScrollHost;
28
+ const state = result.componentRef.injector.get(UiDockState);
29
+ const scrollView = () => result.container.querySelector('scroll-view')!;
30
+ /**
31
+ * Scrolls the content to `scrollTop`, as Lynx reports it.
32
+ */
33
+ const scrollTo = (scrollTop: number) =>
34
+ fireEvent.scroll(scrollView(), {
35
+ detail: { scrollTop, scrollHeight: 2000 },
36
+ });
37
+ return { ...result, host, state, scrollView, scrollTo };
38
+ };
39
+
40
+ describe('UiDockScroll', () => {
41
+ it('minimizes the dock as content scrolls down, and expands it as it scrolls up', async () => {
42
+ const { state, scrollTo } = await renderScroll();
43
+ expect(state.minimized()).toBe(false);
44
+
45
+ // A little way down isn't enough: a small drag leaves the dock whole.
46
+ scrollTo(10);
47
+ scrollTo(20);
48
+ expect(state.minimized()).toBe(false);
49
+ scrollTo(30);
50
+ expect(state.minimized()).toBe(true);
51
+
52
+ // Further down keeps it minimized.
53
+ scrollTo(300);
54
+ expect(state.minimized()).toBe(true);
55
+
56
+ // A glance back up brings it back.
57
+ scrollTo(295);
58
+ scrollTo(290);
59
+ expect(state.minimized()).toBe(true);
60
+ scrollTo(287);
61
+ expect(state.minimized()).toBe(false);
62
+ });
63
+
64
+ it('counts the distance again when content turns', async () => {
65
+ const { state, scrollTo } = await renderScroll();
66
+ scrollTo(100);
67
+ expect(state.minimized()).toBe(true);
68
+ scrollTo(80);
69
+ expect(state.minimized()).toBe(false);
70
+
71
+ // Down 20 after coming up 20 starts a new count, short of minimizing.
72
+ scrollTo(100);
73
+ expect(state.minimized()).toBe(false);
74
+ // An event with no movement keeps the count.
75
+ scrollTo(100);
76
+ scrollTo(104);
77
+ expect(state.minimized()).toBe(true);
78
+ });
79
+
80
+ it('expands the dock at the top, including the bounce above it', async () => {
81
+ const { state, scrollTo } = await renderScroll();
82
+ scrollTo(200);
83
+ expect(state.minimized()).toBe(true);
84
+
85
+ scrollTo(5);
86
+ expect(state.minimized()).toBe(false);
87
+
88
+ scrollTo(200);
89
+ expect(state.minimized()).toBe(true);
90
+ scrollTo(-30);
91
+ expect(state.minimized()).toBe(false);
92
+ });
93
+
94
+ it("ignores iOS's bounce back from past the end", async () => {
95
+ const { state, scrollTo, scrollView } = await renderScroll();
96
+ // The scroll view is 800px tall, so its content ends at 1200.
97
+ fireEvent.layoutchange(scrollView(), {
98
+ detail: { width: 400, height: 800 },
99
+ });
100
+ scrollTo(1150);
101
+ scrollTo(1250);
102
+ expect(state.minimized()).toBe(true);
103
+
104
+ // Bouncing back from 1250 to the end reads as scrolling up, but isn't.
105
+ scrollTo(1230);
106
+ scrollTo(1210);
107
+ expect(state.minimized()).toBe(true);
108
+
109
+ // Scrolling up from the end is.
110
+ scrollTo(1200);
111
+ scrollTo(1180);
112
+ expect(state.minimized()).toBe(false);
113
+ });
114
+
115
+ it('expands the dock when the scroll view goes, and stops listening', async () => {
116
+ const { host, state, scrollTo, scrollView } = await renderScroll();
117
+ const removed = scrollView();
118
+ scrollTo(200);
119
+ expect(state.minimized()).toBe(true);
120
+
121
+ host.shown.set(false);
122
+ await waitForUpdate();
123
+ expect(state.minimized()).toBe(false);
124
+
125
+ fireEvent.scroll(removed, {
126
+ detail: { scrollTop: 600, scrollHeight: 2000 },
127
+ });
128
+ expect(state.minimized()).toBe(false);
129
+ });
130
+ });
@@ -0,0 +1,133 @@
1
+ import {
2
+ DestroyRef,
3
+ Directive,
4
+ ElementRef,
5
+ Injectable,
6
+ Renderer2,
7
+ inject,
8
+ signal,
9
+ } from '@angular/core';
10
+
11
+ /**
12
+ * Whether the dock is minimized, shared by the dock and the scroll views that
13
+ * minimize it. A service rather than an input, because the two sit in
14
+ * different parts of the app: the dock beside the `<router-outlet>`, and the
15
+ * scroll views inside the routed screens, which can't reach it.
16
+ *
17
+ * ```ts
18
+ * // Bring the dock back from code, after a search, for example.
19
+ * inject(UiDockState).minimized.set(false);
20
+ * ```
21
+ *
22
+ * This is what the app asks for. Whether the dock is minimized now is the
23
+ * dock's own `minimized()`: it stays whole where its design doesn't minimize.
24
+ */
25
+ @Injectable({ providedIn: 'root' })
26
+ export class UiDockState {
27
+ /**
28
+ * Whether the dock should be minimized: on iOS 26 and later and the web,
29
+ * the bar shrinks to the selected item's icon, and on Android's Material
30
+ * design it slides away. The frosted design of iOS 15 to 25 doesn't
31
+ * minimize, as Apple's tab bars didn't, so there it stays expanded whatever
32
+ * this says.
33
+ */
34
+ readonly minimized = signal(false);
35
+ }
36
+
37
+ /**
38
+ * How far content scrolls the same way before the dock follows, in px: a
39
+ * longer way down than up, as iOS's tab bar does, so a small drag doesn't
40
+ * hide it, but a glance back up brings it back.
41
+ */
42
+ const MINIMIZE_AFTER = 24;
43
+ const EXPAND_AFTER = 12;
44
+
45
+ /**
46
+ * Near enough to the top that the dock expands, in px: content scrolled back
47
+ * to its start always shows the whole dock.
48
+ */
49
+ const TOP = 8;
50
+
51
+ /** What Lynx sends with `scroll` and `layoutchange`, as far as this reads. */
52
+ type ScrollDetail = { scrollTop: number; scrollHeight: number };
53
+ type LayoutDetail = { height: number };
54
+
55
+ /**
56
+ * Minimizes the dock while this scroll view scrolls down, and expands it when
57
+ * it scrolls up or back to the top, as iOS 26 minimizes its tab bar
58
+ * (`tabBarMinimizeBehavior(.onScrollDown)`). Put it on each screen's
59
+ * `<scroll-view>` (or `<list>`) that should minimize the dock:
60
+ *
61
+ * ```html
62
+ * <scroll-view ui-dock-scroll scroll-orientation="vertical" class="h-full">
63
+ * …
64
+ * </scroll-view>
65
+ * ```
66
+ *
67
+ * It listens to the scroll view's `scroll` events itself, outside Angular's
68
+ * templates: a template listener would run change detection for every event,
69
+ * many times a second while content moves, where this updates the dock only
70
+ * when it should change. AngularLynx already runs it on Lynx's main thread,
71
+ * beside the native scroll, so it needs no main-thread script.
72
+ *
73
+ * Leaving the screen expands the dock again, so the next screen starts with
74
+ * it whole.
75
+ */
76
+ @Directive({ selector: 'scroll-view[ui-dock-scroll], list[ui-dock-scroll]' })
77
+ export class UiDockScroll {
78
+ readonly #state = inject(UiDockState);
79
+ readonly #renderer = inject(Renderer2);
80
+ readonly #element = inject(ElementRef).nativeElement;
81
+
82
+ /** The last scroll position, to tell which way content moved. */
83
+ #lastTop = 0;
84
+ /** How far content has moved the same way, negative upward. */
85
+ #travel = 0;
86
+ /** The scroll view's own height, once Lynx has laid it out. */
87
+ #viewport = 0;
88
+
89
+ protected readonly listeners = [
90
+ this.#renderer.listen(
91
+ this.#element,
92
+ 'bindscroll',
93
+ (event: { detail: ScrollDetail }) => this.#scrolled(event.detail),
94
+ ),
95
+ this.#renderer.listen(
96
+ this.#element,
97
+ 'bindlayoutchange',
98
+ (event: { detail: LayoutDetail }) => {
99
+ this.#viewport = event.detail.height;
100
+ },
101
+ ),
102
+ ];
103
+
104
+ protected readonly cleanup = inject(DestroyRef).onDestroy(() => {
105
+ for (const remove of this.listeners) remove();
106
+ this.#state.minimized.set(false);
107
+ });
108
+
109
+ #scrolled({ scrollTop, scrollHeight }: ScrollDetail): void {
110
+ const delta = scrollTop - this.#lastTop;
111
+ this.#lastTop = scrollTop;
112
+
113
+ // At the top, including iOS's bounce above it, the dock is whole.
114
+ if (scrollTop <= TOP) {
115
+ this.#travel = 0;
116
+ this.#state.minimized.set(false);
117
+ return;
118
+ }
119
+ // iOS bounces past the end and back, which reads as scrolling up. The
120
+ // user didn't, so the dock stays as it is.
121
+ if (this.#viewport > 0 && scrollTop > scrollHeight - this.#viewport) {
122
+ return;
123
+ }
124
+
125
+ // Count the distance moved one way, starting again when content turns.
126
+ this.#travel = delta * this.#travel >= 0 ? this.#travel + delta : delta;
127
+ if (this.#travel >= MINIMIZE_AFTER) {
128
+ this.#state.minimized.set(true);
129
+ } else if (this.#travel <= -EXPAND_AFTER) {
130
+ this.#state.minimized.set(false);
131
+ }
132
+ }
133
+ }