@octane-xplat/ui 0.7.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +4 -3
  2. package/dist/native/Button.tsrx.js +1 -1
  3. package/dist/native/CommandPalette.tsrx.js +7 -1
  4. package/dist/native/Grid.tsrx.js +31 -2
  5. package/dist/native/KeyboardAvoiding.mobile.tsrx.js +2 -2
  6. package/dist/native/Markdown.tsrx.js +233 -0
  7. package/dist/native/Overlay.tsrx.js +15 -26
  8. package/dist/native/Pagination.tsrx.js +15 -12
  9. package/dist/native/PinInput.tsrx.js +25 -7
  10. package/dist/native/Popover.tsrx.js +33 -23
  11. package/dist/native/Pressable.tsrx.js +4 -1
  12. package/dist/native/Screen.tsrx.js +1 -1
  13. package/dist/native/SearchInput.tsrx.js +6 -3
  14. package/dist/native/Sheet.tsrx.js +23 -38
  15. package/dist/native/Slider.tsrx.js +23 -14
  16. package/dist/native/Text.tsrx.js +16 -0
  17. package/dist/native/TextArea.tsrx.js +6 -3
  18. package/dist/native/TextInput.tsrx.js +6 -3
  19. package/dist/native/VirtualList.tsrx.js +305 -121
  20. package/dist/native/WebView.tsrx.js +17 -2
  21. package/dist/native/_virtual/_@oxc-project_runtime@0.150.0/helpers/esm/decorate.js +9 -0
  22. package/dist/native/escape-props.js +2 -0
  23. package/dist/native/grid-auto-placement.js +83 -0
  24. package/dist/native/grid-placement.js +20 -0
  25. package/dist/native/index.shared.js +7 -5
  26. package/dist/native/keyboard-inset.mobile.js +5 -5
  27. package/dist/native/navigation-request.js +26 -0
  28. package/dist/native/octane-client-build.json +1 -1
  29. package/dist/native/overlay-lifetime.mobile.js +65 -0
  30. package/dist/native/pan.tsrx.js +16 -2
  31. package/dist/native/route-table.js +90 -10
  32. package/dist/native/route.js +26 -16
  33. package/dist/native/shared.js +7 -5
  34. package/dist/native/sheet-service.js +2 -2
  35. package/dist/native/svg.mobile.js +1 -1
  36. package/dist/native/{tap-to-blur.mobile.js → tap-to-blur.ios.js} +20 -32
  37. package/dist/native/text-focus.js +10 -0
  38. package/dist/native/text-write.js +18 -11
  39. package/dist/native/toast-anchor.tsrx.js +1 -1
  40. package/dist/native/ui.js +7 -6
  41. package/dist/native/useMeasure.tsrx.js +17 -18
  42. package/dist/native/vendor/ui-svg/index.android.js +2 -2
  43. package/dist/native/vendor/ui-svg/index.ios.js +2 -2
  44. package/dist/native/vendor/ui-svg/{index.common.js → src/index.common.js} +1 -1
  45. package/dist/native/virtual-list-cell-pool.js +63 -0
  46. package/dist/web/Button.web.tsrx.js +1 -1
  47. package/dist/web/KeyboardAvoiding.web.tsrx.js +78 -0
  48. package/dist/web/Markdown.tsrx.js +98 -0
  49. package/dist/web/Overlay.web.tsrx.js +81 -32
  50. package/dist/web/PinInput.web.tsrx.js +24 -10
  51. package/dist/web/Pressable.web.tsrx.js +169 -53
  52. package/dist/web/SearchInput.web.tsrx.js +2 -2
  53. package/dist/web/Sheet.web.tsrx.js +20 -7
  54. package/dist/web/TextArea.web.tsrx.js +22 -17
  55. package/dist/web/TextInput.web.tsrx.js +2 -2
  56. package/dist/web/VirtualList.web.tsrx.js +220 -109
  57. package/dist/web/WebView.web.tsrx.js +3 -3
  58. package/dist/web/{index.shared.js → index.shared.web.js} +7 -5
  59. package/dist/web/modal-focus.web.js +95 -0
  60. package/dist/web/navigation-request.js +26 -0
  61. package/dist/web/octane-client-build.json +1 -1
  62. package/dist/web/pan.web.tsrx.js +27 -10
  63. package/dist/web/route-table.js +90 -10
  64. package/dist/web/route.web.js +88 -22
  65. package/dist/web/sheet-service.web.js +8 -0
  66. package/dist/web/theme/colorScheme.web.js +31 -4
  67. package/dist/web/ui.js +8 -6
  68. package/dist/web/virtual-list-cell-pool.js +63 -0
  69. package/package.json +21 -19
  70. package/platforms/android/native-api-usage.json +7 -7
  71. package/src/CommandPalette.tsrx +8 -1
  72. package/src/Grid.tsrx +32 -1
  73. package/src/Icon.macos.tsrx +1 -0
  74. package/src/KeyboardAvoiding.macos.tsrx +7 -1
  75. package/src/KeyboardAvoiding.mobile.tsrx +2 -2
  76. package/src/KeyboardAvoiding.tsrx +15 -0
  77. package/src/KeyboardAvoiding.web.tsrx +15 -0
  78. package/src/Markdown.tsrx +121 -0
  79. package/src/Meter.tsrx +1 -0
  80. package/src/Overlay.tsrx +15 -30
  81. package/src/Overlay.web.tsrx +12 -3
  82. package/src/Pagination.tsrx +6 -4
  83. package/src/PinInput.macos.tsrx +20 -6
  84. package/src/PinInput.mobile.test.ts +107 -0
  85. package/src/PinInput.tsrx +20 -6
  86. package/src/PinInput.web.test.tsrx +42 -0
  87. package/src/PinInput.web.tsrx +20 -6
  88. package/src/Popover.tsrx +36 -22
  89. package/src/Pressable.tsrx +1 -1
  90. package/src/Pressable.web.test.tsrx +36 -0
  91. package/src/Pressable.web.tsrx +37 -6
  92. package/src/RouteHost.macos.tsrx +1 -0
  93. package/src/Screen.tsrx +1 -1
  94. package/src/SearchInput.tsrx +4 -3
  95. package/src/SearchInput.web.tsrx +1 -1
  96. package/src/Sheet.tsrx +21 -42
  97. package/src/Sheet.web.tsrx +9 -1
  98. package/src/Slider.tsrx +13 -11
  99. package/src/Text.tsrx +36 -0
  100. package/src/TextArea.tsrx +6 -5
  101. package/src/TextArea.web.test.tsrx +39 -0
  102. package/src/TextArea.web.tsrx +7 -5
  103. package/src/TextInput.tsrx +6 -6
  104. package/src/TextInput.web.tsrx +1 -1
  105. package/src/VirtualList.macos.tsrx +93 -6
  106. package/src/VirtualList.mobile.test.tsrx +232 -0
  107. package/src/VirtualList.tsrx +285 -81
  108. package/src/VirtualList.web.tsrx +131 -25
  109. package/src/WebView.tsrx +20 -10
  110. package/src/WebView.web.tsrx +3 -0
  111. package/src/android/BottomNavigationView.android.tsrx +1 -0
  112. package/src/android/MaterialDialog.android.tsrx +1 -0
  113. package/src/anim.macos.tsrx +1 -0
  114. package/src/apply-native-modifiers.native.ts +4 -1
  115. package/src/child-array.ts +12 -7
  116. package/src/colorScheme.macos.ts +1 -0
  117. package/src/controls.macos.tsrx +4 -1
  118. package/src/data-driver.mobile.test.ts +120 -0
  119. package/src/data-lifecycle.mobile.test.ts +251 -0
  120. package/src/escape-props.ts +13 -1
  121. package/src/grid-auto-placement.ts +163 -0
  122. package/src/grid-placement.ts +29 -0
  123. package/src/hosted-unsupported.macos.tsrx +1 -0
  124. package/src/index.macos.ts +23 -3
  125. package/src/index.mobile.ts +0 -1
  126. package/src/index.shared.ts +22 -1
  127. package/src/index.shared.web.ts +291 -0
  128. package/src/index.web.ts +1 -1
  129. package/src/input-readiness.web.test.tsrx +82 -0
  130. package/src/ios/UITabBar.ios.tsrx +1 -0
  131. package/src/keyboard-inset.mobile.test.ts +62 -0
  132. package/src/keyboard-inset.mobile.ts +63 -43
  133. package/src/layout-shells.macos.tsrx +1 -5
  134. package/src/macos-extras.macos.tsrx +2 -0
  135. package/src/modal-focus.web.test.tsrx +63 -0
  136. package/src/modal-focus.web.ts +162 -0
  137. package/src/navigation-request.ts +37 -0
  138. package/src/overlay-lifecycle.mobile.test.ts +316 -0
  139. package/src/overlay-lifetime.mobile.ts +107 -0
  140. package/src/pan.mobile.test.tsrx +116 -0
  141. package/src/pan.tsrx +20 -2
  142. package/src/pan.web.tsrx +35 -7
  143. package/src/platformProps.ts +1 -0
  144. package/src/popover.mobile.test.ts +9 -1
  145. package/src/pressable-accessibility.mobile.test.ts +30 -0
  146. package/src/props.ts +163 -3
  147. package/src/refresh.ts +6 -1
  148. package/src/refresh.web.test.ts +77 -77
  149. package/src/route-android.test.ts +162 -0
  150. package/src/route-history.web.test.ts +80 -0
  151. package/src/route-navigation.web.test.ts +173 -0
  152. package/src/route-table.test.ts +188 -9
  153. package/src/route-table.ts +138 -10
  154. package/src/route.macos.ts +56 -13
  155. package/src/route.ts +54 -24
  156. package/src/route.web.ts +141 -24
  157. package/src/sheet-detents.ts +5 -3
  158. package/src/sheet-service.macos.ts +5 -1
  159. package/src/sheet-service.ts +1 -1
  160. package/src/sheet-service.web.ts +8 -0
  161. package/src/sheet-snap.test.ts +23 -23
  162. package/src/sheet-snap.ts +6 -1
  163. package/src/svg.mobile.ts +9 -3
  164. package/src/tap-to-blur.ios.ts +106 -0
  165. package/src/tap-to-blur.mobile.ts +21 -95
  166. package/src/text-focus.ts +12 -0
  167. package/src/text-input.mobile.test.ts +89 -0
  168. package/src/text-write.ts +21 -11
  169. package/src/theme/colorScheme.linux.ts +2 -4
  170. package/src/theme/colorScheme.web.ts +51 -4
  171. package/src/theme/theme-scheme.macos.ts +8 -2
  172. package/src/theme/tokens.css +1740 -1718
  173. package/src/translate.macos.ts +4 -1
  174. package/src/useMeasure.tsrx +20 -21
  175. package/src/virtual-list-cell-pool.test.ts +53 -0
  176. package/src/virtual-list-cell-pool.ts +81 -0
  177. package/src/virtual-list-layout.test.ts +11 -8
  178. package/src/virtual-list-layout.ts +4 -8
  179. package/src/windows.linux.ts +7 -2
  180. package/src/windows.macos.ts +1 -0
  181. package/types/generated/.tsrx-typegen-manifest.json +16 -3
  182. package/types/generated/Grid.d.ts +0 -1
  183. package/types/generated/KeyboardAvoiding.d.ts +3 -0
  184. package/types/generated/KeyboardAvoiding.web.d.ts +2 -0
  185. package/types/generated/Markdown.d.ts +59 -0
  186. package/types/generated/TextArea.d.ts +2 -2
  187. package/types/generated/TextArea.web.d.ts +1 -1
  188. package/types/generated/TextInput.d.ts +2 -3
  189. package/types/generated/VirtualList.d.ts +3 -3
  190. package/types/generated/escape-props.d.ts +4 -0
  191. package/types/generated/grid-auto-placement.d.ts +13 -0
  192. package/types/generated/grid-placement.d.ts +5 -0
  193. package/types/generated/index.mobile.d.ts +0 -1
  194. package/types/generated/index.shared.d.ts +6 -3
  195. package/types/generated/index.shared.web.d.ts +114 -0
  196. package/types/generated/index.web.d.ts +1 -1
  197. package/types/generated/modal-focus.web.d.ts +3 -0
  198. package/types/generated/navigation-request.d.ts +10 -0
  199. package/types/generated/overlay-lifetime.mobile.d.ts +10 -0
  200. package/types/generated/props.d.ts +111 -3
  201. package/types/generated/route-table.d.ts +14 -3
  202. package/types/generated/svg.mobile.d.ts +2 -2
  203. package/types/generated/tap-to-blur.ios.d.ts +13 -0
  204. package/types/generated/tap-to-blur.mobile.d.ts +1 -15
  205. package/types/generated/text-focus.d.ts +3 -0
  206. package/types/generated/text-write.d.ts +3 -1
  207. package/types/generated/theme/colorScheme.web.d.ts +5 -0
  208. package/types/generated/virtual-list-cell-pool.d.ts +18 -0
  209. package/types/index.macos.d.ts +15 -3
  210. /package/src/vendor/ui-svg/{index.android.ts → src/index.android.ts} +0 -0
  211. /package/src/vendor/ui-svg/{index.common.ts → src/index.common.ts} +0 -0
  212. /package/src/vendor/ui-svg/{index.ios.ts → src/index.ios.ts} +0 -0
  213. /package/src/vendor/ui-svg/{index.ts → src/index.ts} +0 -0
  214. /package/src/vendor/ui-svg/{typings → src/typings}/SVGKit.d.ts +0 -0
  215. /package/src/vendor/ui-svg/{typings → src/typings}/android.d.ts +0 -0
  216. /package/src/vendor/ui-svg/{typings → src/typings}/ios.d.ts +0 -0
  217. /package/types/generated/vendor/ui-svg/{index.common.d.ts → src/index.common.d.ts} +0 -0
  218. /package/types/generated/vendor/ui-svg/{index.d.ts → src/index.d.ts} +0 -0
  219. /package/types/generated/vendor/ui-svg/{index.ios.d.ts → src/index.ios.d.ts} +0 -0
