@react-x11/components 0.1.0 → 0.2.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 (788) hide show
  1. package/README.md +979 -21
  2. package/dist/ansi/cast.d.ts +48 -0
  3. package/dist/ansi/cast.d.ts.map +1 -0
  4. package/dist/ansi/cast.js +135 -0
  5. package/dist/ansi/cast.js.map +1 -0
  6. package/dist/ansi/index.d.ts +10 -0
  7. package/dist/ansi/index.d.ts.map +1 -0
  8. package/dist/ansi/index.js +27 -0
  9. package/dist/ansi/index.js.map +1 -0
  10. package/dist/ansi/palette.d.ts +79 -0
  11. package/dist/ansi/palette.d.ts.map +1 -0
  12. package/dist/ansi/palette.js +155 -0
  13. package/dist/ansi/palette.js.map +1 -0
  14. package/dist/ansi/parse.d.ts +114 -0
  15. package/dist/ansi/parse.d.ts.map +1 -0
  16. package/dist/ansi/parse.js +687 -0
  17. package/dist/ansi/parse.js.map +1 -0
  18. package/dist/ansi/sgr.d.ts +87 -0
  19. package/dist/ansi/sgr.d.ts.map +1 -0
  20. package/dist/ansi/sgr.js +302 -0
  21. package/dist/ansi/sgr.js.map +1 -0
  22. package/dist/ansi/utf8.d.ts +15 -0
  23. package/dist/ansi/utf8.d.ts.map +1 -0
  24. package/dist/ansi/utf8.js +82 -0
  25. package/dist/ansi/utf8.js.map +1 -0
  26. package/dist/calendar/Calendar.d.ts +136 -0
  27. package/dist/calendar/Calendar.d.ts.map +1 -0
  28. package/dist/calendar/Calendar.js +460 -0
  29. package/dist/calendar/Calendar.js.map +1 -0
  30. package/dist/calendar/DatePicker.d.ts +52 -0
  31. package/dist/calendar/DatePicker.d.ts.map +1 -0
  32. package/dist/calendar/DatePicker.js +257 -0
  33. package/dist/calendar/DatePicker.js.map +1 -0
  34. package/dist/calendar/dates.d.ts +90 -0
  35. package/dist/calendar/dates.d.ts.map +1 -0
  36. package/dist/calendar/dates.js +236 -0
  37. package/dist/calendar/dates.js.map +1 -0
  38. package/dist/calendar/index.d.ts +8 -0
  39. package/dist/calendar/index.d.ts.map +1 -0
  40. package/dist/calendar/index.js +15 -0
  41. package/dist/calendar/index.js.map +1 -0
  42. package/dist/charts/components.d.ts +141 -0
  43. package/dist/charts/components.d.ts.map +1 -0
  44. package/dist/charts/components.js +580 -0
  45. package/dist/charts/components.js.map +1 -0
  46. package/dist/charts/data.d.ts +198 -0
  47. package/dist/charts/data.d.ts.map +1 -0
  48. package/dist/charts/data.js +616 -0
  49. package/dist/charts/data.js.map +1 -0
  50. package/dist/charts/hx.d.ts +21 -0
  51. package/dist/charts/hx.d.ts.map +1 -0
  52. package/dist/charts/hx.js +15 -0
  53. package/dist/charts/hx.js.map +1 -0
  54. package/dist/charts/index.d.ts +27 -0
  55. package/dist/charts/index.d.ts.map +1 -0
  56. package/dist/charts/index.js +38 -0
  57. package/dist/charts/index.js.map +1 -0
  58. package/dist/charts/node.d.ts +96 -0
  59. package/dist/charts/node.d.ts.map +1 -0
  60. package/dist/charts/node.js +720 -0
  61. package/dist/charts/node.js.map +1 -0
  62. package/dist/charts/render.d.ts +153 -0
  63. package/dist/charts/render.d.ts.map +1 -0
  64. package/dist/charts/render.js +762 -0
  65. package/dist/charts/render.js.map +1 -0
  66. package/dist/charts/scale.d.ts +42 -0
  67. package/dist/charts/scale.d.ts.map +1 -0
  68. package/dist/charts/scale.js +173 -0
  69. package/dist/charts/scale.js.map +1 -0
  70. package/dist/charts/spec.d.ts +91 -0
  71. package/dist/charts/spec.d.ts.map +1 -0
  72. package/dist/charts/spec.js +33 -0
  73. package/dist/charts/spec.js.map +1 -0
  74. package/dist/charts/timers.d.ts +6 -0
  75. package/dist/charts/timers.d.ts.map +1 -0
  76. package/dist/charts/timers.js +14 -0
  77. package/dist/charts/timers.js.map +1 -0
  78. package/dist/code/hx.d.ts +24 -0
  79. package/dist/code/hx.d.ts.map +1 -0
  80. package/dist/code/hx.js +25 -0
  81. package/dist/code/hx.js.map +1 -0
  82. package/dist/code/index.d.ts +45 -0
  83. package/dist/code/index.d.ts.map +1 -0
  84. package/dist/code/index.js +93 -0
  85. package/dist/code/index.js.map +1 -0
  86. package/dist/code-editor/completion.d.ts +26 -0
  87. package/dist/code-editor/completion.d.ts.map +1 -0
  88. package/dist/code-editor/completion.js +129 -0
  89. package/dist/code-editor/completion.js.map +1 -0
  90. package/dist/code-editor/doc.d.ts +35 -0
  91. package/dist/code-editor/doc.d.ts.map +1 -0
  92. package/dist/code-editor/doc.js +162 -0
  93. package/dist/code-editor/doc.js.map +1 -0
  94. package/dist/code-editor/hx.d.ts +13 -0
  95. package/dist/code-editor/hx.d.ts.map +1 -0
  96. package/dist/code-editor/hx.js +10 -0
  97. package/dist/code-editor/hx.js.map +1 -0
  98. package/dist/code-editor/index.d.ts +88 -0
  99. package/dist/code-editor/index.d.ts.map +1 -0
  100. package/dist/code-editor/index.js +401 -0
  101. package/dist/code-editor/index.js.map +1 -0
  102. package/dist/code-editor/node.d.ts +343 -0
  103. package/dist/code-editor/node.d.ts.map +1 -0
  104. package/dist/code-editor/node.js +1521 -0
  105. package/dist/code-editor/node.js.map +1 -0
  106. package/dist/code-language/hljs.d.ts +47 -0
  107. package/dist/code-language/hljs.d.ts.map +1 -0
  108. package/dist/code-language/hljs.js +211 -0
  109. package/dist/code-language/hljs.js.map +1 -0
  110. package/dist/code-language/index.d.ts +22 -0
  111. package/dist/code-language/index.d.ts.map +1 -0
  112. package/dist/code-language/index.js +20 -0
  113. package/dist/code-language/index.js.map +1 -0
  114. package/dist/code-language/languages/glsl.d.ts +4 -0
  115. package/dist/code-language/languages/glsl.d.ts.map +1 -0
  116. package/dist/code-language/languages/glsl.js +99 -0
  117. package/dist/code-language/languages/glsl.js.map +1 -0
  118. package/dist/code-language/languages/javascript.d.ts +10 -0
  119. package/dist/code-language/languages/javascript.d.ts.map +1 -0
  120. package/dist/code-language/languages/javascript.js +360 -0
  121. package/dist/code-language/languages/javascript.js.map +1 -0
  122. package/dist/code-language/languages/json.d.ts +4 -0
  123. package/dist/code-language/languages/json.d.ts.map +1 -0
  124. package/dist/code-language/languages/json.js +68 -0
  125. package/dist/code-language/languages/json.js.map +1 -0
  126. package/dist/code-language/languages/shell.d.ts +4 -0
  127. package/dist/code-language/languages/shell.d.ts.map +1 -0
  128. package/dist/code-language/languages/shell.js +189 -0
  129. package/dist/code-language/languages/shell.js.map +1 -0
  130. package/dist/code-language/languages/sql.d.ts +11 -0
  131. package/dist/code-language/languages/sql.d.ts.map +1 -0
  132. package/dist/code-language/languages/sql.js +133 -0
  133. package/dist/code-language/languages/sql.js.map +1 -0
  134. package/dist/code-language/lezer.d.ts +24 -0
  135. package/dist/code-language/lezer.d.ts.map +1 -0
  136. package/dist/code-language/lezer.js +148 -0
  137. package/dist/code-language/lezer.js.map +1 -0
  138. package/dist/code-language/registry.d.ts +19 -0
  139. package/dist/code-language/registry.d.ts.map +1 -0
  140. package/dist/code-language/registry.js +56 -0
  141. package/dist/code-language/registry.js.map +1 -0
  142. package/dist/code-language/runs.d.ts +35 -0
  143. package/dist/code-language/runs.d.ts.map +1 -0
  144. package/dist/code-language/runs.js +88 -0
  145. package/dist/code-language/runs.js.map +1 -0
  146. package/dist/code-language/stream.d.ts +70 -0
  147. package/dist/code-language/stream.d.ts.map +1 -0
  148. package/dist/code-language/stream.js +243 -0
  149. package/dist/code-language/stream.js.map +1 -0
  150. package/dist/code-language/textmate.d.ts +27 -0
  151. package/dist/code-language/textmate.d.ts.map +1 -0
  152. package/dist/code-language/textmate.js +86 -0
  153. package/dist/code-language/textmate.js.map +1 -0
  154. package/dist/code-language/theme.d.ts +26 -0
  155. package/dist/code-language/theme.d.ts.map +1 -0
  156. package/dist/code-language/theme.js +108 -0
  157. package/dist/code-language/theme.js.map +1 -0
  158. package/dist/code-language/timers.d.ts +7 -0
  159. package/dist/code-language/timers.d.ts.map +1 -0
  160. package/dist/code-language/timers.js +22 -0
  161. package/dist/code-language/timers.js.map +1 -0
  162. package/dist/code-language/types.d.ts +173 -0
  163. package/dist/code-language/types.d.ts.map +1 -0
  164. package/dist/code-language/types.js +41 -0
  165. package/dist/code-language/types.js.map +1 -0
  166. package/dist/codeblock/index.d.ts +69 -0
  167. package/dist/codeblock/index.d.ts.map +1 -0
  168. package/dist/codeblock/index.js +85 -0
  169. package/dist/codeblock/index.js.map +1 -0
  170. package/dist/color-picker/ColorField.d.ts +39 -0
  171. package/dist/color-picker/ColorField.d.ts.map +1 -0
  172. package/dist/color-picker/ColorField.js +239 -0
  173. package/dist/color-picker/ColorField.js.map +1 -0
  174. package/dist/color-picker/ColorPicker.d.ts +109 -0
  175. package/dist/color-picker/ColorPicker.d.ts.map +1 -0
  176. package/dist/color-picker/ColorPicker.js +757 -0
  177. package/dist/color-picker/ColorPicker.js.map +1 -0
  178. package/dist/color-picker/color.d.ts +105 -0
  179. package/dist/color-picker/color.d.ts.map +1 -0
  180. package/dist/color-picker/color.js +313 -0
  181. package/dist/color-picker/color.js.map +1 -0
  182. package/dist/color-picker/index.d.ts +8 -0
  183. package/dist/color-picker/index.d.ts.map +1 -0
  184. package/dist/color-picker/index.js +19 -0
  185. package/dist/color-picker/index.js.map +1 -0
  186. package/dist/desktop-calendar/eds.d.ts +102 -0
  187. package/dist/desktop-calendar/eds.d.ts.map +1 -0
  188. package/dist/desktop-calendar/eds.js +356 -0
  189. package/dist/desktop-calendar/eds.js.map +1 -0
  190. package/dist/desktop-calendar/ical.d.ts +63 -0
  191. package/dist/desktop-calendar/ical.d.ts.map +1 -0
  192. package/dist/desktop-calendar/ical.js +57 -0
  193. package/dist/desktop-calendar/ical.js.map +1 -0
  194. package/dist/desktop-calendar/index.d.ts +72 -0
  195. package/dist/desktop-calendar/index.d.ts.map +1 -0
  196. package/dist/desktop-calendar/index.js +213 -0
  197. package/dist/desktop-calendar/index.js.map +1 -0
  198. package/dist/embed/client.d.ts +121 -0
  199. package/dist/embed/client.d.ts.map +1 -0
  200. package/dist/embed/client.js +203 -0
  201. package/dist/embed/client.js.map +1 -0
  202. package/dist/embed/host.d.ts +96 -0
  203. package/dist/embed/host.d.ts.map +1 -0
  204. package/dist/embed/host.js +266 -0
  205. package/dist/embed/host.js.map +1 -0
  206. package/dist/embed/index.d.ts +5 -0
  207. package/dist/embed/index.d.ts.map +1 -0
  208. package/dist/embed/index.js +13 -0
  209. package/dist/embed/index.js.map +1 -0
  210. package/dist/embed/timers.d.ts +6 -0
  211. package/dist/embed/timers.d.ts.map +1 -0
  212. package/dist/embed/timers.js +16 -0
  213. package/dist/embed/timers.js.map +1 -0
  214. package/dist/flow/draw.d.ts +45 -0
  215. package/dist/flow/draw.d.ts.map +1 -0
  216. package/dist/flow/draw.js +295 -0
  217. package/dist/flow/draw.js.map +1 -0
  218. package/dist/flow/index.d.ts +64 -0
  219. package/dist/flow/index.d.ts.map +1 -0
  220. package/dist/flow/index.js +306 -0
  221. package/dist/flow/index.js.map +1 -0
  222. package/dist/flow/model.d.ts +158 -0
  223. package/dist/flow/model.d.ts.map +1 -0
  224. package/dist/flow/model.js +566 -0
  225. package/dist/flow/model.js.map +1 -0
  226. package/dist/flow/node.d.ts +429 -0
  227. package/dist/flow/node.d.ts.map +1 -0
  228. package/dist/flow/node.js +3025 -0
  229. package/dist/flow/node.js.map +1 -0
  230. package/dist/flow/paths.d.ts +43 -0
  231. package/dist/flow/paths.d.ts.map +1 -0
  232. package/dist/flow/paths.js +327 -0
  233. package/dist/flow/paths.js.map +1 -0
  234. package/dist/flow/types.d.ts +561 -0
  235. package/dist/flow/types.d.ts.map +1 -0
  236. package/dist/flow/types.js +2 -0
  237. package/dist/flow/types.js.map +1 -0
  238. package/dist/formula/hx.d.ts +24 -0
  239. package/dist/formula/hx.d.ts.map +1 -0
  240. package/dist/formula/hx.js +25 -0
  241. package/dist/formula/hx.js.map +1 -0
  242. package/dist/formula/index.d.ts +74 -0
  243. package/dist/formula/index.d.ts.map +1 -0
  244. package/dist/formula/index.js +155 -0
  245. package/dist/formula/index.js.map +1 -0
  246. package/dist/formula/katex.d.ts +78 -0
  247. package/dist/formula/katex.d.ts.map +1 -0
  248. package/dist/formula/katex.js +130 -0
  249. package/dist/formula/katex.js.map +1 -0
  250. package/dist/formula/layout.d.ts +103 -0
  251. package/dist/formula/layout.d.ts.map +1 -0
  252. package/dist/formula/layout.js +716 -0
  253. package/dist/formula/layout.js.map +1 -0
  254. package/dist/formula/node.d.ts +50 -0
  255. package/dist/formula/node.d.ts.map +1 -0
  256. package/dist/formula/node.js +351 -0
  257. package/dist/formula/node.js.map +1 -0
  258. package/dist/html/controls.d.ts +48 -0
  259. package/dist/html/controls.d.ts.map +1 -0
  260. package/dist/html/controls.js +195 -0
  261. package/dist/html/controls.js.map +1 -0
  262. package/dist/html/css/cascade.d.ts +51 -0
  263. package/dist/html/css/cascade.d.ts.map +1 -0
  264. package/dist/html/css/cascade.js +552 -0
  265. package/dist/html/css/cascade.js.map +1 -0
  266. package/dist/html/css/parse.d.ts +74 -0
  267. package/dist/html/css/parse.d.ts.map +1 -0
  268. package/dist/html/css/parse.js +464 -0
  269. package/dist/html/css/parse.js.map +1 -0
  270. package/dist/html/css/style.d.ts +142 -0
  271. package/dist/html/css/style.d.ts.map +1 -0
  272. package/dist/html/css/style.js +1145 -0
  273. package/dist/html/css/style.js.map +1 -0
  274. package/dist/html/css/ua.d.ts +5 -0
  275. package/dist/html/css/ua.d.ts.map +1 -0
  276. package/dist/html/css/ua.js +171 -0
  277. package/dist/html/css/ua.js.map +1 -0
  278. package/dist/html/css/values.d.ts +78 -0
  279. package/dist/html/css/values.d.ts.map +1 -0
  280. package/dist/html/css/values.js +330 -0
  281. package/dist/html/css/values.js.map +1 -0
  282. package/dist/html/dom.d.ts +112 -0
  283. package/dist/html/dom.d.ts.map +1 -0
  284. package/dist/html/dom.js +374 -0
  285. package/dist/html/dom.js.map +1 -0
  286. package/dist/html/hx.d.ts +24 -0
  287. package/dist/html/hx.d.ts.map +1 -0
  288. package/dist/html/hx.js +29 -0
  289. package/dist/html/hx.js.map +1 -0
  290. package/dist/html/index.d.ts +137 -0
  291. package/dist/html/index.d.ts.map +1 -0
  292. package/dist/html/index.js +339 -0
  293. package/dist/html/index.js.map +1 -0
  294. package/dist/html/layout/block.d.ts +58 -0
  295. package/dist/html/layout/block.d.ts.map +1 -0
  296. package/dist/html/layout/block.js +724 -0
  297. package/dist/html/layout/block.js.map +1 -0
  298. package/dist/html/layout/boxes.d.ts +261 -0
  299. package/dist/html/layout/boxes.d.ts.map +1 -0
  300. package/dist/html/layout/boxes.js +725 -0
  301. package/dist/html/layout/boxes.js.map +1 -0
  302. package/dist/html/layout/flex.d.ts +16 -0
  303. package/dist/html/layout/flex.d.ts.map +1 -0
  304. package/dist/html/layout/flex.js +242 -0
  305. package/dist/html/layout/flex.js.map +1 -0
  306. package/dist/html/layout/floats.d.ts +50 -0
  307. package/dist/html/layout/floats.d.ts.map +1 -0
  308. package/dist/html/layout/floats.js +110 -0
  309. package/dist/html/layout/floats.js.map +1 -0
  310. package/dist/html/layout/inline.d.ts +44 -0
  311. package/dist/html/layout/inline.d.ts.map +1 -0
  312. package/dist/html/layout/inline.js +700 -0
  313. package/dist/html/layout/inline.js.map +1 -0
  314. package/dist/html/layout/table.d.ts +5 -0
  315. package/dist/html/layout/table.d.ts.map +1 -0
  316. package/dist/html/layout/table.js +340 -0
  317. package/dist/html/layout/table.js.map +1 -0
  318. package/dist/html/node.d.ts +149 -0
  319. package/dist/html/node.d.ts.map +1 -0
  320. package/dist/html/node.js +861 -0
  321. package/dist/html/node.js.map +1 -0
  322. package/dist/html/paint.d.ts +48 -0
  323. package/dist/html/paint.d.ts.map +1 -0
  324. package/dist/html/paint.js +495 -0
  325. package/dist/html/paint.js.map +1 -0
  326. package/dist/html/resources.d.ts +48 -0
  327. package/dist/html/resources.d.ts.map +1 -0
  328. package/dist/html/resources.js +156 -0
  329. package/dist/html/resources.js.map +1 -0
  330. package/dist/index.d.ts +48 -0
  331. package/dist/index.d.ts.map +1 -0
  332. package/dist/index.js +92 -0
  333. package/dist/index.js.map +1 -0
  334. package/dist/internal/heights.d.ts +88 -0
  335. package/dist/internal/heights.d.ts.map +1 -0
  336. package/dist/internal/heights.js +203 -0
  337. package/dist/internal/heights.js.map +1 -0
  338. package/dist/internal/hx.d.ts +24 -0
  339. package/dist/internal/hx.d.ts.map +1 -0
  340. package/dist/internal/hx.js +27 -0
  341. package/dist/internal/hx.js.map +1 -0
  342. package/dist/internal/scroll.d.ts +62 -0
  343. package/dist/internal/scroll.d.ts.map +1 -0
  344. package/dist/internal/scroll.js +224 -0
  345. package/dist/internal/scroll.js.map +1 -0
  346. package/dist/internal/text.d.ts +6 -0
  347. package/dist/internal/text.d.ts.map +1 -0
  348. package/dist/internal/text.js +44 -0
  349. package/dist/internal/text.js.map +1 -0
  350. package/dist/internal/timers.d.ts +8 -0
  351. package/dist/internal/timers.d.ts.map +1 -0
  352. package/dist/internal/timers.js +22 -0
  353. package/dist/internal/timers.js.map +1 -0
  354. package/dist/internal/widget.d.ts +40 -0
  355. package/dist/internal/widget.d.ts.map +1 -0
  356. package/dist/internal/widget.js +44 -0
  357. package/dist/internal/widget.js.map +1 -0
  358. package/dist/markdown/ast.d.ts +126 -0
  359. package/dist/markdown/ast.d.ts.map +1 -0
  360. package/dist/markdown/ast.js +8 -0
  361. package/dist/markdown/ast.js.map +1 -0
  362. package/dist/markdown/hx.d.ts +24 -0
  363. package/dist/markdown/hx.d.ts.map +1 -0
  364. package/dist/markdown/hx.js +25 -0
  365. package/dist/markdown/hx.js.map +1 -0
  366. package/dist/markdown/index.d.ts +90 -0
  367. package/dist/markdown/index.d.ts.map +1 -0
  368. package/dist/markdown/index.js +413 -0
  369. package/dist/markdown/index.js.map +1 -0
  370. package/dist/markdown/parse.d.ts +8 -0
  371. package/dist/markdown/parse.d.ts.map +1 -0
  372. package/dist/markdown/parse.js +911 -0
  373. package/dist/markdown/parse.js.map +1 -0
  374. package/dist/markdown/spans.d.ts +20 -0
  375. package/dist/markdown/spans.d.ts.map +1 -0
  376. package/dist/markdown/spans.js +96 -0
  377. package/dist/markdown/spans.js.map +1 -0
  378. package/dist/media-player/backends.d.ts +64 -0
  379. package/dist/media-player/backends.d.ts.map +1 -0
  380. package/dist/media-player/backends.js +110 -0
  381. package/dist/media-player/backends.js.map +1 -0
  382. package/dist/media-player/control.d.ts +40 -0
  383. package/dist/media-player/control.d.ts.map +1 -0
  384. package/dist/media-player/control.js +44 -0
  385. package/dist/media-player/control.js.map +1 -0
  386. package/dist/media-player/hx.d.ts +24 -0
  387. package/dist/media-player/hx.d.ts.map +1 -0
  388. package/dist/media-player/hx.js +25 -0
  389. package/dist/media-player/hx.js.map +1 -0
  390. package/dist/media-player/index.d.ts +107 -0
  391. package/dist/media-player/index.d.ts.map +1 -0
  392. package/dist/media-player/index.js +219 -0
  393. package/dist/media-player/index.js.map +1 -0
  394. package/dist/media-player/mpv.d.ts +4 -0
  395. package/dist/media-player/mpv.d.ts.map +1 -0
  396. package/dist/media-player/mpv.js +120 -0
  397. package/dist/media-player/mpv.js.map +1 -0
  398. package/dist/media-player/vlc.d.ts +8 -0
  399. package/dist/media-player/vlc.d.ts.map +1 -0
  400. package/dist/media-player/vlc.js +83 -0
  401. package/dist/media-player/vlc.js.map +1 -0
  402. package/dist/richtext/index.d.ts +19 -0
  403. package/dist/richtext/index.d.ts.map +1 -0
  404. package/dist/richtext/index.js +4 -0
  405. package/dist/richtext/index.js.map +1 -0
  406. package/dist/richtext/links.d.ts +12 -0
  407. package/dist/richtext/links.d.ts.map +1 -0
  408. package/dist/richtext/links.js +64 -0
  409. package/dist/richtext/links.js.map +1 -0
  410. package/dist/richtext/menu.d.ts +11 -0
  411. package/dist/richtext/menu.d.ts.map +1 -0
  412. package/dist/richtext/menu.js +43 -0
  413. package/dist/richtext/menu.js.map +1 -0
  414. package/dist/richtext/node.d.ts +151 -0
  415. package/dist/richtext/node.d.ts.map +1 -0
  416. package/dist/richtext/node.js +255 -0
  417. package/dist/richtext/node.js.map +1 -0
  418. package/dist/richtext/runs.d.ts +92 -0
  419. package/dist/richtext/runs.d.ts.map +1 -0
  420. package/dist/richtext/runs.js +158 -0
  421. package/dist/richtext/runs.js.map +1 -0
  422. package/dist/table/hx.d.ts +24 -0
  423. package/dist/table/hx.d.ts.map +1 -0
  424. package/dist/table/hx.js +25 -0
  425. package/dist/table/hx.js.map +1 -0
  426. package/dist/table/index.d.ts +169 -0
  427. package/dist/table/index.d.ts.map +1 -0
  428. package/dist/table/index.js +890 -0
  429. package/dist/table/index.js.map +1 -0
  430. package/dist/table/rows.d.ts +136 -0
  431. package/dist/table/rows.d.ts.map +1 -0
  432. package/dist/table/rows.js +146 -0
  433. package/dist/table/rows.js.map +1 -0
  434. package/dist/terminal/backends.d.ts +105 -0
  435. package/dist/terminal/backends.d.ts.map +1 -0
  436. package/dist/terminal/backends.js +185 -0
  437. package/dist/terminal/backends.js.map +1 -0
  438. package/dist/terminal/hx.d.ts +24 -0
  439. package/dist/terminal/hx.d.ts.map +1 -0
  440. package/dist/terminal/hx.js +25 -0
  441. package/dist/terminal/hx.js.map +1 -0
  442. package/dist/terminal/index.d.ts +160 -0
  443. package/dist/terminal/index.d.ts.map +1 -0
  444. package/dist/terminal/index.js +344 -0
  445. package/dist/terminal/index.js.map +1 -0
  446. package/dist/terminal/title.d.ts +24 -0
  447. package/dist/terminal/title.d.ts.map +1 -0
  448. package/dist/terminal/title.js +129 -0
  449. package/dist/terminal/title.js.map +1 -0
  450. package/dist/terminal/vt/colors.d.ts +63 -0
  451. package/dist/terminal/vt/colors.d.ts.map +1 -0
  452. package/dist/terminal/vt/colors.js +203 -0
  453. package/dist/terminal/vt/colors.js.map +1 -0
  454. package/dist/terminal/vt/diff.d.ts +117 -0
  455. package/dist/terminal/vt/diff.d.ts.map +1 -0
  456. package/dist/terminal/vt/diff.js +358 -0
  457. package/dist/terminal/vt/diff.js.map +1 -0
  458. package/dist/terminal/vt/fonts.d.ts +92 -0
  459. package/dist/terminal/vt/fonts.d.ts.map +1 -0
  460. package/dist/terminal/vt/fonts.js +178 -0
  461. package/dist/terminal/vt/fonts.js.map +1 -0
  462. package/dist/terminal/vt/index.d.ts +78 -0
  463. package/dist/terminal/vt/index.d.ts.map +1 -0
  464. package/dist/terminal/vt/index.js +312 -0
  465. package/dist/terminal/vt/index.js.map +1 -0
  466. package/dist/terminal/vt/keys.d.ts +74 -0
  467. package/dist/terminal/vt/keys.d.ts.map +1 -0
  468. package/dist/terminal/vt/keys.js +309 -0
  469. package/dist/terminal/vt/keys.js.map +1 -0
  470. package/dist/terminal/vt/mouse.d.ts +44 -0
  471. package/dist/terminal/vt/mouse.d.ts.map +1 -0
  472. package/dist/terminal/vt/mouse.js +106 -0
  473. package/dist/terminal/vt/mouse.js.map +1 -0
  474. package/dist/terminal/vt/node.d.ts +246 -0
  475. package/dist/terminal/vt/node.d.ts.map +1 -0
  476. package/dist/terminal/vt/node.js +1068 -0
  477. package/dist/terminal/vt/node.js.map +1 -0
  478. package/dist/terminal/vt/pty.d.ts +81 -0
  479. package/dist/terminal/vt/pty.d.ts.map +1 -0
  480. package/dist/terminal/vt/pty.js +201 -0
  481. package/dist/terminal/vt/pty.js.map +1 -0
  482. package/dist/terminal/vt/renderer.d.ts +179 -0
  483. package/dist/terminal/vt/renderer.d.ts.map +1 -0
  484. package/dist/terminal/vt/renderer.js +357 -0
  485. package/dist/terminal/vt/renderer.js.map +1 -0
  486. package/dist/terminal/vt/timers.d.ts +6 -0
  487. package/dist/terminal/vt/timers.d.ts.map +1 -0
  488. package/dist/terminal/vt/timers.js +16 -0
  489. package/dist/terminal/vt/timers.js.map +1 -0
  490. package/dist/terminal/vt/xterm.d.ts +142 -0
  491. package/dist/terminal/vt/xterm.d.ts.map +1 -0
  492. package/dist/terminal/vt/xterm.js +32 -0
  493. package/dist/terminal/vt/xterm.js.map +1 -0
  494. package/dist/terminal-output/hx.d.ts +24 -0
  495. package/dist/terminal-output/hx.d.ts.map +1 -0
  496. package/dist/terminal-output/hx.js +25 -0
  497. package/dist/terminal-output/hx.js.map +1 -0
  498. package/dist/terminal-output/index.d.ts +92 -0
  499. package/dist/terminal-output/index.d.ts.map +1 -0
  500. package/dist/terminal-output/index.js +179 -0
  501. package/dist/terminal-output/index.js.map +1 -0
  502. package/dist/terminal-output/runs.d.ts +27 -0
  503. package/dist/terminal-output/runs.d.ts.map +1 -0
  504. package/dist/terminal-output/runs.js +66 -0
  505. package/dist/terminal-output/runs.js.map +1 -0
  506. package/dist/three/canvas.d.ts +46 -0
  507. package/dist/three/canvas.d.ts.map +1 -0
  508. package/dist/three/canvas.js +305 -0
  509. package/dist/three/canvas.js.map +1 -0
  510. package/dist/three/events.d.ts +64 -0
  511. package/dist/three/events.d.ts.map +1 -0
  512. package/dist/three/events.js +146 -0
  513. package/dist/three/events.js.map +1 -0
  514. package/dist/three/geometries.d.ts +89 -0
  515. package/dist/three/geometries.d.ts.map +1 -0
  516. package/dist/three/geometries.js +345 -0
  517. package/dist/three/geometries.js.map +1 -0
  518. package/dist/three/globals.d.ts +7 -0
  519. package/dist/three/globals.d.ts.map +1 -0
  520. package/dist/three/globals.js +20 -0
  521. package/dist/three/globals.js.map +1 -0
  522. package/dist/three/hx.d.ts +9 -0
  523. package/dist/three/hx.d.ts.map +1 -0
  524. package/dist/three/hx.js +10 -0
  525. package/dist/three/hx.js.map +1 -0
  526. package/dist/three/index.d.ts +22 -0
  527. package/dist/three/index.d.ts.map +1 -0
  528. package/dist/three/index.js +24 -0
  529. package/dist/three/index.js.map +1 -0
  530. package/dist/three/jsx-dev-runtime.d.ts +19 -0
  531. package/dist/three/jsx-dev-runtime.d.ts.map +1 -0
  532. package/dist/three/jsx-dev-runtime.js +2 -0
  533. package/dist/three/jsx-dev-runtime.js.map +1 -0
  534. package/dist/three/jsx-runtime.d.ts +19 -0
  535. package/dist/three/jsx-runtime.d.ts.map +1 -0
  536. package/dist/three/jsx-runtime.js +2 -0
  537. package/dist/three/jsx-runtime.js.map +1 -0
  538. package/dist/three/jsx.d.ts +198 -0
  539. package/dist/three/jsx.d.ts.map +1 -0
  540. package/dist/three/jsx.js +2 -0
  541. package/dist/three/jsx.js.map +1 -0
  542. package/dist/three/materials.d.ts +125 -0
  543. package/dist/three/materials.d.ts.map +1 -0
  544. package/dist/three/materials.js +229 -0
  545. package/dist/three/materials.js.map +1 -0
  546. package/dist/three/math.d.ts +146 -0
  547. package/dist/three/math.d.ts.map +1 -0
  548. package/dist/three/math.js +464 -0
  549. package/dist/three/math.js.map +1 -0
  550. package/dist/three/objects.d.ts +185 -0
  551. package/dist/three/objects.d.ts.map +1 -0
  552. package/dist/three/objects.js +309 -0
  553. package/dist/three/objects.js.map +1 -0
  554. package/dist/three/passes.d.ts +54 -0
  555. package/dist/three/passes.d.ts.map +1 -0
  556. package/dist/three/passes.js +78 -0
  557. package/dist/three/passes.js.map +1 -0
  558. package/dist/three/postprocess.d.ts +85 -0
  559. package/dist/three/postprocess.d.ts.map +1 -0
  560. package/dist/three/postprocess.js +503 -0
  561. package/dist/three/postprocess.js.map +1 -0
  562. package/dist/three/raycast.d.ts +24 -0
  563. package/dist/three/raycast.d.ts.map +1 -0
  564. package/dist/three/raycast.js +138 -0
  565. package/dist/three/raycast.js.map +1 -0
  566. package/dist/three/reconciler.d.ts +48 -0
  567. package/dist/three/reconciler.d.ts.map +1 -0
  568. package/dist/three/reconciler.js +416 -0
  569. package/dist/three/reconciler.js.map +1 -0
  570. package/dist/three/renderer-direct.d.ts +102 -0
  571. package/dist/three/renderer-direct.d.ts.map +1 -0
  572. package/dist/three/renderer-direct.js +782 -0
  573. package/dist/three/renderer-direct.js.map +1 -0
  574. package/dist/three/renderer-indirect.d.ts +68 -0
  575. package/dist/three/renderer-indirect.d.ts.map +1 -0
  576. package/dist/three/renderer-indirect.js +438 -0
  577. package/dist/three/renderer-indirect.js.map +1 -0
  578. package/dist/three/store.d.ts +81 -0
  579. package/dist/three/store.d.ts.map +1 -0
  580. package/dist/three/store.js +110 -0
  581. package/dist/three/store.js.map +1 -0
  582. package/dist/timeline/hx.d.ts +18 -0
  583. package/dist/timeline/hx.d.ts.map +1 -0
  584. package/dist/timeline/hx.js +10 -0
  585. package/dist/timeline/hx.js.map +1 -0
  586. package/dist/timeline/index.d.ts +152 -0
  587. package/dist/timeline/index.d.ts.map +1 -0
  588. package/dist/timeline/index.js +383 -0
  589. package/dist/timeline/index.js.map +1 -0
  590. package/dist/tray-host/hx.d.ts +24 -0
  591. package/dist/tray-host/hx.d.ts.map +1 -0
  592. package/dist/tray-host/hx.js +25 -0
  593. package/dist/tray-host/hx.js.map +1 -0
  594. package/dist/tray-host/index.d.ts +110 -0
  595. package/dist/tray-host/index.d.ts.map +1 -0
  596. package/dist/tray-host/index.js +191 -0
  597. package/dist/tray-host/index.js.map +1 -0
  598. package/dist/tray-host/manager.d.ts +137 -0
  599. package/dist/tray-host/manager.d.ts.map +1 -0
  600. package/dist/tray-host/manager.js +462 -0
  601. package/dist/tray-host/manager.js.map +1 -0
  602. package/dist/tray-host/notify.d.ts +22 -0
  603. package/dist/tray-host/notify.d.ts.map +1 -0
  604. package/dist/tray-host/notify.js +74 -0
  605. package/dist/tray-host/notify.js.map +1 -0
  606. package/dist/tray-host/protocol.d.ts +106 -0
  607. package/dist/tray-host/protocol.d.ts.map +1 -0
  608. package/dist/tray-host/protocol.js +191 -0
  609. package/dist/tray-host/protocol.js.map +1 -0
  610. package/dist/tree/hx.d.ts +24 -0
  611. package/dist/tree/hx.d.ts.map +1 -0
  612. package/dist/tree/hx.js +25 -0
  613. package/dist/tree/hx.js.map +1 -0
  614. package/dist/tree/index.d.ts +221 -0
  615. package/dist/tree/index.d.ts.map +1 -0
  616. package/dist/tree/index.js +751 -0
  617. package/dist/tree/index.js.map +1 -0
  618. package/dist/tree/internal.d.ts +18 -0
  619. package/dist/tree/internal.d.ts.map +1 -0
  620. package/dist/tree/internal.js +66 -0
  621. package/dist/tree/internal.js.map +1 -0
  622. package/dist/tree/rows.d.ts +177 -0
  623. package/dist/tree/rows.d.ts.map +1 -0
  624. package/dist/tree/rows.js +179 -0
  625. package/dist/tree/rows.js.map +1 -0
  626. package/package.json +166 -12
  627. package/src/ansi/cast.ts +159 -0
  628. package/src/ansi/index.ts +53 -0
  629. package/src/ansi/palette.ts +226 -0
  630. package/src/ansi/parse.ts +780 -0
  631. package/src/ansi/sgr.ts +380 -0
  632. package/src/ansi/utf8.ts +84 -0
  633. package/src/calendar/Calendar.ts +744 -0
  634. package/src/calendar/DatePicker.ts +368 -0
  635. package/src/calendar/dates.ts +307 -0
  636. package/src/calendar/index.ts +51 -0
  637. package/src/charts/components.ts +864 -0
  638. package/src/charts/data.ts +776 -0
  639. package/src/charts/hx.ts +39 -0
  640. package/src/charts/index.ts +99 -0
  641. package/src/charts/node.ts +932 -0
  642. package/src/charts/render.ts +982 -0
  643. package/src/charts/scale.ts +214 -0
  644. package/src/charts/spec.ts +122 -0
  645. package/src/charts/timers.ts +24 -0
  646. package/src/code/hx.ts +51 -0
  647. package/src/code/index.ts +174 -0
  648. package/src/code-editor/completion.ts +161 -0
  649. package/src/code-editor/doc.ts +198 -0
  650. package/src/code-editor/hx.ts +26 -0
  651. package/src/code-editor/index.ts +615 -0
  652. package/src/code-editor/node.ts +1926 -0
  653. package/src/code-language/hljs.ts +302 -0
  654. package/src/code-language/index.ts +51 -0
  655. package/src/code-language/languages/glsl.ts +120 -0
  656. package/src/code-language/languages/javascript.ts +419 -0
  657. package/src/code-language/languages/json.ts +72 -0
  658. package/src/code-language/languages/shell.ts +221 -0
  659. package/src/code-language/languages/sql.ts +165 -0
  660. package/src/code-language/lezer.ts +190 -0
  661. package/src/code-language/registry.ts +66 -0
  662. package/src/code-language/runs.ts +128 -0
  663. package/src/code-language/stream.ts +306 -0
  664. package/src/code-language/textmate.ts +134 -0
  665. package/src/code-language/theme.ts +124 -0
  666. package/src/code-language/timers.ts +36 -0
  667. package/src/code-language/types.ts +252 -0
  668. package/src/codeblock/index.ts +146 -0
  669. package/src/color-picker/ColorField.ts +333 -0
  670. package/src/color-picker/ColorPicker.ts +1069 -0
  671. package/src/color-picker/color.ts +399 -0
  672. package/src/color-picker/index.ts +50 -0
  673. package/src/desktop-calendar/eds.ts +484 -0
  674. package/src/desktop-calendar/ical.ts +119 -0
  675. package/src/desktop-calendar/index.ts +287 -0
  676. package/src/embed/client.ts +311 -0
  677. package/src/embed/host.ts +432 -0
  678. package/src/embed/index.ts +33 -0
  679. package/src/embed/timers.ts +32 -0
  680. package/src/flow/draw.ts +418 -0
  681. package/src/flow/index.ts +473 -0
  682. package/src/flow/model.ts +687 -0
  683. package/src/flow/node.ts +3694 -0
  684. package/src/flow/paths.ts +403 -0
  685. package/src/flow/types.ts +634 -0
  686. package/src/formula/hx.ts +51 -0
  687. package/src/formula/index.ts +243 -0
  688. package/src/formula/katex.ts +209 -0
  689. package/src/formula/layout.ts +876 -0
  690. package/src/formula/node.ts +467 -0
  691. package/src/html/controls.ts +244 -0
  692. package/src/html/css/cascade.ts +606 -0
  693. package/src/html/css/parse.ts +500 -0
  694. package/src/html/css/style.ts +1371 -0
  695. package/src/html/css/ua.ts +174 -0
  696. package/src/html/css/values.ts +358 -0
  697. package/src/html/dom.ts +410 -0
  698. package/src/html/hx.ts +55 -0
  699. package/src/html/index.ts +545 -0
  700. package/src/html/layout/block.ts +876 -0
  701. package/src/html/layout/boxes.ts +981 -0
  702. package/src/html/layout/flex.ts +297 -0
  703. package/src/html/layout/floats.ts +125 -0
  704. package/src/html/layout/inline.ts +870 -0
  705. package/src/html/layout/table.ts +380 -0
  706. package/src/html/node.ts +967 -0
  707. package/src/html/paint.ts +605 -0
  708. package/src/html/resources.ts +214 -0
  709. package/src/index.ts +638 -0
  710. package/src/internal/heights.ts +222 -0
  711. package/src/internal/hx.ts +53 -0
  712. package/src/internal/scroll.ts +288 -0
  713. package/src/internal/text.ts +44 -0
  714. package/src/internal/timers.ts +44 -0
  715. package/src/internal/widget.ts +92 -0
  716. package/src/markdown/ast.ts +163 -0
  717. package/src/markdown/hx.ts +51 -0
  718. package/src/markdown/index.ts +686 -0
  719. package/src/markdown/parse.ts +1037 -0
  720. package/src/markdown/spans.ts +143 -0
  721. package/src/media-player/backends.ts +155 -0
  722. package/src/media-player/control.ts +70 -0
  723. package/src/media-player/hx.ts +51 -0
  724. package/src/media-player/index.ts +378 -0
  725. package/src/media-player/mpv.ts +146 -0
  726. package/src/media-player/vlc.ts +97 -0
  727. package/src/richtext/index.ts +42 -0
  728. package/src/richtext/links.ts +78 -0
  729. package/src/richtext/menu.ts +49 -0
  730. package/src/richtext/node.ts +406 -0
  731. package/src/richtext/runs.ts +253 -0
  732. package/src/table/hx.ts +51 -0
  733. package/src/table/index.ts +1270 -0
  734. package/src/table/rows.ts +275 -0
  735. package/src/terminal/backends.ts +242 -0
  736. package/src/terminal/hx.ts +51 -0
  737. package/src/terminal/index.ts +574 -0
  738. package/src/terminal/title.ts +172 -0
  739. package/src/terminal/vt/colors.ts +247 -0
  740. package/src/terminal/vt/diff.ts +467 -0
  741. package/src/terminal/vt/fonts.ts +233 -0
  742. package/src/terminal/vt/index.ts +443 -0
  743. package/src/terminal/vt/keys.ts +369 -0
  744. package/src/terminal/vt/mouse.ts +144 -0
  745. package/src/terminal/vt/node.ts +1242 -0
  746. package/src/terminal/vt/pty.ts +334 -0
  747. package/src/terminal/vt/renderer.ts +555 -0
  748. package/src/terminal/vt/timers.ts +31 -0
  749. package/src/terminal/vt/xterm.ts +193 -0
  750. package/src/terminal-output/hx.ts +51 -0
  751. package/src/terminal-output/index.ts +340 -0
  752. package/src/terminal-output/runs.ts +79 -0
  753. package/src/three/canvas.ts +451 -0
  754. package/src/three/events.ts +197 -0
  755. package/src/three/geometries.ts +429 -0
  756. package/src/three/globals.ts +34 -0
  757. package/src/three/hx.ts +22 -0
  758. package/src/three/index.ts +98 -0
  759. package/src/three/jsx-dev-runtime.ts +27 -0
  760. package/src/three/jsx-runtime.ts +38 -0
  761. package/src/three/jsx.ts +275 -0
  762. package/src/three/materials.ts +270 -0
  763. package/src/three/math.ts +585 -0
  764. package/src/three/objects.ts +397 -0
  765. package/src/three/passes.ts +90 -0
  766. package/src/three/postprocess.ts +677 -0
  767. package/src/three/raycast.ts +189 -0
  768. package/src/three/react-reconciler.d.ts +50 -0
  769. package/src/three/reconciler.ts +624 -0
  770. package/src/three/renderer-direct.ts +1055 -0
  771. package/src/three/renderer-indirect.ts +570 -0
  772. package/src/three/store.ts +207 -0
  773. package/src/timeline/hx.ts +31 -0
  774. package/src/timeline/index.ts +603 -0
  775. package/src/tray-host/hx.ts +51 -0
  776. package/src/tray-host/index.ts +331 -0
  777. package/src/tray-host/manager.ts +639 -0
  778. package/src/tray-host/notify.ts +102 -0
  779. package/src/tray-host/protocol.ts +235 -0
  780. package/src/tree/hx.ts +51 -0
  781. package/src/tree/index.ts +1127 -0
  782. package/src/tree/internal.ts +87 -0
  783. package/src/tree/rows.ts +334 -0
  784. package/src/index.d.ts +0 -2
  785. package/src/index.js +0 -9
  786. package/src/sparkline/index.d.ts +0 -29
  787. package/src/sparkline/index.js +0 -50
  788. package/src/sparkline/node.js +0 -72
