@botiverse/raft-ui 0.50.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (585) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/components/activity-inbox/activity-inbox-shell.d.mts +12 -0
  3. package/dist/components/activity-inbox/activity-inbox-shell.mjs +39 -0
  4. package/dist/components/activity-inbox/activity-inbox-shell.recipe.mjs +14 -0
  5. package/dist/components/activity-inbox/activity-inbox-sidebar.d.mts +6 -0
  6. package/dist/components/activity-inbox/activity-inbox-sidebar.mjs +22 -0
  7. package/dist/components/activity-inbox/activity-inbox-sidebar.recipe.mjs +22 -0
  8. package/dist/components/activity-inbox/activity-inbox-toolbar.d.mts +9 -0
  9. package/dist/components/activity-inbox/activity-inbox-toolbar.mjs +29 -0
  10. package/dist/components/activity-inbox/activity-inbox-toolbar.recipe.mjs +14 -0
  11. package/dist/components/activity-inbox/index.d.mts +4 -0
  12. package/dist/components/alert-dialog/alert-dialog.d.mts +41 -0
  13. package/dist/components/alert-dialog/alert-dialog.mjs +163 -0
  14. package/dist/components/alert-dialog/index.d.mts +2 -0
  15. package/dist/components/announcement-dialog/announcement-dialog.d.mts +18 -0
  16. package/dist/components/announcement-dialog/announcement-dialog.mjs +121 -0
  17. package/dist/components/announcement-dialog/index.d.mts +3 -0
  18. package/dist/components/app-rail/app-rail.d.mts +31 -0
  19. package/dist/components/app-rail/app-rail.mjs +126 -0
  20. package/dist/components/app-rail/app-rail.recipe.mjs +47 -0
  21. package/dist/components/app-rail/index.d.mts +2 -0
  22. package/dist/components/app-shell/app-shell.d.mts +27 -0
  23. package/dist/components/app-shell/app-shell.mjs +217 -0
  24. package/dist/components/app-shell/index.d.mts +2 -0
  25. package/dist/components/avatar/avatar.d.mts +169 -0
  26. package/dist/components/avatar/avatar.mjs +298 -0
  27. package/dist/components/avatar/index.d.mts +2 -0
  28. package/dist/components/badge/badge.d.mts +89 -0
  29. package/dist/components/badge/badge.mjs +203 -0
  30. package/dist/components/badge/index.d.mts +2 -0
  31. package/dist/components/banner/banner.d.mts +141 -0
  32. package/dist/components/banner/banner.mjs +228 -0
  33. package/dist/components/banner/index.d.mts +2 -0
  34. package/dist/components/button/button.d.mts +257 -0
  35. package/dist/components/button/button.mjs +177 -0
  36. package/dist/components/button/index.d.mts +2 -0
  37. package/dist/components/card/card.d.mts +21 -0
  38. package/dist/components/card/card.mjs +114 -0
  39. package/dist/components/card/card.recipe.d.mts +105 -0
  40. package/dist/components/card/card.recipe.mjs +67 -0
  41. package/dist/components/card/index.d.mts +2 -0
  42. package/dist/components/checkbox/checkbox-indicator.d.mts +92 -0
  43. package/dist/components/checkbox/checkbox-indicator.mjs +61 -0
  44. package/dist/components/checkbox/checkbox.d.mts +9 -0
  45. package/dist/components/checkbox/checkbox.mjs +40 -0
  46. package/dist/components/checkbox/index.d.mts +3 -0
  47. package/dist/components/choice-list/choice-list.d.mts +103 -0
  48. package/dist/components/choice-list/choice-list.mjs +301 -0
  49. package/dist/components/choice-list/choice-list.recipe.mjs +44 -0
  50. package/dist/components/choice-list/index.d.mts +2 -0
  51. package/dist/components/code-block/code-block.d.mts +30 -0
  52. package/dist/components/code-block/code-block.mjs +151 -0
  53. package/dist/components/code-block/code-block.recipe.mjs +47 -0
  54. package/dist/components/code-block/index.d.mts +2 -0
  55. package/dist/components/combobox/combobox.d.mts +69 -0
  56. package/dist/components/combobox/combobox.mjs +438 -0
  57. package/dist/components/combobox/index.d.mts +2 -0
  58. package/dist/components/combobox-select/combobox-select.d.mts +4 -0
  59. package/dist/components/combobox-select/combobox-select.list-side.mjs +16 -0
  60. package/dist/components/combobox-select/combobox-select.mjs +473 -0
  61. package/dist/components/combobox-select/combobox-select.options.mjs +21 -0
  62. package/dist/components/combobox-select/combobox-select.types.d.mts +83 -0
  63. package/dist/components/combobox-select/index.d.mts +3 -0
  64. package/dist/components/comment/comment-card.d.mts +40 -0
  65. package/dist/components/comment/comment-card.mjs +83 -0
  66. package/dist/components/comment/comment-entry.d.mts +24 -0
  67. package/dist/components/comment/comment-entry.mjs +75 -0
  68. package/dist/components/comment/comment-highlight.d.mts +18 -0
  69. package/dist/components/comment/comment-highlight.mjs +33 -0
  70. package/dist/components/comment/comment-margin-layout.d.mts +32 -0
  71. package/dist/components/comment/comment-margin-layout.mjs +44 -0
  72. package/dist/components/comment/comment-margin.d.mts +29 -0
  73. package/dist/components/comment/comment-margin.mjs +164 -0
  74. package/dist/components/comment/comment-marker.d.mts +16 -0
  75. package/dist/components/comment/comment-marker.mjs +24 -0
  76. package/dist/components/comment/comment.recipe.mjs +58 -0
  77. package/dist/components/comment/index.d.mts +8 -0
  78. package/dist/components/comment/use-comment-margin-fits.d.mts +16 -0
  79. package/dist/components/comment/use-comment-margin-fits.mjs +34 -0
  80. package/dist/components/composer/composer-attachment.recipe.mjs +85 -0
  81. package/dist/components/composer/composer-attachments-scroll-area.d.mts +5 -0
  82. package/dist/components/composer/composer-attachments-scroll-area.mjs +93 -0
  83. package/dist/components/composer/composer-attachments.d.mts +27 -0
  84. package/dist/components/composer/composer-attachments.mjs +147 -0
  85. package/dist/components/composer/composer-drop-overlay.d.mts +9 -0
  86. package/dist/components/composer/composer-drop-overlay.mjs +60 -0
  87. package/dist/components/composer/composer-drop-overlay.recipe.mjs +23 -0
  88. package/dist/components/composer/composer-root.d.mts +5 -0
  89. package/dist/components/composer/composer-root.mjs +18 -0
  90. package/dist/components/composer/composer-suggestion-list.d.mts +139 -0
  91. package/dist/components/composer/composer-suggestion-list.mjs +160 -0
  92. package/dist/components/composer/composer-toolbar-label.d.mts +5 -0
  93. package/dist/components/composer/composer-toolbar-label.mjs +24 -0
  94. package/dist/components/composer/composer.d.mts +20 -0
  95. package/dist/components/composer/composer.mjs +88 -0
  96. package/dist/components/composer/composer.recipe.mjs +29 -0
  97. package/dist/components/composer/index.d.mts +10 -0
  98. package/dist/components/composer/sortable-composer-attachments.d.mts +37 -0
  99. package/dist/components/composer/sortable-composer-attachments.mjs +247 -0
  100. package/dist/components/composer/use-composer-suggestions.d.mts +83 -0
  101. package/dist/components/composer/use-composer-suggestions.mjs +207 -0
  102. package/dist/components/context-menu/context-menu.d.mts +53 -0
  103. package/dist/components/context-menu/context-menu.mjs +275 -0
  104. package/dist/components/context-menu/index.d.mts +2 -0
  105. package/dist/components/conversation-panel/conversation-panel-empty-state.d.mts +9 -0
  106. package/dist/components/conversation-panel/conversation-panel-empty-state.mjs +57 -0
  107. package/dist/components/conversation-panel/conversation-panel.d.mts +20 -0
  108. package/dist/components/conversation-panel/conversation-panel.mjs +63 -0
  109. package/dist/components/conversation-panel/conversation-panel.recipe.mjs +15 -0
  110. package/dist/components/conversation-panel/index.d.mts +3 -0
  111. package/dist/components/copyable-code/copyable-code.d.mts +72 -0
  112. package/dist/components/copyable-code/copyable-code.mjs +153 -0
  113. package/dist/components/copyable-code/index.d.mts +2 -0
  114. package/dist/components/description-list/description-list.d.mts +59 -0
  115. package/dist/components/description-list/description-list.mjs +96 -0
  116. package/dist/components/description-list/index.d.mts +2 -0
  117. package/dist/components/dialog/dialog-sheet-context.mjs +58 -0
  118. package/dist/components/dialog/dialog-sheet.d.mts +31 -0
  119. package/dist/components/dialog/dialog-sheet.mjs +194 -0
  120. package/dist/components/dialog/dialog.d.mts +85 -0
  121. package/dist/components/dialog/dialog.mjs +371 -0
  122. package/dist/components/dialog/index.d.mts +3 -0
  123. package/dist/components/drag/drag-announcements.d.mts +72 -0
  124. package/dist/components/drag/drag-announcements.mjs +115 -0
  125. package/dist/components/drag/drag-lift.d.mts +100 -0
  126. package/dist/components/drag/drag-lift.mjs +276 -0
  127. package/dist/components/drag/drag-lists.d.mts +98 -0
  128. package/dist/components/drag/drag-lists.mjs +258 -0
  129. package/dist/components/drag/drag-sensors.d.mts +87 -0
  130. package/dist/components/drag/drag-sensors.mjs +169 -0
  131. package/dist/components/drag/drag.recipe.d.mts +7 -0
  132. package/dist/components/drag/drag.recipe.mjs +30 -0
  133. package/dist/components/drag/drop-placeholder.d.mts +41 -0
  134. package/dist/components/drag/drop-placeholder.mjs +161 -0
  135. package/dist/components/drag/index.d.mts +8 -0
  136. package/dist/components/drawer/drawer.d.mts +34 -0
  137. package/dist/components/drawer/drawer.mjs +202 -0
  138. package/dist/components/drawer/drawer.recipe.mjs +175 -0
  139. package/dist/components/drawer/index.d.mts +2 -0
  140. package/dist/components/dropdown-menu/dropdown-menu.d.mts +62 -0
  141. package/dist/components/dropdown-menu/dropdown-menu.mjs +319 -0
  142. package/dist/components/dropdown-menu/index.d.mts +2 -0
  143. package/dist/components/ellipsis/ellipsis.d.mts +55 -0
  144. package/dist/components/ellipsis/ellipsis.mjs +320 -0
  145. package/dist/components/ellipsis/index.d.mts +3 -0
  146. package/dist/components/empty-state/empty-state.d.mts +15 -0
  147. package/dist/components/empty-state/empty-state.mjs +102 -0
  148. package/dist/components/empty-state/index.d.mts +2 -0
  149. package/dist/components/field/field.d.mts +79 -0
  150. package/dist/components/field/field.mjs +100 -0
  151. package/dist/components/field/index.d.mts +2 -0
  152. package/dist/components/file-preview/file-preview.d.mts +17 -0
  153. package/dist/components/file-preview/file-preview.mjs +210 -0
  154. package/dist/components/file-preview/file-preview.recipe.d.mts +99 -0
  155. package/dist/components/file-preview/file-preview.recipe.mjs +39 -0
  156. package/dist/components/file-preview/index.d.mts +2 -0
  157. package/dist/components/files/files.d.mts +35 -0
  158. package/dist/components/files/files.mjs +69 -0
  159. package/dist/components/files/files.recipe.mjs +40 -0
  160. package/dist/components/files/index.d.mts +2 -0
  161. package/dist/components/filter-chip/filter-chip.d.mts +25 -0
  162. package/dist/components/filter-chip/filter-chip.mjs +59 -0
  163. package/dist/components/filter-chip/index.d.mts +2 -0
  164. package/dist/components/float-button/float-button.d.mts +36 -0
  165. package/dist/components/float-button/float-button.mjs +116 -0
  166. package/dist/components/float-button/float-button.position.d.mts +6 -0
  167. package/dist/components/float-button/float-button.position.mjs +41 -0
  168. package/dist/components/float-button/index.d.mts +3 -0
  169. package/dist/components/form-section/form-section.d.mts +16 -0
  170. package/dist/components/form-section/form-section.mjs +53 -0
  171. package/dist/components/form-section/index.d.mts +2 -0
  172. package/dist/components/icon-tile/icon-tile.d.mts +97 -0
  173. package/dist/components/icon-tile/icon-tile.mjs +49 -0
  174. package/dist/components/icon-tile/index.d.mts +2 -0
  175. package/dist/components/icons/action-icons.d.mts +34 -0
  176. package/dist/components/icons/action-icons.mjs +383 -0
  177. package/dist/components/icons/conversation-icons.d.mts +12 -0
  178. package/dist/components/icons/conversation-icons.mjs +147 -0
  179. package/dist/components/icons/index.d.mts +4 -0
  180. package/dist/components/icons/object-icons.d.mts +6 -0
  181. package/dist/components/icons/object-icons.mjs +27 -0
  182. package/dist/components/inbox-item/inbox-item-leading-badge.d.mts +13 -0
  183. package/dist/components/inbox-item/inbox-item-leading-badge.mjs +27 -0
  184. package/dist/components/inbox-item/inbox-item-skeleton.d.mts +46 -0
  185. package/dist/components/inbox-item/inbox-item-skeleton.mjs +89 -0
  186. package/dist/components/inbox-item/inbox-item-skeleton.recipe.mjs +20 -0
  187. package/dist/components/inbox-item/inbox-item.d.mts +71 -0
  188. package/dist/components/inbox-item/inbox-item.mjs +276 -0
  189. package/dist/components/inbox-item/inbox-item.recipe.mjs +137 -0
  190. package/dist/components/inbox-item/index.d.mts +2 -0
  191. package/dist/components/inline-code/index.d.mts +2 -0
  192. package/dist/components/inline-code/inline-code.d.mts +8 -0
  193. package/dist/components/inline-code/inline-code.mjs +30 -0
  194. package/dist/components/input/index.d.mts +2 -0
  195. package/dist/components/input/input.d.mts +16 -0
  196. package/dist/components/input/input.mjs +20 -0
  197. package/dist/components/input/input.recipe.mjs +12 -0
  198. package/dist/components/input-group/index.d.mts +2 -0
  199. package/dist/components/input-group/input-group.d.mts +93 -0
  200. package/dist/components/input-group/input-group.mjs +178 -0
  201. package/dist/components/kbd/index.d.mts +2 -0
  202. package/dist/components/kbd/kbd.d.mts +7 -0
  203. package/dist/components/kbd/kbd.mjs +34 -0
  204. package/dist/components/label/index.d.mts +2 -0
  205. package/dist/components/label/label.d.mts +60 -0
  206. package/dist/components/label/label.mjs +75 -0
  207. package/dist/components/legacy-task-panel/index.d.mts +2 -0
  208. package/dist/components/legacy-task-panel/legacy-task-panel.d.mts +57 -0
  209. package/dist/components/legacy-task-panel/legacy-task-panel.mjs +234 -0
  210. package/dist/components/legacy-task-panel/legacy-task-panel.recipe.mjs +30 -0
  211. package/dist/components/lightbox/index.d.mts +2 -0
  212. package/dist/components/lightbox/lightbox.d.mts +55 -0
  213. package/dist/components/lightbox/lightbox.mjs +336 -0
  214. package/dist/components/list-row/index.d.mts +2 -0
  215. package/dist/components/list-row/list-row-skeleton.d.mts +21 -0
  216. package/dist/components/list-row/list-row-skeleton.mjs +43 -0
  217. package/dist/components/list-row/list-row.d.mts +66 -0
  218. package/dist/components/list-row/list-row.mjs +167 -0
  219. package/dist/components/list-row/list-row.recipe.d.mts +126 -0
  220. package/dist/components/list-row/list-row.recipe.mjs +94 -0
  221. package/dist/components/live-agent-activity-bar/index.d.mts +2 -0
  222. package/dist/components/live-agent-activity-bar/live-agent-activity-bar.d.mts +25 -0
  223. package/dist/components/live-agent-activity-bar/live-agent-activity-bar.mjs +107 -0
  224. package/dist/components/logo/index.d.mts +2 -0
  225. package/dist/components/logo/raft-logo.d.mts +9 -0
  226. package/dist/components/logo/raft-logo.mjs +63 -0
  227. package/dist/components/markdown/index.d.mts +2 -0
  228. package/dist/components/markdown/markdown-parts.d.mts +54 -0
  229. package/dist/components/markdown/markdown-parts.mjs +153 -0
  230. package/dist/components/markdown/markdown.recipe.mjs +77 -0
  231. package/dist/components/message-action-card/index.d.mts +2 -0
  232. package/dist/components/message-action-card/message-action-card-content.recipe.mjs +50 -0
  233. package/dist/components/message-action-card/message-action-card.d.mts +95 -0
  234. package/dist/components/message-action-card/message-action-card.mjs +298 -0
  235. package/dist/components/message-action-card/message-action-card.recipe.mjs +47 -0
  236. package/dist/components/message-attachment/index.d.mts +5 -0
  237. package/dist/components/message-attachment/message-attachment.d.mts +27 -0
  238. package/dist/components/message-attachment/message-attachment.mjs +129 -0
  239. package/dist/components/message-attachment/message-attachment.recipe.mjs +36 -0
  240. package/dist/components/message-attachment/message-image-gallery-rows.d.mts +47 -0
  241. package/dist/components/message-attachment/message-image-gallery-rows.mjs +55 -0
  242. package/dist/components/message-attachment/message-image-gallery.d.mts +32 -0
  243. package/dist/components/message-attachment/message-image-gallery.mjs +105 -0
  244. package/dist/components/message-attachment/message-image-gallery.recipe.mjs +61 -0
  245. package/dist/components/message-attachment/message-video-preview.d.mts +19 -0
  246. package/dist/components/message-attachment/message-video-preview.mjs +92 -0
  247. package/dist/components/message-attachment/message-video-preview.recipe.mjs +33 -0
  248. package/dist/components/message-embed/index.d.mts +2 -0
  249. package/dist/components/message-embed/message-embed.d.mts +12 -0
  250. package/dist/components/message-embed/message-embed.mjs +42 -0
  251. package/dist/components/message-embed/message-embed.recipe.mjs +14 -0
  252. package/dist/components/message-forwarded-bundle/index.d.mts +2 -0
  253. package/dist/components/message-forwarded-bundle/message-forwarded-bundle.d.mts +92 -0
  254. package/dist/components/message-forwarded-bundle/message-forwarded-bundle.mjs +324 -0
  255. package/dist/components/message-forwarded-bundle/message-forwarded-bundle.recipe.mjs +81 -0
  256. package/dist/components/message-item/index.d.mts +13 -0
  257. package/dist/components/message-item/message-footer.d.mts +20 -0
  258. package/dist/components/message-item/message-footer.mjs +54 -0
  259. package/dist/components/message-item/message-footer.recipe.mjs +46 -0
  260. package/dist/components/message-item/message-item-content.d.mts +22 -0
  261. package/dist/components/message-item/message-item-content.mjs +81 -0
  262. package/dist/components/message-item/message-item-delegated.d.mts +38 -0
  263. package/dist/components/message-item/message-item-delegated.mjs +72 -0
  264. package/dist/components/message-item/message-item-gutter.d.mts +10 -0
  265. package/dist/components/message-item/message-item-gutter.mjs +41 -0
  266. package/dist/components/message-item/message-item-header.d.mts +29 -0
  267. package/dist/components/message-item/message-item-header.mjs +107 -0
  268. package/dist/components/message-item/message-item-label.mjs +113 -0
  269. package/dist/components/message-item/message-item-root.d.mts +25 -0
  270. package/dist/components/message-item/message-item-root.mjs +39 -0
  271. package/dist/components/message-item/message-item-toolbar.d.mts +9 -0
  272. package/dist/components/message-item/message-item-toolbar.mjs +131 -0
  273. package/dist/components/message-item/message-item.recipe.d.mts +101 -0
  274. package/dist/components/message-item/message-item.recipe.mjs +114 -0
  275. package/dist/components/message-item/message-multi-select.d.mts +15 -0
  276. package/dist/components/message-item/message-multi-select.mjs +49 -0
  277. package/dist/components/message-item/message-multi-select.recipe.mjs +23 -0
  278. package/dist/components/message-item/message-reaction.d.mts +39 -0
  279. package/dist/components/message-item/message-reaction.mjs +188 -0
  280. package/dist/components/message-item/message-reaction.recipe.mjs +76 -0
  281. package/dist/components/message-item/message-reference.d.mts +50 -0
  282. package/dist/components/message-item/message-reference.mjs +111 -0
  283. package/dist/components/message-item/message-reference.recipe.d.mts +6 -0
  284. package/dist/components/message-item/message-reference.recipe.mjs +63 -0
  285. package/dist/components/message-item/message-text-selection-toolbar.d.mts +24 -0
  286. package/dist/components/message-item/message-text-selection-toolbar.mjs +13 -0
  287. package/dist/components/message-list/index.d.mts +4 -0
  288. package/dist/components/message-list/message-list-jump.d.mts +21 -0
  289. package/dist/components/message-list/message-list-jump.mjs +73 -0
  290. package/dist/components/message-list/message-list-markers.d.mts +13 -0
  291. package/dist/components/message-list/message-list-markers.mjs +70 -0
  292. package/dist/components/message-list/message-list.d.mts +7 -0
  293. package/dist/components/message-list/message-list.mjs +52 -0
  294. package/dist/components/message-list/message-list.recipe.mjs +54 -0
  295. package/dist/components/message-quoted-preview/index.d.mts +2 -0
  296. package/dist/components/message-quoted-preview/message-quoted-preview.d.mts +40 -0
  297. package/dist/components/message-quoted-preview/message-quoted-preview.mjs +144 -0
  298. package/dist/components/message-quoted-preview/message-quoted-preview.recipe.mjs +38 -0
  299. package/dist/components/message-replies/index.d.mts +2 -0
  300. package/dist/components/message-replies/message-replies.d.mts +17 -0
  301. package/dist/components/message-replies/message-replies.mjs +79 -0
  302. package/dist/components/message-replies/message-replies.recipe.mjs +25 -0
  303. package/dist/components/message-translation/index.d.mts +2 -0
  304. package/dist/components/message-translation/message-translation.d.mts +20 -0
  305. package/dist/components/message-translation/message-translation.mjs +64 -0
  306. package/dist/components/message-translation/message-translation.recipe.mjs +27 -0
  307. package/dist/components/meter/index.d.mts +2 -0
  308. package/dist/components/meter/meter.d.mts +107 -0
  309. package/dist/components/meter/meter.mjs +118 -0
  310. package/dist/components/mobile-nav/index.d.mts +3 -0
  311. package/dist/components/mobile-nav/mobile-nav-server.d.mts +71 -0
  312. package/dist/components/mobile-nav/mobile-nav-server.mjs +287 -0
  313. package/dist/components/mobile-nav/mobile-nav.d.mts +12 -0
  314. package/dist/components/mobile-nav/mobile-nav.mjs +73 -0
  315. package/dist/components/mode-bar/index.d.mts +2 -0
  316. package/dist/components/mode-bar/mode-bar.d.mts +33 -0
  317. package/dist/components/mode-bar/mode-bar.mjs +115 -0
  318. package/dist/components/notification-center/index.d.mts +2 -0
  319. package/dist/components/notification-center/notification-center.d.mts +225 -0
  320. package/dist/components/notification-center/notification-center.mjs +340 -0
  321. package/dist/components/panel/index.d.mts +7 -0
  322. package/dist/components/panel/panel-action.d.mts +15 -0
  323. package/dist/components/panel/panel-action.mjs +23 -0
  324. package/dist/components/panel/panel-action.recipe.mjs +40 -0
  325. package/dist/components/panel/panel-header.d.mts +34 -0
  326. package/dist/components/panel/panel-header.mjs +135 -0
  327. package/dist/components/panel/panel-header.recipe.mjs +78 -0
  328. package/dist/components/panel/panel-notice.d.mts +16 -0
  329. package/dist/components/panel/panel-notice.mjs +77 -0
  330. package/dist/components/panel/panel-split-action.d.mts +34 -0
  331. package/dist/components/panel/panel-split-action.mjs +69 -0
  332. package/dist/components/panel/panel-split-action.recipe.mjs +53 -0
  333. package/dist/components/panel/panel-toggle-action.d.mts +6 -0
  334. package/dist/components/panel/panel-toggle-action.mjs +15 -0
  335. package/dist/components/panel/panel.d.mts +25 -0
  336. package/dist/components/panel/panel.mjs +96 -0
  337. package/dist/components/panel/panel.recipe.mjs +24 -0
  338. package/dist/components/picker-trigger-button/index.d.mts +2 -0
  339. package/dist/components/picker-trigger-button/picker-trigger-button.d.mts +7 -0
  340. package/dist/components/picker-trigger-button/picker-trigger-button.mjs +36 -0
  341. package/dist/components/popover/index.d.mts +2 -0
  342. package/dist/components/popover/popover.d.mts +41 -0
  343. package/dist/components/popover/popover.mjs +184 -0
  344. package/dist/components/preview-card/index.d.mts +2 -0
  345. package/dist/components/preview-card/preview-card.d.mts +43 -0
  346. package/dist/components/preview-card/preview-card.mjs +197 -0
  347. package/dist/components/profile-panel/index.d.mts +2 -0
  348. package/dist/components/profile-panel/profile-panel.d.mts +14 -0
  349. package/dist/components/profile-panel/profile-panel.mjs +46 -0
  350. package/dist/components/profile-panel/profile-panel.recipe.mjs +14 -0
  351. package/dist/components/progress/index.d.mts +2 -0
  352. package/dist/components/progress/progress.d.mts +119 -0
  353. package/dist/components/progress/progress.mjs +139 -0
  354. package/dist/components/pull-to-refresh/index.d.mts +4 -0
  355. package/dist/components/pull-to-refresh/pull-to-refresh-gesture.d.mts +30 -0
  356. package/dist/components/pull-to-refresh/pull-to-refresh-gesture.mjs +134 -0
  357. package/dist/components/pull-to-refresh/pull-to-refresh-indicator.d.mts +21 -0
  358. package/dist/components/pull-to-refresh/pull-to-refresh-indicator.mjs +153 -0
  359. package/dist/components/pull-to-refresh/pull-to-refresh-material-progress.mjs +150 -0
  360. package/dist/components/pull-to-refresh/use-pull-to-refresh.d.mts +55 -0
  361. package/dist/components/pull-to-refresh/use-pull-to-refresh.mjs +156 -0
  362. package/dist/components/radio-group/index.d.mts +2 -0
  363. package/dist/components/radio-group/radio-group.d.mts +14 -0
  364. package/dist/components/radio-group/radio-group.mjs +59 -0
  365. package/dist/components/resizable/index.d.mts +2 -0
  366. package/dist/components/resizable/resizable.d.mts +15 -0
  367. package/dist/components/resizable/resizable.mjs +39 -0
  368. package/dist/components/resizable/resizable.recipe.mjs +24 -0
  369. package/dist/components/scroll-area/index.d.mts +2 -0
  370. package/dist/components/scroll-area/scroll-area.d.mts +17 -0
  371. package/dist/components/scroll-area/scroll-area.mjs +55 -0
  372. package/dist/components/scroll-area/scroll-area.recipe.mjs +23 -0
  373. package/dist/components/search/index.d.mts +6 -0
  374. package/dist/components/search/search-entity.d.mts +24 -0
  375. package/dist/components/search/search-entity.mjs +106 -0
  376. package/dist/components/search/search-entity.recipe.mjs +41 -0
  377. package/dist/components/search/search-message.d.mts +25 -0
  378. package/dist/components/search/search-message.mjs +115 -0
  379. package/dist/components/search/search-message.recipe.mjs +32 -0
  380. package/dist/components/search/search-thread.d.mts +17 -0
  381. package/dist/components/search/search-thread.mjs +79 -0
  382. package/dist/components/search/search-thread.recipe.mjs +16 -0
  383. package/dist/components/search/search-trigger.d.mts +15 -0
  384. package/dist/components/search/search-trigger.mjs +59 -0
  385. package/dist/components/search/search.d.mts +23 -0
  386. package/dist/components/search/search.mjs +88 -0
  387. package/dist/components/search/search.recipe.mjs +22 -0
  388. package/dist/components/segmented-control/index.d.mts +2 -0
  389. package/dist/components/segmented-control/segmented-control.d.mts +38 -0
  390. package/dist/components/segmented-control/segmented-control.mjs +306 -0
  391. package/dist/components/select/index.d.mts +2 -0
  392. package/dist/components/select/select.d.mts +95 -0
  393. package/dist/components/select/select.mjs +483 -0
  394. package/dist/components/separator/index.d.mts +2 -0
  395. package/dist/components/separator/separator.d.mts +10 -0
  396. package/dist/components/separator/separator.mjs +49 -0
  397. package/dist/components/sidebar/index.d.mts +5 -0
  398. package/dist/components/sidebar/sidebar-item.d.mts +64 -0
  399. package/dist/components/sidebar/sidebar-item.mjs +201 -0
  400. package/dist/components/sidebar/sidebar-item.recipe.mjs +100 -0
  401. package/dist/components/sidebar/sidebar-menu.d.mts +13 -0
  402. package/dist/components/sidebar/sidebar-menu.mjs +67 -0
  403. package/dist/components/sidebar/sidebar-menu.recipe.mjs +22 -0
  404. package/dist/components/sidebar/sidebar-section.d.mts +41 -0
  405. package/dist/components/sidebar/sidebar-section.mjs +164 -0
  406. package/dist/components/sidebar/sidebar-section.recipe.mjs +45 -0
  407. package/dist/components/sidebar/sidebar.d.mts +17 -0
  408. package/dist/components/sidebar/sidebar.mjs +149 -0
  409. package/dist/components/sidebar/sidebar.recipe.mjs +37 -0
  410. package/dist/components/skeleton/index.d.mts +2 -0
  411. package/dist/components/skeleton/skeleton.d.mts +50 -0
  412. package/dist/components/skeleton/skeleton.mjs +29 -0
  413. package/dist/components/spinner/index.d.mts +2 -0
  414. package/dist/components/spinner/spinner.d.mts +77 -0
  415. package/dist/components/spinner/spinner.mjs +118 -0
  416. package/dist/components/status/index.d.mts +2 -0
  417. package/dist/components/status/status.d.mts +143 -0
  418. package/dist/components/status/status.mjs +56 -0
  419. package/dist/components/steps/index.d.mts +3 -0
  420. package/dist/components/steps/otter-indicator.d.mts +17 -0
  421. package/dist/components/steps/otter-indicator.mjs +331 -0
  422. package/dist/components/steps/steps.d.mts +50 -0
  423. package/dist/components/steps/steps.mjs +174 -0
  424. package/dist/components/switch/index.d.mts +2 -0
  425. package/dist/components/switch/switch.d.mts +9 -0
  426. package/dist/components/switch/switch.mjs +50 -0
  427. package/dist/components/system-message/index.d.mts +2 -0
  428. package/dist/components/system-message/system-message.d.mts +27 -0
  429. package/dist/components/system-message/system-message.mjs +113 -0
  430. package/dist/components/system-message/system-message.recipe.mjs +34 -0
  431. package/dist/components/table/index.d.mts +2 -0
  432. package/dist/components/table/table.d.mts +35 -0
  433. package/dist/components/table/table.mjs +102 -0
  434. package/dist/components/table/table.recipe.mjs +31 -0
  435. package/dist/components/tabs/index.d.mts +2 -0
  436. package/dist/components/tabs/tabs.d.mts +185 -0
  437. package/dist/components/tabs/tabs.mjs +528 -0
  438. package/dist/components/task-board/index.d.mts +2 -0
  439. package/dist/components/task-board/task-board-column.recipe.mjs +46 -0
  440. package/dist/components/task-board/task-board.d.mts +40 -0
  441. package/dist/components/task-board/task-board.mjs +155 -0
  442. package/dist/components/task-card/index.d.mts +2 -0
  443. package/dist/components/task-card/task-card-status.recipe.mjs +17 -0
  444. package/dist/components/task-card/task-card.d.mts +30 -0
  445. package/dist/components/task-card/task-card.mjs +141 -0
  446. package/dist/components/task-card/task-card.recipe.mjs +39 -0
  447. package/dist/components/task-dnd/index.d.mts +2 -0
  448. package/dist/components/task-dnd/task-dnd.d.mts +75 -0
  449. package/dist/components/task-dnd/task-dnd.mjs +341 -0
  450. package/dist/components/task-dnd/task-dnd.recipe.mjs +20 -0
  451. package/dist/components/task-list/index.d.mts +2 -0
  452. package/dist/components/task-list/task-list-section.recipe.mjs +37 -0
  453. package/dist/components/task-list/task-list.d.mts +34 -0
  454. package/dist/components/task-list/task-list.mjs +119 -0
  455. package/dist/components/tasks/index.d.mts +7 -0
  456. package/dist/components/tasks/task-chip.d.mts +14 -0
  457. package/dist/components/tasks/task-chip.mjs +81 -0
  458. package/dist/components/tasks/task-loading.d.mts +7 -0
  459. package/dist/components/tasks/task-loading.mjs +32 -0
  460. package/dist/components/tasks/task-loading.recipe.mjs +8 -0
  461. package/dist/components/tasks/task-status-badge.d.mts +24 -0
  462. package/dist/components/tasks/task-status-badge.mjs +24 -0
  463. package/dist/components/tasks/task-status-icon.d.mts +13 -0
  464. package/dist/components/tasks/task-status-icon.mjs +90 -0
  465. package/dist/components/tasks/task-status.recipe.d.mts +62 -0
  466. package/dist/components/tasks/task-status.recipe.mjs +33 -0
  467. package/dist/components/tasks/tasks-panel.d.mts +22 -0
  468. package/dist/components/tasks/tasks-panel.mjs +114 -0
  469. package/dist/components/tasks/tasks-panel.recipe.mjs +36 -0
  470. package/dist/components/text/index.d.mts +2 -0
  471. package/dist/components/text/text.d.mts +186 -0
  472. package/dist/components/text/text.mjs +114 -0
  473. package/dist/components/text-link/index.d.mts +2 -0
  474. package/dist/components/text-link/text-link.d.mts +36 -0
  475. package/dist/components/text-link/text-link.mjs +60 -0
  476. package/dist/components/text-selection-toolbar/index.d.mts +3 -0
  477. package/dist/components/text-selection-toolbar/text-selection-anchor.d.mts +22 -0
  478. package/dist/components/text-selection-toolbar/text-selection-anchor.mjs +106 -0
  479. package/dist/components/text-selection-toolbar/text-selection-toolbar.d.mts +20 -0
  480. package/dist/components/text-selection-toolbar/text-selection-toolbar.mjs +41 -0
  481. package/dist/components/text-selection-toolbar/text-selection-toolbar.recipe.mjs +19 -0
  482. package/dist/components/textarea/index.d.mts +2 -0
  483. package/dist/components/textarea/textarea.d.mts +16 -0
  484. package/dist/components/textarea/textarea.mjs +62 -0
  485. package/dist/components/thread-panel/index.d.mts +2 -0
  486. package/dist/components/thread-panel/thread-panel.d.mts +31 -0
  487. package/dist/components/thread-panel/thread-panel.mjs +162 -0
  488. package/dist/components/thread-panel/thread-panel.recipe.mjs +36 -0
  489. package/dist/components/timeline/index.d.mts +2 -0
  490. package/dist/components/timeline/timeline.d.mts +44 -0
  491. package/dist/components/timeline/timeline.mjs +81 -0
  492. package/dist/components/toast/index.d.mts +2 -0
  493. package/dist/components/toast/toast.d.mts +263 -0
  494. package/dist/components/toast/toast.mjs +556 -0
  495. package/dist/components/toggle-group/index.d.mts +2 -0
  496. package/dist/components/toggle-group/toggle-group.d.mts +15 -0
  497. package/dist/components/toggle-group/toggle-group.mjs +64 -0
  498. package/dist/components/tooltip/index.d.mts +2 -0
  499. package/dist/components/tooltip/tooltip.d.mts +18 -0
  500. package/dist/components/tooltip/tooltip.mjs +197 -0
  501. package/dist/components/virtual-list/ItemColumns.mjs +37 -0
  502. package/dist/components/virtual-list/VirtualList.d.mts +4 -0
  503. package/dist/components/virtual-list/VirtualList.mjs +1401 -0
  504. package/dist/components/virtual-list/VirtualListContext.mjs +5 -0
  505. package/dist/components/virtual-list/hooks/use-event-callback.mjs +10 -0
  506. package/dist/components/virtual-list/hooks/use-ios-momentum-compensation.mjs +148 -0
  507. package/dist/components/virtual-list/hooks/use-keyboard-scroll.mjs +42 -0
  508. package/dist/components/virtual-list/hooks/use-latest-ref.mjs +10 -0
  509. package/dist/components/virtual-list/hooks/use-layout-effect-with-previous.mjs +16 -0
  510. package/dist/components/virtual-list/hooks/use-memo-with-previous.mjs +22 -0
  511. package/dist/components/virtual-list/hooks/use-non-passive-wheel.mjs +23 -0
  512. package/dist/components/virtual-list/hooks/use-non-virtual-content-resize.mjs +28 -0
  513. package/dist/components/virtual-list/hooks/use-scroll-anchor.mjs +55 -0
  514. package/dist/components/virtual-list/index.d.mts +5 -0
  515. package/dist/components/virtual-list/public-types.d.mts +341 -0
  516. package/dist/components/virtual-list/resize-observer/ResizeObserverWrapper.d.mts +5 -0
  517. package/dist/components/virtual-list/resize-observer/ResizeObserverWrapper.mjs +70 -0
  518. package/dist/components/virtual-list/resize-observer/resize-observer-delegate.d.mts +6 -0
  519. package/dist/components/virtual-list/resize-observer/resize-observer-delegate.mjs +54 -0
  520. package/dist/components/virtual-list/utils/align.mjs +19 -0
  521. package/dist/components/virtual-list/utils/constants.mjs +30 -0
  522. package/dist/components/virtual-list/utils/fenwick-tree.mjs +66 -0
  523. package/dist/components/virtual-list/utils/items-anchor.mjs +49 -0
  524. package/dist/components/virtual-list/utils/measure.mjs +77 -0
  525. package/dist/components/virtual-list/utils/platform.mjs +39 -0
  526. package/dist/components/virtual-list/utils/range.mjs +103 -0
  527. package/dist/components/virtual-list/utils/size-cache.mjs +35 -0
  528. package/dist/components/workspace-button/index.d.mts +2 -0
  529. package/dist/components/workspace-button/workspace-button.d.mts +9 -0
  530. package/dist/components/workspace-button/workspace-button.mjs +28 -0
  531. package/dist/index.d.mts +239 -4864
  532. package/dist/index.mjs +142 -17053
  533. package/dist/lib/chip-remove.mjs +17 -0
  534. package/dist/lib/data-attributes.d.mts +5 -0
  535. package/dist/lib/decision-width.mjs +9 -0
  536. package/dist/lib/default-native-button.mjs +8 -0
  537. package/dist/lib/desktop-media.mjs +10 -0
  538. package/dist/lib/drag-escape.mjs +37 -0
  539. package/dist/lib/frame-insets.mjs +127 -0
  540. package/dist/lib/head-edges.mjs +44 -0
  541. package/dist/lib/hud-text-action.mjs +10 -0
  542. package/dist/lib/inline-chip.mjs +30 -0
  543. package/dist/lib/input-modality.d.mts +36 -0
  544. package/dist/lib/input-modality.mjs +186 -0
  545. package/dist/lib/list-cell.mjs +21 -0
  546. package/dist/lib/located.d.mts +25 -0
  547. package/dist/lib/located.mjs +28 -0
  548. package/dist/lib/overlay-depth.d.mts +42 -0
  549. package/dist/lib/overlay-depth.mjs +59 -0
  550. package/dist/lib/phone-media.mjs +24 -0
  551. package/dist/lib/presence.d.mts +16 -0
  552. package/dist/lib/presence.mjs +16 -0
  553. package/dist/lib/row-tone.d.mts +8 -0
  554. package/dist/lib/search-hit.mjs +18 -0
  555. package/dist/lib/stacked-actions.mjs +112 -0
  556. package/dist/lib/styled-props.d.mts +15 -0
  557. package/dist/lib/tap-activation.mjs +133 -0
  558. package/dist/lib/theme/theme-context.d.mts +10 -0
  559. package/dist/lib/theme/theme-context.mjs +9 -0
  560. package/dist/lib/theme/theme-provider.d.mts +23 -0
  561. package/dist/lib/theme/theme-provider.mjs +61 -0
  562. package/dist/lib/theme/theme-scope.mjs +19 -0
  563. package/dist/lib/theme/use-theme.d.mts +5 -0
  564. package/dist/lib/theme/use-theme.mjs +9 -0
  565. package/dist/lib/touch-drag.d.mts +55 -0
  566. package/dist/lib/touch-drag.mjs +151 -0
  567. package/dist/lib/truncation.d.mts +26 -0
  568. package/dist/lib/truncation.mjs +153 -0
  569. package/dist/lib/tv.d.mts +2 -0
  570. package/dist/lib/tv.mjs +14 -0
  571. package/dist/lib/use-copy-to-clipboard.mjs +58 -0
  572. package/dist/lib/use-merged-ref.mjs +60 -0
  573. package/dist/lib/use-mobile-viewport.mjs +17 -0
  574. package/dist/lib/use-overflow-title.mjs +49 -0
  575. package/dist/lib/use-scrollbar-size.d.mts +16 -0
  576. package/dist/lib/use-scrollbar-size.mjs +46 -0
  577. package/dist/lib/use-truncated.d.mts +20 -0
  578. package/dist/lib/use-truncated.mjs +34 -0
  579. package/dist/lib/without-children.d.mts +8 -0
  580. package/dist/wip.d.mts +61 -1222
  581. package/dist/wip.mjs +43 -3079
  582. package/package.json +8 -1
  583. package/dist/inbox-item-B4nNvpp1.d.mts +0 -1784
  584. package/dist/inbox-item-DHJOrxB8.mjs +0 -7454
  585. package/dist/style.css +0 -1395
