@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,1127 @@
1
+ // <Tree> — a disclosure tree: file browsers, outline panes, property
2
+ // inspectors, anything with a twisty.
3
+ //
4
+ // This is a **successor to react-x11's own `<Tree>`, not a wrapper around
5
+ // it**. Core's is being retired, so nothing here imports it and the two share
6
+ // no code. What is kept is the behaviour a user has already learnt — the
7
+ // keyboard map, type-ahead, the twisty being its own hit target — because
8
+ // that is the part it would be rude to change.
9
+ //
10
+ // Three things it has that core's does not, and they are the reason it lives
11
+ // out here rather than in core:
12
+ //
13
+ // 1. **It reads the app's own data.** `getId` / `getChildren` / `isBranch`
14
+ // and friends mean a filesystem listing, an AST or a normalized store is
15
+ // rendered where it lies, instead of being copied into a shape this
16
+ // component preferred. The defaults describe `{ id, label, children }`,
17
+ // so a tree that has that shape configures nothing.
18
+ // 2. **It virtualizes.** Past a couple of hundred visible rows it builds
19
+ // only the slice on screen, the way `Table` does, and the rest of the
20
+ // list is two spacer boxes so the scrollbar still measures the whole
21
+ // tree. That is what "very large number of elements" costs: nothing.
22
+ // 3. **Every visible part is a seam.** The twisty, the branch edge down the
23
+ // indent, the label, the row's contents and — in `layout="nested"` — the
24
+ // subtree container are each a render prop with a style override beside
25
+ // it. The default look is deliberately plain: a chevron, no branch lines,
26
+ // just indentation.
27
+ //
28
+ // Nothing here registers a host element. A tree 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 { DrawnNode, KeyboardEvent, ScrollableNode } from 'react-x11';
44
+ import {
45
+ XK_DOWN,
46
+ XK_END,
47
+ XK_HOME,
48
+ XK_LEFT,
49
+ XK_PAGE_DOWN,
50
+ XK_PAGE_UP,
51
+ XK_RETURN,
52
+ XK_RIGHT,
53
+ XK_UP,
54
+ } from 'react-x11/keysyms';
55
+
56
+ import { hx } from './hx.js';
57
+ import type { Host } from './hx.js';
58
+ // Shared with <Table> — internal, deliberately not a shared *module*; the
59
+ // header of src/internal/heights.ts says why.
60
+ import { RowHeights } from '../internal/heights.js';
61
+ import { afterLayout, cancelAfterLayout } from '../internal/timers.js';
62
+ import { useReveal } from '../internal/scroll.js';
63
+ import { typeAheadChar, useTypeAhead } from './internal.js';
64
+ import {
65
+ branchEdges,
66
+ findItem,
67
+ groupRows,
68
+ isGroup,
69
+ resolveAccessors,
70
+ visibleRows,
71
+ } from './rows.js';
72
+ import type {
73
+ ResolvedAccessors,
74
+ TreeAccessors,
75
+ TreeGroup,
76
+ TreeItem,
77
+ TreeItemId,
78
+ TreeRow,
79
+ } from './rows.js';
80
+
81
+ // The row model comes out too: it is what the seams are handed, and an app
82
+ // that drives a tree from outside — "reveal this path", "how many rows is
83
+ // that" — does the same arithmetic. `resolveAccessors` is part of it rather
84
+ // than an internal, because `visibleRows` takes accessors with every default
85
+ // already filled in and there is no other way to make a set.
86
+ export type {
87
+ ResolvedAccessors,
88
+ TreeAccessors,
89
+ TreeItem,
90
+ TreeItemId,
91
+ TreeRow,
92
+ TreeGroup,
93
+ } from './rows.js';
94
+ export {
95
+ branchEdges,
96
+ findItem,
97
+ resolveAccessors,
98
+ visibleRows,
99
+ } from './rows.js';
100
+
101
+ /** One level of indent. */
102
+ const INDENT = 14;
103
+ /** The twisty's hit box — what a file browser lets you click to peek into a
104
+ * folder without selecting it. It stays this wide whatever the glyph does. */
105
+ const TWISTY = 12;
106
+ /** The chevron inside it. A chevron's long axis is its box and its short one
107
+ * is half of that, so `size` for one reads as its width. */
108
+ const TWISTY_GLYPH = 10;
109
+ const ROW_HEIGHT = 22;
110
+ /** Rows kept either side of the viewport, so a fast scroll does not show a
111
+ * gap before the next frame catches up. */
112
+ const OVERSCAN = 6;
113
+ /**
114
+ * What to build before the viewport has been measured. `onViewport` cannot
115
+ * arrive until layout has run, which is a frame after the first commit, so
116
+ * there is always one render that has to guess — and guessing "all of them"
117
+ * puts a hundred thousand rows in the tree for a frame.
118
+ */
119
+ const ASSUMED_ROWS = 40;
120
+ /**
121
+ * Where `virtual="auto"` starts virtualizing.
122
+ *
123
+ * Well above any viewport, so a tree that is merely long is still built whole
124
+ * and keeps the two things virtualization costs: a row may be any height, and
125
+ * every row is in the accessibility tree. Well below the point where building
126
+ * every row is felt.
127
+ */
128
+ const VIRTUAL_THRESHOLD = 200;
129
+
130
+ const s = createStyles({
131
+ root: { flexGrow: 1, minHeight: 0, overflow: 'scroll' },
132
+ row: {
133
+ flexShrink: 0,
134
+ flexDirection: 'row',
135
+ alignItems: 'center',
136
+ gap: 4,
137
+ paddingEnd: 8,
138
+ paddingTop: 3,
139
+ paddingBottom: 3,
140
+ cursor: 'pointer',
141
+ transition: { backgroundColor: 80 },
142
+ },
143
+ twisty: {
144
+ width: TWISTY,
145
+ height: TWISTY,
146
+ flexShrink: 0,
147
+ alignItems: 'center',
148
+ justifyContent: 'center',
149
+ },
150
+ guide: { flexShrink: 0, alignSelf: 'stretch' },
151
+ /**
152
+ * The label **wraps**, and the row grows to hold it.
153
+ *
154
+ * This is the whole point of rows being measured rather than fixed: a name
155
+ * too long for the pane becomes two lines of a taller row, not two lines
156
+ * drawn over the row below. An app that wants the one-line, clipped look a
157
+ * file browser has says so — `styles={{ label: { textWrap: 'nowrap' } }}` —
158
+ * and gets it for the whole tree.
159
+ *
160
+ * `textBoxTrim` makes the box the letters rather than the font's ascent and
161
+ * descent, so a label centres on what can be seen. That is what core's
162
+ * `labelContent` gives every label in the widget set.
163
+ */
164
+ label: {
165
+ flexShrink: 1,
166
+ minWidth: 0,
167
+ textBoxTrim: 'cap-alphabetic',
168
+ },
169
+ subtree: { flexShrink: 0 },
170
+ spacer: { flexShrink: 0 },
171
+ });
172
+
173
+ // --- what the seams are told -----------------------------------------------
174
+
175
+ /** A row, plus what only the render knows about it. */
176
+ export interface TreeRowState<T> extends TreeRow<T> {
177
+ selected: boolean;
178
+ /**
179
+ * The ink the row is painted in.
180
+ *
181
+ * Handed over by name because **colour does not cascade into a drawing**:
182
+ * an `<Icon>` or a `<canvas mono>` takes its colour from its own style, so
183
+ * a glyph that has to stay legible on a selected row has to be told. The
184
+ * `<text>` in the default label needs nothing — text does inherit.
185
+ */
186
+ color: string;
187
+ /** Open or close this row's branch. Ignored on a leaf. */
188
+ toggle: (open?: boolean) => void;
189
+ /** Make this row the selection, as a click would. */
190
+ select: () => void;
191
+ }
192
+
193
+ /** What `renderToggle` is told. */
194
+ export interface TreeToggleState<T> extends TreeRowState<T> {
195
+ /** The size the default chevron is drawn at. */
196
+ size: number;
197
+ }
198
+
199
+ /**
200
+ * One column of a row's indent, as `renderGuide` sees it — the tree's branch
201
+ * edge.
202
+ *
203
+ * There is one call per level of depth, outermost first. `own` marks the
204
+ * row's own column, where a `├─` or `└─` connector goes; every earlier column
205
+ * belongs to an ancestor and only ever carries a `│`.
206
+ */
207
+ export interface TreeGuideState<T> {
208
+ row: TreeRowState<T>;
209
+ /** 0 is the outermost column. */
210
+ level: number;
211
+ /** A line passes through this column and continues below this row. */
212
+ continues: boolean;
213
+ /** This is the row's own column rather than an ancestor's. */
214
+ own: boolean;
215
+ /** How wide the column is: the `indent` prop. */
216
+ width: number;
217
+ /** The row's **minimum** height — `rowHeight`. The column stretches to
218
+ * whatever the row turned out to be, and a drawing is told that real size
219
+ * in its own `onDraw`, so this is a hint rather than a measurement.
220
+ * A `<canvas cacheKey>` need not name it: the paint cache already keys on
221
+ * the node's laid-out size. */
222
+ height: number;
223
+ }
224
+
225
+ /** What `renderSubtree` is told, in `layout="nested"`. */
226
+ export interface TreeSubtreeState<T> {
227
+ /** The row the subtree hangs off. Never null: the roots are not a
228
+ * subtree, they are the tree. */
229
+ parent: TreeRow<T>;
230
+ /** The depth of the rows inside it — one more than `parent.depth`. */
231
+ depth: number;
232
+ }
233
+
234
+ /** The expansion that just changed, alongside the whole set. */
235
+ export interface TreeExpandChange<T> {
236
+ id: TreeItemId;
237
+ item: T;
238
+ open: boolean;
239
+ }
240
+
241
+ /** The style overrides, one per part the tree draws. */
242
+ export interface TreeStyles<T> {
243
+ /** A function is called per rendered row, so a style can follow the row's
244
+ * own state without a `renderContent` that repaints everything. */
245
+ row?: StyleProp | ((state: TreeRowState<T>) => StyleProp);
246
+ toggle?: StyleProp;
247
+ guide?: StyleProp | ((state: TreeGuideState<T>) => StyleProp);
248
+ label?: StyleProp;
249
+ subtree?: StyleProp;
250
+ }
251
+
252
+ /**
253
+ * A `<Tree>`'s imperative side.
254
+ *
255
+ * `handleKey` is the one worth knowing about: a filter box above a tree holds
256
+ * the keyboard, and forwarding the arrows to this is how the list below it
257
+ * still walks. It reports whether the tree took the key, so the caller keeps
258
+ * whatever it did not.
259
+ */
260
+ export interface TreeHandle<T = TreeItem> {
261
+ focus: () => void;
262
+ select: (id: TreeItemId | null) => void;
263
+ /** Open or close a branch by id, anywhere in the tree — including one no
264
+ * row is showing. `false` when the id names nothing loaded. */
265
+ setExpanded: (id: TreeItemId, open?: boolean) => boolean;
266
+ /** Bring a row into view, expanding nothing — a collapsed ancestor means
267
+ * there is no row to scroll to, and this reports `false`. */
268
+ scrollToItem: (id: TreeItemId) => boolean;
269
+ handleKey: (ev: KeyboardEvent) => boolean;
270
+ /** The rows on screen right now, in draw order. */
271
+ rows: () => readonly TreeRow<T>[];
272
+ }
273
+
274
+ type BoxProps = Host['box'];
275
+
276
+ export interface TreeProps<T = TreeItem>
277
+ extends
278
+ TreeAccessors<T>,
279
+ Omit<BoxProps, 'style' | 'children' | 'ref' | 'onKeyDown'> {
280
+ /** The roots. */
281
+ items?: readonly T[];
282
+
283
+ /** Controlled expansion. */
284
+ expanded?: readonly TreeItemId[];
285
+ defaultExpanded?: readonly TreeItemId[];
286
+ /**
287
+ * The whole open set, plus the one row that changed — which is what lazy
288
+ * loading needs: `change.open && !loaded.has(change.id)` is the fetch.
289
+ */
290
+ onExpandedChange?: (
291
+ expanded: TreeItemId[],
292
+ change: TreeExpandChange<T>,
293
+ ) => void;
294
+
295
+ /** Controlled selection. One row: see the note on multiple selection in
296
+ * the component's doc comment. */
297
+ selected?: TreeItemId | null;
298
+ defaultSelected?: TreeItemId | null;
299
+ onSelect?: (id: TreeItemId, item: T) => void;
300
+ /** The *open* gesture on top of selection — a double click, Enter, or
301
+ * Space. On a branch it also toggles. */
302
+ onActivate?: (id: TreeItemId, item: T) => void;
303
+
304
+ /** One level of indent, in pixels. `0` puts the whole indent in
305
+ * `renderSubtree`'s hands instead. */
306
+ indent?: number;
307
+ /**
308
+ * The **shortest** a row may be, in pixels. Default 22.
309
+ *
310
+ * A row is not this tall, it is at least this tall: it grows to whatever
311
+ * its content needs, so a label that wraps to two lines gets a row of two
312
+ * lines. Virtualization measures rows rather than assuming them, which is
313
+ * what makes that safe — see {@link estimatedRowHeight}.
314
+ */
315
+ rowHeight?: number;
316
+ /**
317
+ * What a row that has not been measured yet is assumed to be, while
318
+ * virtualizing. Defaults to `rowHeight`.
319
+ *
320
+ * Only the rows on screen have ever been laid out, so the scrollbar is
321
+ * this guess for everything else, and it converges as you scroll. Set it
322
+ * when rows are typically much taller than `rowHeight` — a tree of
323
+ * two-line rows with the default guess starts with a scrollbar that thinks
324
+ * the tree is half its real length.
325
+ */
326
+ estimatedRowHeight?: number;
327
+
328
+ /**
329
+ * Build only the rows on screen. `'auto'` (the default) turns it on past
330
+ * {@link VIRTUAL_THRESHOLD} visible rows.
331
+ *
332
+ * Not available in `layout="nested"`, where the rows are not siblings and
333
+ * a slice of them is not a subtree.
334
+ */
335
+ virtual?: boolean | 'auto';
336
+ /** Rows built either side of the viewport. */
337
+ overscan?: number;
338
+
339
+ /**
340
+ * `'flat'` (the default) makes every row a sibling — which is what lets the
341
+ * tree virtualize, and what the branch guides are drawn per row for.
342
+ *
343
+ * `'nested'` puts each expanded branch's rows inside a container of their
344
+ * own, for a `renderSubtree` that wants to draw a background, a rule down
345
+ * the group, or its own indent. Rows carry their own indent in both, so a
346
+ * subtree container that indents as well should pass `indent={0}`.
347
+ */
348
+ layout?: 'flat' | 'nested';
349
+
350
+ /** The open/close control, inside its hit box. */
351
+ renderToggle?: (state: TreeToggleState<T>) => ReactNode;
352
+ /** One column of the indent — the branch edge. Nothing by default. */
353
+ renderGuide?: (state: TreeGuideState<T>) => ReactNode;
354
+ /** The label cell. An icon in front of the text goes here. */
355
+ renderLabel?: (state: TreeRowState<T>) => ReactNode;
356
+ /**
357
+ * Everything inside the row box, given what would have been there.
358
+ *
359
+ * The row *box* stays this component's — it carries the height
360
+ * virtualization counts on, the role and the aria the tree is read through,
361
+ * and the click that selects. Style it with `styles.row`.
362
+ */
363
+ renderContent?: (state: TreeRowState<T>, content: ReactNode[]) => ReactNode;
364
+ /** The subtree container, in `layout="nested"`. */
365
+ renderSubtree?: (state: TreeSubtreeState<T>, rows: ReactNode) => ReactNode;
366
+
367
+ styles?: TreeStyles<T>;
368
+ style?: StyleProp;
369
+ ref?: Ref<TreeHandle<T>>;
370
+ /** react-x11's queries read this off `props` at run time; its element
371
+ * declarations do not carry it, so it is declared here and reaches the
372
+ * root box through the rest spread. */
373
+ 'data-testname'?: string;
374
+ }
375
+
376
+ /** A string or a number becomes a `<text>`; anything else is already a
377
+ * node. */
378
+ function labelNode(label: ReactNode, style: StyleProp): ReactNode {
379
+ return typeof label === 'string' || typeof label === 'number'
380
+ ? hx('text', { key: 'label', style }, String(label))
381
+ : label;
382
+ }
383
+
384
+ /**
385
+ * `<Tree items />` — a disclosure tree.
386
+ *
387
+ * <Tree items={[{ id: 'src', label: 'src', children: [...] }]} />
388
+ *
389
+ * An item with a `children` array is a branch, **even when the array is
390
+ * empty** — that is how a directory shows a twisty before anything has read
391
+ * it. A branch whose children are not loaded at all says `branch: true`, and
392
+ * fills them in from `onExpandedChange`.
393
+ *
394
+ * Expansion and selection are each controlled (`expanded` + `onExpandedChange`,
395
+ * `selected` + `onSelect`) or uncontrolled (`defaultExpanded`,
396
+ * `defaultSelected`).
397
+ *
398
+ * The tree is a single tab stop. Up/Down walk the visible rows, Right expands
399
+ * a branch and then steps into it, Left collapses and then steps out to the
400
+ * parent, PageUp/PageDown move by a viewport, Home/End jump to the ends,
401
+ * Enter and Space activate, and typing letters jumps to a matching row — the
402
+ * same type-ahead core's `Select` and menus use.
403
+ *
404
+ * **The focus is on the tree, not on the row.** Core's tree focused each row
405
+ * node; a virtualized row is unmounted the moment it scrolls out of view and
406
+ * the focus would go with it, so the container is the focusable thing and the
407
+ * selection is the cursor. That is `Table`'s model, and the honest caveat is
408
+ * the one `Table` carries: while virtualizing, only the rendered rows are in
409
+ * the accessibility tree — the same rows a sighted user can see.
410
+ *
411
+ * **Selection is one row.** Multiple selection is not a prop because there is
412
+ * no version of it everybody agrees on (does Shift extend from the anchor or
413
+ * from the cursor? does Ctrl+click on a branch take its children?), and every
414
+ * such policy is expressible on top of what is here: hold the set yourself,
415
+ * pass `selected` for the cursor, and paint the rest from `styles.row`.
416
+ */
417
+ export function Tree<T = TreeItem>({
418
+ items = [],
419
+ expanded,
420
+ defaultExpanded,
421
+ onExpandedChange,
422
+ selected,
423
+ defaultSelected,
424
+ onSelect,
425
+ onActivate,
426
+ indent = INDENT,
427
+ rowHeight = ROW_HEIGHT,
428
+ estimatedRowHeight,
429
+ virtual = 'auto',
430
+ overscan = OVERSCAN,
431
+ layout = 'flat',
432
+ renderToggle,
433
+ renderGuide,
434
+ renderLabel,
435
+ renderContent,
436
+ renderSubtree,
437
+ styles,
438
+ style,
439
+ ref,
440
+ // the accessors, pulled out so the rest can be spread onto the box
441
+ getId,
442
+ getLabel,
443
+ getText,
444
+ getChildren,
445
+ isBranch,
446
+ isDisabled,
447
+ // ours to chain rather than to hand over: virtualization is measured
448
+ // through both of these
449
+ onScroll,
450
+ onViewport,
451
+ ...boxProps
452
+ }: TreeProps<T>): ReactElement {
453
+ const theme = useTheme();
454
+ const rtl = useDirection() === 'rtl';
455
+ const [ownExpanded, setOwnExpanded] = useState<ReadonlySet<TreeItemId>>(
456
+ () => new Set(defaultExpanded),
457
+ );
458
+ const [ownSelected, setOwnSelected] = useState<TreeItemId | null>(
459
+ defaultSelected ?? null,
460
+ );
461
+ const [view, setView] = useState({ top: 0, height: 0 });
462
+ // Bumped by a measurement pass that found a row taller or shorter than the
463
+ // index believed. It is the only reason the component re-renders for a
464
+ // measurement, and a pass that finds nothing new does not bump it, which is
465
+ // what makes measure → render → measure converge instead of spinning.
466
+ const [, setMeasured] = useState(0);
467
+ const typeAhead = useTypeAhead();
468
+ const scroller = useRef<ScrollableNode | null>(null);
469
+ /** The rows on screen, by id — with the index each was drawn at, so a
470
+ * measurement pass does not have to search the row list for it. */
471
+ const rowNodes = useRef(
472
+ new Map<TreeItemId, { node: DrawnNode; at: number }>(),
473
+ );
474
+ const estimate = estimatedRowHeight ?? rowHeight;
475
+ // `useState` for its lazy initializer rather than `useRef`: one index per
476
+ // mounted tree, built once. Nothing ever calls the setter — what it holds
477
+ // is mutable and its changes are announced through `setMeasured`.
478
+ const [heights] = useState(() => new RowHeights(estimate));
479
+
480
+ const accessors: ResolvedAccessors<T> = useMemo(
481
+ () =>
482
+ resolveAccessors<T>({
483
+ getId,
484
+ getLabel,
485
+ getText,
486
+ getChildren,
487
+ isBranch,
488
+ isDisabled,
489
+ }),
490
+ [getId, getLabel, getText, getChildren, isBranch, isDisabled],
491
+ );
492
+
493
+ const openSet = useMemo(
494
+ () => (expanded ? new Set(expanded) : ownExpanded),
495
+ [expanded, ownExpanded],
496
+ );
497
+ const current = selected === undefined ? ownSelected : selected;
498
+ const rows = useMemo(
499
+ () => visibleRows(items, openSet, accessors),
500
+ [items, openSet, accessors],
501
+ );
502
+
503
+ // Held-down or fast keys arrive in a burst, and every handler in that burst
504
+ // sees the render it started from — so what is open and what is selected
505
+ // are mirrored here and updated the moment they change, or three Downs in a
506
+ // row all step off the same starting point.
507
+ const openRef = useRef(openSet);
508
+ openRef.current = openSet;
509
+ const currentRef = useRef(current);
510
+ currentRef.current = current;
511
+ const rowsRef = useRef(rows);
512
+ rowsRef.current = rows;
513
+ const itemsRef = useRef(items);
514
+ itemsRef.current = items;
515
+ const viewRef = useRef(view);
516
+ viewRef.current = view;
517
+
518
+ const virtualizing =
519
+ layout === 'flat' &&
520
+ (virtual === true ||
521
+ (virtual === 'auto' && rows.length > VIRTUAL_THRESHOLD));
522
+
523
+ // The index is re-pointed at the rows about to be drawn before anything
524
+ // asks it where they are. `rows` is memoized, so this is an identity check
525
+ // on every render that did not change the tree.
526
+ const index = heights;
527
+ index.sync(rows, estimate);
528
+
529
+ // The slice worth building: what is on screen, plus a little either side.
530
+ // Which rows those are is a question for the height index now — with rows
531
+ // of different heights there is no division that answers it.
532
+ const first = virtualizing
533
+ ? Math.max(0, index.indexAt(view.top) - overscan)
534
+ : 0;
535
+ let last = rows.length;
536
+ if (virtualizing) {
537
+ if (view.height > 0) {
538
+ last = Math.min(
539
+ rows.length,
540
+ index.indexAt(view.top + view.height) + 1 + overscan,
541
+ );
542
+ } else {
543
+ // Before the first layout there is no viewport to measure against, and
544
+ // guessing "all of them" would put a hundred thousand rows in the tree
545
+ // for a frame.
546
+ last = Math.min(rows.length, first + ASSUMED_ROWS);
547
+ }
548
+ }
549
+ /** Where the slice starts, and how much of the list is below it — the two
550
+ * spacers that keep the scrollbar measuring the whole tree. */
551
+ const above = virtualizing ? index.offsetAt(first) : 0;
552
+ const below = virtualizing ? index.total() - index.offsetAt(last) : 0;
553
+
554
+ const setExpandedSet = useCallback(
555
+ (next: ReadonlySet<TreeItemId>, change: TreeExpandChange<T>): void => {
556
+ openRef.current = next;
557
+ if (expanded === undefined) setOwnExpanded(next);
558
+ onExpandedChange?.([...next], change);
559
+ },
560
+ [expanded, onExpandedChange],
561
+ );
562
+
563
+ const toggleId = useCallback(
564
+ (id: TreeItemId, item: T, open?: boolean): void => {
565
+ const next = new Set(openRef.current);
566
+ const shouldOpen = open ?? !next.has(id);
567
+ if (shouldOpen) next.add(id);
568
+ else next.delete(id);
569
+ setExpandedSet(next, { id, item, open: shouldOpen });
570
+ },
571
+ [setExpandedSet],
572
+ );
573
+
574
+ /**
575
+ * The scroll the tree owes a row, and the pane's real offset read back
576
+ * after every layout — the two halves of `../internal/scroll.ts`, which
577
+ * says why a reveal cannot be a one-shot and why `onScroll` is not the
578
+ * whole story. A tree grows and shrinks under its own hands: opening a
579
+ * branch is a content that got taller between the ask and the layout, in
580
+ * exactly the way an arriving row is.
581
+ */
582
+ const reveal = useReveal({
583
+ box: scroller,
584
+ rows: rowsRef,
585
+ nodes: rowNodes,
586
+ heights,
587
+ });
588
+
589
+ /** Put a row in view, by the index its call site already has. */
590
+ const revealAt = useCallback(
591
+ (at: number): void => {
592
+ const row = rowsRef.current[at];
593
+ if (row) reveal.to(row.id);
594
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `reveal` is a
595
+ // stable handle
596
+ },
597
+ [reveal],
598
+ );
599
+
600
+ /**
601
+ * Re-read the offset the pane is *actually* at.
602
+ *
603
+ * It moves silently — a queued reveal resolves during layout, and an offset
604
+ * the content outgrew or outshrank is re-clamped there — and a slice built
605
+ * from the offset before those is drawn where the viewport is not: a blank
606
+ * band where the rows should be, until a scroll of your own re-syncs it by
607
+ * accident.
608
+ */
609
+ const syncScroll = useCallback((): void => {
610
+ const box = scroller.current;
611
+ if (!box || !virtualizing) return;
612
+ const y = box.scrollY;
613
+ setView((prev) => (prev.top === y ? prev : { ...prev, top: y }));
614
+ }, [virtualizing]);
615
+
616
+ /**
617
+ * Read back what the rows on screen actually laid out at.
618
+ *
619
+ * The one thing that makes rows of different heights work, and the reason
620
+ * it is not simply "read the height in an effect": **layout runs after
621
+ * React's effects, on the frame flush**, so a `useEffect` sees the
622
+ * *previous* pass's geometry — zero, on the render that created the row.
623
+ * This runs a tick later, when `abs` is current.
624
+ *
625
+ * Idempotent by construction. A row whose measurement has not changed
626
+ * reports no change, so the second pass over the same rows costs a map walk
627
+ * and re-renders nothing, and measure → render → measure terminates.
628
+ */
629
+ const measureRows = useCallback((): boolean => {
630
+ if (!virtualizing) return false;
631
+ const box = scroller.current;
632
+ const rows = rowsRef.current;
633
+ const idx = heights;
634
+ // Rows above the top of the viewport are the ones that move the content
635
+ // under it, so their total change is what has to come back out of the
636
+ // scroll offset — otherwise measuring a row you have already scrolled
637
+ // past yanks the list under the pointer.
638
+ const anchor = box ? idx.indexAt(box.scrollY) : 0;
639
+ let shift = 0;
640
+ let changed = false;
641
+ for (const [id, { node, at }] of rowNodes.current) {
642
+ if (rows[at]?.id !== id) continue; // drawn against a list that has moved
643
+ const height = node.abs.height;
644
+ const was = idx.heightAt(at);
645
+ if (!idx.measure(id, at, height)) continue;
646
+ changed = true;
647
+ if (at < anchor) shift += height - was;
648
+ }
649
+ if (!changed) return false;
650
+ if (shift !== 0 && box) {
651
+ reveal.scrollTo(box.scrollY + shift);
652
+ }
653
+ setMeasured((n) => n + 1);
654
+ return true;
655
+ }, [virtualizing]);
656
+
657
+ /**
658
+ * The one tick after layout, and everything that can only be known there:
659
+ * what the rows measured, whether an owed scroll can go further now that
660
+ * the rows it was waiting for are laid out, and where the pane actually
661
+ * ended up. In that order — each step can move the offset the next one
662
+ * reads.
663
+ */
664
+ useEffect(() => {
665
+ if (!virtualizing) return undefined;
666
+ const id = afterLayout(() => {
667
+ // `measureRows` first, and its answer handed on: a pass that moved the
668
+ // heights has not settled anything, and an owed scroll judged against
669
+ // the layout it is about to invalidate is not owed any less.
670
+ reveal.retry(measureRows());
671
+ syncScroll();
672
+ });
673
+ return () => cancelAfterLayout(id);
674
+ });
675
+
676
+ const goTo = useCallback(
677
+ (row: TreeRow<T> | null | undefined): void => {
678
+ if (!row) return;
679
+ currentRef.current = row.id;
680
+ if (selected === undefined) setOwnSelected(row.id);
681
+ onSelect?.(row.id, row.item);
682
+ revealAt(row.index);
683
+ },
684
+ [selected, onSelect, revealAt],
685
+ );
686
+
687
+ const activate = useCallback(
688
+ (row: TreeRow<T>): void => {
689
+ if (row.branch) toggleId(row.id, row.item);
690
+ onActivate?.(row.id, row.item);
691
+ },
692
+ [toggleId, onActivate],
693
+ );
694
+
695
+ /** Returns whether the key was the tree's, so a control that forwards its
696
+ * keys here keeps the ones it did not use. */
697
+ const handleKey = useCallback(
698
+ (ev: KeyboardEvent): boolean => {
699
+ const rows = rowsRef.current;
700
+ if (!rows.length) return false;
701
+ const index = rows.findIndex((r) => r.id === currentRef.current);
702
+ const row = rows[index];
703
+ const step = (delta: number): TreeRow<T> | null => {
704
+ for (let i = index + delta; i >= 0 && i < rows.length; i += delta) {
705
+ if (!rows[i].disabled) return rows[i];
706
+ }
707
+ return null;
708
+ };
709
+ /** The nearest enabled row at or before `to`, walking `dir` if the
710
+ * landing row is disabled — what a page jump needs. */
711
+ const nearest = (to: number, dir: number): TreeRow<T> | null => {
712
+ const at = Math.max(0, Math.min(rows.length - 1, to));
713
+ for (let i = at; i >= 0 && i < rows.length; i += dir) {
714
+ if (!rows[i].disabled) return rows[i];
715
+ }
716
+ return null;
717
+ };
718
+ // Deeper is the direction the indent grows in, which is the direction
719
+ // the text runs — so it is Left that opens a branch in a mirrored tree,
720
+ // and the two arrows swap wholesale rather than the tree growing a
721
+ // second set of cases.
722
+ const deeper = rtl ? XK_LEFT : XK_RIGHT;
723
+ const shallower = rtl ? XK_RIGHT : XK_LEFT;
724
+ /**
725
+ * How far a page key moves, in rows.
726
+ *
727
+ * A viewport's worth, which with rows of different heights is not a
728
+ * division: it is "where does the row `index` sits at end up if I add a
729
+ * viewport to its offset". One row of overlap is kept, the way every
730
+ * document viewer does, so a page down leaves a line of context.
731
+ */
732
+ const pageFrom = (from: number, dir: number): number => {
733
+ const idx = heights;
734
+ const viewport = viewRef.current.height;
735
+ if (viewport <= 0) return from + dir;
736
+ const at = Math.max(0, Math.min(rows.length - 1, from));
737
+ const target =
738
+ idx.offsetAt(at) + dir * Math.max(1, viewport - rowHeight);
739
+ const landed = idx.indexAt(Math.max(0, target));
740
+ // never stand still: a row taller than the viewport would otherwise
741
+ // make Page Down a no-op
742
+ return landed === at ? at + dir : landed;
743
+ };
744
+
745
+ switch (ev.keysym) {
746
+ case XK_UP:
747
+ goTo(step(-1));
748
+ return true;
749
+ case XK_DOWN:
750
+ goTo(step(1));
751
+ return true;
752
+ case XK_PAGE_UP:
753
+ goTo(nearest(index < 0 ? 0 : pageFrom(index, -1), 1));
754
+ return true;
755
+ case XK_PAGE_DOWN:
756
+ goTo(nearest(index < 0 ? pageFrom(0, 1) : pageFrom(index, 1), -1));
757
+ return true;
758
+ case deeper:
759
+ // open it, then walk into it — one key does both, in order
760
+ if (row?.branch && !row.open) toggleId(row.id, row.item, true);
761
+ else if (row?.branch) goTo(step(1));
762
+ return true;
763
+ case shallower:
764
+ if (row?.branch && row.open) toggleId(row.id, row.item, false);
765
+ else if (row?.parent) goTo(row.parent);
766
+ return true;
767
+ case XK_HOME:
768
+ goTo(rows.find((r) => !r.disabled));
769
+ return true;
770
+ case XK_END:
771
+ goTo([...rows].reverse().find((r) => !r.disabled));
772
+ return true;
773
+ case XK_RETURN:
774
+ if (row) activate(row);
775
+ return true;
776
+ default:
777
+ break;
778
+ }
779
+ if (ev.codepoint === 32) {
780
+ if (row) activate(row);
781
+ return true;
782
+ }
783
+ const char = typeAheadChar(ev);
784
+ if (!char) return false;
785
+ const found = typeAhead(
786
+ char,
787
+ rows,
788
+ index < 0 ? null : index,
789
+ (r: TreeRow<T>) => accessors.getText(r.item),
790
+ (r: TreeRow<T>) => !r.disabled,
791
+ );
792
+ if (found >= 0) goTo(rows[found]);
793
+ return found >= 0;
794
+ },
795
+ [rtl, rowHeight, goTo, toggleId, activate, typeAhead, accessors],
796
+ );
797
+
798
+ useImperativeHandle(
799
+ ref,
800
+ (): TreeHandle<T> => ({
801
+ focus: () => {
802
+ scroller.current?.focus();
803
+ },
804
+ select: (id) => {
805
+ const row = rowsRef.current.find((r) => r.id === id);
806
+ if (row) goTo(row);
807
+ else if (id === null) {
808
+ currentRef.current = null;
809
+ if (selected === undefined) setOwnSelected(null);
810
+ }
811
+ },
812
+ setExpanded: (id, open) => {
813
+ // An id nobody can see is still legitimately expandable — a lazy load
814
+ // finishing under a branch the user collapsed again, say — so the
815
+ // whole tree is searched rather than the rows on screen.
816
+ //
817
+ // An id that is in neither is a no-op, and that is the point: the
818
+ // change this reports carries the item, `TreeExpandChange<T>.item` is
819
+ // `T`, and an id nothing answers to has no item to put there. Doing
820
+ // nothing beats reporting an expansion of `undefined`.
821
+ const item =
822
+ rowsRef.current.find((r) => r.id === id)?.item ??
823
+ findItem(itemsRef.current, id, accessors);
824
+ if (item === undefined) return false;
825
+ toggleId(id, item, open);
826
+ return true;
827
+ },
828
+ scrollToItem: (id) => {
829
+ const row = rowsRef.current.find((r) => r.id === id);
830
+ if (!row) return false;
831
+ revealAt(row.index);
832
+ return true;
833
+ },
834
+ handleKey,
835
+ rows: () => rowsRef.current,
836
+ }),
837
+ [goTo, toggleId, revealAt, handleKey, selected, accessors],
838
+ );
839
+
840
+ // --- rendering -----------------------------------------------------------
841
+
842
+ const rowStyleProp = styles?.row;
843
+ const guideStyleProp = styles?.guide;
844
+
845
+ const renderOneRow = (row: TreeRow<T>): ReactElement => {
846
+ const isSelected = row.id === current;
847
+ const color = row.disabled
848
+ ? theme.textMuted
849
+ : isSelected
850
+ ? theme.hoverText
851
+ : theme.text;
852
+ const state: TreeRowState<T> = {
853
+ ...row,
854
+ selected: isSelected,
855
+ color,
856
+ toggle: (open?: boolean) => toggleId(row.id, row.item, open),
857
+ select: () => goTo(row),
858
+ };
859
+
860
+ const content: ReactNode[] = [];
861
+
862
+ // The indent. With no guide seam it is one padding value rather than
863
+ // `depth` empty boxes — a tree ten deep would otherwise build ten nodes
864
+ // per row to draw nothing.
865
+ if (renderGuide && row.depth > 0) {
866
+ const edges = branchEdges(row);
867
+ for (let level = 0; level < row.depth; level++) {
868
+ const guide: TreeGuideState<T> = {
869
+ row: state,
870
+ level,
871
+ continues: edges[level],
872
+ own: level === row.depth - 1,
873
+ width: indent,
874
+ height: rowHeight,
875
+ };
876
+ content.push(
877
+ hx(
878
+ 'box',
879
+ {
880
+ key: `guide${level}`,
881
+ style: [
882
+ s.guide,
883
+ { width: indent },
884
+ typeof guideStyleProp === 'function'
885
+ ? guideStyleProp(guide)
886
+ : guideStyleProp,
887
+ ],
888
+ },
889
+ renderGuide(guide),
890
+ ),
891
+ );
892
+ }
893
+ }
894
+
895
+ const toggleState: TreeToggleState<T> = { ...state, size: TWISTY_GLYPH };
896
+ content.push(
897
+ hx(
898
+ 'box',
899
+ {
900
+ key: 'toggle',
901
+ style: [s.twisty, styles?.toggle],
902
+ // The twisty is its own hit target: clicking it opens the branch
903
+ // without moving the selection, the way a file browser lets you
904
+ // peek inside a folder you have not chosen.
905
+ onClick: row.branch
906
+ ? (ev) => {
907
+ ev.stopPropagation();
908
+ toggleId(row.id, row.item);
909
+ }
910
+ : undefined,
911
+ },
912
+ renderToggle
913
+ ? renderToggle(toggleState)
914
+ : row.branch
915
+ ? React.createElement(Icon, {
916
+ name: row.open
917
+ ? 'chevronDown'
918
+ : rtl
919
+ ? 'chevronLeft'
920
+ : 'chevronRight',
921
+ size: TWISTY_GLYPH,
922
+ // dimmer than the label on a resting row, and the row's own
923
+ // ink once it is selected
924
+ style: isSelected ? undefined : { color: theme.textMuted },
925
+ })
926
+ : null,
927
+ ),
928
+ );
929
+
930
+ content.push(
931
+ renderLabel
932
+ ? // Keyed here rather than by the app, for the reason `renderSubtree`
933
+ // is: the label sits in an array beside the guides and the twisty,
934
+ // and "add a key to the box you return" is not something a render
935
+ // prop should have to know.
936
+ React.createElement(
937
+ React.Fragment,
938
+ { key: 'label' },
939
+ renderLabel(state),
940
+ )
941
+ : labelNode(accessors.getLabel(row.item), [s.label, styles?.label]),
942
+ );
943
+
944
+ return hx(
945
+ 'box',
946
+ {
947
+ key: String(row.id),
948
+ role: 'treeitem',
949
+ 'aria-level': row.depth + 1,
950
+ 'aria-selected': isSelected,
951
+ 'aria-expanded': row.branch ? row.open : undefined,
952
+ 'aria-posinset': row.posInSet,
953
+ 'aria-setsize': row.setSize,
954
+ // `disabled` rather than `aria-disabled`: on a react-x11 node it is
955
+ // the real thing — it clears the AT-SPI ENABLED/SENSITIVE states and
956
+ // selects the `:disabled` style block — and there is no aria spelling
957
+ // of it to write instead.
958
+ disabled: row.disabled || undefined,
959
+ // The index the row was drawn at travels with the node, so measuring
960
+ // does not have to search a hundred thousand rows for where it is.
961
+ // It can go stale — the rows may move before the tick that measures —
962
+ // and both this and the height index check it rather than trust it.
963
+ ref: (node: DrawnNode | null) => {
964
+ if (node) rowNodes.current.set(row.id, { node, at: row.index });
965
+ else rowNodes.current.delete(row.id);
966
+ },
967
+ onClick: (ev) => {
968
+ if (row.disabled) return;
969
+ goTo(row);
970
+ // Select on the first click, open on the second — the gesture every
971
+ // file list has. `detail` is the click count the renderer already
972
+ // counts for text selection.
973
+ if (ev.detail === 2) activate(row);
974
+ },
975
+ style: [
976
+ s.row,
977
+ // A floor, not a height. The row grows to whatever its content
978
+ // needs — a wrapped label, two lines, a thumbnail — and the height
979
+ // index reads back what it actually became.
980
+ { minHeight: rowHeight },
981
+ // The indent is what says "inside", so it is measured from the edge
982
+ // the row's label begins at.
983
+ { paddingStart: renderGuide ? 4 : 4 + row.depth * indent },
984
+ {
985
+ backgroundColor: isSelected ? theme.hoverBackground : 'transparent',
986
+ // The row's ink, said once: `color` inherits, so the label takes
987
+ // it without being handed it.
988
+ color,
989
+ },
990
+ !row.disabled && {
991
+ ':hover': {
992
+ backgroundColor: isSelected
993
+ ? theme.hoverBackground
994
+ : theme.surfaceHover,
995
+ },
996
+ // The selection only moves on the release, and `:active` marks
997
+ // the whole press chain, so a press on the label or the twisty
998
+ // still darkens the row it is in.
999
+ ':active': {
1000
+ backgroundColor: isSelected
1001
+ ? theme.accentActive
1002
+ : theme.surfaceActive,
1003
+ },
1004
+ },
1005
+ typeof rowStyleProp === 'function'
1006
+ ? rowStyleProp(state)
1007
+ : rowStyleProp,
1008
+ ],
1009
+ },
1010
+ renderContent ? renderContent(state, content) : content,
1011
+ );
1012
+ };
1013
+
1014
+ /** `layout="nested"`: the same rows, wrapped group by group. */
1015
+ const renderGroup = (group: TreeGroup<T>, key: string): ReactNode => {
1016
+ const children = group.rows.map((node, i) =>
1017
+ isGroup(node) ? renderGroup(node, `${key}.${i}`) : renderOneRow(node),
1018
+ );
1019
+ if (!group.parent) return children;
1020
+ const state: TreeSubtreeState<T> = {
1021
+ parent: group.parent,
1022
+ depth: group.parent.depth + 1,
1023
+ };
1024
+ // The key is this component's either way, and a fragment is how a seam
1025
+ // gets one without being told to write it. A `renderSubtree` that had to
1026
+ // remember a key would get it wrong the obvious way — keying on the
1027
+ // parent id, which is already the key of the parent *row* beside it.
1028
+ if (renderSubtree) {
1029
+ return React.createElement(
1030
+ React.Fragment,
1031
+ { key: `subtree:${group.parent.id}` },
1032
+ renderSubtree(state, children),
1033
+ );
1034
+ }
1035
+ return hx(
1036
+ 'box',
1037
+ {
1038
+ key: `subtree:${group.parent.id}`,
1039
+ role: 'group',
1040
+ style: [s.subtree, styles?.subtree],
1041
+ },
1042
+ children,
1043
+ );
1044
+ };
1045
+
1046
+ const body: ReactNode[] = [];
1047
+ if (layout === 'nested') {
1048
+ body.push(renderGroup(groupRows(rows), 'root'));
1049
+ } else {
1050
+ if (virtualizing && first > 0) {
1051
+ body.push(
1052
+ hx('box', {
1053
+ key: 'spacer:before',
1054
+ style: [s.spacer, { height: above }],
1055
+ }),
1056
+ );
1057
+ }
1058
+ for (let i = first; i < last; i++) body.push(renderOneRow(rows[i]));
1059
+ if (virtualizing && last < rows.length) {
1060
+ body.push(
1061
+ hx('box', {
1062
+ key: 'spacer:after',
1063
+ style: [s.spacer, { height: below }],
1064
+ }),
1065
+ );
1066
+ }
1067
+ }
1068
+
1069
+ return hx(
1070
+ 'box',
1071
+ {
1072
+ theme,
1073
+ role: 'tree',
1074
+ // The tree takes the focus, not the row — see the doc comment.
1075
+ focusable: true,
1076
+ ...boxProps,
1077
+ ref: scroller,
1078
+ style: [s.root, style],
1079
+ /**
1080
+ * `preventDefault` is the load-bearing half.
1081
+ *
1082
+ * The tree's root is a scroll container **and** the focused node, and a
1083
+ * focused scroller has default key actions of its own: Down and Up
1084
+ * scroll by a wheel notch, the Page keys by a viewport, Home and End to
1085
+ * the ends, Space by a page. Without this, every arrow did both — moved
1086
+ * the selection *and* scrolled the list under it — which reads as the
1087
+ * tree scrolling whenever you use the keyboard rather than only when
1088
+ * the selection would otherwise leave the viewport.
1089
+ *
1090
+ * `handleKey` reports whether the tree took the key, so a key it did
1091
+ * not take (a letter that matched nothing) still gets the default.
1092
+ */
1093
+ onKeyDown: (ev) => {
1094
+ if (handleKey(ev)) ev.preventDefault();
1095
+ },
1096
+ // Layout, not scrolling, is what first tells a list how much of it is
1097
+ // worth building — and it is also where a page key gets its distance,
1098
+ // so this is measured whether or not the tree virtualizes.
1099
+ onViewport: (ev) => {
1100
+ setView((prev) =>
1101
+ prev.height === ev.height ? prev : { ...prev, height: ev.height },
1102
+ );
1103
+ // The content just changed size, which is both the moment an owed
1104
+ // scroll can reach further than the clamp let it and the moment the
1105
+ // pane may have re-clamped its offset without saying so. It is not a
1106
+ // moment anything can be *settled* in: this runs from layout, a tick
1107
+ // before the pass that reads the rows it just drew back.
1108
+ reveal.retry(virtualizing);
1109
+ syncScroll();
1110
+ onViewport?.(ev);
1111
+ },
1112
+ onScroll: (ev) => {
1113
+ // A scroll this component did not ask for is the user taking over,
1114
+ // and an owed reveal must not yank the tree back out from under them
1115
+ // on the next layout.
1116
+ reveal.heard(ev.scrollY);
1117
+ if (virtualizing) {
1118
+ setView((prev) =>
1119
+ prev.top === ev.scrollY ? prev : { ...prev, top: ev.scrollY },
1120
+ );
1121
+ }
1122
+ onScroll?.(ev);
1123
+ },
1124
+ },
1125
+ body,
1126
+ );
1127
+ }