@@ -0,0 +1,1270 @@
1
+ // <Table> — the data table: columns as data, rows from the app's own
2
+ // objects, and only the rows in view actually built.
3
+ //
4
+ // This is a **successor to react-x11's own `<Table>`, not a wrapper around
5
+ // it** — the same relationship `<Tree>` has to core's tree. Core's may be
6
+ // stripped down or removed altogether; nothing here imports it, and the two
7
+ // share no code. What is kept is the behaviour a user has already learnt —
8
+ // select on the first click, open on the second, the header that stays put,
9
+ // the resize grips, the sort toggle — and the prop names core's call sites
10
+ // already use (`columns`, `rows`, `sort`, `selected`, `onActivate`,
11
+ // `onColumnResize`), so migrating is changing the import.
12
+ //
13
+ // What it grows, per docs/prd-table.md (the bar `<Tree>` set):
14
+ //
15
+ // 1. **The data is the app's.** `getId` plus per-column `value` render any
16
+ // row shape where it lies; a row of `{ id, … }` configures nothing.
17
+ // 2. **Rows may be any height.** `rowHeight` declares them uniform and the
18
+ // slice is arithmetic, core's model; omit it and the rows are measured —
19
+ // the tree's model, same machinery — so a `render` seam may wrap text or
20
+ // stack lines without breaking the scrollbar.
21
+ // 3. **Every visible part is a seam**: `column.render`,
22
+ // `column.renderHeader`, `renderRow`, `renderEmpty`, and a `styles` bag
23
+ // whose row/cell entries may be functions of row state.
24
+ // 4. **Ceremony is additive.** Sorting, selection, multi-selection,
25
+ // virtualization and the seams are independent opt-ins on this one
26
+ // element — the PRD's continuity contract. There is no second API.
27
+ //
28
+ // Nothing here registers a host element. A table is `<box>`, `<text>` and
29
+ // core's chevron, so there is no `registerElement` call, no JSX augmentation
30
+ // and no side effect at import time at all.
31
+ import React, {
32
+ useCallback,
33
+ useEffect,
34
+ useImperativeHandle,
35
+ useMemo,
36
+ useRef,
37
+ useState,
38
+ } from 'react';
39
+ import type { ReactElement, ReactNode, Ref } from 'react';
40
+ import { createStyles } from 'react-x11/style';
41
+ import type { StyleProp } from 'react-x11';
42
+ import { Icon, useDirection, useTheme } from 'react-x11';
43
+ import type {
44
+ DrawnNode,
45
+ KeyboardEvent,
46
+ MouseEvent,
47
+ ScrollableNode,
48
+ } from 'react-x11';
49
+ import {
50
+ XK_DOWN,
51
+ XK_END,
52
+ XK_HOME,
53
+ XK_LEFT,
54
+ XK_PAGE_DOWN,
55
+ XK_PAGE_UP,
56
+ XK_RETURN,
57
+ XK_RIGHT,
58
+ XK_UP,
59
+ } from 'react-x11/keysyms';
60
+
61
+ import { hx } from './hx.js';
62
+ import type { Host } from './hx.js';
63
+ // Shared with <Tree> — internal, deliberately not a shared *module*; the
64
+ // header of src/internal/heights.ts says why.
65
+ import { RowHeights } from '../internal/heights.js';
66
+ import { afterLayout, cancelAfterLayout } from '../internal/timers.js';
67
+ import { useReveal } from '../internal/scroll.js';
68
+ import {
69
+ MIN_COLUMN,
70
+ columnValue,
71
+ orderRows,
72
+ resolveGetId,
73
+ resolveWidths,
74
+ } from './rows.js';
75
+ import type {
76
+ TableCellState,
77
+ TableColumn,
78
+ TableHeaderCellState,
79
+ TableRow,
80
+ TableRowId,
81
+ TableRowState,
82
+ TableSort,
83
+ } from './rows.js';
84
+
85
+ // The row and column model comes out too: it is what the seams are handed,
86
+ // and an app that drives a table from outside — "what order are the rows in",
87
+ // "how wide did that column get" — does the same arithmetic.
88
+ export type {
89
+ ResolvedWidths,
90
+ TableCellState,
91
+ TableColumn,
92
+ TableHeaderCellState,
93
+ TableRow,
94
+ TableRowId,
95
+ TableRowState,
96
+ TableSort,
97
+ } from './rows.js';
98
+ export {
99
+ MIN_COLUMN,
100
+ UNSIZED_MIN,
101
+ columnValue,
102
+ defaultCompare,
103
+ orderRows,
104
+ resolveGetId,
105
+ resolveWidths,
106
+ } from './rows.js';
107
+
108
+ /** The header strip's height. Core's number — independent of the body's
109
+ * `rowHeight`, so a table of tall rows keeps a normal header. */
110
+ const HEADER_HEIGHT = 24;
111
+ /** The default row-height guess, and core's fixed row height. */
112
+ const ROW_HEIGHT = 24;
113
+ // The band you can grab, and the line you can see — two different sizes on
114
+ // purpose. A separator wants to be a hairline; a resize handle wants to be
115
+ // wide enough to hit without aiming. The band is centred **on** the rule:
116
+ // it is the boundary being moved, so a band lying to one side would light up
117
+ // off-centre and read as belonging to the column it covers.
118
+ const RULE = 1;
119
+ const HALF = 3;
120
+ const GRIP = HALF + RULE + HALF;
121
+ /** What one Left/Right on a focused grip is worth. */
122
+ const STEP = 16;
123
+ /** Rows kept either side of the viewport, so a fast scroll does not show a
124
+ * gap before the next frame catches up. */
125
+ const OVERSCAN = 6;
126
+ /**
127
+ * What to build before the viewport has been measured. `onViewport` cannot
128
+ * arrive until layout has run, which is a frame after the first commit, so
129
+ * there is always one render that has to guess — and guessing "all of them"
130
+ * puts a hundred thousand rows in the tree for a frame.
131
+ */
132
+ const ASSUMED_ROWS = 40;
133
+ /**
134
+ * Where `virtual="auto"` starts virtualizing.
135
+ *
136
+ * Well above any viewport, so a table that is merely long is still built
137
+ * whole and keeps the one thing virtualization costs — every row in the
138
+ * accessibility tree. Well below the point where building every row is felt.
139
+ * The tree's threshold, for the tree's reasons.
140
+ */
141
+ const VIRTUAL_THRESHOLD = 200;
142
+ /** The sort chevron, matched to the capitals of the 12px header caption. */
143
+ const SORT_MARK = 9;
144
+
145
+ const s = createStyles({
146
+ root: { flexGrow: 1, minHeight: 0, minWidth: 0 },
147
+ headerClip: { flexShrink: 0, overflow: 'hidden' },
148
+ headerRow: { flexDirection: 'row', flexShrink: 0 },
149
+ header: {
150
+ flexDirection: 'row',
151
+ alignItems: 'center',
152
+ height: HEADER_HEIGHT,
153
+ paddingStart: 8,
154
+ // Matching the leading inset: the sort mark sits against this edge, and
155
+ // without it the chevron touches the rule between the columns.
156
+ paddingEnd: 8,
157
+ flexShrink: 0,
158
+ transition: { backgroundColor: 80 },
159
+ },
160
+ headerLabel: {
161
+ fontSize: 12,
162
+ textWrap: 'nowrap',
163
+ textBoxTrim: 'cap-alphabetic',
164
+ },
165
+ /** Pushes the sort mark to the far end of the header, so the caption sits
166
+ * at the column's start edge — where the eye scans — and the marks of
167
+ * every sortable column line up instead of drifting with caption length. */
168
+ headerSpring: { flexGrow: 1 },
169
+ grip: {
170
+ flexShrink: 0,
171
+ cursor: 'col-resize',
172
+ alignSelf: 'stretch',
173
+ alignItems: 'center',
174
+ flexDirection: 'row',
175
+ justifyContent: 'center',
176
+ transition: { backgroundColor: 80 },
177
+ },
178
+ rule: { width: RULE, flexShrink: 0, alignSelf: 'stretch' },
179
+ body: { flexGrow: 1, minHeight: 0, overflow: 'scroll' },
180
+ rowsBox: { flexDirection: 'column', flexShrink: 0 },
181
+ row: {
182
+ flexDirection: 'row',
183
+ flexShrink: 0,
184
+ transition: { backgroundColor: 80 },
185
+ },
186
+ cell: {
187
+ justifyContent: 'center',
188
+ paddingStart: 8,
189
+ flexShrink: 0,
190
+ overflow: 'hidden',
191
+ },
192
+ cellText: { fontSize: 12, textWrap: 'nowrap', textBoxTrim: 'cap-alphabetic' },
193
+ spacer: { flexShrink: 0 },
194
+ sortMark: { marginStart: 4 },
195
+ empty: {
196
+ flexGrow: 1,
197
+ alignItems: 'center',
198
+ justifyContent: 'center',
199
+ padding: 16,
200
+ },
201
+ });
202
+
203
+ /** The selection that just changed, alongside the whole set. `id`/`row` name
204
+ * the row the gesture landed on; a select-all has no single row to name. */
205
+ export interface TableSelectChange<Row> {
206
+ type: 'replace' | 'toggle' | 'range' | 'all';
207
+ id?: TableRowId;
208
+ row?: Row;
209
+ }
210
+
211
+ /** The style overrides, one per part the table draws. Function forms are
212
+ * called per rendered row or cell, so a style can follow row state — zebra
213
+ * striping is `row: (s) => s.index % 2 === 1 && { backgroundColor: … }` —
214
+ * without a render prop that repaints everything. */
215
+ export interface TableStyles<Row> {
216
+ /** The header strip — the clipping box the header row scrolls inside. */
217
+ header?: StyleProp;
218
+ headerCell?: StyleProp | ((state: TableHeaderCellState<Row>) => StyleProp);
219
+ row?: StyleProp | ((state: TableRowState<Row>) => StyleProp);
220
+ cell?: StyleProp | ((state: TableCellState<Row>) => StyleProp);
221
+ }
222
+
223
+ /**
224
+ * A `<Table>`'s imperative side.
225
+ *
226
+ * `handleKey` is the one worth knowing about: a filter box above a table
227
+ * holds the keyboard, and forwarding the arrows to this is how the list
228
+ * below it still walks. It reports whether the table took the key, so the
229
+ * caller keeps whatever it did not.
230
+ */
231
+ export interface TableHandle<Row = any> {
232
+ focus: () => void;
233
+ /** Select a row by id, as a click would; `null` clears an uncontrolled
234
+ * selection. An id naming no row is a no-op. */
235
+ select: (id: TableRowId | null) => void;
236
+ /** Bring a row into view. `false` when the id names nothing. */
237
+ scrollToRow: (id: TableRowId) => boolean;
238
+ handleKey: (ev: KeyboardEvent) => boolean;
239
+ /** The rows in display order — the sorted model, not the rendered slice. */
240
+ rows: () => readonly TableRow<Row>[];
241
+ }
242
+
243
+ type BoxProps = Host['box'];
244
+
245
+ interface TableBaseProps<Row> extends Omit<
246
+ BoxProps,
247
+ 'style' | 'children' | 'ref' | 'onKeyDown'
248
+ > {
249
+ columns?: readonly TableColumn<Row>[];
250
+ rows?: readonly Row[];
251
+ /** The row's key. Defaults to `row.id`, and a row that resolves no id is a
252
+ * remedial `TypeError` rather than a broken render. Memoize it. */
253
+ getId?: (row: Row) => TableRowId;
254
+
255
+ /** Controlled sort **descriptor**. Who *orders the rows* is a separate
256
+ * question — see {@link presorted}. */
257
+ sort?: TableSort | null;
258
+ defaultSort?: TableSort | null;
259
+ onSortChange?: (sort: TableSort | null) => void;
260
+ /**
261
+ * The rows already arrive in display order — a server sorted them — so the
262
+ * table only renders the sort indicator and reports toggles. Composes with
263
+ * either descriptor mode; without it the table orders the rows itself,
264
+ * `column.compare ?? natural order over value()`.
265
+ */
266
+ presorted?: boolean;
267
+
268
+ /** The *open* gesture on top of selection — a double click, or Enter. */
269
+ onActivate?: (id: TableRowId, row: Row) => void;
270
+ /** Right-click on a row. The row is selected first unless it is already
271
+ * part of the selection — the file-manager convention, so a context menu
272
+ * always applies to what is under the pointer. */
273
+ onRowContextMenu?: (id: TableRowId, row: Row, ev: MouseEvent) => void;
274
+ /** A column was resized — by drag, or by Left/Right on the focused grip.
275
+ * The resize also converts the column to fixed at that width. */
276
+ onColumnResize?: (id: string, width: number) => void;
277
+
278
+ /**
279
+ * Declare every row exactly this tall, in pixels — core's model: the
280
+ * visible slice is arithmetic and nothing is ever measured. **Omit it and
281
+ * rows are measured instead**: any row may be any height, at the cost of
282
+ * the measure pass. `rowHeight={24}` restores core's exact behaviour.
283
+ */
284
+ rowHeight?: number;
285
+ /** What an unmeasured row is assumed — and floored — at, while measuring.
286
+ * Default 24. The scrollbar is this guess for every row not yet seen, and
287
+ * it converges as you scroll. */
288
+ estimatedRowHeight?: number;
289
+ /** Build only the rows on screen. `'auto'` (the default) turns it on past
290
+ * 200 rows. */
291
+ virtual?: boolean | 'auto';
292
+ /** Rows built either side of the viewport. */
293
+ overscan?: number;
294
+
295
+ /**
296
+ * Everything inside the row box, given what would have been there.
297
+ *
298
+ * The row *box* stays this component's — it carries the height
299
+ * virtualization counts on, the role and aria the table is read through,
300
+ * and the click that selects. Style it with `styles.row`.
301
+ */
302
+ renderRow?: (state: TableRowState<Row>, content: ReactNode[]) => ReactNode;
303
+ /** The body's content when the rows resolve empty. Nothing by default —
304
+ * the header still shows. */
305
+ renderEmpty?: () => ReactNode;
306
+
307
+ styles?: TableStyles<Row>;
308
+ style?: StyleProp;
309
+ /** Whether the table is a tab stop. Default true; `false` for a table
310
+ * inside a popup that owns the focus. */
311
+ focusable?: boolean;
312
+ ref?: Ref<TableHandle<Row>>;
313
+ /** react-x11's queries read this off `props` at run time; its element
314
+ * declarations do not carry it, so it is declared here and reaches the
315
+ * root box through the rest spread. */
316
+ 'data-testname'?: string;
317
+ }
318
+
319
+ /** Single selection — the default, and core's behaviour. */
320
+ export interface TableSingleSelectProps<Row> extends TableBaseProps<Row> {
321
+ selectionMode?: 'single';
322
+ selected?: TableRowId | null;
323
+ defaultSelected?: TableRowId | null;
324
+ onSelect?: (id: TableRowId, row: Row) => void;
325
+ }
326
+
327
+ /** Multiple selection: the file-manager grammar — click replaces, Ctrl
328
+ * toggles, Shift extends from the anchor, Ctrl+A takes everything. */
329
+ export interface TableMultiSelectProps<Row> extends TableBaseProps<Row> {
330
+ selectionMode: 'multiple';
331
+ selected?: readonly TableRowId[];
332
+ defaultSelected?: readonly TableRowId[];
333
+ onSelectedChange?: (
334
+ selected: TableRowId[],
335
+ change: TableSelectChange<Row>,
336
+ ) => void;
337
+ }
338
+
339
+ /** Display only: no cursor, no hover, clicks select nothing. */
340
+ export interface TableStaticProps<Row> extends TableBaseProps<Row> {
341
+ selectionMode: 'none';
342
+ }
343
+
344
+ /**
345
+ * The props are a discriminated union on `selectionMode`, the Calendar's
346
+ * precedent: one id and an array of ids are different shapes, and the
347
+ * compiler walking the caller through the change is the point. The wrong
348
+ * pairing at run time is a remedial `TypeError`, not an empty render.
349
+ */
350
+ export type TableProps<Row = any> =
351
+ | TableSingleSelectProps<Row>
352
+ | TableMultiSelectProps<Row>
353
+ | TableStaticProps<Row>;
354
+
355
+ /** Every prop of every variant, loosened — what the implementation reads
356
+ * after the runtime check has vouched for the pairing. */
357
+ interface TableAllProps<Row> extends TableBaseProps<Row> {
358
+ selectionMode?: 'none' | 'single' | 'multiple';
359
+ selected?: TableRowId | null | readonly TableRowId[];
360
+ defaultSelected?: TableRowId | null | readonly TableRowId[];
361
+ onSelect?: (id: TableRowId, row: Row) => void;
362
+ onSelectedChange?: (
363
+ selected: TableRowId[],
364
+ change: TableSelectChange<Row>,
365
+ ) => void;
366
+ }
367
+
368
+ const EMPTY_SET: ReadonlySet<TableRowId> = new Set();
369
+
370
+ /**
371
+ * `<Table columns rows />` — a data table with a header that stays put.
372
+ *
373
+ * <Table
374
+ * columns={[{ id: 'name', label: 'Name', flex: 1 }]}
375
+ * rows={[{ id: 1, name: 'index.ts' }]}
376
+ * />
377
+ *
378
+ * Columns are `{ id, label, width | flex, align, value, render }` — core's
379
+ * shape. `value(row)` feeds sorting and the default cell text;
380
+ * `render(row, state)` replaces the cell entirely, and the state says
381
+ * whether it is on the selected row, because that row is a filled bar and a
382
+ * colour chosen against the resting background is unreadable on it. A
383
+ * column declaring neither `width` nor `flex` stretches (`flex: 1`, floored
384
+ * at 120px), so a table with no sizing config fills its box.
385
+ *
386
+ * Sorting is uncontrolled unless you pass `sort`; the header reports
387
+ * `onSortChange` either way, and `presorted` says the rows already arrive
388
+ * ordered. Selection is single by default (`selected` + `onSelect`),
389
+ * `selectionMode="multiple"` for the set (`selected` + `onSelectedChange`),
390
+ * `"none"` for display. `onActivate` is the *open* gesture on top — a double
391
+ * click or Enter.
392
+ *
393
+ * The table is a single tab stop; Up/Down walk the rows, PageUp/PageDown
394
+ * move by a viewport, Home/End jump, Shift extends in multiple mode, Space
395
+ * toggles there, Ctrl+A takes everything. **The focus is on the table, not
396
+ * the row** — a virtualized row is unmounted the moment it scrolls out and
397
+ * focus would go with it, so the selection is the cursor. The honest caveat:
398
+ * while virtualizing, only the rendered rows are in the accessibility tree —
399
+ * the same rows a sighted user can see.
400
+ */
401
+ export function Table<Row = any>(props: TableProps<Row>): ReactElement {
402
+ const {
403
+ columns = [],
404
+ rows = [],
405
+ getId,
406
+ sort,
407
+ defaultSort,
408
+ onSortChange,
409
+ presorted = false,
410
+ onActivate,
411
+ onRowContextMenu,
412
+ onColumnResize,
413
+ rowHeight,
414
+ estimatedRowHeight,
415
+ virtual = 'auto',
416
+ overscan = OVERSCAN,
417
+ renderRow,
418
+ renderEmpty,
419
+ styles,
420
+ style,
421
+ focusable = true,
422
+ ref,
423
+ selectionMode = 'single',
424
+ selected,
425
+ defaultSelected,
426
+ onSelect,
427
+ onSelectedChange,
428
+ // ours to chain rather than to hand over: virtualization and the sticky
429
+ // header are measured through both of these
430
+ onScroll,
431
+ onViewport,
432
+ ...boxProps
433
+ } = props as TableAllProps<Row>;
434
+
435
+ // The union keeps a caller honest at compile time; this keeps a caller
436
+ // honest who got the props from somewhere the compiler could not see.
437
+ if (selectionMode === 'multiple') {
438
+ if (selected !== undefined && !Array.isArray(selected)) {
439
+ throw new TypeError(
440
+ '<Table selectionMode="multiple"> takes `selected` as an array of ' +
441
+ 'ids. Pass `selected={[id]}`, or drop selectionMode for single ' +
442
+ 'selection.',
443
+ );
444
+ }
445
+ if (defaultSelected !== undefined && !Array.isArray(defaultSelected)) {
446
+ throw new TypeError(
447
+ '<Table selectionMode="multiple"> takes `defaultSelected` as an ' +
448
+ 'array of ids.',
449
+ );
450
+ }
451
+ } else if (Array.isArray(selected) || Array.isArray(defaultSelected)) {
452
+ throw new TypeError(
453
+ `<Table> got an array for \`selected\` but selectionMode is ` +
454
+ `'${selectionMode}' — pass selectionMode="multiple", or one id.`,
455
+ );
456
+ }
457
+
458
+ const theme = useTheme();
459
+ const rtl = useDirection() === 'rtl';
460
+ const [ownSort, setOwnSort] = useState<TableSort | null>(defaultSort ?? null);
461
+ const [ownSingle, setOwnSingle] = useState<TableRowId | null>(() =>
462
+ Array.isArray(defaultSelected)
463
+ ? null
464
+ : ((defaultSelected as TableRowId | null | undefined) ?? null),
465
+ );
466
+ const [ownMulti, setOwnMulti] = useState<readonly TableRowId[]>(() =>
467
+ Array.isArray(defaultSelected) ? defaultSelected : [],
468
+ );
469
+ /** The keyboard cursor in multiple mode; in single mode the selection is
470
+ * the cursor. */
471
+ const [cursorId, setCursorId] = useState<TableRowId | null>(null);
472
+ const [userWidths, setUserWidths] = useState<
473
+ Readonly<Record<string, number>>
474
+ >({});
475
+ const [scrollX, setScrollX] = useState(0);
476
+ const [view, setView] = useState({ top: 0, height: 0, width: 0 });
477
+ // Bumped by a measurement pass that found a row taller or shorter than the
478
+ // index believed. It is the only reason the component re-renders for a
479
+ // measurement, and a pass that finds nothing new does not bump it — which
480
+ // is what makes measure → render → measure converge instead of spinning.
481
+ const [, setMeasured] = useState(0);
482
+
483
+ const readId = useMemo(() => resolveGetId<Row>(getId), [getId]);
484
+ const activeSort = sort === undefined ? ownSort : sort;
485
+
486
+ const ordered = useMemo(
487
+ () => orderRows(rows, columns, readId, activeSort, presorted),
488
+ [rows, columns, readId, activeSort, presorted],
489
+ );
490
+
491
+ const selectedIds: ReadonlySet<TableRowId> = useMemo(() => {
492
+ if (selectionMode === 'none') return EMPTY_SET;
493
+ if (selectionMode === 'multiple') {
494
+ const list =
495
+ selected === undefined ? ownMulti : (selected as readonly TableRowId[]);
496
+ return new Set(list);
497
+ }
498
+ const one =
499
+ selected === undefined ? ownSingle : (selected as TableRowId | null);
500
+ return one === null || one === undefined ? EMPTY_SET : new Set([one]);
501
+ }, [selectionMode, selected, ownMulti, ownSingle]);
502
+
503
+ const cursor =
504
+ selectionMode === 'multiple'
505
+ ? cursorId
506
+ : selected === undefined
507
+ ? ownSingle
508
+ : (selected as TableRowId | null);
509
+
510
+ // Held-down or fast keys arrive in a burst, and every handler in that
511
+ // burst sees the render it started from — so the order, the selection and
512
+ // the cursor are mirrored here and updated the moment they change, or
513
+ // three Downs in a row all step off the same starting point.
514
+ const orderedRef = useRef(ordered);
515
+ orderedRef.current = ordered;
516
+ const columnsRef = useRef(columns);
517
+ columnsRef.current = columns;
518
+ const selectedRef = useRef(selectedIds);
519
+ selectedRef.current = selectedIds;
520
+ const cursorRef = useRef(cursor);
521
+ cursorRef.current = cursor;
522
+ const viewRef = useRef(view);
523
+ viewRef.current = view;
524
+ /** Where a Shift range grows from — the last plain click or plain step. */
525
+ const anchorRef = useRef<TableRowId | null>(null);
526
+
527
+ const root = useRef<DrawnNode | null>(null);
528
+ const body = useRef<ScrollableNode | null>(null);
529
+ /** The rows on screen, by id — with the index each was drawn at, so a
530
+ * measurement pass does not have to search the row list for it. */
531
+ const rowNodes = useRef(
532
+ new Map<TableRowId, { node: DrawnNode; at: number }>(),
533
+ );
534
+ const drag = useRef<{ id: string; from: number; width: number } | null>(null);
535
+ const userWidthsRef = useRef(userWidths);
536
+ userWidthsRef.current = userWidths;
537
+
538
+ /** Declared uniform: divide, never measure — core's model. */
539
+ const uniform = rowHeight !== undefined;
540
+ const estimate = rowHeight ?? estimatedRowHeight ?? ROW_HEIGHT;
541
+ // `useState` for its lazy initializer rather than `useRef`: one index per
542
+ // mounted table, built once. Nothing ever calls the setter — what it holds
543
+ // is mutable and its changes are announced through `setMeasured`. With
544
+ // `rowHeight` declared nothing is ever measured, every height stays the
545
+ // estimate, and the index computes exactly `i * rowHeight` — one code
546
+ // path for both models.
547
+ const [heights] = useState(() => new RowHeights(estimate));
548
+ heights.sync(ordered, estimate);
549
+
550
+ const virtualizing =
551
+ virtual === true ||
552
+ (virtual === 'auto' && ordered.length > VIRTUAL_THRESHOLD);
553
+
554
+ // The slice worth building: what is on screen, plus a little either side.
555
+ const first = virtualizing
556
+ ? Math.max(0, heights.indexAt(view.top) - overscan)
557
+ : 0;
558
+ let last = ordered.length;
559
+ if (virtualizing) {
560
+ if (view.height > 0) {
561
+ last = Math.min(
562
+ ordered.length,
563
+ heights.indexAt(view.top + view.height) + 1 + overscan,
564
+ );
565
+ } else {
566
+ last = Math.min(ordered.length, first + ASSUMED_ROWS);
567
+ }
568
+ }
569
+ /** Where the slice starts, and how much of the list is below it — the two
570
+ * spacers that keep the scrollbar measuring the whole table. */
571
+ const above = virtualizing ? heights.offsetAt(first) : 0;
572
+ const below = virtualizing ? heights.total() - heights.offsetAt(last) : 0;
573
+
574
+ /** Columns resolve to pixels once, at the table level, per (columns,
575
+ * viewport, resizes) — every row agrees on the grid by construction. */
576
+ const { widths, total } = useMemo(
577
+ () => resolveWidths(columns, userWidths, view.width),
578
+ [columns, userWidths, view.width],
579
+ );
580
+
581
+ /**
582
+ * The scroll the table owes a row, and the pane's real offset read back
583
+ * after every layout — the two halves of `../internal/scroll.ts`, which
584
+ * says why a reveal cannot be a one-shot and why `onScroll` is not the
585
+ * whole story.
586
+ */
587
+ const reveal = useReveal({
588
+ box: body,
589
+ rows: orderedRef,
590
+ nodes: rowNodes,
591
+ heights,
592
+ });
593
+
594
+ /**
595
+ * Re-read the offset the body is *actually* at.
596
+ *
597
+ * The pane moves silently — it resolves a queued reveal during layout, and
598
+ * re-clamps an offset the content outgrew or outshrank — and a slice built
599
+ * from the offset before those is drawn where the viewport is not: a blank
600
+ * band where the rows should be, and no way back until a scroll of your own
601
+ * re-syncs it by accident.
602
+ */
603
+ const syncScroll = useCallback((): void => {
604
+ const box = body.current;
605
+ if (!box) return;
606
+ const { scrollX: x, scrollY: y } = box;
607
+ setScrollX((prev) => (prev === x ? prev : x));
608
+ // Only a virtualizing table reads the vertical offset — a whole one has
609
+ // no slice to rebuild, and re-rendering it on a scroll it already drew
610
+ // would be work for nothing.
611
+ if (virtualizing)
612
+ setView((prev) => (prev.top === y ? prev : { ...prev, top: y }));
613
+ }, [virtualizing]);
614
+
615
+ /**
616
+ * Read back what the rows on screen actually laid out at.
617
+ *
618
+ * Runs a tick after the commit because layout runs on the frame flush —
619
+ * an effect reads the *previous* pass's geometry (see ./timers.ts).
620
+ * Idempotent: a row whose measurement has not changed reports no change,
621
+ * so the second pass over the same rows re-renders nothing. Rows above
622
+ * the viewport anchor shift the scroll offset by their delta, or
623
+ * measuring a row already scrolled past yanks the list under the pointer.
624
+ */
625
+ const measureRows = useCallback((): boolean => {
626
+ if (uniform || !virtualizing) return false;
627
+ // Before the first `onViewport` the flex columns sit on their floors and
628
+ // every row is laid out against a width that is about to change — there
629
+ // is nothing honest to measure yet.
630
+ if (viewRef.current.width <= 0) return false;
631
+ // A row laid out at a width the columns no longer resolve to is a
632
+ // measurement of the wrong table, and it must not be recorded — a row
633
+ // that scrolls out before the corrected pass would keep a wrong-width
634
+ // height forever. The expectation is recomputed from event-fresh inputs
635
+ // rather than read from the last render: this tick runs *between* an
636
+ // `onViewport` and the re-render it causes, which is exactly when the
637
+ // rendered state is stale. The tree never needs any of this: its row
638
+ // width never depends on a resolved column model.
639
+ const expected = resolveWidths(
640
+ columnsRef.current,
641
+ userWidthsRef.current,
642
+ viewRef.current.width,
643
+ ).total;
644
+ const box = body.current;
645
+ const rows = orderedRef.current;
646
+ const anchor = box ? heights.indexAt(box.scrollY) : 0;
647
+ let shift = 0;
648
+ let changed = false;
649
+ for (const [id, { node, at }] of rowNodes.current) {
650
+ if (rows[at]?.id !== id) continue; // drawn against a list that moved
651
+ if (node.abs.width !== expected) continue;
652
+ const height = node.abs.height;
653
+ const was = heights.heightAt(at);
654
+ if (!heights.measure(id, at, height)) continue;
655
+ changed = true;
656
+ if (at < anchor) shift += height - was;
657
+ }
658
+ if (!changed) return false;
659
+ if (shift !== 0 && box) {
660
+ reveal.scrollTo(box.scrollY + shift);
661
+ }
662
+ setMeasured((n) => n + 1);
663
+ return true;
664
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `heights` is a
665
+ // stable instance
666
+ }, [uniform, virtualizing]);
667
+
668
+ /**
669
+ * The one tick after layout, and everything that can only be known there:
670
+ * what the rows measured, whether an owed scroll can go further now that
671
+ * the new rows are laid out, and where the body actually ended up. In that
672
+ * order — each step can move the offset the next one reads.
673
+ *
674
+ * Scheduled for every render a virtualized table makes, because every one
675
+ * of them can move the offset its next slice is built from. A whole table
676
+ * needs none of it: `onViewport` is when its content can have been
677
+ * re-clamped, and it rebuilds no slice anyway.
678
+ */
679
+ useEffect(() => {
680
+ if (!virtualizing) return undefined;
681
+ const id = afterLayout(() => {
682
+ // `measureRows` first, and its answer handed on: a pass that moved the
683
+ // heights has not settled anything, and an owed scroll judged against
684
+ // the layout it is about to invalidate is not owed any less.
685
+ reveal.retry(measureRows());
686
+ syncScroll();
687
+ });
688
+ return () => cancelAfterLayout(id);
689
+ });
690
+
691
+ /** Put a row in view, by the index its call site already has. */
692
+ const revealAt = useCallback(
693
+ (at: number): void => {
694
+ const row = orderedRef.current[at];
695
+ if (row) reveal.to(row.id);
696
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `reveal` is a
697
+ // stable handle
698
+ },
699
+ [reveal],
700
+ );
701
+
702
+ const commitSingle = useCallback(
703
+ (row: TableRow<Row>): void => {
704
+ cursorRef.current = row.id;
705
+ if (selected === undefined) setOwnSingle(row.id);
706
+ onSelect?.(row.id, row.row);
707
+ },
708
+ [selected, onSelect],
709
+ );
710
+
711
+ const commitMulti = useCallback(
712
+ (ids: TableRowId[], change: TableSelectChange<Row>): void => {
713
+ selectedRef.current = new Set(ids);
714
+ if (selected === undefined) setOwnMulti(ids);
715
+ onSelectedChange?.(ids, change);
716
+ },
717
+ [selected, onSelectedChange],
718
+ );
719
+
720
+ /**
721
+ * One gesture, pointer or keyboard: land on a row, with modifiers.
722
+ * Multiple mode is the file-manager grammar — plain replaces and moves the
723
+ * anchor, Ctrl toggles, Shift extends from the anchor. The cursor and the
724
+ * anchor are **ids, not indexes**, so a re-sort moves the rows, not the
725
+ * user's place.
726
+ */
727
+ const tap = useCallback(
728
+ (row: TableRow<Row>, mods: { ctrl: boolean; shift: boolean }): void => {
729
+ if (selectionMode === 'none') return;
730
+ if (selectionMode === 'single') {
731
+ commitSingle(row);
732
+ revealAt(row.index);
733
+ return;
734
+ }
735
+ cursorRef.current = row.id;
736
+ setCursorId(row.id);
737
+ const anchor = anchorRef.current;
738
+ if (mods.shift && anchor !== null) {
739
+ const all = orderedRef.current;
740
+ let a = all.findIndex((r) => r.id === anchor);
741
+ let b = row.index;
742
+ if (a < 0) a = b;
743
+ const [lo, hi] = a <= b ? [a, b] : [b, a];
744
+ commitMulti(
745
+ all.slice(lo, hi + 1).map((r) => r.id),
746
+ { type: 'range', id: row.id, row: row.row },
747
+ );
748
+ } else if (mods.ctrl) {
749
+ const next = new Set(selectedRef.current);
750
+ if (next.has(row.id)) next.delete(row.id);
751
+ else next.add(row.id);
752
+ anchorRef.current = row.id;
753
+ commitMulti([...next], { type: 'toggle', id: row.id, row: row.row });
754
+ } else {
755
+ anchorRef.current = row.id;
756
+ commitMulti([row.id], { type: 'replace', id: row.id, row: row.row });
757
+ }
758
+ revealAt(row.index);
759
+ },
760
+ [selectionMode, commitSingle, commitMulti, revealAt],
761
+ );
762
+
763
+ const activate = useCallback(
764
+ (row: TableRow<Row>): void => {
765
+ onActivate?.(row.id, row.row);
766
+ },
767
+ [onActivate],
768
+ );
769
+
770
+ const toggleSort = (column: TableColumn<Row>): void => {
771
+ const next: TableSort =
772
+ activeSort?.column === column.id && activeSort.direction === 'asc'
773
+ ? { column: column.id, direction: 'desc' }
774
+ : { column: column.id, direction: 'asc' };
775
+ if (sort === undefined) setOwnSort(next);
776
+ onSortChange?.(next);
777
+ };
778
+
779
+ const resize = (column: TableColumn<Row>, width: number): void => {
780
+ const next = Math.max(column.minWidth ?? MIN_COLUMN, Math.round(width));
781
+ if (userWidthsRef.current[column.id] === next) return;
782
+ const map = { ...userWidthsRef.current, [column.id]: next };
783
+ userWidthsRef.current = map;
784
+ setUserWidths(map);
785
+ onColumnResize?.(column.id, next);
786
+ };
787
+
788
+ const resizeProps = (column: TableColumn<Row>, resolved: number) => ({
789
+ // The handle takes focus on the press, so it is in the tab order whether
790
+ // or not it answers a key. Left/Right, the pair `SplitPane`'s divider
791
+ // takes; the table's own keys are Up/Down and never collide.
792
+ focusable: true,
793
+ onMouseDown: (ev: MouseEvent) => {
794
+ drag.current = { id: column.id, from: ev.x, width: resolved };
795
+ ev.capturePointer();
796
+ },
797
+ onMouseMove: (ev: MouseEvent) => {
798
+ const d = drag.current;
799
+ if (!d || d.id !== column.id) return;
800
+ // dragging the grip away from the column's own start edge widens it,
801
+ // and which way that is follows the layout
802
+ resize(column, d.width + (rtl ? d.from - ev.x : ev.x - d.from));
803
+ },
804
+ onMouseUp: () => {
805
+ drag.current = null;
806
+ },
807
+ onKeyDown: (ev: KeyboardEvent) => {
808
+ const narrower = rtl ? XK_RIGHT : XK_LEFT;
809
+ const wider = rtl ? XK_LEFT : XK_RIGHT;
810
+ if (ev.keysym === narrower) resize(column, resolved - STEP);
811
+ else if (ev.keysym === wider) resize(column, resolved + STEP);
812
+ },
813
+ });
814
+
815
+ /** Returns whether the key was the table's, so a control that forwards its
816
+ * keys here keeps the ones it did not use. */
817
+ const handleKey = useCallback(
818
+ (ev: KeyboardEvent): boolean => {
819
+ const rows = orderedRef.current;
820
+ if (!rows.length || selectionMode === 'none') return false;
821
+ const at = rows.findIndex((r) => r.id === cursorRef.current);
822
+ const clamp = (i: number): number =>
823
+ Math.max(0, Math.min(rows.length - 1, i));
824
+ const move = (to: number): void => {
825
+ const row = rows[clamp(to)];
826
+ if (row) {
827
+ tap(row, {
828
+ ctrl: false,
829
+ shift: selectionMode === 'multiple' && ev.shiftKey,
830
+ });
831
+ }
832
+ };
833
+ /**
834
+ * How far a page key moves, in rows — with measured heights that is
835
+ * not a division: it is "where does this row's offset land after a
836
+ * viewport", with one row of overlap kept, and never standing still
837
+ * even when a row is taller than the viewport.
838
+ */
839
+ const pageFrom = (from: number, dir: number): number => {
840
+ const viewport = viewRef.current.height;
841
+ if (viewport <= 0) return from + dir;
842
+ const here = clamp(from);
843
+ const target =
844
+ heights.offsetAt(here) + dir * Math.max(1, viewport - estimate);
845
+ const landed = heights.indexAt(Math.max(0, target));
846
+ return landed === here ? here + dir : landed;
847
+ };
848
+
849
+ switch (ev.keysym) {
850
+ case XK_UP:
851
+ move((at < 0 ? rows.length : at) - 1);
852
+ return true;
853
+ case XK_DOWN:
854
+ move((at < 0 ? -1 : at) + 1);
855
+ return true;
856
+ case XK_PAGE_UP:
857
+ move(pageFrom(at < 0 ? 0 : at, -1));
858
+ return true;
859
+ case XK_PAGE_DOWN:
860
+ move(pageFrom(at < 0 ? 0 : at, 1));
861
+ return true;
862
+ case XK_HOME:
863
+ move(0);
864
+ return true;
865
+ case XK_END:
866
+ move(rows.length - 1);
867
+ return true;
868
+ case XK_RETURN: {
869
+ const row = rows[at];
870
+ if (row) activate(row);
871
+ return true;
872
+ }
873
+ default:
874
+ break;
875
+ }
876
+ if (ev.codepoint === 32) {
877
+ const row = rows[at];
878
+ if (!row) return false;
879
+ if (selectionMode === 'multiple')
880
+ tap(row, { ctrl: true, shift: false });
881
+ else activate(row);
882
+ return true;
883
+ }
884
+ if (
885
+ selectionMode === 'multiple' &&
886
+ ev.ctrlKey &&
887
+ (ev.key === 'a' || ev.key === 'A')
888
+ ) {
889
+ commitMulti(
890
+ rows.map((r) => r.id),
891
+ { type: 'all' },
892
+ );
893
+ return true;
894
+ }
895
+ return false;
896
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `heights` is
897
+ // a stable instance
898
+ },
899
+ [selectionMode, tap, activate, commitMulti, estimate],
900
+ );
901
+
902
+ useImperativeHandle(
903
+ ref,
904
+ (): TableHandle<Row> => ({
905
+ focus: () => {
906
+ root.current?.focus();
907
+ },
908
+ select: (id) => {
909
+ if (id === null) {
910
+ cursorRef.current = null;
911
+ setCursorId(null);
912
+ anchorRef.current = null;
913
+ if (selected !== undefined) return; // controlled: the caller owns it
914
+ if (selectionMode === 'multiple') setOwnMulti([]);
915
+ else setOwnSingle(null);
916
+ return;
917
+ }
918
+ const row = orderedRef.current.find((r) => r.id === id);
919
+ if (row) tap(row, { ctrl: false, shift: false });
920
+ },
921
+ scrollToRow: (id) => {
922
+ const row = orderedRef.current.find((r) => r.id === id);
923
+ if (!row) return false;
924
+ revealAt(row.index);
925
+ return true;
926
+ },
927
+ handleKey,
928
+ rows: () => orderedRef.current,
929
+ }),
930
+ [tap, handleKey, revealAt, selectionMode, selected],
931
+ );
932
+
933
+ // --- rendering -----------------------------------------------------------
934
+
935
+ const rowStyleProp = styles?.row;
936
+ const cellStyleProp = styles?.cell;
937
+ const headerCellStyleProp = styles?.headerCell;
938
+ const selectable = selectionMode !== 'none';
939
+
940
+ const renderOneRow = (entry: TableRow<Row>): ReactElement => {
941
+ const isSelected = selectedIds.has(entry.id);
942
+ const color = isSelected ? theme.hoverText : theme.text;
943
+ const state: TableRowState<Row> = { ...entry, selected: isSelected, color };
944
+
945
+ const content: ReactNode[] = columns.map((column, at) => {
946
+ const cellState: TableCellState<Row> = { ...state, column };
947
+ return hx(
948
+ 'box',
949
+ {
950
+ key: column.id,
951
+ role: 'cell',
952
+ style: [
953
+ s.cell,
954
+ { width: widths[at] },
955
+ uniform && { height: rowHeight },
956
+ column.align === 'end' && {
957
+ alignItems: 'flex-end',
958
+ paddingEnd: 8,
959
+ },
960
+ column.align === 'center' && { alignItems: 'center' },
961
+ typeof cellStyleProp === 'function'
962
+ ? cellStyleProp(cellState)
963
+ : cellStyleProp,
964
+ ],
965
+ },
966
+ column.render
967
+ ? // A cell that draws itself still has to know it is on the
968
+ // selected row — see the doc comment. Keyed by the component,
969
+ // the way every seam's return is.
970
+ React.createElement(
971
+ React.Fragment,
972
+ { key: 'content' },
973
+ column.render(entry.row, cellState),
974
+ )
975
+ : hx(
976
+ 'text',
977
+ { style: [s.cellText, { color }] },
978
+ String(columnValue(entry.row, column) ?? ''),
979
+ ),
980
+ );
981
+ });
982
+
983
+ return hx(
984
+ 'box',
985
+ {
986
+ key: String(entry.id),
987
+ role: 'row',
988
+ 'aria-selected': selectable ? isSelected : undefined,
989
+ 'aria-posinset': entry.index + 1,
990
+ 'aria-setsize': ordered.length,
991
+ // The index the row was drawn at travels with the node, so measuring
992
+ // does not have to search the row list for it. It can go stale — the
993
+ // rows may move before the tick that measures — and both this and
994
+ // the height index check it rather than trust it.
995
+ ref: (node: DrawnNode | null) => {
996
+ if (node) rowNodes.current.set(entry.id, { node, at: entry.index });
997
+ else rowNodes.current.delete(entry.id);
998
+ },
999
+ onClick: (ev: MouseEvent) => {
1000
+ // A right-click also arrives here as a click; the selection it
1001
+ // implies is `onContextMenu`'s to make (select-unless-selected),
1002
+ // not the left button's replace.
1003
+ if (ev.button !== 1) return;
1004
+ tap(entry, { ctrl: ev.ctrlKey, shift: ev.shiftKey });
1005
+ // Select on the first click, open on the second — the gesture
1006
+ // every file list has. `detail` is the click count the renderer
1007
+ // already counts for text selection.
1008
+ if (ev.detail === 2) activate(entry);
1009
+ },
1010
+ onContextMenu: onRowContextMenu
1011
+ ? (ev: MouseEvent) => {
1012
+ // The menu applies to what is under the pointer, so the row is
1013
+ // selected first — unless it is already part of the selection,
1014
+ // which a menu over "the selected files" must not collapse.
1015
+ if (selectable && !selectedRef.current.has(entry.id)) {
1016
+ tap(entry, { ctrl: false, shift: false });
1017
+ }
1018
+ onRowContextMenu(entry.id, entry.row, ev);
1019
+ }
1020
+ : undefined,
1021
+ style: [
1022
+ s.row,
1023
+ // Declared uniform: exactly this tall, content clipped — core's
1024
+ // row. Measured: a floor, and the row grows to whatever its
1025
+ // content needs; the height index reads back what it became.
1026
+ uniform
1027
+ ? { height: rowHeight, alignItems: 'center' }
1028
+ : { minHeight: estimate },
1029
+ selectable && { cursor: 'pointer' },
1030
+ {
1031
+ backgroundColor: isSelected ? theme.hoverBackground : 'transparent',
1032
+ // The row's ink, said once: `color` inherits, so default cells
1033
+ // take it without being handed it.
1034
+ color,
1035
+ },
1036
+ selectable && {
1037
+ // pressed even on the selected row: a re-press on the row that
1038
+ // is already current is the one click in the table that would
1039
+ // otherwise look ignored
1040
+ ':active': {
1041
+ backgroundColor: isSelected
1042
+ ? theme.accentActive
1043
+ : theme.surfaceActive,
1044
+ },
1045
+ },
1046
+ selectable &&
1047
+ !isSelected && {
1048
+ ':hover': { backgroundColor: theme.surfaceHover },
1049
+ },
1050
+ typeof rowStyleProp === 'function'
1051
+ ? rowStyleProp(state)
1052
+ : rowStyleProp,
1053
+ ],
1054
+ },
1055
+ renderRow ? renderRow(state, content) : content,
1056
+ );
1057
+ };
1058
+
1059
+ const headerCells = columns.map((column, at) => {
1060
+ const canSort = column.sortable !== false;
1061
+ const headerState: TableHeaderCellState<Row> = {
1062
+ column,
1063
+ sort: activeSort,
1064
+ resolvedWidth: widths[at],
1065
+ };
1066
+ const isLast = at === columns.length - 1;
1067
+ return React.createElement(
1068
+ React.Fragment,
1069
+ { key: column.id },
1070
+ hx(
1071
+ 'box',
1072
+ {
1073
+ role: 'columnheader',
1074
+ focusable: true,
1075
+ onClick: canSort ? () => toggleSort(column) : undefined,
1076
+ style: [
1077
+ s.header,
1078
+ // The header cell and the grab band are **siblings**, not a cell
1079
+ // with a handle inside it — a click fires on the nearest common
1080
+ // ancestor of press and release, so a grip nested in the header
1081
+ // made every resize end in a sort. The band is centred on the
1082
+ // rule and takes HALF from the column on each side, so every
1083
+ // header but the first is inset by that much.
1084
+ {
1085
+ width: Math.max(0, widths[at] - HALF - RULE - (at ? HALF : 0)),
1086
+ },
1087
+ canSort && { cursor: 'pointer' },
1088
+ typeof headerCellStyleProp === 'function'
1089
+ ? headerCellStyleProp(headerState)
1090
+ : headerCellStyleProp,
1091
+ ],
1092
+ },
1093
+ column.renderHeader
1094
+ ? React.createElement(
1095
+ React.Fragment,
1096
+ { key: 'content' },
1097
+ column.renderHeader(headerState),
1098
+ )
1099
+ : [
1100
+ hx(
1101
+ 'text',
1102
+ {
1103
+ key: 'label',
1104
+ style: [s.headerLabel, { color: theme.text }],
1105
+ },
1106
+ column.label ?? column.id,
1107
+ ),
1108
+ hx('box', { key: 'spring', style: s.headerSpring }),
1109
+ activeSort?.column === column.id
1110
+ ? React.createElement(Icon, {
1111
+ key: 'mark',
1112
+ name:
1113
+ activeSort.direction === 'asc'
1114
+ ? 'chevronUp'
1115
+ : 'chevronDown',
1116
+ size: SORT_MARK,
1117
+ color: theme.textMuted,
1118
+ style: s.sortMark,
1119
+ })
1120
+ : null,
1121
+ ],
1122
+ ),
1123
+ // The band is invisible until the pointer is on it, and answers the
1124
+ // press itself — a captured press keeps `:active` for the whole drag,
1125
+ // wherever the pointer wanders. The rule is its child, so the lit band
1126
+ // is symmetric about the line it moves.
1127
+ hx(
1128
+ 'box',
1129
+ {
1130
+ ...resizeProps(column, widths[at]),
1131
+ role: 'separator',
1132
+ 'aria-orientation': 'vertical',
1133
+ 'aria-label': `Resize ${column.label || column.id}`,
1134
+ 'aria-valuenow': widths[at],
1135
+ 'aria-valuemin': column.minWidth ?? MIN_COLUMN,
1136
+ style: [
1137
+ s.grip,
1138
+ {
1139
+ width: isLast ? HALF + RULE : GRIP,
1140
+ justifyContent: isLast ? 'flex-end' : 'center',
1141
+ },
1142
+ { backgroundColor: 'transparent' },
1143
+ { ':hover': { backgroundColor: theme.track } },
1144
+ { ':active': { backgroundColor: theme.accent } },
1145
+ ],
1146
+ },
1147
+ hx('box', { style: [s.rule, { backgroundColor: theme.border }] }),
1148
+ ),
1149
+ );
1150
+ });
1151
+
1152
+ const bodyChildren: ReactNode[] = [];
1153
+ if (ordered.length === 0) {
1154
+ if (renderEmpty) {
1155
+ bodyChildren.push(
1156
+ hx('box', { key: 'empty', style: s.empty }, renderEmpty()),
1157
+ );
1158
+ }
1159
+ } else {
1160
+ if (virtualizing && first > 0) {
1161
+ bodyChildren.push(
1162
+ hx('box', {
1163
+ key: 'spacer:before',
1164
+ style: [s.spacer, { height: above }],
1165
+ }),
1166
+ );
1167
+ }
1168
+ for (let i = first; i < last; i++)
1169
+ bodyChildren.push(renderOneRow(ordered[i]));
1170
+ if (virtualizing && last < ordered.length) {
1171
+ bodyChildren.push(
1172
+ hx('box', {
1173
+ key: 'spacer:after',
1174
+ style: [s.spacer, { height: below }],
1175
+ }),
1176
+ );
1177
+ }
1178
+ }
1179
+
1180
+ return hx(
1181
+ 'box',
1182
+ {
1183
+ theme,
1184
+ // The table role, with the honest caveat that virtualization keeps
1185
+ // only the rendered rows in the accessible tree. `aria-label` via
1186
+ // boxProps names the table.
1187
+ role: 'table',
1188
+ // the *table* takes focus, not the row — see the doc comment
1189
+ focusable,
1190
+ ...boxProps,
1191
+ ref: root,
1192
+ style: [s.root, style],
1193
+ /**
1194
+ * `preventDefault` is the load-bearing half: the focused table sits
1195
+ * over a scroll container with default key actions of its own, and
1196
+ * without it every arrow both moved the selection and scrolled the
1197
+ * list under it. A key the table did not take keeps its default.
1198
+ */
1199
+ onKeyDown: (ev) => {
1200
+ if (handleKey(ev)) ev.preventDefault();
1201
+ },
1202
+ },
1203
+ // the header scrolls sideways with the body but never vertically, so it
1204
+ // lives outside the scrolling pane and is shifted by the body's scrollX —
1205
+ // `marginStart`, so it tracks in the direction the columns actually run
1206
+ hx(
1207
+ 'box',
1208
+ {
1209
+ style: [
1210
+ s.headerClip,
1211
+ { backgroundColor: theme.surfaceHover },
1212
+ styles?.header,
1213
+ ],
1214
+ },
1215
+ hx(
1216
+ 'box',
1217
+ { style: [s.headerRow, { marginStart: -scrollX, width: total }] },
1218
+ headerCells,
1219
+ ),
1220
+ ),
1221
+ hx(
1222
+ 'box',
1223
+ {
1224
+ ref: body,
1225
+ style: s.body,
1226
+ onScroll: (ev) => {
1227
+ // A scroll this component did not ask for is the user taking over,
1228
+ // and an owed `scrollToRow` must not yank the list back out from
1229
+ // under them on the next layout.
1230
+ reveal.heard(ev.scrollY);
1231
+ setScrollX((prev) => (prev === ev.scrollX ? prev : ev.scrollX));
1232
+ if (virtualizing) {
1233
+ setView((prev) =>
1234
+ prev.top === ev.scrollY ? prev : { ...prev, top: ev.scrollY },
1235
+ );
1236
+ }
1237
+ onScroll?.(ev);
1238
+ },
1239
+ // Layout, not scrolling, is what first tells a table how much of it
1240
+ // is worth building — and the viewport width is what the flex
1241
+ // columns resolve against, so this is measured whether or not the
1242
+ // table virtualizes.
1243
+ onViewport: (ev) => {
1244
+ // The ref first, at event time: the measure tick runs before the
1245
+ // re-render this setState causes, and it must see this viewport.
1246
+ viewRef.current = {
1247
+ ...viewRef.current,
1248
+ height: ev.height,
1249
+ width: ev.width,
1250
+ };
1251
+ setView((prev) =>
1252
+ prev.height === ev.height && prev.width === ev.width
1253
+ ? prev
1254
+ : { ...prev, height: ev.height, width: ev.width },
1255
+ );
1256
+ // The content just changed size, which is both the moment an owed
1257
+ // scroll can reach further than the clamp let it and the moment the
1258
+ // container may have re-clamped the offset without saying so. It is
1259
+ // not a moment anything can be *settled* in while rows are still
1260
+ // being measured: this runs from layout, a tick before the pass that
1261
+ // reads those rows back.
1262
+ reveal.retry(virtualizing && !uniform);
1263
+ syncScroll();
1264
+ onViewport?.(ev);
1265
+ },
1266
+ },
1267
+ hx('box', { style: [s.rowsBox, { width: total }] }, bodyChildren),
1268
+ ),
1269
+ );
1270
+ }