package/dist/wip.mjs CHANGED
@@ -1,3079 +1,43 @@
1
- import { cn } from "./cn.mjs";
2
- import { $ as MessageListJumpPillGroup, $n as PreviewCardActions, A as useDragSensors, Aa as OverlayLayerProvider, Ai as Checkbox, B as keepGrabInside, Bi as ListRowMeta, Bn as RadioGroup, Br as MouseOnlyPointerSensor, C as DragRefusalHint, Cr as MobileNavServerPopoverRowLabel, D as MOUSE_DRAG_DISTANCE_PX, E as DragRemeasure, Ea as Button$1, F as DRAG_SIBLING_TRANSITION, Fi as ListRowBottom, G as DEFAULT_DRAG_MESSAGES, Gn as InputGroupAddon, Gr as useMouseOnlyPointerSensor, Hi as ListRowTop, Hn as RadioGroupItem, Hr as TOUCH_DRAG_HOLD_MS, I as DROP_GLIDE, Ia as tv, J as dragInstructions, Ji as TooltipContent, Jn as measureContent, K as SILENT_DRAG_ANNOUNCEMENTS, Kn as InputGroupInput, L as DragLift, Li as ListRowDescription, Ln as AvatarGroupButton, Lt as MessageReferenceReadMark, M as DRAG_KEYBOARD_OFFSET_PX, N as DRAG_LIFT_Z_INDEX, Na as useThemeScopeProps, Ni as ListRow, Nt as MessageReferenceIcon, O as listKeyboardCoordinates, Oa as Spinner, Or as PopoverBody, P as DRAG_SETTLE, Pa as useTheme, Pi as ListRowBody, Q as MessageListJumpPillDismiss, Qi as usePopupCollisionPadding, R as ORIGIN_OPACITY, S as Skeleton, Sa as TextLink, Sr as MobileNavServerPopoverRowDescription, T as DRAG_KEYBOARD_CODES, Tr as PopoverActions, U as LIFT_CLASS, Un as chipRemoveBase, Ur as TOUCH_DRAG_TOLERANCE_PX, V as useDragSiblingTransition, Vi as ListRowTitle, Vr as TOUCH_DRAG_DISTANCE_PX, Wn as InputGroup, Wr as useHoldThenMoveTouchSensor, X as MessageListJumpControls, Xn as observeTruncation, Y as dragMessages, Yn as measureElement, Z as MessageListJumpPill, Zi as TooltipTrigger, Zn as readNow, _ as InboxItemTrailing, _a as DialogTrigger, _r as MobileNavServerPopoverDestinations, a as InboxItemContext, aa as DialogClose, ar as PreviewCardFooter, b as useLatestRef, br as MobileNavServerPopoverRow, c as InboxItemLeading, dn as MessageItemDelegatedByButton, ea as DialogCommit, fn as MessageItemDelegatedByName, g as InboxItemTitle, ga as DialogTitle, gr as MobileNavServerPopoverDestination, h as InboxItemTimestamp, hr as MobileNavServerPopoverContent, i as InboxItemContent, ii as DropdownMenuLinkItem, ir as PreviewCardEyebrow, j as DRAG_AUTO_SCROLL_ZONE_PX, ja as useParentOverlayKind, k as useDragAcrossLists, ka as defaultNativeButton, la as DialogFooterNote, ln as MessageItemDelegatedAvatar, lt as MessageQuotedPreviewAuthorDelegatedBy, ma as DialogSheetBack, mr as MobileNavServerPopover, o as InboxItemHeading, oa as DialogContent, pr as MobileNavServerItem, q as createDragAnnouncements, qi as Tooltip, qn as useOverflowTitle, r as InboxItemBody, ra as Dialog, sr as PreviewCardMeta, t as InboxItem, ta as DialogSheetContent, tr as PreviewCardBody, un as MessageItemDelegatedBy, vr as MobileNavServerPopoverGroup, w as DropPlaceholder, x as hudTextAction, xr as MobileNavServerPopoverRowBody, y as inboxItem, yr as MobileNavServerPopoverList, z as cancelDrag, zr as HoldThenMoveTouchSensor } from "./inbox-item-DHJOrxB8.mjs";
3
- import { Check, ChevronDown, LoaderCircle, Plus, Search, X } from "lucide-react";
4
- import { createContext, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
- import { Button } from "@base-ui/react/button";
7
- import { mergeProps } from "@base-ui/react/merge-props";
8
- import { useRender } from "@base-ui/react/use-render";
9
- import { Combobox } from "@base-ui/react/combobox";
10
- import { arrayMove, verticalListSortingStrategy } from "@dnd-kit/sortable";
11
- import { Meter as Meter$1 } from "@base-ui/react/meter";
12
- import { CheckboxGroup } from "@base-ui/react/checkbox-group";
13
- //#region src/lib/phone-media.ts
14
- /**
15
- * A phone: a touch screen narrower than the desktop layout. The complement, on
16
- * both axes, of the desktop query `(width >= 48rem) and (pointer: fine)`
17
- * (design/foundations/spacing.md): a narrow window with a mouse keeps the
18
- * desktop behaviour, and so does a tablet used by touch, which is wide.
19
- */
20
- const PHONE_MEDIA_QUERY = "(width < 48rem) and (pointer: coarse)";
21
- function subscribe(onStoreChange) {
22
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => void 0;
23
- const media = window.matchMedia(PHONE_MEDIA_QUERY);
24
- media.addEventListener("change", onStoreChange);
25
- return () => media.removeEventListener("change", onStoreChange);
26
- }
27
- function getSnapshot() {
28
- return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(width < 48rem) and (pointer: coarse)").matches;
29
- }
30
- /** Whether `PHONE_MEDIA_QUERY` matches. `false` on the server and before hydration. */
31
- function usePhoneMedia() {
32
- return useSyncExternalStore(subscribe, getSnapshot, () => false);
33
- }
34
- //#endregion
35
- //#region src/components/combobox-select/combobox-select.list-side.ts
36
- /** One option row (36px) and the list's 6px padding above and below. */
37
- const ROW = 36;
38
- const LIST_PADDING = 12;
39
- /** The gap between the field and the list (the positioner's sideOffset). */
40
- const SIDE_OFFSET = 4;
41
- /**
42
- * Whether fewer than three rows of a phone's list fit between the field and the
43
- * bottom of the visual viewport. The list always opens below the field on a
44
- * phone and never flips; when this is true the field is scrolled up instead.
45
- */
46
- function roomBelowIsShort(field, viewport, rows = 3) {
47
- return viewport.top + viewport.height - field.bottom - SIDE_OFFSET < rows * ROW + LIST_PADDING;
48
- }
49
- //#endregion
50
- //#region src/components/combobox-select/combobox-select.options.ts
51
- function isOptionGroup(option) {
52
- return typeof option === "object" && option !== null && "type" in option && option.type === "group" && "children" in option && Array.isArray(option.children);
53
- }
54
- function normalizeOption(option, labelField, valueField, disabledField) {
55
- const fields = option;
56
- const value = fields[valueField];
57
- const label = fields[labelField];
58
- if (typeof value !== "string" && typeof value !== "number") throw new Error("ComboboxSelect options must have a string or number value.");
59
- return {
60
- value,
61
- label: typeof label === "string" || typeof label === "number" ? String(label) : String(value),
62
- disabled: Boolean(fields[disabledField]),
63
- option
64
- };
65
- }
66
- function optionKey(value) {
67
- return `${typeof value}:${value}`;
68
- }
69
- //#endregion
70
- //#region src/components/combobox-select/combobox-select.tsx
71
- const defaultMessages = {
72
- clear: "Clear selection",
73
- toggle: "Toggle options",
74
- selected: "Selected options",
75
- chipDescription: "Press Backspace or Delete to remove",
76
- inputDescription: "From the start of the input, press Left Arrow to focus selected options",
77
- remove: (label) => `Remove ${label}`
78
- };
79
- const defaultRenderCreateLabel = (query) => `Create “${query}”`;
80
- /** An option's or a chip's text, with its whole value as the title when it is cut off. */
81
- function OverflowText({ children }) {
82
- return /* @__PURE__ */ jsx("span", {
83
- ref: useOverflowTitle(),
84
- className: "r-combobox-select__text",
85
- children
86
- });
87
- }
88
- function recordLabel(record, renderLabel) {
89
- return record.option === void 0 ? record.label : renderLabel?.(record.option) ?? record.label;
90
- }
91
- function renderOptionContent({ record, selected, renderLabel, renderOption, renderCreateLabel }) {
92
- if (record.createQuery !== void 0) return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Plus, { "aria-hidden": true }), /* @__PURE__ */ jsx(OverflowText, { children: renderCreateLabel(record.label) })] });
93
- const content = record.option === void 0 ? record.label : renderOption?.(record.option, { selected }) ?? recordLabel(record, renderLabel);
94
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(OverflowText, { children: content }), /* @__PURE__ */ jsx(Combobox.ItemIndicator, {
95
- className: "r-combobox-select__indicator",
96
- children: /* @__PURE__ */ jsx(Check, { "aria-hidden": true })
97
- })] });
98
- }
99
- function renderSelectedChip({ record, index, tagLimit, strings, renderLabel, renderTag }) {
100
- const label = recordLabel(record, renderLabel);
101
- const content = record.option === void 0 ? record.label : renderTag?.(record.option) ?? label;
102
- const overflow = index >= tagLimit;
103
- return /* @__PURE__ */ jsxs(Combobox.Chip, {
104
- className: "r-combobox-select__chip",
105
- "data-overflow": overflow || void 0,
106
- "aria-label": record.label,
107
- "aria-description": strings.chipDescription,
108
- children: [/* @__PURE__ */ jsx(OverflowText, { children: content }), /* @__PURE__ */ jsx(Combobox.ChipRemove, {
109
- className: "r-combobox-select__remove",
110
- "aria-label": strings.remove(record.label),
111
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
112
- })]
113
- }, optionKey(record.value));
114
- }
115
- /**
116
- * On a phone the list always opens below the field, between the field and the
117
- * keyboard, as a native autocomplete does, and never flips: which side has more
118
- * room depends on whether the keyboard happens to be up, so choosing a side made
119
- * the same field open up one time and down the next.
120
- */
121
- const PHONE_LIST_COLLISION = {
122
- side: "none",
123
- align: "shift",
124
- fallbackAxisSide: "none"
125
- };
126
- function ComboboxSelectFramePositioner({ collisionPadding, container, ...props }) {
127
- const padding = usePopupCollisionPadding(collisionPadding, 8, container);
128
- return /* @__PURE__ */ jsx(Combobox.Positioner, {
129
- collisionPadding: padding,
130
- ...props
131
- });
132
- }
133
- /**
134
- * Keeps room for the list below the field on a phone: when fewer than three
135
- * rows fit between the field and the bottom of the visual viewport (the
136
- * keyboard's top edge while it is up), scrolls the field up to the top of
137
- * what is visible instead of flipping the list above it.
138
- */
139
- function keepRoomBelow(field) {
140
- const view = field?.ownerDocument.defaultView;
141
- if (!field || !view) return;
142
- const visual = view.visualViewport;
143
- const viewport = visual ? {
144
- top: visual.offsetTop,
145
- height: visual.height
146
- } : {
147
- top: 0,
148
- height: view.innerHeight
149
- };
150
- if (roomBelowIsShort(field.getBoundingClientRect(), viewport)) {
151
- if (!field.style.scrollMarginTop) field.style.scrollMarginTop = "2.5rem";
152
- field.scrollIntoView({
153
- block: "start",
154
- inline: "nearest",
155
- behavior: "instant"
156
- });
157
- }
158
- }
159
- /** Options-driven composition. The stable compound Combobox API stays unchanged. */
160
- function isItemEqualToValue(a, b) {
161
- return a.createQuery === b.createQuery && a.value === b.value;
162
- }
163
- function itemToStringLabel(record) {
164
- return record.label;
165
- }
166
- function itemToStringValue(record) {
167
- return String(record.value);
168
- }
169
- function ComboboxSelect({ options, multiple, value, defaultValue, onValueChange, labelField = "label", valueField = "value", disabledField = "disabled", fallbackOption, filterable = true, filter, remote = false, onSearch, loading = false, create = false, creatable, onCreate, clearable = false, showArrow = true, clearFilterAfterSelect = true, onClear, maxTagCount, size = "md", status, placeholder = "Select an option", renderLabel, renderOption, renderTag, renderCreateLabel = defaultRenderCreateLabel, emptyContent = "No options found", loadingContent = "Loading…", messages, listMaxHeight, inputProps, portalProps, positionerProps, popupProps, className, style, id: idProp, ref, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, "aria-invalid": ariaInvalid, inputValue, defaultInputValue, onInputValueChange, disabled, readOnly, autoHighlight = true, onOpenChange, ...rootProps }) {
170
- const generatedId = useId();
171
- const id = idProp ?? generatedId;
172
- const { resolvedMode } = useTheme();
173
- const scopedPortalProps = useThemeScopeProps(portalProps ?? {});
174
- const strings = {
175
- ...defaultMessages,
176
- ...messages
177
- };
178
- const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue ?? (multiple ? [] : null));
179
- const selectedValue = value === void 0 ? uncontrolledValue : value;
180
- const selectedKeys = useMemo(() => Array.isArray(selectedValue) ? selectedValue : selectedValue == null ? [] : [selectedValue], [selectedValue]);
181
- const [query, setQuery] = useState(String(defaultInputValue ?? ""));
182
- const [createdOptions, setCreatedOptions] = useState([]);
183
- const collator = useMemo(() => new Intl.Collator(void 0, {
184
- sensitivity: "base",
185
- usage: "search"
186
- }), []);
187
- const normalized = useMemo(() => {
188
- const normalize = (option) => normalizeOption(option, labelField, valueField, disabledField);
189
- const groups = [];
190
- options.forEach((option, index) => {
191
- if (isOptionGroup(option)) groups.push({
192
- key: `group:${optionKey(option.key)}`,
193
- label: option.label,
194
- items: option.children.map(normalize)
195
- });
196
- else groups.push({
197
- key: `option:${index}`,
198
- items: [normalize(option)]
199
- });
200
- });
201
- const records = new Map(groups.flatMap((group) => group.items.map((record) => [record.value, record])));
202
- const created = createdOptions.map(normalize).filter((record) => !records.has(record.value));
203
- created.forEach((record) => records.set(record.value, record));
204
- if (created.length) groups.push({
205
- key: "created",
206
- items: created
207
- });
208
- return {
209
- groups,
210
- records
211
- };
212
- }, [
213
- options,
214
- createdOptions,
215
- labelField,
216
- valueField,
217
- disabledField
218
- ]);
219
- const [retained, setRetained] = useState(() => /* @__PURE__ */ new Map());
220
- const selected = useMemo(() => {
221
- return selectedKeys.map((key) => normalized.records.get(key) ?? retained.get(key) ?? (fallbackOption ? normalizeOption(fallbackOption(key), labelField, valueField, disabledField) : {
222
- value: key,
223
- label: String(key),
224
- disabled: false
225
- }));
226
- }, [
227
- selectedKeys,
228
- normalized,
229
- retained,
230
- fallbackOption,
231
- labelField,
232
- valueField,
233
- disabledField
234
- ]);
235
- if (retained.size !== selected.length || !selected.every((record) => retained.get(record.value) === record)) setRetained(new Map(selected.map((record) => [record.value, record])));
236
- const search = String(inputValue ?? query).trim();
237
- const canCreate = (create || creatable) && filterable && !loading && search !== "" && ![...normalized.records.values(), ...selected].some((record) => record.label.trim().toLocaleLowerCase() === search.toLocaleLowerCase());
238
- const items = useMemo(() => {
239
- if (!canCreate) return normalized.groups;
240
- const candidate = {
241
- value: search,
242
- label: search,
243
- disabled: false,
244
- createQuery: search
245
- };
246
- return [...normalized.groups, {
247
- key: "create",
248
- items: [candidate]
249
- }];
250
- }, [
251
- canCreate,
252
- normalized.groups,
253
- search
254
- ]);
255
- const css = `r-combobox-select r-combobox-select--${size}${resolvedMode === "dark" ? " r-combobox-select--dark" : ""}`;
256
- const tagLimit = maxTagCount === void 0 ? Infinity : Math.max(0, Math.floor(maxTagCount));
257
- const inputClassName = ["r-combobox-select__input", typeof inputProps?.className === "string" ? inputProps.className : null].filter(Boolean).join(" ");
258
- const phone = usePhoneMedia();
259
- const controlClassName = [
260
- css,
261
- "r-combobox-select__control has-focus-visible:shadow-focus",
262
- phone ? "r-combobox-select__control--phone" : null,
263
- className
264
- ].filter(Boolean).join(" ");
265
- const positionerClassName = ["r-combobox-select__positioner", typeof positionerProps?.className === "string" ? positionerProps.className : null].filter(Boolean).join(" ");
266
- const popupClassName = [
267
- css,
268
- "r-combobox-select__popup",
269
- phone ? "r-combobox-select__popup--anchored" : null,
270
- typeof popupProps?.className === "string" ? popupProps.className : null
271
- ].filter(Boolean).join(" ");
272
- const inputRef = ref ?? inputProps?.ref;
273
- const inputLabel = ariaLabel ?? inputProps?.["aria-label"];
274
- const inputLabelledBy = ariaLabelledBy ?? inputProps?.["aria-labelledby"];
275
- const inputDescribedBy = ariaDescribedBy ?? inputProps?.["aria-describedby"];
276
- const inputInvalid = ariaInvalid ?? (status === "error" ? true : inputProps?.["aria-invalid"]);
277
- const inputDescription = multiple && selected.length ? strings.inputDescription : inputProps?.["aria-description"];
278
- const inputPlaceholder = multiple && selected.length ? "" : placeholder;
279
- const inputReadOnly = readOnly || !filterable;
280
- const controlValue = multiple ? selected : selected[0] ?? null;
281
- const handleValueChange = useCallback((next, details) => {
282
- const nextRecords = next === null ? [] : Array.isArray(next) ? next : [next];
283
- const createRecord = nextRecords.find((record) => record.createQuery !== void 0);
284
- let newOption;
285
- let resolved = nextRecords;
286
- if (createRecord) {
287
- const created = onCreate ? onCreate(createRecord.label) : {
288
- [labelField]: createRecord.label,
289
- [valueField]: createRecord.label
290
- };
291
- if (created === null) {
292
- details.cancel();
293
- return;
294
- }
295
- const record = normalizeOption(created, labelField, valueField, disabledField);
296
- if (record.disabled) {
297
- details.cancel();
298
- return;
299
- }
300
- newOption = created;
301
- const existing = normalized.records.get(record.value) ?? retained.get(record.value);
302
- resolved = nextRecords.map((item) => item === createRecord ? existing ?? record : item);
303
- resolved = [...new Map(resolved.map((item) => [item.value, item])).values()];
304
- }
305
- const nextValue = multiple ? resolved.map((record) => record.value) : resolved[0]?.value ?? null;
306
- const nextOptions = multiple ? resolved.map((record) => record.option) : resolved[0]?.option ?? null;
307
- onValueChange?.(nextValue, nextOptions, details);
308
- if (details.isCanceled) return;
309
- if (newOption !== void 0) setCreatedOptions((previous) => [...previous, newOption]);
310
- setRetained(new Map(resolved.map((record) => [record.value, record])));
311
- if (value === void 0) setUncontrolledValue(nextValue);
312
- if (details.reason === "clear-press") onClear?.();
313
- }, [
314
- multiple,
315
- onCreate,
316
- labelField,
317
- valueField,
318
- disabledField,
319
- normalized.records,
320
- retained,
321
- onValueChange,
322
- value,
323
- onClear
324
- ]);
325
- const handleInputValueChange = useCallback((next, details) => {
326
- onInputValueChange?.(next, details);
327
- if (details.isCanceled) return;
328
- const shouldKeepQuery = details.reason === "input-change" || details.reason === "item-press" && !clearFilterAfterSelect;
329
- setQuery(shouldKeepQuery ? next : "");
330
- if (details.reason === "input-change" || details.reason === "clear-press") onSearch?.(next);
331
- }, [
332
- onInputValueChange,
333
- clearFilterAfterSelect,
334
- onSearch
335
- ]);
336
- const filterOptions = useCallback((record, pattern) => {
337
- if (record.createQuery !== void 0) return true;
338
- if (remote || !filterable || filter === false) return true;
339
- return record.option !== void 0 && typeof filter === "function" ? filter(pattern, record.option) : collator.compare(record.label.slice(0, pattern.length), pattern) === 0 || record.label.toLocaleLowerCase().includes(pattern.toLocaleLowerCase());
340
- }, [
341
- remote,
342
- filterable,
343
- filter,
344
- collator
345
- ]);
346
- const selectedChips = selected.map((record, index) => renderSelectedChip({
347
- record,
348
- index,
349
- tagLimit,
350
- strings,
351
- renderLabel,
352
- renderTag
353
- }));
354
- const overflowCount = selected.length > tagLimit ? selected.length - tagLimit : 0;
355
- const overflowTitle = selected.slice(tagLimit).map((record) => record.label).join(", ");
356
- const overflow = overflowCount ? /* @__PURE__ */ jsxs("span", {
357
- className: "r-combobox-select__overflow",
358
- title: overflowTitle,
359
- children: ["+", overflowCount]
360
- }) : null;
361
- function renderRecord(record) {
362
- const itemKey = record.createQuery !== void 0 ? "create" : optionKey(record.value);
363
- const content = renderOptionContent({
364
- record,
365
- selected: selected.some((item) => item.value === record.value),
366
- renderLabel,
367
- renderOption,
368
- renderCreateLabel
369
- });
370
- return /* @__PURE__ */ jsx(Combobox.Item, {
371
- value: record,
372
- disabled: record.disabled || loading,
373
- className: "r-combobox-select__option",
374
- children: content
375
- }, itemKey);
376
- }
377
- function renderGroup(group) {
378
- const groupLabel = group.label == null ? null : /* @__PURE__ */ jsx(Combobox.GroupLabel, {
379
- className: "r-combobox-select__group-label",
380
- children: group.label
381
- });
382
- return /* @__PURE__ */ jsxs(Combobox.Group, {
383
- items: group.items,
384
- children: [groupLabel, /* @__PURE__ */ jsx(Combobox.Collection, { children: renderRecord })]
385
- }, group.key);
386
- }
387
- const input = /* @__PURE__ */ jsx(Combobox.Input, {
388
- ...inputProps,
389
- ref: inputRef,
390
- id,
391
- "data-slot": "combobox-select-input",
392
- className: inputClassName,
393
- "aria-label": inputLabel,
394
- "aria-labelledby": inputLabelledBy,
395
- "aria-describedby": inputDescribedBy,
396
- "aria-invalid": inputInvalid,
397
- "aria-description": inputDescription,
398
- placeholder: inputPlaceholder,
399
- readOnly: inputReadOnly
400
- });
401
- const controlContent = multiple ? /* @__PURE__ */ jsxs(Combobox.Chips, {
402
- className: "r-combobox-select__chips",
403
- "aria-label": strings.selected,
404
- children: [
405
- selectedChips,
406
- overflow,
407
- input
408
- ]
409
- }) : input;
410
- const loadingMessage = loading ? /* @__PURE__ */ jsx("div", {
411
- className: "r-combobox-select__message",
412
- children: loadingContent
413
- }) : null;
414
- const emptyMessage = !loading ? /* @__PURE__ */ jsx("div", {
415
- className: "r-combobox-select__message",
416
- children: emptyContent
417
- }) : null;
418
- const loadingIndicator = loading ? /* @__PURE__ */ jsx(LoaderCircle, {
419
- className: "r-combobox-select__spinner",
420
- "aria-hidden": true
421
- }) : null;
422
- const clearAction = clearable ? /* @__PURE__ */ jsx(Combobox.Clear, {
423
- className: "r-combobox-select__action",
424
- "aria-label": strings.clear,
425
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
426
- }) : null;
427
- const triggerAction = showArrow ? /* @__PURE__ */ jsx(Combobox.Trigger, {
428
- className: "r-combobox-select__action",
429
- "aria-label": strings.toggle,
430
- children: /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true })
431
- }) : null;
432
- const actions = /* @__PURE__ */ jsxs("span", {
433
- className: "r-combobox-select__actions",
434
- children: [
435
- loadingIndicator,
436
- clearAction,
437
- triggerAction
438
- ]
439
- });
440
- const valueChange = handleValueChange;
441
- const listHeight = typeof listMaxHeight === "number" ? `${listMaxHeight}px` : listMaxHeight;
442
- const fieldRef = useRef(null);
443
- const [phoneOpen, setPhoneOpen] = useState(false);
444
- const handleOpenChange = useCallback((next, details) => {
445
- onOpenChange?.(next, details);
446
- if (phone && !details.isCanceled) setPhoneOpen(next);
447
- }, [onOpenChange, phone]);
448
- useEffect(() => {
449
- if (!phone || !phoneOpen) return;
450
- const field = fieldRef.current;
451
- const visual = field?.ownerDocument.defaultView?.visualViewport;
452
- let frame = 0;
453
- const check = () => {
454
- cancelAnimationFrame(frame);
455
- frame = requestAnimationFrame(() => keepRoomBelow(field));
456
- };
457
- check();
458
- visual?.addEventListener("resize", check);
459
- return () => {
460
- cancelAnimationFrame(frame);
461
- visual?.removeEventListener("resize", check);
462
- };
463
- }, [phone, phoneOpen]);
464
- const popupPropsStyle = popupProps?.style;
465
- const popupStyle = useMemo(() => {
466
- if (listHeight === void 0) return popupPropsStyle;
467
- return (state) => ({
468
- ...typeof popupPropsStyle === "function" ? popupPropsStyle(state) : popupPropsStyle,
469
- "--combobox-select-list-max-height": listHeight
470
- });
471
- }, [listHeight, popupPropsStyle]);
472
- return /* @__PURE__ */ jsxs(Combobox.Root, {
473
- ...rootProps,
474
- onOpenChange: handleOpenChange,
475
- id,
476
- multiple,
477
- disabled,
478
- readOnly,
479
- items,
480
- value: controlValue,
481
- onValueChange: valueChange,
482
- inputValue,
483
- defaultInputValue,
484
- onInputValueChange: handleInputValueChange,
485
- filter: filterOptions,
486
- itemToStringLabel,
487
- itemToStringValue,
488
- isItemEqualToValue,
489
- autoHighlight,
490
- children: [/* @__PURE__ */ jsxs(Combobox.InputGroup, {
491
- ref: fieldRef,
492
- "data-slot": "combobox-select",
493
- "data-status": status,
494
- className: controlClassName,
495
- style,
496
- children: [controlContent, actions]
497
- }), /* @__PURE__ */ jsx(Combobox.Portal, {
498
- ...scopedPortalProps,
499
- children: /* @__PURE__ */ jsx(ComboboxSelectFramePositioner, {
500
- sideOffset: 4,
501
- align: "start",
502
- ...phone ? {
503
- side: "bottom",
504
- collisionAvoidance: PHONE_LIST_COLLISION
505
- } : null,
506
- ...positionerProps,
507
- container: portalProps?.container,
508
- className: positionerClassName,
509
- children: /* @__PURE__ */ jsxs(Combobox.Popup, {
510
- initialFocus: false,
511
- finalFocus: false,
512
- ...popupProps,
513
- style: popupStyle,
514
- "data-slot": "combobox-select-popup",
515
- className: popupClassName,
516
- children: [
517
- /* @__PURE__ */ jsx(Combobox.Status, { children: loadingMessage }),
518
- /* @__PURE__ */ jsx(Combobox.List, {
519
- className: "r-combobox-select__list",
520
- "aria-busy": loading,
521
- children: renderGroup
522
- }),
523
- /* @__PURE__ */ jsx(Combobox.Empty, { children: emptyMessage })
524
- ]
525
- })
526
- })
527
- })]
528
- });
529
- }
530
- //#endregion
531
- //#region src/components/announcement-dialog/announcement-dialog.tsx
532
- const announcement = tv({ slots: {
533
- content: ["@container/announcement max-w-xl gap-0 overflow-clip p-0"],
534
- body: [
535
- "min-h-0 max-h-[70dvh] overflow-y-auto pb-5",
536
- "[&_[data-slot=announcement-picture]+[data-slot=announcement-text]]:mt-5",
537
- "px-5 pt-5"
538
- ],
539
- header: ["flex min-w-0 items-center justify-between gap-3"],
540
- label: ["flex min-w-0 items-center gap-2 wrap-break-word", "text-caption text-foreground-muted"],
541
- dot: ["shrink-0", "size-2.5 rounded-[2px] border-[1.5px] border-sticker-ink bg-attention-400"],
542
- title: ["mt-3 wrap-break-word normal-case", "text-headline font-bold tracking-normal"],
543
- picture: ["mt-5 h-44 w-full overflow-hidden border border-line-hairline [&_img]:size-full [&_img]:object-contain", "rounded-container bg-fill-muted"],
544
- text: [
545
- "mt-3 min-w-0 wrap-break-word [&_p+p]:mt-3",
546
- "[&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-5 [&_ol]:pl-5 [&_h1]:text-xl [&_h2]:text-lg [&_h3]:text-base [&_h1]:font-bold [&_h2]:font-bold [&_h3]:font-bold [&_pre]:max-w-full [&_pre]:overflow-x-auto",
547
- "text-subhead text-foreground-muted"
548
- ],
549
- footer: ["flex shrink-0 flex-wrap items-center justify-between gap-3 border-line-hairline py-4 @max-[28rem]/announcement:flex-col", "border-t-0 px-5 pt-0 pb-5"],
550
- action: ["ml-auto h-auto min-h-8 min-w-0 max-w-full py-1 whitespace-normal text-center font-bold @max-[28rem]/announcement:ml-0 @max-[28rem]/announcement:w-full"],
551
- secondary: ["min-w-0 max-w-full whitespace-normal @max-[28rem]/announcement:w-full"]
552
- } });
553
- function AnnouncementDialogContent({ className, initialFocus, ref, ...props }) {
554
- const localRef = useRef(null);
555
- const setRef = useCallback((node) => {
556
- localRef.current = node;
557
- if (typeof ref === "function") return ref(node);
558
- if (ref) ref.current = node;
559
- }, [ref]);
560
- const focusAction = useCallback(() => localRef.current?.querySelector("[data-announcement-action]:not(:disabled)") ?? true, []);
561
- const { content } = announcement();
562
- return /* @__PURE__ */ jsx(DialogContent, {
563
- ref: setRef,
564
- initialFocus: initialFocus ?? focusAction,
565
- className: content({ className }),
566
- ...props
567
- });
568
- }
569
- function AnnouncementDialogBody({ className, ...props }) {
570
- const { body } = announcement();
571
- return /* @__PURE__ */ jsx("div", {
572
- "data-slot": "announcement-body",
573
- className: body({ className }),
574
- ...props
575
- });
576
- }
577
- function AnnouncementDialogHeader({ className, ...props }) {
578
- const { header } = announcement();
579
- return /* @__PURE__ */ jsx("div", {
580
- "data-slot": "announcement-header",
581
- className: header({ className }),
582
- ...props
583
- });
584
- }
585
- function AnnouncementDialogLabel({ className, children, ...props }) {
586
- const { label, dot } = announcement();
587
- return /* @__PURE__ */ jsxs("div", {
588
- "data-slot": "announcement-label",
589
- className: label({ className }),
590
- ...props,
591
- children: [/* @__PURE__ */ jsx("span", {
592
- "aria-hidden": "true",
593
- className: dot()
594
- }), children]
595
- });
596
- }
597
- function AnnouncementDialogTitle({ className, ...props }) {
598
- const { title } = announcement();
599
- return /* @__PURE__ */ jsx(DialogTitle, {
600
- className: title({ className }),
601
- ...props
602
- });
603
- }
604
- function AnnouncementDialogPicture({ className, ...props }) {
605
- const { picture } = announcement();
606
- return /* @__PURE__ */ jsx("div", {
607
- "data-slot": "announcement-picture",
608
- className: picture({ className }),
609
- ...props
610
- });
611
- }
612
- function AnnouncementDialogText({ className, ...props }) {
613
- const { text } = announcement();
614
- return /* @__PURE__ */ jsx("div", {
615
- "data-slot": "announcement-text",
616
- className: text({ className }),
617
- ...props
618
- });
619
- }
620
- function AnnouncementDialogFooter({ className, ...props }) {
621
- const { footer } = announcement();
622
- return /* @__PURE__ */ jsx("div", {
623
- "data-slot": "announcement-footer",
624
- className: footer({ className }),
625
- ...props
626
- });
627
- }
628
- function AnnouncementDialogAction({ className, ...props }) {
629
- const { action } = announcement();
630
- return /* @__PURE__ */ jsx(Button$1, {
631
- variant: "accent",
632
- "data-announcement-action": true,
633
- className: action({ className }),
634
- ...props
635
- });
636
- }
637
- function AnnouncementDialogSecondaryAction({ className, ...props }) {
638
- const { secondary } = announcement();
639
- return /* @__PURE__ */ jsx(Button$1, {
640
- variant: "ghost",
641
- className: secondary({ className }),
642
- ...props
643
- });
644
- }
645
- //#endregion
646
- //#region src/components/comment/comment.recipe.ts
647
- const comment = tv({
648
- slots: {
649
- margin: ["relative min-w-0"],
650
- marginItem: [
651
- "absolute inset-x-0 top-0",
652
- "data-animate:transition-transform data-animate:duration-150 data-animate:ease-out",
653
- "motion-reduce:transition-none",
654
- "data-active:z-10"
655
- ],
656
- card: [
657
- "relative flex min-w-0 flex-col gap-2",
658
- "rounded-container border border-line-hairline p-3",
659
- "bg-layer-card text-foreground shadow-none outline-none",
660
- "data-active:border-[1.5px] data-active:border-line-strong data-active:p-[11.5px] data-active:shadow-float"
661
- ],
662
- cardTrigger: [
663
- "absolute -inset-px z-10 cursor-pointer",
664
- "rounded-container border-0 bg-transparent p-0",
665
- "hover:bg-fill-subtle active:bg-fill-medium",
666
- "outline-none focus-visible:shadow-focus"
667
- ],
668
- cardQuote: ["sr-only"],
669
- cardMeta: [
670
- "flex min-w-0 items-center gap-1",
671
- "font-sans text-caption text-foreground-hint",
672
- "[&_svg]:size-3.5 [&_svg]:shrink-0",
673
- "[button&]:enabled:cursor-pointer [button&]:border-0 [button&]:bg-transparent [button&]:p-0 [button&]:text-start",
674
- "[button&]:decoration-[1.5px] [button&]:underline-offset-[3px] [button&]:enabled:hover:underline",
675
- "[button&]:rounded-chip [button&]:outline-none [button&]:focus-visible:shadow-focus"
676
- ],
677
- entry: ["flex min-w-0 flex-col gap-1", "[[data-slot=comment-entry]+&]:mt-1"],
678
- entryHeader: ["flex min-w-0 items-center gap-1.5", "*:data-[slot=avatar]:me-0.5"],
679
- entryAuthor: ["min-w-0 truncate", "text-subhead font-bold text-foreground"],
680
- entryTime: ["shrink-0 whitespace-nowrap", "font-mono text-mono-meta tabular-nums text-foreground-hint"],
681
- entryBody: [
682
- "wrap-break-word select-text",
683
- "text-callout text-foreground",
684
- "selection:bg-selection"
685
- ],
686
- marker: [
687
- "relative inline-flex h-6 shrink-0 items-center gap-1 align-top whitespace-nowrap",
688
- "rounded-chip border border-line-hairline py-0 ps-[3px] pe-1.5",
689
- "font-mono text-mono-meta font-bold tabular-nums text-foreground",
690
- "bg-layer-card [--avatar-group-surface:var(--layer-card)]",
691
- "cursor-pointer hover:overlay-medium active:overlay-medium",
692
- "data-active:border-[1.5px] data-active:border-line-strong data-active:bg-brand-soft data-active:ps-[2.5px] data-active:pe-[5.5px]",
693
- "data-popup-open:border-[1.5px] data-popup-open:border-line-strong data-popup-open:bg-brand-soft data-popup-open:ps-[2.5px] data-popup-open:pe-[5.5px]",
694
- "data-active:[--avatar-group-surface:var(--brand-soft)] data-popup-open:[--avatar-group-surface:var(--brand-soft)]",
695
- "outline-none focus-visible:shadow-focus",
696
- "after:absolute after:-inset-x-1 after:-inset-y-2.5"
697
- ]
698
- },
699
- variants: { clamp: { true: { entryBody: ["line-clamp-2"] } } }
700
- });
701
- //#endregion
702
- //#region src/components/comment/comment-card.tsx
703
- /**
704
- * The card of one discussion: its comments, a line such as "2 replies", and
705
- * the reply box when it is open. A `div`; which parts it holds (collapsed or
706
- * open) is the caller's choice. To open it with a click, add
707
- * `CommentCardTrigger`.
708
- */
709
- function CommentCard({ className, render, active = false, ...props }) {
710
- const { card } = comment();
711
- const defaultProps = {
712
- "data-slot": "comment-card",
713
- "data-active": active ? "" : void 0,
714
- "aria-current": active ? "true" : void 0,
715
- className: card({ className })
716
- };
717
- return useRender({
718
- defaultTagName: "div",
719
- render,
720
- props: mergeProps(defaultProps, props)
721
- });
722
- }
723
- /**
724
- * A button laid over the whole card, for a collapsed card that opens when it
725
- * is clicked or chosen with the keyboard. It has no text of its own: give it
726
- * an `aria-label` ("Comment from Martin").
727
- */
728
- function CommentCardTrigger({ className, render, ...props }) {
729
- const { cardTrigger } = comment();
730
- const defaultProps = {
731
- "data-slot": "comment-card-trigger",
732
- type: "button",
733
- className: cardTrigger({ className })
734
- };
735
- return useRender({
736
- defaultTagName: "button",
737
- render,
738
- props: mergeProps(defaultProps, props)
739
- });
740
- }
741
- /**
742
- * A short line about the discussion, in the caller's words: how many replies
743
- * it has, that it is about the whole document, or that the passage it was on
744
- * can no longer be found. When choosing it does something (makes this the
745
- * discussion being read), render it as a button,
746
- * `render={<button type="button" />}`: it then underlines on hover, as
747
- * clickable text, and takes the focus ring.
748
- */
749
- function CommentCardMeta({ className, render, ...props }) {
750
- const { cardMeta } = comment();
751
- const defaultProps = {
752
- "data-slot": "comment-card-meta",
753
- className: cardMeta({ className })
754
- };
755
- return useRender({
756
- defaultTagName: "div",
757
- render,
758
- props: mergeProps(defaultProps, props)
759
- });
760
- }
761
- /**
762
- * The passage the discussion is about, in the caller's words (usually the
763
- * quoted text). Hidden on screen, where the card's place beside the
764
- * highlighted passage says it; give it an `id` and point the
765
- * `aria-describedby` of `CommentCardTrigger` or `CommentMarker` at it, so a
766
- * screen reader says which passage a card is about.
767
- */
768
- function CommentCardQuote({ className, render, ...props }) {
769
- const { cardQuote } = comment();
770
- const defaultProps = {
771
- "data-slot": "comment-card-quote",
772
- className: cardQuote({ className })
773
- };
774
- return useRender({
775
- defaultTagName: "span",
776
- render,
777
- props: mergeProps(defaultProps, props)
778
- });
779
- }
780
- //#endregion
781
- //#region src/components/comment/comment-entry.tsx
782
- /** One comment in a discussion: its header, then its text. */
783
- function CommentEntry({ className, render, ...props }) {
784
- const { entry } = comment();
785
- const defaultProps = {
786
- "data-slot": "comment-entry",
787
- className: entry({ className })
788
- };
789
- return useRender({
790
- defaultTagName: "div",
791
- render,
792
- props: mergeProps(defaultProps, props)
793
- });
794
- }
795
- /** One line: the author's `Avatar` (`size="xs"`), `CommentEntryAuthor`, then `CommentEntryTime`. */
796
- function CommentEntryHeader({ className, render, ...props }) {
797
- const { entryHeader } = comment();
798
- const defaultProps = {
799
- "data-slot": "comment-entry-header",
800
- className: entryHeader({ className })
801
- };
802
- return useRender({
803
- defaultTagName: "div",
804
- render,
805
- props: mergeProps(defaultProps, props)
806
- });
807
- }
808
- /** Who wrote the comment, in bold. It truncates; the time after it doesn't. */
809
- function CommentEntryAuthor({ className, render, ...props }) {
810
- const { entryAuthor } = comment();
811
- const defaultProps = {
812
- "data-slot": "comment-entry-author",
813
- className: entryAuthor({ className })
814
- };
815
- return useRender({
816
- defaultTagName: "span",
817
- render,
818
- props: mergeProps(defaultProps, props)
819
- });
820
- }
821
- /**
822
- * When the comment was written, right after the author. A `<time>`: pass the
823
- * moment in `dateTime` (ISO 8601).
824
- */
825
- function CommentEntryTime({ className, render, ...props }) {
826
- const { entryTime } = comment();
827
- const defaultProps = {
828
- "data-slot": "comment-entry-time",
829
- className: entryTime({ className })
830
- };
831
- return useRender({
832
- defaultTagName: "time",
833
- render,
834
- props: mergeProps(defaultProps, props)
835
- });
836
- }
837
- /** What the comment says. */
838
- function CommentEntryBody({ className, render, clamp = false, ...props }) {
839
- const { entryBody } = comment({ clamp });
840
- const defaultProps = {
841
- "data-slot": "comment-entry-body",
842
- "data-clamp": clamp ? "" : void 0,
843
- className: entryBody({ className })
844
- };
845
- return useRender({
846
- defaultTagName: "div",
847
- render,
848
- props: mergeProps(defaultProps, props)
849
- });
850
- }
851
- //#endregion
852
- //#region src/components/comment/comment-highlight.ts
853
- /** The highlight of every passage that has comments (`::highlight(raft-comment)` in styles.css). */
854
- const COMMENT_HIGHLIGHT = "raft-comment";
855
- /** The highlight of the passage of the open discussion (`::highlight(raft-comment-active)`). */
856
- const COMMENT_HIGHLIGHT_ACTIVE = "raft-comment-active";
857
- /**
858
- * Highlights the passages comments are on with the CSS Custom Highlight API,
859
- * so the document's DOM is never changed: no `<mark>` is wrapped around its
860
- * text. Register the ranges of every anchored comment under
861
- * `COMMENT_HIGHLIGHT`, and the open discussion's under
862
- * `COMMENT_HIGHLIGHT_ACTIVE`, which paints over it. Passing no ranges clears
863
- * the name. Returns a function that clears it.
864
- *
865
- * Where the browser has no `CSS.highlights`, it does nothing: the passages are
866
- * not highlighted, and the cards still say where they belong.
867
- */
868
- function setCommentHighlights(name, ranges) {
869
- const registry = typeof CSS === "undefined" ? void 0 : CSS.highlights;
870
- if (!registry || typeof Highlight === "undefined") return () => {};
871
- const list = [...ranges];
872
- if (list.length === 0) {
873
- registry.delete(name);
874
- return () => {};
875
- }
876
- const highlight = new Highlight(...list);
877
- if (name === "raft-comment-active") highlight.priority = 1;
878
- registry.set(name, highlight);
879
- return () => {
880
- if (registry.get(name) === highlight) registry.delete(name);
881
- };
882
- }
883
- //#endregion
884
- //#region src/components/comment/comment-margin-layout.ts
885
- /**
886
- * Places the cards of a comment margin the way Google Docs does. Cards keep
887
- * document order (by their anchor's top; equal tops keep the order given) and
888
- * never overlap: each sits at its anchor, or `gap` below the card before it.
889
- * The active card is pinned to its anchor instead, the cards above it are
890
- * pushed up to make room and the cards below it pushed down. Nothing goes
891
- * above 0: when the cards above the active one don't fit between 0 and its
892
- * anchor, they stack from 0 and the active card follows them.
893
- */
894
- function resolveCommentMarginLayout(items, { gap = 8, activeId = null } = {}) {
895
- const order = items.map((item, index) => ({
896
- item,
897
- index
898
- })).sort((a, b) => a.item.top - b.item.top || a.index - b.index).map(({ item }) => item);
899
- const tops = order.map((item) => item.top);
900
- const stackDown = (from, floor) => {
901
- let next = floor;
902
- for (let i = from; i < order.length; i += 1) {
903
- tops[i] = Math.max(order[i].top, next);
904
- next = tops[i] + order[i].height + gap;
905
- }
906
- };
907
- const pinned = activeId == null ? -1 : order.findIndex((item) => item.id === activeId);
908
- if (pinned < 0) stackDown(0, 0);
909
- else {
910
- tops[pinned] = Math.max(order[pinned].top, 0);
911
- for (let i = pinned - 1; i >= 0; i -= 1) tops[i] = Math.min(order[i].top, tops[i + 1] - gap - order[i].height);
912
- if (pinned > 0 && tops[0] < 0) stackDown(0, 0);
913
- else stackDown(pinned + 1, tops[pinned] + order[pinned].height + gap);
914
- }
915
- const result = {};
916
- let height = 0;
917
- order.forEach((item, i) => {
918
- result[item.id] = tops[i];
919
- height = Math.max(height, tops[i] + item.height);
920
- });
921
- return {
922
- tops: result,
923
- height
924
- };
925
- }
926
- //#endregion
927
- //#region src/components/comment/comment-margin.tsx
928
- const CommentMarginContext = createContext(null);
929
- function useCommentMargin() {
930
- const context = use(CommentMarginContext);
931
- if (!context) throw new Error("CommentMarginItem must be used within CommentMargin.");
932
- return context;
933
- }
934
- const heightOf = (element) => element.getBoundingClientRect().height;
935
- /**
936
- * The column beside a document that holds its comment cards, each level with
937
- * the passage it is about. It measures its `CommentMarginItem`s with one
938
- * `ResizeObserver`, resolves their collisions with
939
- * `resolveCommentMarginLayout`, and takes the height the lowest card needs.
940
- * Place it beside the document so that its top is the top the items' `top`
941
- * is measured from.
942
- */
943
- function CommentMargin({ className, render, style, gap = 8, ...props }) {
944
- const { margin } = comment();
945
- const [entries, setEntries] = useState(() => /* @__PURE__ */ new Map());
946
- const [animate, setAnimate] = useState(false);
947
- const observer = useRef(null);
948
- const observed = useRef(/* @__PURE__ */ new Map());
949
- const patch = useCallback((id, next) => {
950
- setEntries((current) => {
951
- const previous = current.get(id);
952
- if (next === null) {
953
- if (!previous) return current;
954
- const without = new Map(current);
955
- without.delete(id);
956
- return without;
957
- }
958
- if (!previous && next.top === void 0) return current;
959
- const merged = {
960
- top: 0,
961
- height: 0,
962
- active: false,
963
- ...previous,
964
- ...next
965
- };
966
- if (previous && previous.top === merged.top && previous.height === merged.height && previous.active === merged.active) return current;
967
- const updated = new Map(current);
968
- updated.set(id, merged);
969
- return updated;
970
- });
971
- }, []);
972
- const place = useCallback((id, placement) => patch(id, placement), [patch]);
973
- const observe = useCallback((id, element) => {
974
- observer.current ??= new ResizeObserver((records) => {
975
- for (const record of records) {
976
- const target = observed.current.get(record.target);
977
- if (target) patch(target, { height: heightOf(record.target) });
978
- }
979
- });
980
- observed.current.set(element, id);
981
- patch(id, { height: heightOf(element) });
982
- observer.current.observe(element);
983
- return () => {
984
- observer.current?.unobserve(element);
985
- observed.current.delete(element);
986
- };
987
- }, [patch]);
988
- useEffect(() => () => observer.current?.disconnect(), []);
989
- const layout = useMemo(() => {
990
- const items = [...entries].map(([id, { top, height }]) => ({
991
- id,
992
- top,
993
- height
994
- }));
995
- const activeId = [...entries].find(([, entry]) => entry.active)?.[0];
996
- return resolveCommentMarginLayout(items, {
997
- gap,
998
- activeId
999
- });
1000
- }, [entries, gap]);
1001
- const measured = entries.size > 0;
1002
- useEffect(() => {
1003
- if (!measured) return;
1004
- const frame = requestAnimationFrame(() => setAnimate(true));
1005
- return () => cancelAnimationFrame(frame);
1006
- }, [measured]);
1007
- const context = useMemo(() => ({
1008
- tops: layout.tops,
1009
- animate,
1010
- place,
1011
- observe
1012
- }), [
1013
- layout.tops,
1014
- animate,
1015
- place,
1016
- observe
1017
- ]);
1018
- const defaultProps = {
1019
- "data-slot": "comment-margin",
1020
- className: margin({ className }),
1021
- style: {
1022
- minHeight: layout.height,
1023
- ...style
1024
- }
1025
- };
1026
- const element = useRender({
1027
- defaultTagName: "div",
1028
- render,
1029
- props: mergeProps(defaultProps, props)
1030
- });
1031
- return /* @__PURE__ */ jsx(CommentMarginContext, {
1032
- value: context,
1033
- children: element
1034
- });
1035
- }
1036
- /**
1037
- * One card's place in a `CommentMargin`. It sits at `top`, or lower when the
1038
- * card above it is in the way; the active one stays at `top` and the others
1039
- * make room. Items with the same `top` keep the order they were first
1040
- * rendered in. The resolved top is `data-top`, applied as a `translateY`.
1041
- */
1042
- function CommentMarginItem({ className, render, style, top, active = false, ...props }) {
1043
- const { marginItem } = comment();
1044
- const { tops, animate, place, observe } = useCommentMargin();
1045
- const id = useId();
1046
- const [element, setElement] = useState(null);
1047
- useLayoutEffect(() => {
1048
- place(id, {
1049
- top,
1050
- active
1051
- });
1052
- }, [
1053
- place,
1054
- id,
1055
- top,
1056
- active
1057
- ]);
1058
- useLayoutEffect(() => element ? observe(id, element) : void 0, [
1059
- observe,
1060
- id,
1061
- element
1062
- ]);
1063
- useLayoutEffect(() => () => place(id, null), [place, id]);
1064
- const resolved = tops[id] ?? Math.max(top, 0);
1065
- const defaultProps = {
1066
- "data-slot": "comment-margin-item",
1067
- "data-top": String(resolved),
1068
- "data-active": active ? "" : void 0,
1069
- "data-animate": animate ? "" : void 0,
1070
- className: marginItem({ className }),
1071
- style: {
1072
- ...style,
1073
- transform: `translateY(${resolved}px)`
1074
- }
1075
- };
1076
- return useRender({
1077
- defaultTagName: "div",
1078
- render,
1079
- ref: setElement,
1080
- props: mergeProps(defaultProps, props)
1081
- });
1082
- }
1083
- //#endregion
1084
- //#region src/components/comment/comment-marker.tsx
1085
- /**
1086
- * Where there is no room for a margin: a small button beside a passage that
1087
- * has comments, with the authors' avatars (an `AvatarGroup` of up to 3
1088
- * `size="2xs"` avatars) and the count. It opens the discussion, in a bottom
1089
- * sheet. Give it an `aria-label` that starts with the count ("2 comments").
1090
- */
1091
- function CommentMarker({ className, render, nativeButton, active = false, ...props }) {
1092
- const { marker } = comment();
1093
- return /* @__PURE__ */ jsx(Button, {
1094
- ...props,
1095
- render,
1096
- nativeButton: nativeButton ?? defaultNativeButton(render),
1097
- "data-slot": "comment-marker",
1098
- "data-active": active ? "" : void 0,
1099
- className: marker({ className })
1100
- });
1101
- }
1102
- //#endregion
1103
- //#region src/lib/desktop-media.ts
1104
- /**
1105
- * Where the desktop layout and its metrics begin: from 768px, with a mouse or
1106
- * a trackpad (design/foundations/spacing.md#hit-targets). The same query as
1107
- * the desktop blocks in styles.css and foundation.css, for code that has to
1108
- * decide in JavaScript what CSS decides with `md:pointer-fine:`.
1109
- */
1110
- const DESKTOP_MEDIA_QUERY = "(width >= 48rem) and (pointer: fine)";
1111
- //#endregion
1112
- //#region src/components/comment/use-comment-margin-fits.ts
1113
- function subscribeDesktop(onChange) {
1114
- if (typeof window === "undefined") return () => void 0;
1115
- const media = window.matchMedia(DESKTOP_MEDIA_QUERY);
1116
- media.addEventListener("change", onChange);
1117
- return () => media.removeEventListener("change", onChange);
1118
- }
1119
- const getDesktop = () => typeof window !== "undefined" && window.matchMedia("(width >= 48rem) and (pointer: fine)").matches;
1120
- /**
1121
- * Whether a viewer has room for a comment margin beside its document: true
1122
- * only on a desktop (from 768px with a mouse or a trackpad, the query the
1123
- * desktop metrics use) and while `element`, the viewer that holds the
1124
- * document and the margin, is at least `minWidth` wide. Otherwise show
1125
- * `CommentMarker`s and open discussions in a bottom sheet. Cards and markers
1126
- * are different trees, so this is measured in JavaScript (a
1127
- * `ResizeObserver` on the element), not decided by a container query.
1128
- */
1129
- function useCommentMarginFits(element, { minWidth = 784 } = {}) {
1130
- const desktop = useSyncExternalStore(subscribeDesktop, getDesktop, () => false);
1131
- const [width, setWidth] = useState(0);
1132
- useLayoutEffect(() => {
1133
- if (!element) return;
1134
- const measure = () => setWidth(element.getBoundingClientRect().width);
1135
- measure();
1136
- const observer = new ResizeObserver(measure);
1137
- observer.observe(element);
1138
- return () => observer.disconnect();
1139
- }, [element]);
1140
- return desktop && element !== null && width >= minWidth;
1141
- }
1142
- //#endregion
1143
- //#region src/components/mode-bar/mode-bar.tsx
1144
- /**
1145
- * A dark strip across a panel, under its head, while the panel is in a mode
1146
- * people entered and leave on purpose (commenting on an Artifact). It is the
1147
- * HUD surface, not an intent tint: nothing is wrong, so it is not a notice;
1148
- * the dark fill says "the panel works differently now" until the mode ends.
1149
- * Its edges and height for one line are PanelNotice's, so the two strips are
1150
- * one size under a head.
1151
- */
1152
- const modeBar = tv({ slots: {
1153
- root: [
1154
- "flex shrink-0 items-start",
1155
- "min-h-10 gap-4 px-5 py-2.5 max-md:px-4",
1156
- "bg-layer-hud font-sans text-callout text-foreground-on-hud"
1157
- ],
1158
- content: ["min-w-0 flex-1 wrap-break-word"],
1159
- title: ["font-bold"],
1160
- description: ["text-foreground-on-hud-muted"],
1161
- action: [
1162
- "relative inline-flex shrink-0 items-center whitespace-nowrap select-none",
1163
- "border-0 bg-transparent p-0",
1164
- ...hudTextAction,
1165
- "disabled:pointer-events-none disabled:opacity-40"
1166
- ]
1167
- } });
1168
- /**
1169
- * The strip. Put `ModeBarContent` in it, then the `ModeBarAction` that ends
1170
- * the mode.
1171
- */
1172
- function ModeBar({ className, render, ...props }) {
1173
- const { root } = modeBar();
1174
- const defaultProps = {
1175
- "data-slot": "mode-bar",
1176
- className: root({ className })
1177
- };
1178
- return useRender({
1179
- defaultTagName: "div",
1180
- render,
1181
- props: mergeProps(defaultProps, props)
1182
- });
1183
- }
1184
- /**
1185
- * The words: `ModeBarTitle`, then `ModeBarDescription`, on one line that
1186
- * wraps. A polite live region, so a change to the title (a draft count) is
1187
- * read out; the action stays outside it.
1188
- */
1189
- function ModeBarContent({ className, render, ...props }) {
1190
- const { content } = modeBar();
1191
- const defaultProps = {
1192
- "data-slot": "mode-bar-content",
1193
- role: "status",
1194
- className: content({ className })
1195
- };
1196
- return useRender({
1197
- defaultTagName: "div",
1198
- render,
1199
- props: mergeProps(defaultProps, props)
1200
- });
1201
- }
1202
- /** The mode and its state, bold in the HUD ink: "Commenting · 1 draft." */
1203
- function ModeBarTitle({ className, render, ...props }) {
1204
- const { title } = modeBar();
1205
- const defaultProps = {
1206
- "data-slot": "mode-bar-title",
1207
- className: title({ className })
1208
- };
1209
- return useRender({
1210
- defaultTagName: "span",
1211
- render,
1212
- props: mergeProps(defaultProps, props)
1213
- });
1214
- }
1215
- /**
1216
- * What to do in the mode, in the muted HUD ink, after the title on the same
1217
- * line. It brings the space before it, so the two read and copy as one
1218
- * sentence.
1219
- */
1220
- function ModeBarDescription({ className, render, children, ...props }) {
1221
- const { description } = modeBar();
1222
- const defaultProps = {
1223
- "data-slot": "mode-bar-description",
1224
- className: description({ className }),
1225
- children: /* @__PURE__ */ jsxs(Fragment$1, { children: [" ", children] })
1226
- };
1227
- return useRender({
1228
- defaultTagName: "span",
1229
- render,
1230
- props: mergeProps(defaultProps, props)
1231
- });
1232
- }
1233
- /**
1234
- * The text action that ends the mode (Done), at the end of the first line. A
1235
- * real `button`; on the HUD it is a text action, like a toast's, never a
1236
- * Button.
1237
- */
1238
- function ModeBarAction({ className, render, ...props }) {
1239
- const { action } = modeBar();
1240
- const defaultProps = {
1241
- "data-slot": "mode-bar-action",
1242
- type: "button",
1243
- className: action({ className })
1244
- };
1245
- return useRender({
1246
- defaultTagName: "button",
1247
- render,
1248
- props: mergeProps(defaultProps, props)
1249
- });
1250
- }
1251
- //#endregion
1252
- //#region src/components/drag/drag-lists.tsx
1253
- /** dnd-kit's vertical strategy, except that nothing moves while the hand is outside the list. */
1254
- const dragListStrategy = (args) => args.overIndex < 0 ? null : verticalListSortingStrategy(args);
1255
- /**
1256
- * Items in one or more lists that can be reordered and moved between lists
1257
- * (a sidebar's sections, a board's columns): the lists half of the drag
1258
- * model (design/patterns/drag-and-drop.md#where-it-will-land). While dragging,
1259
- * the item stands at its landing spot (its own slot is the place; siblings
1260
- * slide apart), a list can refuse it, and every step is announced by name.
1261
- * The keyboard is measured from the item's real place, so the arrows never
1262
- * skip one after a move into another list.
1263
- *
1264
- * Spread `handlers`, `collisionDetection` and `accessibility` on the
1265
- * `DndContext`, pass `coordinateGetter` to `useDragSensors`, and render
1266
- * `DragRemeasure version={items}` inside it.
1267
- */
1268
- function useDragLists({ lists, items, onItemsChange, listName, itemName, refuse, collisionDetection, announce, messages }) {
1269
- const say = useMemo(() => dragMessages(messages), [messages]);
1270
- const across = useDragAcrossLists(collisionDetection);
1271
- const itemsRef = useRef(items);
1272
- useLayoutEffect(() => {
1273
- itemsRef.current = items;
1274
- }, [items]);
1275
- const [activeId, setActiveId] = useState(null);
1276
- const [originList, setOriginList] = useState(null);
1277
- const [refusal, setRefusalState] = useState(null);
1278
- const refusalRef = useRef(refusal);
1279
- const setRefusal = (next) => {
1280
- refusalRef.current = next;
1281
- setRefusalState(next);
1282
- };
1283
- const origin = useRef(null);
1284
- const inserted = useRef(null);
1285
- const listOf = useCallback((state, id) => lists.includes(id) ? id : lists.find((list) => state[list].includes(String(id))) ?? null, [lists]);
1286
- const isList = (id) => lists.includes(id);
1287
- const where = (state, id) => {
1288
- const list = listOf(state, id);
1289
- return {
1290
- place: listName(list),
1291
- position: state[list].indexOf(id) + 1,
1292
- count: state[list].length
1293
- };
1294
- };
1295
- const update = (next) => {
1296
- itemsRef.current = next;
1297
- onItemsChange(next);
1298
- };
1299
- /** Still home: in its own list, at its own index (pass the sortable's projected index). */
1300
- const isHome = useCallback((id, index) => {
1301
- const start = origin.current;
1302
- return start != null && listOf(itemsRef.current, id) === start.list && index === start.index;
1303
- }, [listOf]);
1304
- const onDragStart = ({ active, activatorEvent }) => {
1305
- const id = String(active.id);
1306
- const list = listOf(items, id);
1307
- if (!list) return;
1308
- origin.current = {
1309
- list,
1310
- index: items[list].indexOf(id),
1311
- snapshot: items,
1312
- keyboard: typeof KeyboardEvent !== "undefined" && activatorEvent instanceof KeyboardEvent
1313
- };
1314
- inserted.current = null;
1315
- setActiveId(id);
1316
- setOriginList(list);
1317
- announce(say.pickedUp({
1318
- name: itemName(id),
1319
- ...where(items, id)
1320
- }));
1321
- };
1322
- const onDragOver = ({ active, over }) => {
1323
- const start = origin.current;
1324
- if (!start) return;
1325
- const id = String(active.id);
1326
- if (!over) {
1327
- if (refusalRef.current) setRefusal(null);
1328
- announce(say.notOver({ name: itemName(id) }));
1329
- return;
1330
- }
1331
- const current = itemsRef.current;
1332
- const from = listOf(current, id);
1333
- const to = listOf(current, over.id);
1334
- if (!from || !to) return;
1335
- const reason = refuse?.(start.list, to, id) ?? null;
1336
- if (reason) {
1337
- setRefusal({
1338
- list: to,
1339
- reason
1340
- });
1341
- update(start.snapshot);
1342
- inserted.current = null;
1343
- announce(say.refused({
1344
- name: itemName(id),
1345
- place: listName(to),
1346
- reason
1347
- }));
1348
- return;
1349
- }
1350
- if (refusalRef.current) setRefusal(null);
1351
- if (from !== to) {
1352
- const fromList = current[from].filter((item) => item !== id);
1353
- const toList = [...current[to]];
1354
- let at = toList.indexOf(String(over.id));
1355
- if (at < 0) at = toList.length;
1356
- else if (start.keyboard) {
1357
- if (lists.indexOf(from) > lists.indexOf(to)) at += 1;
1358
- } else {
1359
- const rect = active.rect.current.translated;
1360
- if (rect && rect.top + rect.height / 2 > over.rect.top + over.rect.height / 2) at += 1;
1361
- }
1362
- toList.splice(at, 0, id);
1363
- const next = {
1364
- ...current,
1365
- [from]: fromList,
1366
- [to]: toList
1367
- };
1368
- across.moved(start.keyboard);
1369
- update(next);
1370
- inserted.current = isList(over.id) ? null : over.id;
1371
- announce(say.over({
1372
- name: itemName(id),
1373
- ...where(next, id)
1374
- }));
1375
- return;
1376
- }
1377
- if (over.id === id) {
1378
- inserted.current = null;
1379
- return;
1380
- }
1381
- const list = current[to];
1382
- if (inserted.current === over.id) {
1383
- announce(say.over({
1384
- name: itemName(id),
1385
- ...where(current, id)
1386
- }));
1387
- return;
1388
- }
1389
- inserted.current = null;
1390
- const overIndex = isList(over.id) ? list.length - 1 : list.indexOf(String(over.id));
1391
- announce(say.over({
1392
- name: itemName(id),
1393
- place: listName(to),
1394
- position: overIndex + 1,
1395
- count: list.length
1396
- }));
1397
- };
1398
- const finish = (dropped, overId) => {
1399
- const start = origin.current;
1400
- const id = activeId;
1401
- const refused = refusalRef.current;
1402
- origin.current = null;
1403
- across.reset();
1404
- setActiveId(null);
1405
- setOriginList(null);
1406
- setRefusal(null);
1407
- if (!start || !id) return;
1408
- if (!dropped || overId == null || refused) {
1409
- update(start.snapshot);
1410
- const home = {
1411
- name: itemName(id),
1412
- ...where(start.snapshot, id)
1413
- };
1414
- announce(refused && dropped ? say.refusedDrop({
1415
- ...home,
1416
- reason: refused.reason
1417
- }) : say.cancelled(home));
1418
- return;
1419
- }
1420
- const current = itemsRef.current;
1421
- const list = listOf(current, id);
1422
- const ids = current[list];
1423
- const fromIndex = ids.indexOf(id);
1424
- const toIndex = inserted.current === overId ? fromIndex : isList(overId) ? ids.length - 1 : ids.indexOf(String(overId));
1425
- const next = toIndex >= 0 && toIndex !== fromIndex ? {
1426
- ...current,
1427
- [list]: arrayMove([...ids], fromIndex, toIndex)
1428
- } : current;
1429
- update(next);
1430
- announce(say.dropped({
1431
- name: itemName(id),
1432
- ...where(next, id)
1433
- }));
1434
- };
1435
- const accessibility = useMemo(() => ({
1436
- announcements: SILENT_DRAG_ANNOUNCEMENTS,
1437
- screenReaderInstructions: dragInstructions(messages)
1438
- }), [messages]);
1439
- return {
1440
- /** The dragged item's id, or null. */
1441
- activeId,
1442
- /** The list that refuses the item under the hand, and why. */
1443
- refusal,
1444
- /** The list the drag started in, while dragging. */
1445
- originList,
1446
- isHome,
1447
- listOf,
1448
- collisionDetection: across.collisionDetection,
1449
- coordinateGetter: across.coordinateGetter,
1450
- accessibility,
1451
- handlers: {
1452
- onDragStart,
1453
- onDragOver,
1454
- onDragEnd: ({ over }) => finish(true, over?.id ?? null),
1455
- onDragCancel: () => finish(false, null)
1456
- }
1457
- };
1458
- }
1459
- /**
1460
- * Wraps each item's content in a list. The dragged item's own node is at 40%
1461
- * while it is still home, and the dashed place (its size, its radius) once it
1462
- * stands anywhere else. Every other item renders as it is: the wrapper stays
1463
- * the same, so the item keeps focus when its drag starts.
1464
- */
1465
- function DraggedSlot({ dragging = false, home, radius, className, children }) {
1466
- const place = dragging && !home;
1467
- return /* @__PURE__ */ jsxs("div", {
1468
- "data-slot": "drag-slot",
1469
- className: "relative",
1470
- children: [/* @__PURE__ */ jsx("div", {
1471
- "data-drag-origin": dragging && home ? "" : void 0,
1472
- className: place ? "invisible" : void 0,
1473
- children
1474
- }), place ? /* @__PURE__ */ jsx(DropPlaceholder, { className: cn("absolute inset-0", radius, className) }) : null]
1475
- });
1476
- }
1477
- /** The last announcement, for a polite live region (`DragLiveRegion`). Repeats are dropped. */
1478
- function useDragAnnouncer() {
1479
- const [message, setMessage] = useState("");
1480
- const last = useRef("");
1481
- return {
1482
- message,
1483
- announce: useCallback((text) => {
1484
- if (text === last.current) return;
1485
- last.current = text;
1486
- setMessage(text);
1487
- }, [])
1488
- };
1489
- }
1490
- /** A polite, visually hidden live region for drag announcements. */
1491
- function DragLiveRegion({ message }) {
1492
- return /* @__PURE__ */ jsx("span", {
1493
- className: "sr-only",
1494
- role: "status",
1495
- "aria-live": "polite",
1496
- "aria-atomic": "true",
1497
- children: message
1498
- });
1499
- }
1500
- //#endregion
1501
- //#region src/components/ellipsis/ellipsis.tsx
1502
- const ellipsis = tv({
1503
- slots: {
1504
- root: ["group/ellipsis min-w-0 max-w-full"],
1505
- text: ["min-w-0"],
1506
- content: [],
1507
- spacer: ["float-end h-[calc(100%-1lh)] w-0", "data-hidden:hidden group-data-measuring/ellipsis:hidden"],
1508
- after: ["data-hidden:hidden group-data-measuring/ellipsis:hidden"]
1509
- },
1510
- variants: {
1511
- multiline: {
1512
- true: {
1513
- root: ["flex"],
1514
- text: ["flex-1", "wrap-break-word"],
1515
- after: ["float-end clear-end ps-1"]
1516
- },
1517
- false: {
1518
- root: ["inline-flex items-baseline gap-x-1 align-bottom"],
1519
- after: ["shrink-0"]
1520
- }
1521
- },
1522
- expanded: {
1523
- true: {},
1524
- false: {}
1525
- },
1526
- clickable: {
1527
- true: { text: ["cursor-pointer"] },
1528
- false: {}
1529
- }
1530
- },
1531
- compoundVariants: [
1532
- {
1533
- multiline: false,
1534
- expanded: false,
1535
- class: { text: ["truncate", "print:overflow-visible print:whitespace-normal"] }
1536
- },
1537
- {
1538
- multiline: false,
1539
- expanded: true,
1540
- class: { text: ["wrap-anywhere whitespace-normal"] }
1541
- },
1542
- {
1543
- multiline: true,
1544
- expanded: false,
1545
- class: { text: ["line-clamp-(--ellipsis-lines)", "print:line-clamp-none"] }
1546
- },
1547
- {
1548
- multiline: true,
1549
- expanded: true,
1550
- class: { text: ["line-clamp-none"] }
1551
- }
1552
- ]
1553
- });
1554
- const EllipsisContext = createContext(null);
1555
- const INTERACTIVE = "a[href], button, input, select, textarea, summary, [tabindex], [contenteditable]:not([contenteditable='false']), [role='button'], [role='link']";
1556
- const TOUCH_MEDIA_QUERY = "(hover: none), (pointer: coarse)";
1557
- function isTouchScreen() {
1558
- return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(TOUCH_MEDIA_QUERY).matches;
1559
- }
1560
- function contains(ref, target) {
1561
- return target instanceof Node && ref.current?.contains(target) === true;
1562
- }
1563
- function hasSelection(within) {
1564
- const selection = within.ownerDocument.defaultView?.getSelection();
1565
- return !!selection && !selection.isCollapsed && selection.anchorNode != null && within.contains(selection.anchorNode);
1566
- }
1567
- function useExpanded(expandedProp, defaultExpanded, onExpandedChange) {
1568
- const [expandedState, setExpandedState] = useState(defaultExpanded);
1569
- const expanded = expandedProp ?? expandedState;
1570
- return [expanded, useCallback(() => {
1571
- const next = !expanded;
1572
- if (expandedProp === void 0) setExpandedState(next);
1573
- onExpandedChange?.(next);
1574
- }, [
1575
- expanded,
1576
- expandedProp,
1577
- onExpandedChange
1578
- ])];
1579
- }
1580
- /**
1581
- * Registers the text with the shared measurer (lib/truncation.ts) and keeps
1582
- * its answer: whether the text, alone, is cut, and whether it has a control
1583
- * inside. While it is expanded the last collapsed answer is kept, so the
1584
- * text and its control stay what they were until it collapses and is
1585
- * measured again. `layout` names what remounts or reclamps the text.
1586
- */
1587
- function useTruncation(expanded, layout, onNotTruncated) {
1588
- const rootRef = useRef(null);
1589
- const textRef = useRef(null);
1590
- const contentRef = useRef(null);
1591
- const afterRef = useRef(null);
1592
- const setTextRef = useCallback((node) => {
1593
- textRef.current = node;
1594
- }, []);
1595
- const [truncation, setTruncation] = useState(void 0);
1596
- const [interactive, setInteractive] = useState(false);
1597
- const expandedRef = useRef(expanded);
1598
- const targetRef = useRef(null);
1599
- const commit = useCallback((next) => {
1600
- setInteractive(contentRef.current?.querySelector(INTERACTIVE) != null);
1601
- if (expandedRef.current) return;
1602
- setTruncation(next);
1603
- if (next !== true) onNotTruncated();
1604
- }, [onNotTruncated]);
1605
- useLayoutEffect(() => {
1606
- expandedRef.current = expanded;
1607
- const root = rootRef.current;
1608
- const text = textRef.current;
1609
- const content = contentRef.current;
1610
- if (!root || !text || !content) return void 0;
1611
- const target = {
1612
- observe: () => [text, afterRef.current],
1613
- watch: () => content,
1614
- isolate: () => {
1615
- root.setAttribute("data-measuring", "");
1616
- return () => root.removeAttribute("data-measuring");
1617
- },
1618
- read: () => measureContent(text, content),
1619
- commit
1620
- };
1621
- targetRef.current = target;
1622
- return observeTruncation(target);
1623
- }, [
1624
- commit,
1625
- expanded,
1626
- layout
1627
- ]);
1628
- return {
1629
- rootRef,
1630
- textRef,
1631
- setTextRef,
1632
- contentRef,
1633
- afterRef,
1634
- truncation,
1635
- interactive,
1636
- measureNow: useCallback(() => {
1637
- const target = targetRef.current;
1638
- if (!target) return void 0;
1639
- const next = readNow(target);
1640
- commit(next);
1641
- return next;
1642
- }, [commit])
1643
- };
1644
- }
1645
- /** The tooltip's `onOpenChange`: it opens only on a text measured as cut, now. */
1646
- function useOpenWhenTruncated(setOpen, expanded, afterRef, measureNow) {
1647
- return useCallback((next, details) => {
1648
- if (!next) {
1649
- setOpen(false);
1650
- return;
1651
- }
1652
- if (isTouchScreen() || expanded) return;
1653
- if (contains(afterRef, details.event?.target)) return;
1654
- if (measureNow() === true) setOpen(true);
1655
- }, [
1656
- afterRef,
1657
- expanded,
1658
- measureNow,
1659
- setOpen
1660
- ]);
1661
- }
1662
- /**
1663
- * Makes the text the button that expands and collapses it, while
1664
- * `clickable`. It stays one while expanded, `aria-expanded="true"`, so focus
1665
- * stays on it and it collapses again the same way.
1666
- */
1667
- function useExpandOnClick(clickable, expanded, toggle, textRef) {
1668
- const onClick = useCallback(() => {
1669
- if (!clickable) return;
1670
- if (textRef.current && hasSelection(textRef.current)) return;
1671
- toggle();
1672
- }, [
1673
- clickable,
1674
- textRef,
1675
- toggle
1676
- ]);
1677
- const onKeyDown = useCallback((event) => {
1678
- if (!clickable || event.target !== event.currentTarget) return;
1679
- if (event.key === "Enter" || event.key === " ") {
1680
- event.preventDefault();
1681
- toggle();
1682
- }
1683
- }, [clickable, toggle]);
1684
- if (!clickable) return {};
1685
- return {
1686
- role: "button",
1687
- tabIndex: 0,
1688
- "aria-expanded": expanded,
1689
- onClick,
1690
- onKeyDown
1691
- };
1692
- }
1693
- function useRootStyle(style, lines) {
1694
- return useMemo(() => lines > 1 ? {
1695
- ...style,
1696
- ["--ellipsis-lines"]: String(lines)
1697
- } : style, [style, lines]);
1698
- }
1699
- function Ellipsis({ children, lines = 1, tooltip = true, after, expanded: expandedProp, defaultExpanded = false, onExpandedChange, expandOnClick = false, className, style, ...props }) {
1700
- const textId = useId();
1701
- const [open, setOpen] = useState(false);
1702
- const closeTooltip = useCallback(() => setOpen(false), []);
1703
- const [expanded, toggle] = useExpanded(expandedProp, defaultExpanded, onExpandedChange);
1704
- const multiline = lines > 1;
1705
- const hasTooltip = tooltip !== false && tooltip != null;
1706
- const hasAfter = after != null;
1707
- const measured = useTruncation(expanded, `${lines} ${hasAfter} ${hasTooltip}`, closeTooltip);
1708
- const { rootRef, textRef, setTextRef, contentRef, afterRef, truncation } = measured;
1709
- const handleOpenChange = useOpenWhenTruncated(setOpen, expanded, afterRef, measured.measureNow);
1710
- const truncated = truncation === true && !expanded;
1711
- const clickable = expandOnClick && !hasAfter && !measured.interactive && (truncation === true || expanded);
1712
- const afterNode = typeof after === "function" ? after({
1713
- truncated,
1714
- expanded,
1715
- toggle
1716
- }) : after;
1717
- const afterHidden = !truncated && !expanded;
1718
- const slots = ellipsis({
1719
- multiline,
1720
- expanded,
1721
- clickable
1722
- });
1723
- const context = useMemo(() => ({
1724
- expanded,
1725
- toggle,
1726
- textId
1727
- }), [
1728
- expanded,
1729
- toggle,
1730
- textId
1731
- ]);
1732
- const rootStyle = useRootStyle(style, lines);
1733
- const clickProps = useExpandOnClick(clickable, expanded, toggle, textRef);
1734
- const afterElement = hasAfter ? /* @__PURE__ */ jsx("span", {
1735
- ref: afterRef,
1736
- "data-slot": "ellipsis-after",
1737
- "data-hidden": afterHidden ? "" : void 0,
1738
- inert: afterHidden,
1739
- className: slots.after(),
1740
- onPointerEnter: closeTooltip,
1741
- children: afterNode
1742
- }) : null;
1743
- const floatingAfter = multiline && hasAfter;
1744
- const textChildren = /* @__PURE__ */ jsxs(Fragment$1, { children: [
1745
- floatingAfter && /* @__PURE__ */ jsx("span", {
1746
- "aria-hidden": true,
1747
- "data-slot": "ellipsis-spacer",
1748
- "data-hidden": afterHidden ? "" : void 0,
1749
- className: slots.spacer()
1750
- }),
1751
- floatingAfter && afterElement,
1752
- /* @__PURE__ */ jsx("span", {
1753
- ref: contentRef,
1754
- "data-slot": "ellipsis-content",
1755
- className: slots.content(),
1756
- children
1757
- })
1758
- ] });
1759
- const textProps = {
1760
- id: textId,
1761
- "data-slot": "ellipsis-text",
1762
- "data-truncated": truncated ? "" : void 0,
1763
- className: slots.text(),
1764
- ...clickProps
1765
- };
1766
- const text = hasTooltip ? /* @__PURE__ */ jsxs(Tooltip, {
1767
- open: open && truncated,
1768
- onOpenChange: handleOpenChange,
1769
- children: [/* @__PURE__ */ jsx(TooltipTrigger, {
1770
- ref: setTextRef,
1771
- render: /* @__PURE__ */ jsx("span", {}),
1772
- ...textProps,
1773
- className: slots.text({ class: "select-auto" }),
1774
- children: textChildren
1775
- }), /* @__PURE__ */ jsx(TooltipContent, { children: tooltip === true ? children : tooltip })]
1776
- }) : /* @__PURE__ */ jsx("span", {
1777
- ref: setTextRef,
1778
- ...textProps,
1779
- children: textChildren
1780
- });
1781
- return /* @__PURE__ */ jsx(EllipsisContext.Provider, {
1782
- value: context,
1783
- children: /* @__PURE__ */ jsxs("span", {
1784
- ref: rootRef,
1785
- "data-slot": "ellipsis",
1786
- className: slots.root({ className }),
1787
- style: rootStyle,
1788
- ...props,
1789
- children: [text, !multiline ? afterElement : null]
1790
- })
1791
- });
1792
- }
1793
- /**
1794
- * The control that expands an Ellipsis and collapses it again, for its
1795
- * `after`. A real button, with `aria-expanded` and `aria-controls` pointing
1796
- * at the text. The label is the caller's: "Show More" / "Show Less".
1797
- */
1798
- function EllipsisToggle({ onClick, ...props }) {
1799
- const context = useContext(EllipsisContext);
1800
- if (!context) throw new Error("EllipsisToggle must be inside an Ellipsis `after`.");
1801
- const { expanded, toggle, textId } = context;
1802
- const handleClick = useCallback((event) => {
1803
- onClick?.(event);
1804
- if (!event.defaultPrevented) toggle();
1805
- }, [onClick, toggle]);
1806
- return /* @__PURE__ */ jsx(TextLink, {
1807
- "aria-expanded": expanded,
1808
- "aria-controls": textId,
1809
- onClick: handleClick,
1810
- ...props
1811
- });
1812
- }
1813
- //#endregion
1814
- //#region src/lib/use-truncated.ts
1815
- /**
1816
- * Whether the text in the element `ref` points at is cut off by its CSS
1817
- * (`truncate`, `line-clamp`, a fixed size with `overflow: hidden`): `true`,
1818
- * `false`, or `undefined` until it is measured or while it has no box.
1819
- *
1820
- * It measures fractional sizes with a 0.5px tolerance, past either inline
1821
- * edge and below the bottom edge, so it needs no line count. It measures again
1822
- * when the element changes size, when its text changes and when a web font
1823
- * finishes loading, in one batched frame for every measured element on the
1824
- * page (lib/truncation.ts), before that frame paints.
1825
- *
1826
- * The element is read when the component mounts: keep `ref` on one element
1827
- * for the component's life, or give the component a `key` that changes with
1828
- * the element.
1829
- */
1830
- function useTruncated(ref) {
1831
- const [truncated, setTruncated] = useState(void 0);
1832
- useLayoutEffect(() => {
1833
- const element = ref.current;
1834
- if (!element) return void 0;
1835
- return observeTruncation({
1836
- observe: () => [element],
1837
- watch: () => element,
1838
- read: () => measureElement(element),
1839
- commit: setTruncated
1840
- });
1841
- }, [ref]);
1842
- return truncated;
1843
- }
1844
- //#endregion
1845
- //#region src/components/inbox-item/inbox-item-leading-badge.tsx
1846
- /**
1847
- * A small badge in the bottom-right corner of `InboxItemLeading`, the corner a
1848
- * presence light takes: on a thread row, the thread mark, on the channel's
1849
- * `IconTile` or on the direct message's `Avatar` alike. Put it inside
1850
- * `InboxItemLeading`, after the tile or avatar, with one icon as its child;
1851
- * leave the avatar's `AvatarBadge` out, since the corner holds one mark. It
1852
- * is hidden from assistive technology unless given a `role`.
1853
- */
1854
- function InboxItemLeadingBadge({ className, render, ...props }) {
1855
- const { itemLeadingBadge } = inboxItem();
1856
- const defaultProps = {
1857
- "data-slot": "inbox-item-leading-badge",
1858
- "aria-hidden": props.role ? void 0 : true,
1859
- className: itemLeadingBadge({ className })
1860
- };
1861
- return useRender({
1862
- defaultTagName: "span",
1863
- render,
1864
- props: mergeProps(defaultProps, props)
1865
- });
1866
- }
1867
- //#endregion
1868
- //#region src/components/inbox-item/inbox-item-skeleton.recipe.ts
1869
- const inboxItemSkeleton = tv({ slots: {
1870
- list: ["flex min-w-0 flex-col gap-0.5"],
1871
- row: [
1872
- "[--inbox-item-skeleton-context:5rem] [--inbox-item-skeleton-title:60%] [--inbox-item-skeleton-body:80%]",
1873
- "nth-[3n+2]:[--inbox-item-skeleton-context:4rem] nth-[3n+2]:[--inbox-item-skeleton-title:45%] nth-[3n+2]:[--inbox-item-skeleton-body:70%]",
1874
- "nth-[3n]:[--inbox-item-skeleton-context:6rem] nth-[3n]:[--inbox-item-skeleton-title:55%] nth-[3n]:[--inbox-item-skeleton-body:75%]"
1875
- ],
1876
- leading: ["size-[35px]"],
1877
- line: ["min-w-0 flex-1"],
1878
- title: ["pl-0"],
1879
- bar: ["inline-block h-[0.75em] max-w-full align-middle"],
1880
- contextBar: ["w-(--inbox-item-skeleton-context)"],
1881
- titleBar: ["w-(--inbox-item-skeleton-title)"],
1882
- bodyBar: ["w-(--inbox-item-skeleton-body)"],
1883
- timestampBar: ["w-9"]
1884
- } });
1885
- //#endregion
1886
- //#region src/components/inbox-item/inbox-item-skeleton.tsx
1887
- /**
1888
- * A placeholder for one `InboxItem` while the list loads. It is an
1889
- * `InboxItem` itself, with the same parts as the loaded row (an optional
1890
- * leading block, the context line, the title, the excerpt, the timestamp),
1891
- * each holding a bar instead of text. The padding, the divider and every
1892
- * line's height are the loaded row's, so nothing moves when the content
1893
- * arrives. Match `density`, `leading` and `context` to the rows it stands for.
1894
- * Hidden from assistive technology: mark the region that stays while the
1895
- * list loads `aria-busy`.
1896
- */
1897
- function InboxItemSkeleton({ density, leading = false, context = false, ...props }) {
1898
- const slots = inboxItemSkeleton();
1899
- const bar = (width) => /* @__PURE__ */ jsx(Skeleton, {
1900
- variant: "line",
1901
- className: slots.bar({ className: width })
1902
- });
1903
- return /* @__PURE__ */ jsxs(InboxItem, {
1904
- "aria-hidden": true,
1905
- "data-skeleton": "",
1906
- density,
1907
- disabled: true,
1908
- ...props,
1909
- className: slots.row({ className: props.className }),
1910
- children: [
1911
- leading ? /* @__PURE__ */ jsx(InboxItemLeading, { children: /* @__PURE__ */ jsx(Skeleton, { className: slots.leading() }) }) : null,
1912
- /* @__PURE__ */ jsxs(InboxItemContent, { children: [
1913
- context ? /* @__PURE__ */ jsx(InboxItemContext, { children: /* @__PURE__ */ jsx("span", {
1914
- className: slots.line(),
1915
- children: bar(slots.contextBar())
1916
- }) }) : null,
1917
- /* @__PURE__ */ jsx(InboxItemHeading, { children: /* @__PURE__ */ jsx(InboxItemTitle, {
1918
- className: slots.title(),
1919
- children: bar(slots.titleBar())
1920
- }) }),
1921
- /* @__PURE__ */ jsx(InboxItemBody, { children: bar(slots.bodyBar()) })
1922
- ] }),
1923
- /* @__PURE__ */ jsx(InboxItemTrailing, { children: /* @__PURE__ */ jsx(InboxItemTimestamp, { children: bar(slots.timestampBar()) }) })
1924
- ]
1925
- });
1926
- }
1927
- /**
1928
- * The list of `InboxItemSkeleton` rows that stands in for a list of
1929
- * `InboxItem` rows while it loads, with nothing between or around the rows
1930
- * that the loaded list doesn't have (no border, no card): only the list's
1931
- * 2px gap, which the loaded list has too (design/patterns/lists.md). Renders
1932
- * `count` rows of the given shape, or its children, once `delay` (1 second by
1933
- * default) has passed. It isn't `aria-busy` itself: it goes away when the
1934
- * content arrives, so mark the region that stays, such as the list's panel or
1935
- * viewport, `aria-busy` while it loads.
1936
- */
1937
- function InboxItemSkeletonList({ count = 6, delay = 1e3, density, leading, context, children, className, render, ...props }) {
1938
- const { list } = inboxItemSkeleton();
1939
- const shown = useDelayPassed(delay);
1940
- const defaultProps = {
1941
- "data-slot": "inbox-item-skeleton-list",
1942
- "data-waiting": shown ? void 0 : "",
1943
- className: list({ className }),
1944
- children: shown ? children ?? Array.from({ length: count }, (_, index) => /* @__PURE__ */ jsx(InboxItemSkeleton, {
1945
- density,
1946
- leading,
1947
- context
1948
- }, index)) : null
1949
- };
1950
- return useRender({
1951
- defaultTagName: "div",
1952
- render,
1953
- props: mergeProps(defaultProps, props)
1954
- });
1955
- }
1956
- /** Whether `delay` milliseconds have passed since mount; at once for `0`. */
1957
- function useDelayPassed(delay) {
1958
- const [passed, setPassed] = useState(false);
1959
- useEffect(() => {
1960
- if (delay <= 0) return;
1961
- const timer = setTimeout(() => setPassed(true), delay);
1962
- return () => clearTimeout(timer);
1963
- }, [delay]);
1964
- return passed || delay <= 0;
1965
- }
1966
- //#endregion
1967
- //#region src/components/list-row/list-row-skeleton.tsx
1968
- const listRowSkeleton = tv({ slots: {
1969
- row: [
1970
- "[--list-row-skeleton-title:45%] [--list-row-skeleton-description:75%]",
1971
- "nth-[3n+2]:[--list-row-skeleton-title:35%] nth-[3n+2]:[--list-row-skeleton-description:60%]",
1972
- "nth-[3n]:[--list-row-skeleton-title:55%] nth-[3n]:[--list-row-skeleton-description:70%]"
1973
- ],
1974
- avatar: ["size-[35px] shrink-0"],
1975
- icon: ["size-5 shrink-0"],
1976
- bar: ["inline-block h-[0.75em] max-w-full align-middle"],
1977
- titleBar: ["w-(--list-row-skeleton-title)"],
1978
- descriptionBar: ["w-(--list-row-skeleton-description)"],
1979
- metaBar: ["w-9"]
1980
- } });
1981
- /**
1982
- * A placeholder for one `ListRow` while the list loads. It is a display-only
1983
- * `ListRow` of the same `variant`, with a bar in its title, its time and its
1984
- * description, so it is the loaded row's size and its divider sits in the
1985
- * same place. Hidden from assistive technology: mark the region that stays
1986
- * while the list loads, such as its `ListRowGroup`, `aria-busy`.
1987
- */
1988
- function ListRowSkeleton({ variant = "default", leading = variant === "default", description = variant === "default", className, ...props }) {
1989
- const slots = listRowSkeleton();
1990
- const bar = (width) => /* @__PURE__ */ jsx(Skeleton, {
1991
- variant: "line",
1992
- className: slots.bar({ className: width })
1993
- });
1994
- return /* @__PURE__ */ jsxs(ListRow, {
1995
- "aria-hidden": true,
1996
- "data-skeleton": "",
1997
- interactive: false,
1998
- variant,
1999
- ...props,
2000
- className: slots.row({ className }),
2001
- children: [leading ? /* @__PURE__ */ jsx(Skeleton, { className: variant === "default" ? slots.avatar() : slots.icon() }) : null, /* @__PURE__ */ jsxs(ListRowBody, { children: [/* @__PURE__ */ jsxs(ListRowTop, { children: [/* @__PURE__ */ jsx(ListRowTitle, { children: bar(slots.titleBar()) }), variant === "default" ? /* @__PURE__ */ jsx(ListRowMeta, { children: bar(slots.metaBar()) }) : null] }), description ? /* @__PURE__ */ jsx(ListRowBottom, { children: /* @__PURE__ */ jsx(ListRowDescription, { children: bar(slots.descriptionBar()) }) }) : null] })]
2002
- });
2003
- }
2004
- //#endregion
2005
- //#region src/components/meter/meter.tsx
2006
- const meter = tv({
2007
- slots: {
2008
- root: [
2009
- "group/meter grid w-full min-w-0 gap-1.5",
2010
- "[--meter-indicator:var(--info)]",
2011
- "[--meter-value:var(--foreground-muted)]",
2012
- "font-sans"
2013
- ],
2014
- header: ["flex min-w-0 flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5"],
2015
- label: ["min-w-0 truncate", "text-footnote font-semibold text-foreground"],
2016
- value: ["shrink-0 text-right tabular-nums", "text-footnote text-(--meter-value)"],
2017
- track: [
2018
- "relative w-full overflow-hidden bg-(--fill-muted)",
2019
- "h-2 group-data-[size=sm]/meter:h-1.5",
2020
- "rounded-sticker"
2021
- ],
2022
- indicator: [
2023
- "block h-full bg-(--meter-indicator)",
2024
- "transition-[width,background-color] duration-200 ease-out",
2025
- "rounded-[inherit] inset-ring-[1.5px] inset-ring-sticker-ink"
2026
- ]
2027
- },
2028
- variants: {
2029
- warning: { true: { root: ["[--meter-indicator:var(--warning)]", "[--meter-value:var(--warning-strong)]"] } },
2030
- size: {
2031
- sm: {},
2032
- md: {}
2033
- }
2034
- },
2035
- defaultVariants: {
2036
- warning: false,
2037
- size: "md"
2038
- }
2039
- });
2040
- const MeterTextContext = createContext("");
2041
- function clamp(value, min, max) {
2042
- return Math.min(max, Math.max(min, Number.isNaN(value) ? min : value));
2043
- }
2044
- /**
2045
- * How much of an allowance is used, such as a usage limit that resets. The bar
2046
- * fills as it is used: the info fill, like Progress, until `warnAt` of `max`
2047
- * is used (80% unless set), then the warning color, and a full warning bar
2048
- * when used up; never danger, and no success color.
2049
- */
2050
- function Meter({ size = "md", used, max = 100, warnAt, low, valueText, className, ...props }) {
2051
- const value = clamp(used, 0, max);
2052
- const share = max > 0 ? value / max : 1;
2053
- const warnFrom = warnAt !== void 0 ? clamp(warnAt, 0, 1) : low !== void 0 && max > 0 ? 1 - clamp(low, 0, max) / max : .8;
2054
- const level = share >= 1 ? "full" : share >= warnFrom ? "high" : "normal";
2055
- const { root } = meter({
2056
- size,
2057
- warning: level !== "normal"
2058
- });
2059
- return /* @__PURE__ */ jsx(MeterTextContext.Provider, {
2060
- value: valueText,
2061
- children: /* @__PURE__ */ jsx(Meter$1.Root, {
2062
- "data-slot": "meter",
2063
- "data-size": size,
2064
- "data-level": level,
2065
- min: 0,
2066
- max,
2067
- value,
2068
- "aria-valuetext": valueText,
2069
- className: root({ className }),
2070
- ...props
2071
- })
2072
- });
2073
- }
2074
- function MeterHeader({ className, ...props }) {
2075
- const { header } = meter();
2076
- return /* @__PURE__ */ jsx("div", {
2077
- "data-slot": "meter-header",
2078
- className: header({ className }),
2079
- ...props
2080
- });
2081
- }
2082
- function MeterLabel({ className, ...props }) {
2083
- const { label } = meter();
2084
- return /* @__PURE__ */ jsx(Meter$1.Label, {
2085
- "data-slot": "meter-label",
2086
- className: label({ className }),
2087
- ...props
2088
- });
2089
- }
2090
- /** Shows the meter's `valueText`, at the end of `MeterHeader`. */
2091
- function MeterValue({ className, ...props }) {
2092
- const { value } = meter();
2093
- const valueText = useContext(MeterTextContext);
2094
- return /* @__PURE__ */ jsx(Meter$1.Value, {
2095
- "data-slot": "meter-value",
2096
- className: value({ className }),
2097
- ...props,
2098
- children: () => valueText
2099
- });
2100
- }
2101
- function MeterTrack({ className, ...props }) {
2102
- const { track } = meter();
2103
- return /* @__PURE__ */ jsx(Meter$1.Track, {
2104
- "data-slot": "meter-track",
2105
- className: track({ className }),
2106
- ...props
2107
- });
2108
- }
2109
- function MeterIndicator({ className, ...props }) {
2110
- const { indicator } = meter();
2111
- return /* @__PURE__ */ jsx(Meter$1.Indicator, {
2112
- "data-slot": "meter-indicator",
2113
- className: indicator({ className }),
2114
- ...props
2115
- });
2116
- }
2117
- //#endregion
2118
- //#region src/components/filter-chip/filter-chip.tsx
2119
- const filterChip = tv({ slots: {
2120
- root: [
2121
- "inline-flex h-8 min-w-0 shrink items-center",
2122
- "gap-1.5 rounded-control bg-info-soft pr-1 pl-2.5",
2123
- "font-sans text-control font-semibold text-foreground",
2124
- "[&>svg]:pointer-events-none [&>svg]:shrink-0",
2125
- "[&>svg:not([class*='size-'])]:size-3.5 [&>svg:not([class*='stroke-'])]:stroke-2"
2126
- ],
2127
- label: ["min-w-0 truncate"],
2128
- remove: [
2129
- ...chipRemoveBase,
2130
- "relative size-6",
2131
- "after:absolute after:content-[''] pointer-coarse:after:-inset-2.5"
2132
- ]
2133
- } });
2134
- /**
2135
- * The filter in effect, beside the filter button that chose it: an `info-soft`
2136
- * chip as high as the button, with an optional 14px icon, a `FilterChipLabel`
2137
- * and a `FilterChipRemove` × that resets the filter.
2138
- */
2139
- function FilterChip({ className, ...props }) {
2140
- const { root } = filterChip();
2141
- return /* @__PURE__ */ jsx("span", {
2142
- "data-slot": "filter-chip",
2143
- className: root({ className }),
2144
- ...props
2145
- });
2146
- }
2147
- /** The name of the active filter. It truncates when the row has no room. */
2148
- function FilterChipLabel({ className, ...props }) {
2149
- const { label } = filterChip();
2150
- return /* @__PURE__ */ jsx("span", {
2151
- "data-slot": "filter-chip-label",
2152
- className: label({ className }),
2153
- ...props
2154
- });
2155
- }
2156
- /** The × that resets the filter to its default. Shows an X unless given children. */
2157
- function FilterChipRemove({ className, children, render, nativeButton, ...props }) {
2158
- const { remove } = filterChip();
2159
- return /* @__PURE__ */ jsx(Button, {
2160
- ...props,
2161
- render,
2162
- nativeButton: nativeButton ?? defaultNativeButton(render),
2163
- type: "button",
2164
- "data-slot": "filter-chip-remove",
2165
- className: remove({ className }),
2166
- children: children ?? /* @__PURE__ */ jsx(X, { "aria-hidden": true })
2167
- });
2168
- }
2169
- //#endregion
2170
- //#region src/components/pull-to-refresh/pull-to-refresh-gesture.ts
2171
- /**
2172
- * The pull-to-refresh gesture as a pure state machine (pull-to-refresh.md,
2173
- * "Behavior"). `usePullToRefresh` feeds it the scroller's touch events; this
2174
- * file owns every decision, so the rules can be tested without a DOM.
2175
- */
2176
- /** How far the indicator has to be pulled, after damping, for a release to refresh. */
2177
- const PULL_TO_REFRESH_THRESHOLD = 100;
2178
- /**
2179
- * The furthest the indicator follows the finger. 40px past the threshold is
2180
- * enough to show the pull is armed and still going; any further and a long
2181
- * drag would push the list off a small phone's screen.
2182
- */
2183
- const PULL_TO_REFRESH_MAX_DISTANCE = 140;
2184
- /** The indicator's height while the refresh runs. */
2185
- const PULL_TO_REFRESH_HOLD_DISTANCE = 60;
2186
- /**
2187
- * The rubber band: the indicator moves this fraction of the finger's travel,
2188
- * so it lags and the pull reads as resistance. 0.55 is iOS's own
2189
- * scroll-view constant.
2190
- */
2191
- const PULL_TO_REFRESH_DAMPING = .55;
2192
- /**
2193
- * How long the indicator takes to go back, to the hold height or to 0, once
2194
- * the finger lifts or the refresh ends: ease-out (`ease-slide`), with no
2195
- * overshoot. Instant with reduced motion.
2196
- */
2197
- const PULL_TO_REFRESH_SNAP_BACK_MS = 300;
2198
- /** A finger held this long before it pulls is a long press (a menu, a drag), not a pull. */
2199
- const PULL_TO_REFRESH_LONG_PRESS_MS = 500;
2200
- /** How far the finger may drift while it could still be a long press. */
2201
- const PULL_TO_REFRESH_TOLERANCE = 10;
2202
- /** Where a touch must not start a pull: text entry, and anything the consumer marks. */
2203
- const PULL_TO_REFRESH_IGNORE_SELECTOR = [
2204
- "input",
2205
- "textarea",
2206
- "select",
2207
- "[contenteditable=\"\"]",
2208
- "[contenteditable=\"true\"]",
2209
- "[contenteditable=\"plaintext-only\"]",
2210
- "[data-pull-to-refresh-ignore]"
2211
- ].join(", ");
2212
- const DEFAULT_PULL_GESTURE_CONFIG = {
2213
- threshold: 100,
2214
- maxDistance: 140,
2215
- holdDistance: 60,
2216
- latch: true
2217
- };
2218
- const IDLE_PULL_GESTURE = {
2219
- phase: "idle",
2220
- startX: 0,
2221
- startY: 0,
2222
- distance: 0,
2223
- armed: false
2224
- };
2225
- /** How far the indicator is for a finger that has travelled `delta` px down. */
2226
- function pullDistance(delta, maxDistance = 140) {
2227
- if (delta <= 0) return 0;
2228
- return Math.min(maxDistance, delta * PULL_TO_REFRESH_DAMPING);
2229
- }
2230
- /** How far towards the threshold the pull is, from 0 to 1. */
2231
- function pullProgress(distance, threshold = 100) {
2232
- if (threshold <= 0) return 1;
2233
- return Math.min(1, Math.max(0, distance / threshold));
2234
- }
2235
- /**
2236
- * Which of the spinner's 4 pixels is lit while pulling, clockwise from the
2237
- * top left: one step per quarter of the threshold, the last from the
2238
- * threshold on (the same walk as the native apps' indicator).
2239
- */
2240
- function pullStep(progress) {
2241
- return Math.min(3, Math.floor(Math.max(0, progress) * 4));
2242
- }
2243
- /** The state the indicator shows for a gesture. */
2244
- function pullState(gesture) {
2245
- if (gesture.phase === "refreshing") return "refreshing";
2246
- if (gesture.phase !== "pulling") return "idle";
2247
- return gesture.distance > 0 || gesture.armed ? "pulling" : "idle";
2248
- }
2249
- function step(gesture, preventDefault = false, refresh = false, armed = false) {
2250
- return {
2251
- gesture,
2252
- preventDefault,
2253
- refresh,
2254
- armed
2255
- };
2256
- }
2257
- /** The next gesture for an event. */
2258
- function stepPullGesture(gesture, event, config = DEFAULT_PULL_GESTURE_CONFIG) {
2259
- if (gesture.phase === "refreshing") return step(event.type === "settle" ? IDLE_PULL_GESTURE : gesture);
2260
- switch (event.type) {
2261
- case "start":
2262
- if (event.fingers !== 1 || !event.atTop || event.ignored) return step(IDLE_PULL_GESTURE);
2263
- return step({
2264
- ...IDLE_PULL_GESTURE,
2265
- phase: "tracking",
2266
- startX: event.x,
2267
- startY: event.y
2268
- });
2269
- case "move": {
2270
- if (gesture.phase === "idle") return step(gesture);
2271
- if (event.fingers !== 1) return step(IDLE_PULL_GESTURE);
2272
- if (!event.atTop && !gesture.armed) return step(IDLE_PULL_GESTURE);
2273
- const dx = event.x - gesture.startX;
2274
- const dy = event.y - gesture.startY;
2275
- if (gesture.phase === "tracking") {
2276
- if (Math.abs(dx) > PULL_TO_REFRESH_TOLERANCE && Math.abs(dx) > dy) return step(IDLE_PULL_GESTURE);
2277
- if (dy < -6) return step(IDLE_PULL_GESTURE);
2278
- if (dy <= 6) return step(gesture);
2279
- }
2280
- const distance = pullDistance(dy, config.maxDistance);
2281
- const armed = config.latch && gesture.armed || distance >= config.threshold;
2282
- return step({
2283
- ...gesture,
2284
- phase: "pulling",
2285
- distance,
2286
- armed
2287
- }, armed || dy > 6, false, armed && !gesture.armed);
2288
- }
2289
- case "hold": return step(gesture.phase === "tracking" ? IDLE_PULL_GESTURE : gesture);
2290
- case "end":
2291
- if (gesture.phase === "pulling" && gesture.armed) return step({
2292
- ...gesture,
2293
- phase: "refreshing",
2294
- distance: config.holdDistance
2295
- }, false, true);
2296
- return step(IDLE_PULL_GESTURE);
2297
- case "cancel":
2298
- case "settle": return step(IDLE_PULL_GESTURE);
2299
- }
2300
- }
2301
- //#endregion
2302
- //#region src/components/pull-to-refresh/use-pull-to-refresh.ts
2303
- /** A primary pointer that is a finger: the only input pull to refresh answers. */
2304
- const PULL_TO_REFRESH_POINTER_QUERY = "(pointer: coarse)";
2305
- /** The one platform check: Android, by its user agent (Client Hints first). */
2306
- function detectPullToRefreshPlatform() {
2307
- if (typeof navigator === "undefined") return "default";
2308
- const hints = navigator.userAgentData;
2309
- if (hints?.platform) return hints.platform === "Android" ? "android" : "default";
2310
- return /\bAndroid\b/i.test(navigator.userAgent) ? "android" : "default";
2311
- }
2312
- function hapticTick() {
2313
- if (typeof navigator !== "undefined" && typeof navigator.vibrate === "function") navigator.vibrate(10);
2314
- }
2315
- const subscribeNever = () => () => void 0;
2316
- const defaultSnapshot = () => "default";
2317
- function subscribeCoarse(onChange) {
2318
- if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => void 0;
2319
- const media = window.matchMedia(PULL_TO_REFRESH_POINTER_QUERY);
2320
- media.addEventListener("change", onChange);
2321
- return () => media.removeEventListener("change", onChange);
2322
- }
2323
- function coarseSnapshot() {
2324
- return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(pointer: coarse)").matches;
2325
- }
2326
- function ignoredTarget(target) {
2327
- return target instanceof Element && target.closest(PULL_TO_REFRESH_IGNORE_SELECTOR) !== null;
2328
- }
2329
- /**
2330
- * Pull to refresh for a scroll container on a touch screen
2331
- * (pull-to-refresh.md). Pulling down from the top of `scrollRef` past the
2332
- * threshold calls `onRefresh`; render a `PullToRefreshIndicator` with the
2333
- * result as the scroller's first child.
2334
- *
2335
- * On Android it takes Material 3's look (`PullToRefreshPlatform`). It does
2336
- * nothing on a fine pointer. While it is on, the scroller gets
2337
- * `overscroll-behavior-y: contain`, so a browser tab's own pull to reload
2338
- * doesn't run as well. `scrollRef` is read when the hook turns on; a
2339
- * scroller that is replaced later needs the hook remounted (a `key`).
2340
- */
2341
- function usePullToRefresh(scrollRef, { onRefresh, disabled = false, threshold = DEFAULT_PULL_GESTURE_CONFIG.threshold, maxDistance = DEFAULT_PULL_GESTURE_CONFIG.maxDistance, holdDistance = DEFAULT_PULL_GESTURE_CONFIG.holdDistance, platform: platformOverride }) {
2342
- const coarse = useSyncExternalStore(subscribeCoarse, coarseSnapshot, () => false);
2343
- const detected = useSyncExternalStore(subscribeNever, detectPullToRefreshPlatform, defaultSnapshot);
2344
- const platform = platformOverride ?? detected;
2345
- const enabled = coarse && !disabled;
2346
- const [gesture, setGesture] = useState(IDLE_PULL_GESTURE);
2347
- const gestureRef = useRef(IDLE_PULL_GESTURE);
2348
- const onRefreshRef = useLatestRef(onRefresh);
2349
- useEffect(() => {
2350
- const element = scrollRef.current;
2351
- if (!enabled || !element) return;
2352
- const config = {
2353
- threshold,
2354
- maxDistance,
2355
- holdDistance,
2356
- latch: platform !== "android"
2357
- };
2358
- let holdTimer;
2359
- let live = true;
2360
- const dispatch = (event, native) => {
2361
- const next = stepPullGesture(gestureRef.current, event, config);
2362
- if (next.preventDefault && native?.cancelable) native.preventDefault();
2363
- if (next.gesture !== gestureRef.current) {
2364
- gestureRef.current = next.gesture;
2365
- setGesture(next.gesture);
2366
- }
2367
- if (next.gesture.phase !== "tracking") clearTimeout(holdTimer);
2368
- if (next.armed && platform === "android") hapticTick();
2369
- if (next.refresh) {
2370
- const settle = () => {
2371
- if (live) dispatch({ type: "settle" });
2372
- };
2373
- let result;
2374
- try {
2375
- result = Promise.resolve(onRefreshRef.current());
2376
- } catch (error) {
2377
- result = Promise.reject(error);
2378
- }
2379
- result.then(settle, settle);
2380
- }
2381
- };
2382
- const atTop = () => element.scrollTop <= 0;
2383
- const onTouchStart = (event) => {
2384
- const touch = event.touches[0];
2385
- dispatch({
2386
- type: "start",
2387
- x: touch?.clientX ?? 0,
2388
- y: touch?.clientY ?? 0,
2389
- fingers: event.touches.length,
2390
- atTop: atTop(),
2391
- ignored: ignoredTarget(event.target)
2392
- });
2393
- if (gestureRef.current.phase === "tracking") {
2394
- clearTimeout(holdTimer);
2395
- holdTimer = setTimeout(() => dispatch({ type: "hold" }), PULL_TO_REFRESH_LONG_PRESS_MS);
2396
- }
2397
- };
2398
- const onTouchMove = (event) => {
2399
- if (event.defaultPrevented && gestureRef.current.phase === "tracking") {
2400
- dispatch({ type: "cancel" });
2401
- return;
2402
- }
2403
- const touch = event.touches[0];
2404
- dispatch({
2405
- type: "move",
2406
- x: touch?.clientX ?? 0,
2407
- y: touch?.clientY ?? 0,
2408
- fingers: event.touches.length,
2409
- atTop: atTop()
2410
- }, event);
2411
- };
2412
- const onTouchEnd = () => dispatch({ type: "end" });
2413
- const onCancel = () => dispatch({ type: "cancel" });
2414
- const previousOverscroll = element.style.overscrollBehaviorY;
2415
- element.style.overscrollBehaviorY = "contain";
2416
- element.addEventListener("touchstart", onTouchStart, { passive: true });
2417
- element.addEventListener("touchmove", onTouchMove, { passive: false });
2418
- element.addEventListener("touchend", onTouchEnd, { passive: true });
2419
- element.addEventListener("touchcancel", onCancel, { passive: true });
2420
- element.addEventListener("contextmenu", onCancel);
2421
- element.addEventListener("dragstart", onCancel);
2422
- return () => {
2423
- live = false;
2424
- clearTimeout(holdTimer);
2425
- element.style.overscrollBehaviorY = previousOverscroll;
2426
- element.removeEventListener("touchstart", onTouchStart);
2427
- element.removeEventListener("touchmove", onTouchMove);
2428
- element.removeEventListener("touchend", onTouchEnd);
2429
- element.removeEventListener("touchcancel", onCancel);
2430
- element.removeEventListener("contextmenu", onCancel);
2431
- element.removeEventListener("dragstart", onCancel);
2432
- gestureRef.current = IDLE_PULL_GESTURE;
2433
- setGesture(IDLE_PULL_GESTURE);
2434
- };
2435
- }, [
2436
- enabled,
2437
- scrollRef,
2438
- onRefreshRef,
2439
- threshold,
2440
- maxDistance,
2441
- holdDistance,
2442
- platform
2443
- ]);
2444
- const state = pullState(gesture);
2445
- const distance = state === "idle" ? 0 : gesture.distance;
2446
- return {
2447
- state,
2448
- distance,
2449
- progress: state === "refreshing" || gesture.armed ? 1 : pullProgress(distance, threshold),
2450
- platform
2451
- };
2452
- }
2453
- //#endregion
2454
- //#region src/components/pull-to-refresh/pull-to-refresh-material-progress.tsx
2455
- /**
2456
- * Android's own pull-to-refresh progress, drawn as Material 3 draws it
2457
- * (Compose's `PullToRefreshDefaults.Indicator`, the one Chrome on Android
2458
- * shows too): a 2.5px arc of radius 7.5px in a 40px disc. While pulling the
2459
- * arc grows to 0.8 of the circle and turns a little with the pull, with an
2460
- * arrowhead at its end, at 0.3 opacity until the pull is armed and fully
2461
- * opaque from then on. While refreshing it is Material's indeterminate arc,
2462
- * which grows and shrinks as it turns. Internal: `PullToRefreshIndicator`
2463
- * renders it on Android.
2464
- */
2465
- /** The disc's centre, in its 40px box. */
2466
- const CENTER = 20;
2467
- const MATERIAL_ARC_RADIUS = 7.5;
2468
- const MATERIAL_STROKE_WIDTH = 2.5;
2469
- const ARROW_WIDTH = 10;
2470
- const ARROW_HEIGHT = 5;
2471
- /** The longest the arc gets while pulling, as a share of the circle. */
2472
- const MATERIAL_MAX_ARC = .8;
2473
- const CIRCUMFERENCE = 2 * Math.PI * MATERIAL_ARC_RADIUS;
2474
- /**
2475
- * The arc for a pull's progress (0 to 1), as Material computes it: nothing
2476
- * for the first 40%, then the arc grows to `MATERIAL_MAX_ARC` of the circle
2477
- * and its start turns from -45° to +27° (12 o'clock is -90°), and the
2478
- * arrowhead grows with it.
2479
- */
2480
- function materialArc(progress) {
2481
- const adjusted = Math.max(Math.min(1, progress) - .4, 0) * 5 / 3;
2482
- const rotation = (-.25 + .4 * adjusted) * .5;
2483
- return {
2484
- startAngle: rotation * 360,
2485
- endAngle: (rotation + adjusted * MATERIAL_MAX_ARC) * 360,
2486
- arrowScale: Math.min(1, adjusted)
2487
- };
2488
- }
2489
- const point = (degrees) => {
2490
- const radians = degrees * Math.PI / 180;
2491
- return `${CENTER + MATERIAL_ARC_RADIUS * Math.cos(radians)} ${CENTER + MATERIAL_ARC_RADIUS * Math.sin(radians)}`;
2492
- };
2493
- /** An SVG path for the arc, clockwise from `startAngle` to `endAngle`. */
2494
- function arcPath({ startAngle, endAngle }) {
2495
- const sweep = endAngle - startAngle;
2496
- if (sweep <= .01) return "";
2497
- const large = sweep > 180 ? 1 : 0;
2498
- return `M ${point(startAngle)} A ${MATERIAL_ARC_RADIUS} ${MATERIAL_ARC_RADIUS} 0 ${large} 1 ${point(endAngle)}`;
2499
- }
2500
- /**
2501
- * The arrowhead: a triangle whose base straddles the arc at 3 o'clock and
2502
- * whose tip points clockwise, turned to the arc's end.
2503
- */
2504
- function arrowPoints(scale) {
2505
- const half = ARROW_WIDTH * scale / 2;
2506
- const base = 17.5;
2507
- const x = 27.5;
2508
- return `${x - half},${base} ${x + half},${base} ${x},${base + ARROW_HEIGHT * scale}`;
2509
- }
2510
- const MATERIAL_STYLE = `
2511
- @keyframes raft-ptr-material-rotate {
2512
- to {
2513
- transform: rotate(360deg);
2514
- }
2515
- }
2516
-
2517
- @keyframes raft-ptr-material-dash {
2518
- 0% {
2519
- stroke-dasharray: 0.4 ${CIRCUMFERENCE};
2520
- stroke-dashoffset: 0;
2521
- }
2522
- 50% {
2523
- stroke-dasharray: ${CIRCUMFERENCE * .7} ${CIRCUMFERENCE};
2524
- stroke-dashoffset: ${-CIRCUMFERENCE * .1};
2525
- }
2526
- 100% {
2527
- stroke-dasharray: ${CIRCUMFERENCE * .7} ${CIRCUMFERENCE};
2528
- stroke-dashoffset: ${-CIRCUMFERENCE * .99};
2529
- }
2530
- }
2531
-
2532
- [data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] {
2533
- animation: raft-ptr-material-rotate 1.4s linear infinite;
2534
- }
2535
-
2536
- [data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] [data-material-indeterminate] {
2537
- animation: raft-ptr-material-dash 1.4s ease-in-out infinite;
2538
- }
2539
-
2540
- @media (prefers-reduced-motion: reduce) {
2541
- [data-slot="pull-to-refresh-material-progress"][data-state="refreshing"],
2542
- [data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] [data-material-indeterminate] {
2543
- animation: none;
2544
- }
2545
-
2546
- [data-slot="pull-to-refresh-material-progress"][data-state="refreshing"] [data-material-indeterminate] {
2547
- stroke-dasharray: ${CIRCUMFERENCE * .75} ${CIRCUMFERENCE};
2548
- }
2549
- }
2550
- `;
2551
- const materialStyle = /* @__PURE__ */ jsx("style", {
2552
- href: "raft-ui-pull-to-refresh-material",
2553
- precedence: "default",
2554
- children: MATERIAL_STYLE
2555
- });
2556
- const materialProgress = tv({ slots: {
2557
- root: ["size-10 shrink-0 overflow-visible", "text-info-strong"],
2558
- determinate: ["transition-opacity duration-150 ease-out motion-reduce:transition-none", "opacity-30 data-armed:opacity-100"],
2559
- arc: ["fill-none stroke-current [stroke-linecap:butt]"],
2560
- arrow: ["fill-current"],
2561
- indeterminate: ["fill-none stroke-current [stroke-linecap:round]"]
2562
- } });
2563
- function PullToRefreshMaterialProgress({ state, progress }) {
2564
- const { root, determinate, arc, arrow, indeterminate } = materialProgress();
2565
- const refreshing = state === "refreshing";
2566
- const armed = progress >= 1;
2567
- const values = materialArc(refreshing ? 1 : progress);
2568
- return /* @__PURE__ */ jsxs("svg", {
2569
- "data-slot": "pull-to-refresh-material-progress",
2570
- "data-state": state,
2571
- "data-armed": !refreshing && armed ? "" : void 0,
2572
- viewBox: "0 0 40 40",
2573
- "aria-hidden": "true",
2574
- className: root(),
2575
- children: [materialStyle, refreshing ? /* @__PURE__ */ jsx("circle", {
2576
- "data-material-indeterminate": "",
2577
- cx: CENTER,
2578
- cy: CENTER,
2579
- r: MATERIAL_ARC_RADIUS,
2580
- strokeWidth: MATERIAL_STROKE_WIDTH,
2581
- transform: `rotate(-90 ${CENTER} ${CENTER})`,
2582
- className: indeterminate()
2583
- }) : /* @__PURE__ */ jsxs("g", {
2584
- "data-armed": armed ? "" : void 0,
2585
- className: determinate(),
2586
- children: [/* @__PURE__ */ jsx("path", {
2587
- "data-material-arc": "",
2588
- d: arcPath(values),
2589
- strokeWidth: MATERIAL_STROKE_WIDTH,
2590
- className: arc()
2591
- }), values.arrowScale > 0 && /* @__PURE__ */ jsx("polygon", {
2592
- "data-material-arrow": "",
2593
- points: arrowPoints(values.arrowScale),
2594
- transform: `rotate(${values.endAngle - 2.5} ${CENTER} ${CENTER})`,
2595
- className: arrow()
2596
- })]
2597
- })]
2598
- });
2599
- }
2600
- //#endregion
2601
- //#region src/components/pull-to-refresh/pull-to-refresh-indicator.tsx
2602
- const pullStepStyle = /* @__PURE__ */ jsx("style", {
2603
- href: "raft-ui-pull-to-refresh",
2604
- precedence: "default",
2605
- children: `
2606
- [data-slot="pull-to-refresh-indicator"][data-pull-step] [data-spinner-pixel],
2607
- [data-slot="pull-to-refresh-indicator"][data-state="idle"] [data-spinner-pixel] {
2608
- animation: none;
2609
- opacity: 0.2;
2610
- }
2611
-
2612
- [data-slot="pull-to-refresh-indicator"][data-pull-step="0"] [data-spinner-pixel]:nth-child(1),
2613
- [data-slot="pull-to-refresh-indicator"][data-pull-step="1"] [data-spinner-pixel]:nth-child(2),
2614
- [data-slot="pull-to-refresh-indicator"][data-pull-step="2"] [data-spinner-pixel]:nth-child(4),
2615
- [data-slot="pull-to-refresh-indicator"][data-pull-step="3"] [data-spinner-pixel]:nth-child(3) {
2616
- opacity: 1;
2617
- }
2618
- `
2619
- });
2620
- const SNAP_BACK = ["duration-300 ease-slide motion-reduce:transition-none", "data-[state=pulling]:transition-none"];
2621
- const pullToRefreshIndicator = tv({
2622
- slots: {
2623
- root: ["flex w-full shrink-0 justify-center", "text-foreground-icon"],
2624
- surface: ["flex items-center justify-center"],
2625
- spinner: [
2626
- "transition-opacity duration-300 ease-slide motion-reduce:transition-none",
2627
- "in-data-[state=pulling]:transition-none",
2628
- "in-data-[state=pulling]:opacity-(--pull-progress) in-data-[state=idle]:opacity-0"
2629
- ],
2630
- status: ["sr-only"]
2631
- },
2632
- variants: { platform: {
2633
- default: {
2634
- root: [
2635
- "relative items-center overflow-hidden",
2636
- "transition-[height]",
2637
- ...SNAP_BACK
2638
- ],
2639
- surface: ["size-full"]
2640
- },
2641
- android: {
2642
- root: ["sticky top-0 z-10 h-0 overflow-visible"],
2643
- surface: [
2644
- "absolute inset-x-0 top-0 mx-auto size-10 rounded-full bg-layer-popover shadow-float",
2645
- "translate-y-[calc(var(--pull-distance)-100%)]",
2646
- "transition-[translate,opacity,scale]",
2647
- ...SNAP_BACK,
2648
- "data-[state=idle]:opacity-0",
2649
- "data-[exit=finish]:scale-0 data-[exit=finish]:duration-150 data-[exit=finish]:ease-out"
2650
- ]
2651
- }
2652
- } },
2653
- defaultVariants: { platform: "default" }
2654
- });
2655
- /**
2656
- * How the Android disc leaves once idle: `finish` when a refresh has just
2657
- * ended (it scales down where it held), `cancel` otherwise (it slides back
2658
- * up). The finish keeps the distance it held, so it doesn't also slide.
2659
- * `gone` once the scale-down has ended: the disc stays scaled away and its
2660
- * arc stops turning.
2661
- */
2662
- function useAndroidExit(state, distance) {
2663
- const [previous, setPrevious] = useState(state);
2664
- const [exit, setExit] = useState({
2665
- kind: "cancel",
2666
- distance: 0
2667
- });
2668
- const [held, setHeld] = useState(distance);
2669
- if (state !== previous) {
2670
- setPrevious(state);
2671
- if (state === "idle") setExit(previous === "refreshing" ? {
2672
- kind: "finish",
2673
- distance: held
2674
- } : {
2675
- kind: "cancel",
2676
- distance: 0
2677
- });
2678
- }
2679
- if (state === "refreshing" && distance !== held) setHeld(distance);
2680
- const settle = () => {
2681
- if (exit.kind === "finish") setExit({
2682
- ...exit,
2683
- kind: "gone"
2684
- });
2685
- };
2686
- return {
2687
- ...exit,
2688
- settle
2689
- };
2690
- }
2691
- /**
2692
- * The pull's indicator. Render it as the scroller's first child: by default
2693
- * it is the space the pull opens, with the DS Spinner in it, so the content
2694
- * moves down with the pull; on Android it is Material 3's own indicator, a
2695
- * disc with a progress arc that travels down over the content. It holds a
2696
- * polite live region that announces `label` while refreshing.
2697
- */
2698
- function PullToRefreshIndicator({ pull, label, className, style, ...props }) {
2699
- const { state, distance, progress, platform } = pull;
2700
- const { root, surface, spinner, status } = pullToRefreshIndicator({ platform });
2701
- const android = platform === "android";
2702
- const exit = useAndroidExit(state, distance);
2703
- const finishing = android && state === "idle" && exit.kind !== "cancel";
2704
- return /* @__PURE__ */ jsxs("div", {
2705
- "data-slot": "pull-to-refresh-indicator",
2706
- "data-state": state,
2707
- "data-platform": platform,
2708
- "data-pull-step": !android && state === "pulling" ? pullStep(progress) : void 0,
2709
- className: root({ className }),
2710
- style: {
2711
- ...style,
2712
- height: android ? void 0 : distance,
2713
- ["--pull-progress"]: progress,
2714
- ["--pull-distance"]: `${finishing ? exit.distance : distance}px`
2715
- },
2716
- ...props,
2717
- children: [
2718
- !android && pullStepStyle,
2719
- /* @__PURE__ */ jsx("div", {
2720
- "data-slot": "pull-to-refresh-surface",
2721
- "data-state": state,
2722
- "data-exit": finishing ? "finish" : void 0,
2723
- className: surface(),
2724
- onTransitionEnd: (event) => {
2725
- if (event.target === event.currentTarget && event.propertyName === "scale") exit.settle();
2726
- },
2727
- children: android ? /* @__PURE__ */ jsx(PullToRefreshMaterialProgress, {
2728
- state: finishing && exit.kind === "finish" ? "refreshing" : state,
2729
- progress
2730
- }) : /* @__PURE__ */ jsx(Spinner, {
2731
- "aria-hidden": "true",
2732
- role: "presentation",
2733
- "aria-label": void 0,
2734
- className: spinner()
2735
- })
2736
- }),
2737
- /* @__PURE__ */ jsx("span", {
2738
- role: "status",
2739
- "aria-live": "polite",
2740
- className: status(),
2741
- children: state === "refreshing" ? label : ""
2742
- })
2743
- ]
2744
- });
2745
- }
2746
- //#endregion
2747
- //#region src/components/choice-list/choice-list.recipe.ts
2748
- const choiceList = tv({
2749
- slots: {
2750
- root: ["grid min-w-0 gap-2"],
2751
- searchInput: ["[&::-webkit-search-cancel-button]:appearance-none"],
2752
- frame: ["flex min-w-0 flex-col overflow-hidden", "rounded-container border border-line-hairline bg-layer-card"],
2753
- scroller: ["min-h-0 min-w-0 p-1"],
2754
- rows: ["grid grid-cols-[minmax(0,1fr)_auto] gap-x-0 gap-y-0.5"],
2755
- group: ["col-span-2 col-start-1 grid grid-cols-subgrid grid-rows-subgrid gap-x-0 gap-y-0.5"],
2756
- section: ["col-span-2 col-start-1 grid grid-cols-subgrid grid-rows-subgrid"],
2757
- heading: ["col-span-2 px-2.5 pt-2 pb-1", "font-sans text-caption font-bold text-foreground-hint"],
2758
- item: [
2759
- "col-span-2 flex min-w-0 items-start gap-2.5 rounded-chip px-2.5",
2760
- "min-h-option py-option-inset",
2761
- "pointer-coarse:min-h-11 pointer-coarse:py-[calc((--spacing(11)-1lh)/2)]",
2762
- "text-left font-sans text-subhead",
2763
- "not-data-disabled:hover:bg-brand-soft not-data-disabled:active:overlay-medium",
2764
- "data-disabled:cursor-not-allowed"
2765
- ],
2766
- media: ["flex h-lh shrink-0 items-center"],
2767
- body: ["grid min-w-0 flex-1 gap-0.5"],
2768
- label: ["truncate font-semibold text-foreground", "in-data-disabled:text-foreground-disabled"],
2769
- description: ["block truncate font-sans text-footnote font-normal text-foreground-muted", "in-data-disabled:text-foreground-disabled"],
2770
- action: [
2771
- "relative z-10 col-start-2 flex h-lh items-center self-start pr-1",
2772
- "mt-option-inset pointer-coarse:mt-[calc((--spacing(11)-1lh)/2)]",
2773
- "font-sans text-subhead"
2774
- ],
2775
- note: ["px-2.5 py-2 font-sans text-callout text-foreground-hint"],
2776
- footer: ["shrink-0 border-t border-line-seam p-1"]
2777
- },
2778
- variants: {
2779
- scroll: { true: { scroller: ["max-h-72 overflow-y-auto overscroll-contain"] } },
2780
- fill: { true: {
2781
- root: ["flex min-h-0 flex-1 flex-col"],
2782
- frame: ["min-h-0 flex-1"],
2783
- scroller: ["flex-1 overflow-y-auto overscroll-contain"]
2784
- } },
2785
- hasAction: { true: { item: ["pr-11"] } }
2786
- }
2787
- });
2788
- //#endregion
2789
- //#region src/components/choice-list/choice-list.tsx
2790
- const SEARCH_ABOVE = 5;
2791
- const MAX_DESCRIPTION_LINES = 2;
2792
- function isDrawn(line) {
2793
- return line !== null && line !== void 0 && line !== false && line !== "";
2794
- }
2795
- function descriptionLines(description) {
2796
- return (Array.isArray(description) ? description : [description]).filter(isDrawn);
2797
- }
2798
- function CutLine({ id, slot, className, children }) {
2799
- const ref = useOverflowTitle();
2800
- return /* @__PURE__ */ jsx("span", {
2801
- ref,
2802
- id,
2803
- "data-slot": slot,
2804
- className,
2805
- children
2806
- });
2807
- }
2808
- const NOT_TEXT_INPUTS = /* @__PURE__ */ new Set([
2809
- "button",
2810
- "checkbox",
2811
- "radio",
2812
- "submit",
2813
- "reset",
2814
- "image",
2815
- "file",
2816
- "range",
2817
- "color"
2818
- ]);
2819
- function isTextEntry(element) {
2820
- if (!(element instanceof HTMLElement)) return false;
2821
- if (element.isContentEditable || element instanceof HTMLTextAreaElement) return true;
2822
- if (element instanceof HTMLSelectElement) return true;
2823
- return element instanceof HTMLInputElement && !NOT_TEXT_INPUTS.has(element.type);
2824
- }
2825
- function searchTextOf(item) {
2826
- if (item.searchText !== void 0) return item.searchText;
2827
- return typeof item.label === "string" ? item.label : "";
2828
- }
2829
- function visibleItems(items, needle, pinned, chosen, matchedValues) {
2830
- if (!needle) return {
2831
- rows: items,
2832
- noMatch: false
2833
- };
2834
- const matched = matchedValues ? new Set(matchedValues) : null;
2835
- const matches = (item) => matched ? matched.has(item.value) : searchTextOf(item).toLowerCase().includes(needle);
2836
- const pinnedSet = new Set(pinned);
2837
- const isPinned = (item) => pinnedSet.has(item.value);
2838
- const top = items.filter((item) => isPinned(item) || chosen.includes(item.value) && !matches(item));
2839
- const byValue = matchedValues ? new Map(items.map((item) => [item.value, item])) : null;
2840
- const rest = (byValue && matched ? [...matched].flatMap((value) => byValue.get(value) ?? []) : items).filter((item) => !top.includes(item) && matches(item));
2841
- return {
2842
- rows: [...top, ...rest],
2843
- noMatch: !items.some(matches)
2844
- };
2845
- }
2846
- function layoutSections(rows, groups) {
2847
- const known = new Set(groups?.map((group) => group.value));
2848
- const sections = groups ? groups.map((group) => ({
2849
- group,
2850
- rows: rows.filter((item) => item.group === group.value)
2851
- })) : [];
2852
- sections.push({
2853
- group: null,
2854
- rows: rows.filter((item) => !groups || item.group === void 0 || !known.has(item.group))
2855
- });
2856
- const drawn = sections.filter((section) => section.rows.length > 0);
2857
- const gridRowOf = /* @__PURE__ */ new Map();
2858
- let gridRows = 0;
2859
- for (const section of drawn) {
2860
- if (section.group) gridRows += 1;
2861
- for (const item of section.rows) {
2862
- gridRows += 1;
2863
- gridRowOf.set(item.value, gridRows);
2864
- }
2865
- }
2866
- return {
2867
- sections: drawn,
2868
- gridRowOf,
2869
- gridRows
2870
- };
2871
- }
2872
- function ChoiceList(props) {
2873
- const { items, search = "auto", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, disabled = false, searchLabel, searchPlaceholder, defaultSearchValue = "", searchValue, onSearchValueChange, matchedValues, clearSearchLabel, noMatchLabel, empty, groups, footer, fill = false, className } = props;
2874
- const value = props.value;
2875
- const chosen = useMemo(() => value === null ? [] : typeof value === "string" ? [value] : value, [value]);
2876
- const [ownQuery, setOwnQuery] = useState(defaultSearchValue);
2877
- const query = searchValue ?? ownQuery;
2878
- const setQuery = useCallback((next) => {
2879
- if (searchValue === void 0) setOwnQuery(next);
2880
- onSearchValueChange?.(next);
2881
- }, [searchValue, onSearchValueChange]);
2882
- const [pinned, setPinned] = useState(chosen);
2883
- const inputRef = useRef(null);
2884
- const baseId = useId();
2885
- const searchable = items.length > 0 && (search === "always" || items.length > SEARCH_ABOVE);
2886
- const [wasSearchable, setWasSearchable] = useState(searchable);
2887
- if (wasSearchable !== searchable) {
2888
- setWasSearchable(searchable);
2889
- if (!searchable) setOwnQuery("");
2890
- }
2891
- useEffect(() => {
2892
- if (!searchable && searchValue) onSearchValueChange?.("");
2893
- }, [
2894
- searchable,
2895
- searchValue,
2896
- onSearchValueChange
2897
- ]);
2898
- const [pinnedFor, setPinnedFor] = useState(query);
2899
- if (pinnedFor !== query) {
2900
- setPinnedFor(query);
2901
- setPinned(chosen);
2902
- }
2903
- const { rows, noMatch } = visibleItems(items, searchable ? query.trim().toLowerCase() : "", pinned, chosen, matchedValues);
2904
- const hasAction = rows.some((item) => item.action !== void 0 && item.action !== null);
2905
- const slots = choiceList({
2906
- scroll: searchable && !fill,
2907
- fill
2908
- });
2909
- useEffect(() => {
2910
- if (process.env.NODE_ENV === "production") return;
2911
- if (!ariaLabel && !ariaLabelledBy) console.warn("raft-ui: a ChoiceList has neither aria-label nor aria-labelledby.");
2912
- if (items.some((item) => descriptionLines(item.description).length > MAX_DESCRIPTION_LINES)) console.warn(`raft-ui: a ChoiceList item has more than ${MAX_DESCRIPTION_LINES} description lines; only the first ${MAX_DESCRIPTION_LINES} are drawn.`);
2913
- }, [
2914
- ariaLabel,
2915
- ariaLabelledBy,
2916
- items
2917
- ]);
2918
- const focusSearch = search === "always" && searchable;
2919
- const focusedSearch = useRef(false);
2920
- useEffect(() => {
2921
- if (!focusSearch || focusedSearch.current) return;
2922
- focusedSearch.current = true;
2923
- const input = inputRef.current;
2924
- if (!input || typeof window.matchMedia !== "function") return;
2925
- if (!window.matchMedia("(pointer: fine)").matches) return;
2926
- if (isTextEntry(document.activeElement)) return;
2927
- input.focus({ preventScroll: true });
2928
- }, [focusSearch]);
2929
- const changeQuery = useCallback((event) => {
2930
- setQuery(event.currentTarget.value);
2931
- }, [setQuery]);
2932
- const clearQuery = useCallback(() => {
2933
- setQuery("");
2934
- inputRef.current?.focus();
2935
- }, [setQuery]);
2936
- const { sections, gridRowOf, gridRows } = layoutSections(rows, groups);
2937
- const groupProps = {
2938
- "aria-label": ariaLabel,
2939
- "aria-labelledby": ariaLabelledBy,
2940
- disabled,
2941
- className: slots.group(),
2942
- style: { gridRow: `1 / span ${gridRows}` }
2943
- };
2944
- const rowElement = (item) => {
2945
- const id = `${baseId}-${items.indexOf(item)}`;
2946
- const lines = descriptionLines(item.description).slice(0, MAX_DESCRIPTION_LINES);
2947
- const rowDisabled = disabled || item.disabled === true;
2948
- const markProps = {
2949
- value: item.value,
2950
- disabled: item.disabled,
2951
- "aria-labelledby": `${id}-label`,
2952
- "aria-describedby": lines.length > 0 ? `${id}-description` : void 0
2953
- };
2954
- return /* @__PURE__ */ jsxs("label", {
2955
- "data-slot": "choice-list-item",
2956
- "data-disabled": rowDisabled ? "" : void 0,
2957
- className: slots.item({ hasAction }),
2958
- children: [
2959
- props.multiple ? /* @__PURE__ */ jsx(Checkbox, { ...markProps }) : /* @__PURE__ */ jsx(RadioGroupItem, { ...markProps }),
2960
- item.media !== void 0 && item.media !== null ? /* @__PURE__ */ jsx("span", {
2961
- "data-slot": "choice-list-media",
2962
- className: slots.media(),
2963
- children: item.media
2964
- }) : null,
2965
- /* @__PURE__ */ jsxs("span", {
2966
- "data-slot": "choice-list-body",
2967
- className: slots.body(),
2968
- children: [/* @__PURE__ */ jsx(CutLine, {
2969
- id: `${id}-label`,
2970
- slot: "choice-list-label",
2971
- className: slots.label(),
2972
- children: item.label
2973
- }), lines.length > 0 ? /* @__PURE__ */ jsx("span", {
2974
- id: `${id}-description`,
2975
- className: "contents",
2976
- children: lines.map((line, index) => /* @__PURE__ */ jsx(CutLine, {
2977
- slot: "choice-list-description",
2978
- className: slots.description(),
2979
- children: line
2980
- }, index))
2981
- }) : null]
2982
- })
2983
- ]
2984
- }, item.value);
2985
- };
2986
- const rowElements = sections.map((section, index) => {
2987
- if (!section.group) return section.rows.map(rowElement);
2988
- const headingId = `${baseId}-group-${index}`;
2989
- const start = (gridRowOf.get(section.rows[0].value) ?? 1) - 1;
2990
- return /* @__PURE__ */ jsxs("div", {
2991
- role: "group",
2992
- "aria-labelledby": headingId,
2993
- "data-slot": "choice-list-section",
2994
- className: slots.section(),
2995
- style: { gridRow: `${start} / span ${section.rows.length + 1}` },
2996
- children: [/* @__PURE__ */ jsx("div", {
2997
- id: headingId,
2998
- "data-slot": "choice-list-heading",
2999
- className: slots.heading(),
3000
- children: section.group.label
3001
- }), section.rows.map(rowElement)]
3002
- }, section.group.value);
3003
- });
3004
- return /* @__PURE__ */ jsxs("div", {
3005
- "data-slot": "choice-list",
3006
- className: slots.root({ className }),
3007
- children: [searchable ? /* @__PURE__ */ jsxs(InputGroup, {
3008
- "data-slot": "choice-list-search",
3009
- children: [
3010
- /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(Search, { "aria-hidden": true }) }),
3011
- /* @__PURE__ */ jsx(InputGroupInput, {
3012
- ref: inputRef,
3013
- type: "search",
3014
- value: query,
3015
- onChange: changeQuery,
3016
- "aria-label": searchLabel,
3017
- placeholder: searchPlaceholder,
3018
- disabled,
3019
- autoComplete: "off",
3020
- className: slots.searchInput()
3021
- }),
3022
- query ? /* @__PURE__ */ jsx(InputGroupAddon, {
3023
- align: "inline-end",
3024
- children: /* @__PURE__ */ jsx(Button$1, {
3025
- variant: "ghost",
3026
- size: "icon-md",
3027
- "aria-label": clearSearchLabel,
3028
- onClick: clearQuery,
3029
- children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
3030
- })
3031
- }) : null
3032
- ]
3033
- }) : null, /* @__PURE__ */ jsxs("div", {
3034
- "data-slot": "choice-list-frame",
3035
- className: slots.frame(),
3036
- children: [/* @__PURE__ */ jsx("div", {
3037
- "data-slot": "choice-list-scroller",
3038
- className: slots.scroller(),
3039
- children: items.length === 0 ? /* @__PURE__ */ jsx("p", {
3040
- "data-slot": "choice-list-note",
3041
- className: slots.note(),
3042
- children: empty
3043
- }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [rows.length > 0 ? /* @__PURE__ */ jsxs("div", {
3044
- "data-slot": "choice-list-rows",
3045
- className: slots.rows(),
3046
- children: [props.multiple ? /* @__PURE__ */ jsx(CheckboxGroup, {
3047
- "data-slot": "choice-list-group",
3048
- value: props.value,
3049
- onValueChange: props.onValueChange,
3050
- ...groupProps,
3051
- children: rowElements
3052
- }) : /* @__PURE__ */ jsx(RadioGroup, {
3053
- "data-slot": "choice-list-group",
3054
- value: props.value,
3055
- onValueChange: props.onValueChange,
3056
- ...groupProps,
3057
- children: rowElements
3058
- }), sections.flatMap((section) => section.rows.map((item) => item.action !== void 0 && item.action !== null ? /* @__PURE__ */ jsx("div", {
3059
- "data-slot": "choice-list-action",
3060
- className: slots.action(),
3061
- style: { gridRow: gridRowOf.get(item.value) },
3062
- children: item.action
3063
- }, item.value) : null))]
3064
- }) : null, searchable ? /* @__PURE__ */ jsx("p", {
3065
- role: "status",
3066
- "data-slot": "choice-list-status",
3067
- className: noMatch ? slots.note() : "sr-only",
3068
- children: noMatch ? noMatchLabel : null
3069
- }) : null] })
3070
- }), footer !== void 0 && footer !== null ? /* @__PURE__ */ jsx("div", {
3071
- "data-slot": "choice-list-footer",
3072
- className: slots.footer(),
3073
- children: footer
3074
- }) : null]
3075
- })]
3076
- });
3077
- }
3078
- //#endregion
3079
- export { Dialog as AnnouncementDialog, AnnouncementDialogAction, AnnouncementDialogBody, DialogClose as AnnouncementDialogClose, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle, DialogTrigger as AnnouncementDialogTrigger, AvatarGroupButton, COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, ChoiceList, ComboboxSelect, CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger, CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime, CommentMargin, CommentMarginItem, CommentMarker, DEFAULT_DRAG_MESSAGES, DRAG_AUTO_SCROLL_ZONE_PX, DRAG_KEYBOARD_CODES, DRAG_KEYBOARD_OFFSET_PX, DRAG_LIFT_Z_INDEX, DRAG_SETTLE, DRAG_SIBLING_TRANSITION, DROP_GLIDE, DialogCommit, DialogFooterNote, DialogSheetBack, DialogSheetContent, DragLift, DragLiveRegion, DragRefusalHint, DragRemeasure, DraggedSlot, DropPlaceholder, DropdownMenuLinkItem, Ellipsis, EllipsisToggle, FilterChip, FilterChipLabel, FilterChipRemove, HoldThenMoveTouchSensor, InboxItemLeadingBadge, InboxItemSkeleton, InboxItemSkeletonList, LIFT_CLASS, ListRowSkeleton, MOUSE_DRAG_DISTANCE_PX, MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName, MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup, MessageQuotedPreviewAuthorDelegatedBy, MessageReferenceIcon, MessageReferenceReadMark, Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue, MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel, ModeBar, ModeBarAction, ModeBarContent, ModeBarDescription, ModeBarTitle, MouseOnlyPointerSensor, ORIGIN_OPACITY, OverlayLayerProvider, PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_POINTER_QUERY, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD, PopoverActions, PopoverBody, PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta, PullToRefreshIndicator, TOUCH_DRAG_DISTANCE_PX, TOUCH_DRAG_HOLD_MS, TOUCH_DRAG_TOLERANCE_PX, cancelDrag, createDragAnnouncements, dragInstructions, dragListStrategy, dragMessages, keepGrabInside, listKeyboardCoordinates, resolveCommentMarginLayout, setCommentHighlights, useCommentMarginFits, useDragAcrossLists, useDragAnnouncer, useDragLists, useDragSensors, useDragSiblingTransition, useHoldThenMoveTouchSensor, useMouseOnlyPointerSensor, useParentOverlayKind, usePullToRefresh, useTruncated };
1
+ import "./lib/drag-escape.mjs";
2
+ import { OverlayLayerProvider, useParentOverlayKind } from "./lib/overlay-depth.mjs";
3
+ import { Dialog, DialogClose, DialogFooterNote, DialogSheetBack, DialogTrigger } from "./components/dialog/dialog.mjs";
4
+ import { DialogCommit, DialogSheetContent } from "./components/dialog/dialog-sheet.mjs";
5
+ import { DropdownMenuLinkItem } from "./components/dropdown-menu/dropdown-menu.mjs";
6
+ import { HoldThenMoveTouchSensor, MouseOnlyPointerSensor, TOUCH_DRAG_DISTANCE_PX, TOUCH_DRAG_HOLD_MS, TOUCH_DRAG_TOLERANCE_PX, useHoldThenMoveTouchSensor, useMouseOnlyPointerSensor } from "./lib/touch-drag.mjs";
7
+ import { PopoverActions, PopoverBody } from "./components/popover/popover.mjs";
8
+ import { MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel } from "./components/mobile-nav/mobile-nav-server.mjs";
9
+ import { PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta } from "./components/preview-card/preview-card.mjs";
10
+ import { AvatarGroupButton } from "./components/avatar/avatar.mjs";
11
+ import { MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName } from "./components/message-item/message-item-delegated.mjs";
12
+ import { MessageReferenceIcon, MessageReferenceReadMark } from "./components/message-item/message-reference.mjs";
13
+ import { MessageQuotedPreviewAuthorDelegatedBy } from "./components/message-quoted-preview/message-quoted-preview.mjs";
14
+ import { MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup } from "./components/message-list/message-list-jump.mjs";
15
+ import { DEFAULT_DRAG_MESSAGES, createDragAnnouncements, dragInstructions, dragMessages } from "./components/drag/drag-announcements.mjs";
16
+ import { LIFT_CLASS } from "./components/drag/drag.recipe.mjs";
17
+ import { DRAG_AUTO_SCROLL_ZONE_PX, DRAG_KEYBOARD_OFFSET_PX, DRAG_LIFT_Z_INDEX, DRAG_SETTLE, DRAG_SIBLING_TRANSITION, DROP_GLIDE, DragLift, ORIGIN_OPACITY, cancelDrag, keepGrabInside, useDragSiblingTransition } from "./components/drag/drag-lift.mjs";
18
+ import { DRAG_KEYBOARD_CODES, DragRemeasure, MOUSE_DRAG_DISTANCE_PX, listKeyboardCoordinates, useDragAcrossLists, useDragSensors } from "./components/drag/drag-sensors.mjs";
19
+ import { DragRefusalHint, DropPlaceholder } from "./components/drag/drop-placeholder.mjs";
20
+ import { PanelSplitAction, PanelSplitActionMain, PanelSplitActionMenu, PanelSplitActionToggle } from "./components/panel/panel-split-action.mjs";
21
+ import { ModeBar, ModeBarAction, ModeBarContent, ModeBarDescription, ModeBarTitle } from "./components/mode-bar/mode-bar.mjs";
22
+ import { ComboboxSelect } from "./components/combobox-select/combobox-select.mjs";
23
+ import { AnnouncementDialogAction, AnnouncementDialogBody, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle } from "./components/announcement-dialog/announcement-dialog.mjs";
24
+ import { CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger } from "./components/comment/comment-card.mjs";
25
+ import { CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime } from "./components/comment/comment-entry.mjs";
26
+ import { COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, setCommentHighlights } from "./components/comment/comment-highlight.mjs";
27
+ import { resolveCommentMarginLayout } from "./components/comment/comment-margin-layout.mjs";
28
+ import { CommentMargin, CommentMarginItem } from "./components/comment/comment-margin.mjs";
29
+ import { CommentMarker } from "./components/comment/comment-marker.mjs";
30
+ import { useCommentMarginFits } from "./components/comment/use-comment-margin-fits.mjs";
31
+ import { DragLiveRegion, DraggedSlot, dragListStrategy, useDragAnnouncer, useDragLists } from "./components/drag/drag-lists.mjs";
32
+ import { Ellipsis, EllipsisToggle } from "./components/ellipsis/ellipsis.mjs";
33
+ import { useTruncated } from "./lib/use-truncated.mjs";
34
+ import { InboxItemLeadingBadge } from "./components/inbox-item/inbox-item-leading-badge.mjs";
35
+ import { InboxItemSkeleton, InboxItemSkeletonList } from "./components/inbox-item/inbox-item-skeleton.mjs";
36
+ import { ListRowSkeleton } from "./components/list-row/list-row-skeleton.mjs";
37
+ import { Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue } from "./components/meter/meter.mjs";
38
+ import { FilterChip, FilterChipLabel, FilterChipRemove } from "./components/filter-chip/filter-chip.mjs";
39
+ import { PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD } from "./components/pull-to-refresh/pull-to-refresh-gesture.mjs";
40
+ import { PULL_TO_REFRESH_POINTER_QUERY, usePullToRefresh } from "./components/pull-to-refresh/use-pull-to-refresh.mjs";
41
+ import { PullToRefreshIndicator } from "./components/pull-to-refresh/pull-to-refresh-indicator.mjs";
42
+ import { ChoiceList } from "./components/choice-list/choice-list.mjs";
43
+ export { Dialog as AnnouncementDialog, AnnouncementDialogAction, AnnouncementDialogBody, DialogClose as AnnouncementDialogClose, AnnouncementDialogContent, AnnouncementDialogFooter, AnnouncementDialogHeader, AnnouncementDialogLabel, AnnouncementDialogPicture, AnnouncementDialogSecondaryAction, AnnouncementDialogText, AnnouncementDialogTitle, DialogTrigger as AnnouncementDialogTrigger, AvatarGroupButton, COMMENT_HIGHLIGHT, COMMENT_HIGHLIGHT_ACTIVE, ChoiceList, ComboboxSelect, CommentCard, CommentCardMeta, CommentCardQuote, CommentCardTrigger, CommentEntry, CommentEntryAuthor, CommentEntryBody, CommentEntryHeader, CommentEntryTime, CommentMargin, CommentMarginItem, CommentMarker, DEFAULT_DRAG_MESSAGES, DRAG_AUTO_SCROLL_ZONE_PX, DRAG_KEYBOARD_CODES, DRAG_KEYBOARD_OFFSET_PX, DRAG_LIFT_Z_INDEX, DRAG_SETTLE, DRAG_SIBLING_TRANSITION, DROP_GLIDE, DialogCommit, DialogFooterNote, DialogSheetBack, DialogSheetContent, DragLift, DragLiveRegion, DragRefusalHint, DragRemeasure, DraggedSlot, DropPlaceholder, DropdownMenuLinkItem, Ellipsis, EllipsisToggle, FilterChip, FilterChipLabel, FilterChipRemove, HoldThenMoveTouchSensor, InboxItemLeadingBadge, InboxItemSkeleton, InboxItemSkeletonList, LIFT_CLASS, ListRowSkeleton, MOUSE_DRAG_DISTANCE_PX, MessageItemDelegatedAvatar, MessageItemDelegatedBy, MessageItemDelegatedByButton, MessageItemDelegatedByName, MessageListJumpControls, MessageListJumpPill, MessageListJumpPillDismiss, MessageListJumpPillGroup, MessageQuotedPreviewAuthorDelegatedBy, MessageReferenceIcon, MessageReferenceReadMark, Meter, MeterHeader, MeterIndicator, MeterLabel, MeterTrack, MeterValue, MobileNavServerItem, MobileNavServerPopover, MobileNavServerPopoverContent, MobileNavServerPopoverDestination, MobileNavServerPopoverDestinations, MobileNavServerPopoverGroup, MobileNavServerPopoverList, MobileNavServerPopoverRow, MobileNavServerPopoverRowBody, MobileNavServerPopoverRowDescription, MobileNavServerPopoverRowLabel, ModeBar, ModeBarAction, ModeBarContent, ModeBarDescription, ModeBarTitle, MouseOnlyPointerSensor, ORIGIN_OPACITY, OverlayLayerProvider, PULL_TO_REFRESH_DAMPING, PULL_TO_REFRESH_HOLD_DISTANCE, PULL_TO_REFRESH_MAX_DISTANCE, PULL_TO_REFRESH_POINTER_QUERY, PULL_TO_REFRESH_SNAP_BACK_MS, PULL_TO_REFRESH_THRESHOLD, PanelSplitAction, PanelSplitActionMain, PanelSplitActionMenu, PanelSplitActionToggle, PopoverActions, PopoverBody, PreviewCardActions, PreviewCardBody, PreviewCardEyebrow, PreviewCardFooter, PreviewCardMeta, PullToRefreshIndicator, TOUCH_DRAG_DISTANCE_PX, TOUCH_DRAG_HOLD_MS, TOUCH_DRAG_TOLERANCE_PX, cancelDrag, createDragAnnouncements, dragInstructions, dragListStrategy, dragMessages, keepGrabInside, listKeyboardCoordinates, resolveCommentMarginLayout, setCommentHighlights, useCommentMarginFits, useDragAcrossLists, useDragAnnouncer, useDragLists, useDragSensors, useDragSiblingTransition, useHoldThenMoveTouchSensor, useMouseOnlyPointerSensor, useParentOverlayKind, usePullToRefresh, useTruncated };