@@ -0,0 +1,232 @@
1
+ /** @jsxImportSource @nativescript-community/octane */
2
+ import { useState } from 'octane'
3
+ import { afterEach, describe, expect, it, vi } from 'vitest'
4
+ import { createObjectContainer, createObjectDriver, createUniversalRoot } from 'octane/universal/native'
5
+ import { VirtualList } from './VirtualList.tsrx'
6
+ import { VirtualListSizeIndex } from './virtual-list-layout'
7
+
8
+ vi.mock('@nativescript/core', () => ({ isAndroid: true, isIOS: false }))
9
+
10
+ const items = Array.from({ length: 500 }, (_, index) => ({ id: 'r' + index }))
11
+ const keyExtractor = (item: { id: string }) => item.id
12
+ const renderItem = (item: { id: string }) => <label id={'row-' + item.id} text={item.id} />
13
+ function StatefulRow(props: { id: string }) @{
14
+ const [taps, setTaps] = useState(0)
15
+
16
+ <label id={'row-' + props.id} text={props.id + ':' + taps} onTap={() => setTaps(taps + 1)} />
17
+ }
18
+
19
+ const renderStatefulItem = (item: { id: string }) => <StatefulRow id={item.id} />
20
+
21
+ const roots: { unmount: () => void }[] = []
22
+
23
+ function collect(nodes: any[]): any[] {
24
+ return nodes.flatMap((node) => [node, ...collect(node.children ?? [])])
25
+ }
26
+
27
+ async function mount(viewportWidth = 100, itemRenderer = renderItem) {
28
+ vi.useFakeTimers()
29
+ const container = createObjectContainer('nativescript')
30
+ const driver = createObjectDriver('nativescript')
31
+ const commits: any[] = []
32
+ const prepare = driver.prepareBatch
33
+ driver.prepareBatch = (...args) => {
34
+ commits.push(args[1])
35
+ return prepare(...args)
36
+ }
37
+
38
+ const root = createUniversalRoot(container, driver)
39
+ roots.push(root)
40
+ root.render(VirtualList as any, { items, keyExtractor, renderItem: itemRenderer })
41
+ const scroll: any = container.children[0]
42
+ scroll.verticalOffset = 0
43
+ scroll.getActualSize = () => ({ width: viewportWidth, height: 200 })
44
+ scroll.scrollToVerticalOffset = vi.fn((offset) => { scroll.verticalOffset = offset })
45
+ container.dispatchEvent(scroll, 'loaded', { object: scroll })
46
+ const content = scroll.children[0]
47
+ content.getActualSize = () => ({ width: 100, height: 500 * 48 })
48
+ container.dispatchEvent(content, 'layoutchanged', { object: content })
49
+ await vi.advanceTimersByTimeAsync(32)
50
+ scroll.scrollToVerticalOffset.mockClear()
51
+ commits.length = 0
52
+ const move = (offset: number) => {
53
+ scroll.verticalOffset = offset
54
+ container.dispatchEvent(scroll, 'scroll', { object: scroll })
55
+ }
56
+
57
+ const measure = (id: string, height: number, emit = true) => {
58
+ const row = collect(container.children).find((node) =>
59
+ String(node.props.className).split(' ').includes('vx-virtual-list-row') && collect(node.children).some((child) => child.props.id === id),
60
+ )
61
+
62
+ if (!row) throw new Error('Missing row ' + id)
63
+ row.getActualSize = () => ({ width: 100, height })
64
+ if (emit) container.dispatchEvent(row, 'layoutchanged', { object: row })
65
+ }
66
+
67
+ return { container, root, scroll, commits, move, measure }
68
+ }
69
+
70
+ afterEach(() => {
71
+ for (const root of roots.splice(0)) root.unmount()
72
+ vi.useRealTimers()
73
+ vi.unstubAllGlobals()
74
+ vi.restoreAllMocks()
75
+ })
76
+
77
+ describe('native VirtualList scroll and measurement scheduling', () => {
78
+ it('does not restore an obsolete anchor after the user advances during measurement', async () => {
79
+ const { scroll, move, measure } = await mount()
80
+ move(480)
81
+ await vi.advanceTimersByTimeAsync(32)
82
+ scroll.scrollToVerticalOffset.mockClear()
83
+ measure('row-r16', 60)
84
+ await Promise.resolve()
85
+ move(492)
86
+ await vi.advanceTimersByTimeAsync(32)
87
+ expect(scroll.verticalOffset).toBe(492)
88
+ expect(scroll.scrollToVerticalOffset).not.toHaveBeenCalled()
89
+ })
90
+
91
+ it('applies an above-anchor height correction while preserving user movement', async () => {
92
+ const { scroll, move, measure } = await mount()
93
+ move(480)
94
+ await vi.advanceTimersByTimeAsync(32)
95
+ measure('row-r5', 60)
96
+ move(492)
97
+ await vi.advanceTimersByTimeAsync(32)
98
+ expect(scroll.verticalOffset).toBe(504)
99
+ })
100
+
101
+ it('coalesces a scroll burst instead of committing inside each native callback', async () => {
102
+ const { commits, move } = await mount()
103
+ move(240)
104
+ move(288)
105
+ move(336)
106
+ expect(commits).toHaveLength(0)
107
+ await vi.advanceTimersByTimeAsync(32)
108
+ expect(commits).toHaveLength(1)
109
+ })
110
+
111
+ it('does not rebuild unvisited estimates when another sample preserves the type average', async () => {
112
+ const reset = vi.spyOn(VirtualListSizeIndex.prototype, 'reset')
113
+ const { measure } = await mount()
114
+ for (let index = 0; index < 4; index++) measure('row-r' + index, 36 + index * 12)
115
+ await vi.advanceTimersByTimeAsync(180)
116
+ const count = reset.mock.calls.length
117
+ for (let index = 4; index < 8; index++) measure('row-r' + index, 36 + (index % 4) * 12)
118
+ await vi.advanceTimersByTimeAsync(180)
119
+ expect(reset).toHaveBeenCalledTimes(count)
120
+ })
121
+
122
+ it('retains exact row sizes when borders make content narrower than the viewport', async () => {
123
+ const { scroll, move, measure } = await mount(102)
124
+ for (let index = 0; index < 4; index++) measure('row-r' + index, 36 + index * 12)
125
+ await vi.advanceTimersByTimeAsync(180)
126
+ move(200)
127
+ await vi.advanceTimersByTimeAsync(32)
128
+ scroll.scrollToVerticalOffset.mockClear()
129
+ measure('row-r0', 36)
130
+ await vi.advanceTimersByTimeAsync(180)
131
+ expect(scroll.verticalOffset).toBe(200)
132
+ expect(scroll.scrollToVerticalOffset).not.toHaveBeenCalled()
133
+ })
134
+
135
+ it('rejects a queued measurement after the keyed item is replaced', async () => {
136
+ const { root, scroll, move, measure } = await mount()
137
+ move(480)
138
+ await vi.advanceTimersByTimeAsync(32)
139
+ measure('row-r5', 120)
140
+ const replacement = items.map((item, index) => index === 5 ? { ...item } : item)
141
+ root.render(VirtualList as any, { items: replacement, keyExtractor, renderItem })
142
+ // The replacement has its own current geometry; the queued 120-height result is obsolete.
143
+ measure('row-r5', 48, false)
144
+ await vi.advanceTimersByTimeAsync(32)
145
+ expect(scroll.verticalOffset).toBe(480)
146
+ })
147
+
148
+ it('commits a burst of row measurements together', async () => {
149
+ const { commits, move, measure } = await mount()
150
+ move(480)
151
+ await vi.advanceTimersByTimeAsync(32)
152
+ commits.length = 0
153
+ measure('row-r12', 60)
154
+ measure('row-r13', 72)
155
+ measure('row-r14', 84)
156
+ expect(commits).toHaveLength(0)
157
+ await vi.advanceTimersByTimeAsync(32)
158
+ expect(commits).toHaveLength(1)
159
+ })
160
+
161
+ it('coalesces through native animation frames and cancels a pending frame on disposal', async () => {
162
+ const { root, commits, move } = await mount()
163
+ vi.stubGlobal('requestAnimationFrame', (callback: (time: number) => void) => setTimeout(() => callback(Date.now()), 8))
164
+ const cancel = vi.fn((id: ReturnType<typeof setTimeout>) => clearTimeout(id))
165
+ vi.stubGlobal('cancelAnimationFrame', cancel)
166
+ move(240)
167
+ move(288)
168
+ expect(commits).toHaveLength(0)
169
+ await vi.advanceTimersByTimeAsync(32)
170
+ expect(commits).toHaveLength(1)
171
+ move(480)
172
+ await vi.advanceTimersByTimeAsync(8)
173
+ root.unmount()
174
+ await vi.advanceTimersByTimeAsync(0)
175
+ expect(cancel).toHaveBeenCalled()
176
+ const count = commits.length
177
+ await vi.advanceTimersByTimeAsync(32)
178
+ expect(commits).toHaveLength(count)
179
+ })
180
+
181
+ it('keeps physical hosts alive on reassignment and destroys them on disposal', async () => {
182
+ const { container, root, move } = await mount()
183
+ const cells = collect(container.children).filter((node) => String(node.props.className).split(' ').includes('vx-virtual-list-cell'))
184
+ expect(cells.length).toBeGreaterThan(0)
185
+ for (const cell of cells) cell.destroyNode = vi.fn()
186
+ move(480)
187
+ await vi.advanceTimersByTimeAsync(32)
188
+ for (const cell of cells) {
189
+ expect(cell.reusable).toBe(true)
190
+ expect(cell.destroyNode).not.toHaveBeenCalled()
191
+ }
192
+
193
+ root.unmount()
194
+ await vi.advanceTimersByTimeAsync(0)
195
+ for (const cell of cells) expect(cell.destroyNode).toHaveBeenCalledWith(true)
196
+ })
197
+
198
+ it('retains keyed state across prepend and resets it after leaving the window', async () => {
199
+ const { container, root, scroll, move } = await mount(100, renderStatefulItem)
200
+ move(480)
201
+ await vi.advanceTimersByTimeAsync(32)
202
+ const findRow = () => collect(container.children).find((node) => node.props.id === 'row-r10')
203
+ const row = findRow()
204
+ container.dispatchEvent(row, 'tap', { object: row })
205
+ expect(findRow().props.text).toBe('r10:1')
206
+ const prepended = [{ id: 'prepended' }, ...items]
207
+ root.render(VirtualList as any, { items: prepended, keyExtractor, renderItem: renderStatefulItem })
208
+ await vi.advanceTimersByTimeAsync(32)
209
+ expect(scroll.verticalOffset).toBe(528)
210
+ expect(findRow()).toBe(row)
211
+ expect(findRow().props.text).toBe('r10:1')
212
+ move(4000)
213
+ await vi.advanceTimersByTimeAsync(32)
214
+ expect(findRow()).toBeUndefined()
215
+ for (const visible of collect(container.children).filter((node) => /^row-r/.test(String(node.props.id)))) {
216
+ expect(visible.props.text).toMatch(/:0$/)
217
+ }
218
+
219
+ move(528)
220
+ await vi.advanceTimersByTimeAsync(32)
221
+ expect(findRow().props.text).toBe('r10:0')
222
+ })
223
+
224
+ it('cancels queued work when the list is disposed', async () => {
225
+ const { root, commits, move } = await mount()
226
+ move(480)
227
+ root.unmount()
228
+ const count = commits.length
229
+ await vi.advanceTimersByTimeAsync(100)
230
+ expect(commits).toHaveLength(count)
231
+ })
232
+ })