@exsto/ds 0.0.1-rc.2 → 0.0.1-rc.22

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 (346) hide show
  1. package/README.md +171 -28
  2. package/dist/date-picker.js +2 -0
  3. package/dist/ds/src/date-picker.d.ts +9 -0
  4. package/dist/ds/src/file-input.d.ts +9 -0
  5. package/dist/ds/src/index.d.ts +2 -3
  6. package/dist/ds/src/rich-text-editor.d.ts +8 -0
  7. package/dist/ds/src/time-field.d.ts +9 -0
  8. package/dist/file-input.js +2 -0
  9. package/dist/index.js +79 -2
  10. package/dist/rich-text-editor.js +4 -0
  11. package/dist/source-hash.txt +1 -0
  12. package/dist/styles.css +1201 -404
  13. package/dist/theme.css +125 -7
  14. package/dist/time-field.js +2 -0
  15. package/dist/ui/src/components/edyn/animated-number/animated-number.d.ts +30 -0
  16. package/dist/ui/src/components/edyn/animated-number/animated-number.js +108 -0
  17. package/dist/ui/src/components/edyn/animated-number/animated-number.js.map +1 -0
  18. package/dist/ui/src/components/edyn/animated-number/animated-number.test.d.ts +9 -0
  19. package/dist/ui/src/components/edyn/animated-number/index.d.ts +1 -0
  20. package/dist/ui/src/components/edyn/audio-player/audio-player.d.ts +3 -2
  21. package/dist/ui/src/components/edyn/audio-player/audio-player.js +86 -0
  22. package/dist/ui/src/components/edyn/audio-player/audio-player.js.map +1 -0
  23. package/dist/ui/src/components/edyn/date-picker/date-picker.d.ts +86 -0
  24. package/dist/ui/src/components/edyn/date-picker/date-picker.js +302 -0
  25. package/dist/ui/src/components/edyn/date-picker/date-picker.js.map +1 -0
  26. package/dist/ui/src/components/edyn/date-picker/date-picker.state.test.d.ts +9 -0
  27. package/dist/ui/src/components/edyn/date-picker/date-text.d.ts +40 -0
  28. package/dist/ui/src/components/edyn/date-picker/date-text.js +105 -0
  29. package/dist/ui/src/components/edyn/date-picker/date-text.js.map +1 -0
  30. package/dist/ui/src/components/edyn/date-picker/index.d.ts +2 -0
  31. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.d.ts +28 -0
  32. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js +70 -0
  33. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.js.map +1 -0
  34. package/dist/ui/src/components/edyn/date-range-picker/date-range-picker.test.d.ts +9 -0
  35. package/dist/ui/src/components/edyn/date-range-picker/index.d.ts +1 -0
  36. package/dist/ui/src/components/edyn/file-input/file-input.d.ts +90 -0
  37. package/dist/ui/src/components/edyn/file-input/file-input.js +261 -0
  38. package/dist/ui/src/components/edyn/file-input/file-input.js.map +1 -0
  39. package/dist/ui/src/components/edyn/file-input/file-input.test.d.ts +8 -0
  40. package/dist/ui/src/components/edyn/file-input/index.d.ts +1 -0
  41. package/dist/ui/src/components/edyn/initials-avatar/index.d.ts +1 -0
  42. package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.d.ts +54 -0
  43. package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.js +86 -0
  44. package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.js.map +1 -0
  45. package/dist/ui/src/components/edyn/initials-avatar/initials-avatar.test.d.ts +10 -0
  46. package/dist/ui/src/components/edyn/multi-select/index.d.ts +1 -0
  47. package/dist/ui/src/components/edyn/multi-select/multi-select.d.ts +117 -0
  48. package/dist/ui/src/components/edyn/multi-select/multi-select.js +342 -0
  49. package/dist/ui/src/components/edyn/multi-select/multi-select.js.map +1 -0
  50. package/dist/ui/src/components/edyn/multi-select/multi-select.test.d.ts +9 -0
  51. package/dist/ui/src/components/edyn/presence/index.d.ts +1 -0
  52. package/dist/ui/src/components/edyn/presence/presence.d.ts +15 -0
  53. package/dist/ui/src/components/edyn/presence/presence.js +58 -0
  54. package/dist/ui/src/components/edyn/presence/presence.js.map +1 -0
  55. package/dist/ui/src/components/edyn/presence/presence.test.d.ts +8 -0
  56. package/dist/ui/src/components/edyn/progress-line/index.d.ts +1 -0
  57. package/dist/ui/src/components/edyn/progress-line/progress-line.d.ts +23 -0
  58. package/dist/ui/src/components/edyn/progress-line/progress-line.js +100 -0
  59. package/dist/ui/src/components/edyn/progress-line/progress-line.js.map +1 -0
  60. package/dist/ui/src/components/edyn/progress-line/progress-line.test.d.ts +9 -0
  61. package/dist/ui/src/components/edyn/rich-text-content/allow-list.d.ts +11 -0
  62. package/dist/ui/src/components/edyn/rich-text-content/allow-list.js +37 -0
  63. package/dist/ui/src/components/edyn/rich-text-content/allow-list.js.map +1 -0
  64. package/dist/ui/src/components/edyn/rich-text-content/index.d.ts +2 -0
  65. package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.d.ts +22 -0
  66. package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.js +124 -0
  67. package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.js.map +1 -0
  68. package/dist/ui/src/components/edyn/rich-text-content/rich-text-content.test.d.ts +8 -0
  69. package/dist/ui/src/components/edyn/rich-text-editor/extensions.d.ts +18 -0
  70. package/dist/ui/src/components/edyn/rich-text-editor/extensions.js +124 -0
  71. package/dist/ui/src/components/edyn/rich-text-editor/extensions.js.map +1 -0
  72. package/dist/ui/src/components/edyn/rich-text-editor/index.d.ts +3 -0
  73. package/dist/ui/src/components/edyn/rich-text-editor/mention-list.d.ts +17 -0
  74. package/dist/ui/src/components/edyn/rich-text-editor/mention-list.js +232 -0
  75. package/dist/ui/src/components/edyn/rich-text-editor/mention-list.js.map +1 -0
  76. package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.d.ts +53 -0
  77. package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.js +244 -0
  78. package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.js.map +1 -0
  79. package/dist/ui/src/components/edyn/rich-text-editor/rich-text-editor.test.d.ts +6 -0
  80. package/dist/ui/src/components/edyn/rich-text-editor/serialize.d.ts +13 -0
  81. package/dist/ui/src/components/edyn/rich-text-editor/serialize.js +55 -0
  82. package/dist/ui/src/components/edyn/rich-text-editor/serialize.js.map +1 -0
  83. package/dist/ui/src/components/edyn/rich-text-editor/sync.d.ts +19 -0
  84. package/dist/ui/src/components/edyn/rich-text-editor/sync.js +34 -0
  85. package/dist/ui/src/components/edyn/rich-text-editor/sync.js.map +1 -0
  86. package/dist/ui/src/components/edyn/rich-text-editor/sync.test.d.ts +11 -0
  87. package/dist/ui/src/components/edyn/rich-text-editor/toolbar.d.ts +20 -0
  88. package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js +235 -0
  89. package/dist/ui/src/components/edyn/rich-text-editor/toolbar.js.map +1 -0
  90. package/dist/ui/src/components/edyn/rich-text-editor/types.d.ts +68 -0
  91. package/dist/ui/src/components/edyn/rich-text-editor/types.js +26 -0
  92. package/dist/ui/src/components/edyn/rich-text-editor/types.js.map +1 -0
  93. package/dist/ui/src/components/edyn/time-field/index.d.ts +1 -0
  94. package/dist/ui/src/components/edyn/time-field/time-field.d.ts +85 -0
  95. package/dist/ui/src/components/edyn/time-field/time-field.js +337 -0
  96. package/dist/ui/src/components/edyn/time-field/time-field.js.map +1 -0
  97. package/dist/ui/src/components/edyn/time-field/time-field.test.d.ts +8 -0
  98. package/dist/ui/src/components/edyn/video-player/video-player.d.ts +2 -2
  99. package/dist/ui/src/components/edyn/video-player/video-player.js +132 -0
  100. package/dist/ui/src/components/edyn/video-player/video-player.js.map +1 -0
  101. package/dist/ui/src/components/forwarded-props.test.d.ts +8 -0
  102. package/dist/ui/src/components/ui/accordion/accordion.d.ts +9 -8
  103. package/dist/ui/src/components/ui/accordion/accordion.js +83 -0
  104. package/dist/ui/src/components/ui/accordion/accordion.js.map +1 -0
  105. package/dist/ui/src/components/ui/alert/alert.js +47 -0
  106. package/dist/ui/src/components/ui/alert/alert.js.map +1 -0
  107. package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js +97 -0
  108. package/dist/ui/src/components/ui/alert-dialog/alert-dialog.js.map +1 -0
  109. package/dist/ui/src/components/ui/aspect-ratio/aspect-ratio.js +15 -0
  110. package/dist/ui/src/components/ui/aspect-ratio/aspect-ratio.js.map +1 -0
  111. package/dist/ui/src/components/ui/attachment/attachment.d.ts +1 -1
  112. package/dist/ui/src/components/ui/attachment/attachment.js +105 -0
  113. package/dist/ui/src/components/ui/attachment/attachment.js.map +1 -0
  114. package/dist/ui/src/components/ui/avatar/avatar.js +51 -0
  115. package/dist/ui/src/components/ui/avatar/avatar.js.map +1 -0
  116. package/dist/ui/src/components/ui/badge/badge.d.ts +1 -1
  117. package/dist/ui/src/components/ui/badge/badge.js +47 -0
  118. package/dist/ui/src/components/ui/badge/badge.js.map +1 -0
  119. package/dist/ui/src/components/ui/breadcrumb/breadcrumb.js +73 -0
  120. package/dist/ui/src/components/ui/breadcrumb/breadcrumb.js.map +1 -0
  121. package/dist/ui/src/components/ui/bubble/bubble.js +74 -0
  122. package/dist/ui/src/components/ui/bubble/bubble.js.map +1 -0
  123. package/dist/ui/src/components/ui/button/button.behavior.test.d.ts +8 -0
  124. package/dist/ui/src/components/ui/button/button.d.ts +1 -1
  125. package/dist/ui/src/components/ui/button/button.js +68 -0
  126. package/dist/ui/src/components/ui/button/button.js.map +1 -0
  127. package/dist/ui/src/components/ui/button-group/button-group.js +37 -0
  128. package/dist/ui/src/components/ui/button-group/button-group.js.map +1 -0
  129. package/dist/ui/src/components/ui/calendar/calendar.behavior.test.d.ts +9 -0
  130. package/dist/ui/src/components/ui/calendar/calendar.d.ts +1 -1
  131. package/dist/ui/src/components/ui/calendar/calendar.js +141 -0
  132. package/dist/ui/src/components/ui/calendar/calendar.js.map +1 -0
  133. package/dist/ui/src/components/ui/card/card.js +57 -0
  134. package/dist/ui/src/components/ui/card/card.js.map +1 -0
  135. package/dist/ui/src/components/ui/carousel/carousel.js +136 -0
  136. package/dist/ui/src/components/ui/carousel/carousel.js.map +1 -0
  137. package/dist/ui/src/components/ui/chart/chart.js +168 -0
  138. package/dist/ui/src/components/ui/chart/chart.js.map +1 -0
  139. package/dist/ui/src/components/ui/checkbox/checkbox.js +21 -0
  140. package/dist/ui/src/components/ui/checkbox/checkbox.js.map +1 -0
  141. package/dist/ui/src/components/ui/collapsible/collapsible.js +26 -0
  142. package/dist/ui/src/components/ui/collapsible/collapsible.js.map +1 -0
  143. package/dist/ui/src/components/ui/combobox/combobox.d.ts +15 -1
  144. package/dist/ui/src/components/ui/combobox/combobox.js +111 -0
  145. package/dist/ui/src/components/ui/combobox/combobox.js.map +1 -0
  146. package/dist/ui/src/components/ui/command/command.d.ts +17 -1
  147. package/dist/ui/src/components/ui/command/command.js +118 -0
  148. package/dist/ui/src/components/ui/command/command.js.map +1 -0
  149. package/dist/ui/src/components/ui/context-menu/context-menu.d.ts +1 -1
  150. package/dist/ui/src/components/ui/context-menu/context-menu.js +145 -0
  151. package/dist/ui/src/components/ui/context-menu/context-menu.js.map +1 -0
  152. package/dist/ui/src/components/ui/dialog/dialog.js +94 -0
  153. package/dist/ui/src/components/ui/dialog/dialog.js.map +1 -0
  154. package/dist/ui/src/components/ui/direction/direction.js +2 -0
  155. package/dist/ui/src/components/ui/drawer/drawer.js +88 -0
  156. package/dist/ui/src/components/ui/drawer/drawer.js.map +1 -0
  157. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.d.ts +12 -1
  158. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js +159 -0
  159. package/dist/ui/src/components/ui/dropdown-menu/dropdown-menu.js.map +1 -0
  160. package/dist/ui/src/components/ui/empty/empty.d.ts +5 -1
  161. package/dist/ui/src/components/ui/empty/empty.js +72 -0
  162. package/dist/ui/src/components/ui/empty/empty.js.map +1 -0
  163. package/dist/ui/src/components/ui/field/field.js +36 -0
  164. package/dist/ui/src/components/ui/field/field.js.map +1 -0
  165. package/dist/ui/src/components/ui/hover-card/hover-card.js +39 -0
  166. package/dist/ui/src/components/ui/hover-card/hover-card.js.map +1 -0
  167. package/dist/ui/src/components/ui/indicator/indicator.js +79 -0
  168. package/dist/ui/src/components/ui/indicator/indicator.js.map +1 -0
  169. package/dist/ui/src/components/ui/input/input.js +16 -0
  170. package/dist/ui/src/components/ui/input/input.js.map +1 -0
  171. package/dist/ui/src/components/ui/input-group/input-group.d.ts +1 -1
  172. package/dist/ui/src/components/ui/input-group/input-group.js +77 -0
  173. package/dist/ui/src/components/ui/input-group/input-group.js.map +1 -0
  174. package/dist/ui/src/components/ui/input-otp/input-otp.js +48 -0
  175. package/dist/ui/src/components/ui/input-otp/input-otp.js.map +1 -0
  176. package/dist/ui/src/components/ui/item/item.d.ts +1 -1
  177. package/dist/ui/src/components/ui/item/item.js +122 -0
  178. package/dist/ui/src/components/ui/item/item.js.map +1 -0
  179. package/dist/ui/src/components/ui/kbd/kbd.js +21 -0
  180. package/dist/ui/src/components/ui/kbd/kbd.js.map +1 -0
  181. package/dist/ui/src/components/ui/label/label.js +14 -0
  182. package/dist/ui/src/components/ui/label/label.js.map +1 -0
  183. package/dist/ui/src/components/ui/marker/marker.js +44 -0
  184. package/dist/ui/src/components/ui/marker/marker.js.map +1 -0
  185. package/dist/ui/src/components/ui/menubar/menubar.js +140 -0
  186. package/dist/ui/src/components/ui/menubar/menubar.js.map +1 -0
  187. package/dist/ui/src/components/ui/message/message.js +50 -0
  188. package/dist/ui/src/components/ui/message/message.js.map +1 -0
  189. package/dist/ui/src/components/ui/message-scroller/message-scroller.js +61 -0
  190. package/dist/ui/src/components/ui/message-scroller/message-scroller.js.map +1 -0
  191. package/dist/ui/src/components/ui/native-select/native-select.js +39 -0
  192. package/dist/ui/src/components/ui/native-select/native-select.js.map +1 -0
  193. package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js +87 -0
  194. package/dist/ui/src/components/ui/navigation-menu/navigation-menu.js.map +1 -0
  195. package/dist/ui/src/components/ui/pagination/pagination.d.ts +3 -2
  196. package/dist/ui/src/components/ui/pagination/pagination.js +104 -0
  197. package/dist/ui/src/components/ui/pagination/pagination.js.map +1 -0
  198. package/dist/ui/src/components/ui/popover/popover.d.ts +1 -1
  199. package/dist/ui/src/components/ui/popover/popover.js +59 -0
  200. package/dist/ui/src/components/ui/popover/popover.js.map +1 -0
  201. package/dist/ui/src/components/ui/progress/progress.behavior.test.d.ts +8 -0
  202. package/dist/ui/src/components/ui/progress/progress.d.ts +43 -1
  203. package/dist/ui/src/components/ui/progress/progress.js +70 -0
  204. package/dist/ui/src/components/ui/progress/progress.js.map +1 -0
  205. package/dist/ui/src/components/ui/questionnaire/questionnaire.js +171 -0
  206. package/dist/ui/src/components/ui/questionnaire/questionnaire.js.map +1 -0
  207. package/dist/ui/src/components/ui/radio-group/radio-group.js +29 -0
  208. package/dist/ui/src/components/ui/radio-group/radio-group.js.map +1 -0
  209. package/dist/ui/src/components/ui/resizable/resizable.js +29 -0
  210. package/dist/ui/src/components/ui/resizable/resizable.js.map +1 -0
  211. package/dist/ui/src/components/ui/scroll-area/index.d.ts +1 -0
  212. package/dist/ui/src/components/ui/scroll-area/popup-list-scroll.d.ts +13 -0
  213. package/dist/ui/src/components/ui/scroll-area/popup-list-scroll.js +69 -0
  214. package/dist/ui/src/components/ui/scroll-area/popup-list-scroll.js.map +1 -0
  215. package/dist/ui/src/components/ui/scroll-area/scroll-area.behavior.test.d.ts +12 -0
  216. package/dist/ui/src/components/ui/scroll-area/scroll-area.d.ts +57 -6
  217. package/dist/ui/src/components/ui/scroll-area/scroll-area.js +125 -0
  218. package/dist/ui/src/components/ui/scroll-area/scroll-area.js.map +1 -0
  219. package/dist/ui/src/components/ui/select/select.js +97 -0
  220. package/dist/ui/src/components/ui/select/select.js.map +1 -0
  221. package/dist/ui/src/components/ui/separator/separator.behavior.test.d.ts +14 -0
  222. package/dist/ui/src/components/ui/separator/separator.d.ts +14 -0
  223. package/dist/ui/src/components/ui/separator/separator.js +30 -0
  224. package/dist/ui/src/components/ui/separator/separator.js.map +1 -0
  225. package/dist/ui/src/components/ui/sheet/sheet.d.ts +8 -0
  226. package/dist/ui/src/components/ui/sheet/sheet.js +99 -0
  227. package/dist/ui/src/components/ui/sheet/sheet.js.map +1 -0
  228. package/dist/ui/src/components/ui/sidebar/sidebar.js +383 -0
  229. package/dist/ui/src/components/ui/sidebar/sidebar.js.map +1 -0
  230. package/dist/ui/src/components/ui/skeleton/skeleton.js +14 -0
  231. package/dist/ui/src/components/ui/skeleton/skeleton.js.map +1 -0
  232. package/dist/ui/src/components/ui/slider/slider.js +49 -0
  233. package/dist/ui/src/components/ui/slider/slider.js.map +1 -0
  234. package/dist/ui/src/components/ui/spinner/spinner.js +28 -0
  235. package/dist/ui/src/components/ui/spinner/spinner.js.map +1 -0
  236. package/dist/ui/src/components/ui/switch/switch.js +20 -0
  237. package/dist/ui/src/components/ui/switch/switch.js.map +1 -0
  238. package/dist/ui/src/components/ui/table/table.behavior.test.d.ts +4 -5
  239. package/dist/ui/src/components/ui/table/table.d.ts +15 -1
  240. package/dist/ui/src/components/ui/table/table.js +81 -0
  241. package/dist/ui/src/components/ui/table/table.js.map +1 -0
  242. package/dist/ui/src/components/ui/tabs/tabs.d.ts +14 -8
  243. package/dist/ui/src/components/ui/tabs/tabs.js +115 -0
  244. package/dist/ui/src/components/ui/tabs/tabs.js.map +1 -0
  245. package/dist/ui/src/components/ui/textarea/textarea.js +14 -0
  246. package/dist/ui/src/components/ui/textarea/textarea.js.map +1 -0
  247. package/dist/ui/src/components/ui/toast/toast.js +124 -0
  248. package/dist/ui/src/components/ui/toast/toast.js.map +1 -0
  249. package/dist/ui/src/components/ui/toggle/toggle.js +37 -0
  250. package/dist/ui/src/components/ui/toggle/toggle.js.map +1 -0
  251. package/dist/ui/src/components/ui/toggle-group/toggle-group.js +56 -0
  252. package/dist/ui/src/components/ui/toggle-group/toggle-group.js.map +1 -0
  253. package/dist/ui/src/components/ui/tooltip/tooltip.js +43 -0
  254. package/dist/ui/src/components/ui/tooltip/tooltip.js.map +1 -0
  255. package/dist/ui/src/hooks/use-holds-focusable.d.ts +3 -0
  256. package/dist/ui/src/hooks/use-holds-focusable.js +33 -0
  257. package/dist/ui/src/hooks/use-holds-focusable.js.map +1 -0
  258. package/dist/ui/src/hooks/use-media-player.js +140 -0
  259. package/dist/ui/src/hooks/use-media-player.js.map +1 -0
  260. package/dist/ui/src/hooks/use-merged-refs.js +18 -0
  261. package/dist/ui/src/hooks/use-merged-refs.js.map +1 -0
  262. package/dist/ui/src/hooks/use-mobile.js +20 -0
  263. package/dist/ui/src/hooks/use-mobile.js.map +1 -0
  264. package/dist/ui/src/index.d.ts +8 -0
  265. package/dist/ui/src/lib/first-weekday.d.ts +6 -0
  266. package/dist/ui/src/lib/first-weekday.js +34 -0
  267. package/dist/ui/src/lib/first-weekday.js.map +1 -0
  268. package/dist/ui/src/lib/focus-guards.d.ts +2 -0
  269. package/dist/ui/src/lib/focus-guards.js +44 -0
  270. package/dist/ui/src/lib/focus-guards.js.map +1 -0
  271. package/dist/ui/src/lib/focus-ring.test.d.ts +8 -0
  272. package/dist/ui/src/lib/popup-list.d.ts +75 -0
  273. package/dist/ui/src/lib/popup-list.js +48 -0
  274. package/dist/ui/src/lib/popup-list.js.map +1 -0
  275. package/dist/ui/src/lib/popup-list.test.d.ts +10 -0
  276. package/dist/ui/src/lib/utils.js +21 -0
  277. package/dist/ui/src/lib/utils.js.map +1 -0
  278. package/package.json +36 -8
  279. package/dist/chunks/toggle-group-ByNq8g7H.js +0 -4263
  280. package/dist/chunks/toggle-group-ByNq8g7H.js.map +0 -1
  281. package/dist/dom/src/context.d.ts +0 -34
  282. package/dist/dom/src/define-element.d.ts +0 -11
  283. package/dist/dom/src/field-control.d.ts +0 -5
  284. package/dist/dom/src/index.d.ts +0 -6
  285. package/dist/dom/src/mode.d.ts +0 -8
  286. package/dist/dom/src/styles.d.ts +0 -6
  287. package/dist/dom/src/theme-state.d.ts +0 -1
  288. package/dist/dom/src/types.d.ts +0 -65
  289. package/dist/ds/src/elements.d.ts +0 -12
  290. package/dist/elements/src/edx-accordion.d.ts +0 -14
  291. package/dist/elements/src/edx-accordion.test.d.ts +0 -1
  292. package/dist/elements/src/edx-alert.d.ts +0 -14
  293. package/dist/elements/src/edx-audio-player.d.ts +0 -11
  294. package/dist/elements/src/edx-avatar.d.ts +0 -10
  295. package/dist/elements/src/edx-badge.d.ts +0 -12
  296. package/dist/elements/src/edx-breadcrumb.d.ts +0 -11
  297. package/dist/elements/src/edx-button-group.d.ts +0 -7
  298. package/dist/elements/src/edx-button.d.ts +0 -20
  299. package/dist/elements/src/edx-calendar.d.ts +0 -21
  300. package/dist/elements/src/edx-card.d.ts +0 -8
  301. package/dist/elements/src/edx-checkbox.d.ts +0 -18
  302. package/dist/elements/src/edx-combobox.d.ts +0 -28
  303. package/dist/elements/src/edx-content-components.d.ts +0 -2
  304. package/dist/elements/src/edx-dialog.d.ts +0 -20
  305. package/dist/elements/src/edx-drawer.d.ts +0 -21
  306. package/dist/elements/src/edx-dropdown-menu.d.ts +0 -29
  307. package/dist/elements/src/edx-field.d.ts +0 -9
  308. package/dist/elements/src/edx-field.test.d.ts +0 -1
  309. package/dist/elements/src/edx-indicator.d.ts +0 -14
  310. package/dist/elements/src/edx-input-group.d.ts +0 -32
  311. package/dist/elements/src/edx-input-otp.d.ts +0 -19
  312. package/dist/elements/src/edx-input.d.ts +0 -31
  313. package/dist/elements/src/edx-interactive-components.d.ts +0 -2
  314. package/dist/elements/src/edx-label.d.ts +0 -6
  315. package/dist/elements/src/edx-pagination.d.ts +0 -8
  316. package/dist/elements/src/edx-popover.d.ts +0 -24
  317. package/dist/elements/src/edx-progress.d.ts +0 -10
  318. package/dist/elements/src/edx-radio-group.d.ts +0 -22
  319. package/dist/elements/src/edx-radio-group.test.d.ts +0 -1
  320. package/dist/elements/src/edx-scroll-area.d.ts +0 -15
  321. package/dist/elements/src/edx-scroll-area.test.d.ts +0 -1
  322. package/dist/elements/src/edx-select.d.ts +0 -34
  323. package/dist/elements/src/edx-separator.d.ts +0 -10
  324. package/dist/elements/src/edx-skeleton.d.ts +0 -8
  325. package/dist/elements/src/edx-slider.d.ts +0 -17
  326. package/dist/elements/src/edx-slider.test.d.ts +0 -1
  327. package/dist/elements/src/edx-switch.d.ts +0 -18
  328. package/dist/elements/src/edx-table.d.ts +0 -13
  329. package/dist/elements/src/edx-table.test.d.ts +0 -1
  330. package/dist/elements/src/edx-tabs.d.ts +0 -15
  331. package/dist/elements/src/edx-tabs.test.d.ts +0 -1
  332. package/dist/elements/src/edx-textarea.d.ts +0 -25
  333. package/dist/elements/src/edx-toggle-group.d.ts +0 -27
  334. package/dist/elements/src/edx-toggle-group.test.d.ts +0 -1
  335. package/dist/elements/src/edx-toggle.d.ts +0 -18
  336. package/dist/elements/src/edx-tooltip.d.ts +0 -28
  337. package/dist/elements/src/edx-video-player.d.ts +0 -16
  338. package/dist/elements/src/field-control-bridge.d.ts +0 -7
  339. package/dist/elements/src/index.d.ts +0 -14
  340. package/dist/elements/src/projected-content.test.d.ts +0 -1
  341. package/dist/elements/src/registry.d.ts +0 -39
  342. package/dist/elements/src/registry.test.d.ts +0 -1
  343. package/dist/elements.js +0 -4020
  344. package/dist/elements.js.map +0 -1
  345. /package/dist/{dom/src/define-element.test.d.ts → ui/src/components/edyn/date-picker/date-text.test.d.ts} +0 -0
  346. /package/dist/{dom/src/native-slot.test.d.ts → ui/src/lib/first-weekday.test.d.ts} +0 -0
@@ -0,0 +1,232 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { popupItemClass } from "../../../lib/popup-list.js";
3
+ import { Popover, PopoverContent } from "../../ui/popover/popover.js";
4
+ import { ScrollArea } from "../../ui/scroll-area/scroll-area.js";
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { IconLoader2 } from "@tabler/icons-react";
8
+ //#region ../ui/src/components/edyn/rich-text-editor/mention-list.tsx
9
+ var FIRST = {
10
+ status: "loading",
11
+ items: [],
12
+ page: 1,
13
+ hasMore: false,
14
+ more: false,
15
+ active: 0
16
+ };
17
+ var toPage = (result) => Array.isArray(result) ? {
18
+ items: result,
19
+ hasMore: false
20
+ } : result;
21
+ var optionId = (listId, index) => `${listId}-${index}`;
22
+ /** The list that opens under the caret when the writer types @. */
23
+ var MentionList = ({ query, rect, config, labels, listId, onPick, keyHandler, onActiveChange }) => {
24
+ const [state, setState] = useState(FIRST);
25
+ const refSearch = useRef(config.search);
26
+ refSearch.current = config.search;
27
+ const refFirst = useRef(true);
28
+ const refController = useRef(null);
29
+ const refViewport = useRef(null);
30
+ const debounce = config.debounce ?? 250;
31
+ useEffect(() => {
32
+ const controller = new AbortController();
33
+ refController.current = controller;
34
+ setState((previous) => ({
35
+ ...previous,
36
+ status: "loading",
37
+ page: 1,
38
+ more: false
39
+ }));
40
+ const timer = setTimeout(async () => {
41
+ try {
42
+ const page = toPage(await refSearch.current(query, {
43
+ page: 1,
44
+ signal: controller.signal
45
+ }));
46
+ if (controller.signal.aborted) return;
47
+ setState({
48
+ status: "ready",
49
+ items: page.items,
50
+ page: 1,
51
+ hasMore: page.hasMore === true,
52
+ more: false,
53
+ active: 0
54
+ });
55
+ } catch {
56
+ if (!controller.signal.aborted) setState((previous) => ({
57
+ ...previous,
58
+ status: "error",
59
+ items: [],
60
+ hasMore: false
61
+ }));
62
+ }
63
+ }, refFirst.current ? 0 : debounce);
64
+ refFirst.current = false;
65
+ return () => {
66
+ clearTimeout(timer);
67
+ controller.abort();
68
+ };
69
+ }, [query, debounce]);
70
+ const loadMore = () => {
71
+ const controller = refController.current;
72
+ if (!controller || state.status !== "ready" || !state.hasMore || state.more) return;
73
+ const next = state.page + 1;
74
+ setState((previous) => ({
75
+ ...previous,
76
+ more: true
77
+ }));
78
+ refSearch.current(query, {
79
+ page: next,
80
+ signal: controller.signal
81
+ }).then((result) => {
82
+ if (controller.signal.aborted) return;
83
+ const page = toPage(result);
84
+ setState((previous) => ({
85
+ ...previous,
86
+ items: [...previous.items, ...page.items.filter((item) => !previous.items.some((known) => known.id === item.id))],
87
+ page: next,
88
+ hasMore: page.hasMore === true,
89
+ more: false
90
+ }));
91
+ }).catch(() => {
92
+ if (!controller.signal.aborted) setState((previous) => ({
93
+ ...previous,
94
+ more: false,
95
+ hasMore: false
96
+ }));
97
+ });
98
+ };
99
+ const refLoadMore = useRef(loadMore);
100
+ refLoadMore.current = loadMore;
101
+ const activeItem = state.items[state.active];
102
+ useEffect(() => {
103
+ onActiveChange(activeItem ? optionId(listId, state.active) : void 0);
104
+ }, [
105
+ activeItem,
106
+ listId,
107
+ onActiveChange,
108
+ state.active
109
+ ]);
110
+ useEffect(() => {
111
+ document.getElementById(optionId(listId, state.active))?.scrollIntoView({ block: "nearest" });
112
+ }, [listId, state.active]);
113
+ const watchSentinel = useCallback((node) => {
114
+ if (!node) return;
115
+ const observer = new IntersectionObserver((entries) => {
116
+ if (entries.some((entry) => entry.isIntersecting)) refLoadMore.current();
117
+ }, { root: refViewport.current });
118
+ observer.observe(node);
119
+ return () => observer.disconnect();
120
+ }, []);
121
+ useEffect(() => {
122
+ keyHandler.current = (event) => {
123
+ const count = state.items.length;
124
+ if (event.key === "Escape") {
125
+ event.stopPropagation();
126
+ return false;
127
+ }
128
+ if (count === 0) return false;
129
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
130
+ const step = event.key === "ArrowDown" ? 1 : -1;
131
+ const next = (state.active + step + count) % count;
132
+ if (step === 1 && state.active === count - 1) refLoadMore.current();
133
+ setState((previous) => ({
134
+ ...previous,
135
+ active: next
136
+ }));
137
+ return true;
138
+ }
139
+ if ((event.key === "Enter" || event.key === "Tab") && activeItem) {
140
+ onPick(activeItem);
141
+ return true;
142
+ }
143
+ return false;
144
+ };
145
+ return () => {
146
+ keyHandler.current = null;
147
+ };
148
+ }, [
149
+ activeItem,
150
+ keyHandler,
151
+ onPick,
152
+ state.active,
153
+ state.items.length
154
+ ]);
155
+ const anchor = { getBoundingClientRect: () => rect?.() ?? new DOMRect(0, 0, 0, 0) };
156
+ const announcement = state.status === "loading" ? labels.mentionSearching : state.status === "error" ? labels.mentionError : state.items.length === 0 ? labels.mentionEmpty : labels.mentionResults.replace("{count}", String(state.items.length));
157
+ return /* @__PURE__ */ jsx(Popover, {
158
+ open: true,
159
+ onOpenChange: () => void 0,
160
+ children: /* @__PURE__ */ jsxs(PopoverContent, {
161
+ anchor,
162
+ align: "start",
163
+ side: "bottom",
164
+ sideOffset: 6,
165
+ initialFocus: false,
166
+ finalFocus: false,
167
+ "data-slot": "rich-text-mention-popup",
168
+ className: "w-72 gap-0 p-1",
169
+ children: [/* @__PURE__ */ jsxs(ScrollArea, {
170
+ className: "max-h-60",
171
+ viewportRef: refViewport,
172
+ children: [
173
+ /* @__PURE__ */ jsx("div", {
174
+ role: "listbox",
175
+ id: listId,
176
+ "aria-label": labels.mentionList,
177
+ className: cn("flex flex-col gap-px", state.items.length === 0 && "hidden"),
178
+ children: state.items.map((item, index) => /* @__PURE__ */ jsxs("div", {
179
+ id: optionId(listId, index),
180
+ role: "option",
181
+ "aria-selected": index === state.active,
182
+ "data-slot": "rich-text-mention-option",
183
+ "data-highlighted": index === state.active ? "" : void 0,
184
+ className: cn(popupItemClass("base-ui"), "h-auto min-h-8 flex-col items-start gap-0"),
185
+ onMouseDown: (event) => event.preventDefault(),
186
+ onMouseMove: () => setState((previous) => previous.active === index ? previous : {
187
+ ...previous,
188
+ active: index
189
+ }),
190
+ onClick: () => onPick(item),
191
+ children: [/* @__PURE__ */ jsx("span", {
192
+ className: "truncate",
193
+ children: item.label
194
+ }), item.secondary ? /* @__PURE__ */ jsx("span", {
195
+ className: "truncate text-xs text-foreground/70",
196
+ children: item.secondary
197
+ }) : null]
198
+ }, item.id))
199
+ }),
200
+ state.status === "loading" && state.items.length === 0 ? /* @__PURE__ */ jsxs("div", {
201
+ className: "flex items-center gap-2 px-2 py-1.5 text-sm text-muted-foreground",
202
+ children: [/* @__PURE__ */ jsx(IconLoader2, {
203
+ className: "size-4 animate-spin motion-reduce:animate-none",
204
+ "aria-hidden": true
205
+ }), labels.mentionSearching]
206
+ }) : null,
207
+ state.status === "ready" && state.items.length === 0 ? /* @__PURE__ */ jsx("div", {
208
+ className: "px-2 py-1.5 text-sm text-muted-foreground",
209
+ children: labels.mentionEmpty
210
+ }) : null,
211
+ state.status === "error" ? /* @__PURE__ */ jsx("div", {
212
+ className: "px-2 py-1.5 text-sm text-destructive",
213
+ children: labels.mentionError
214
+ }) : null,
215
+ state.hasMore ? /* @__PURE__ */ jsx("div", {
216
+ ref: watchSentinel,
217
+ className: "flex items-center gap-2 px-2 py-1.5 text-xs text-muted-foreground",
218
+ children: state.more ? labels.mentionLoadingMore : null
219
+ }, state.items.length) : null
220
+ ]
221
+ }), /* @__PURE__ */ jsx("div", {
222
+ role: "status",
223
+ className: "sr-only",
224
+ children: announcement
225
+ })]
226
+ })
227
+ });
228
+ };
229
+ //#endregion
230
+ export { MentionList };
231
+
232
+ //# sourceMappingURL=mention-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention-list.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/rich-text-editor/mention-list.tsx"],"sourcesContent":["import { Popover, PopoverContent } from '@edyn/ui/components/ui/popover';\nimport { ScrollArea } from '@edyn/ui/components/ui/scroll-area';\nimport { popupItemClass } from '@edyn/ui/lib/popup-list';\nimport { cn } from '@edyn/ui/lib/utils';\nimport { IconLoader2 } from '@tabler/icons-react';\nimport { type RefObject, useCallback, useEffect, useRef, useState } from 'react';\nimport type { IRichTextLabels, IRichTextMention, IRichTextMentionItem, IRichTextMentionPage } from './types';\n\ninterface IListState {\n status: 'loading' | 'ready' | 'error';\n items: IRichTextMentionItem[];\n page: number;\n hasMore: boolean;\n more: boolean;\n active: number;\n}\n\nconst FIRST: IListState = { status: 'loading', items: [], page: 1, hasMore: false, more: false, active: 0 };\n\nconst toPage = (result: IRichTextMentionItem[] | IRichTextMentionPage): IRichTextMentionPage =>\n Array.isArray(result) ? { items: result, hasMore: false } : result;\n\nexport interface IMentionListProps {\n query: string;\n /** Where the @ was typed. */\n rect: (() => DOMRect | null) | null;\n config: IRichTextMention;\n labels: IRichTextLabels;\n listId: string;\n onPick: (item: IRichTextMentionItem) => void;\n /** The editor keeps focus, so the list is driven by its keys: the editor calls this one. */\n keyHandler: RefObject<((event: KeyboardEvent) => boolean) | null>;\n /** The id of the option the editor should name as `aria-activedescendant`. */\n onActiveChange: (optionId: string | undefined) => void;\n}\n\nconst optionId = (listId: string, index: number): string => `${listId}-${index}`;\n\n/** The list that opens under the caret when the writer types @. */\nexport const MentionList = ({ query, rect, config, labels, listId, onPick, keyHandler, onActiveChange }: IMentionListProps) => {\n const [state, setState] = useState<IListState>(FIRST);\n const refSearch = useRef(config.search);\n refSearch.current = config.search;\n const refFirst = useRef(true);\n const refController = useRef<AbortController | null>(null);\n const refViewport = useRef<HTMLDivElement | null>(null);\n const debounce = config.debounce ?? 250;\n\n useEffect(() => {\n const controller = new AbortController();\n refController.current = controller;\n setState((previous) => ({ ...previous, status: 'loading', page: 1, more: false }));\n const timer = setTimeout(\n async () => {\n try {\n const page = toPage(await refSearch.current(query, { page: 1, signal: controller.signal }));\n if (controller.signal.aborted) {\n return;\n }\n setState({ status: 'ready', items: page.items, page: 1, hasMore: page.hasMore === true, more: false, active: 0 });\n } catch {\n if (!controller.signal.aborted) {\n setState((previous) => ({ ...previous, status: 'error', items: [], hasMore: false }));\n }\n }\n },\n refFirst.current ? 0 : debounce,\n );\n refFirst.current = false;\n return () => {\n clearTimeout(timer);\n controller.abort();\n };\n }, [query, debounce]);\n\n const loadMore = () => {\n const controller = refController.current;\n if (!controller || state.status !== 'ready' || !state.hasMore || state.more) {\n return;\n }\n const next = state.page + 1;\n setState((previous) => ({ ...previous, more: true }));\n refSearch\n .current(query, { page: next, signal: controller.signal })\n .then((result) => {\n if (controller.signal.aborted) {\n return;\n }\n const page = toPage(result);\n setState((previous) => ({\n ...previous,\n items: [...previous.items, ...page.items.filter((item) => !previous.items.some((known) => known.id === item.id))],\n page: next,\n hasMore: page.hasMore === true,\n more: false,\n }));\n })\n .catch(() => {\n if (!controller.signal.aborted) {\n setState((previous) => ({ ...previous, more: false, hasMore: false }));\n }\n });\n };\n const refLoadMore = useRef(loadMore);\n refLoadMore.current = loadMore;\n\n const activeItem = state.items[state.active];\n\n useEffect(() => {\n onActiveChange(activeItem ? optionId(listId, state.active) : undefined);\n }, [activeItem, listId, onActiveChange, state.active]);\n\n useEffect(() => {\n document.getElementById(optionId(listId, state.active))?.scrollIntoView({ block: 'nearest' });\n }, [listId, state.active]);\n\n const watchSentinel = useCallback((node: HTMLDivElement | null) => {\n if (!node) {\n return;\n }\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries.some((entry) => entry.isIntersecting)) {\n refLoadMore.current();\n }\n },\n { root: refViewport.current },\n );\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n useEffect(() => {\n keyHandler.current = (event) => {\n const count = state.items.length;\n if (event.key === 'Escape') {\n event.stopPropagation();\n return false;\n }\n if (count === 0) {\n return false;\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n const step = event.key === 'ArrowDown' ? 1 : -1;\n const next = (state.active + step + count) % count;\n if (step === 1 && state.active === count - 1) {\n refLoadMore.current();\n }\n setState((previous) => ({ ...previous, active: next }));\n return true;\n }\n if ((event.key === 'Enter' || event.key === 'Tab') && activeItem) {\n onPick(activeItem);\n return true;\n }\n return false;\n };\n return () => {\n keyHandler.current = null;\n };\n }, [activeItem, keyHandler, onPick, state.active, state.items.length]);\n\n const anchor = { getBoundingClientRect: () => rect?.() ?? new DOMRect(0, 0, 0, 0) };\n const announcement =\n state.status === 'loading'\n ? labels.mentionSearching\n : state.status === 'error'\n ? labels.mentionError\n : state.items.length === 0\n ? labels.mentionEmpty\n : labels.mentionResults.replace('{count}', String(state.items.length));\n\n return (\n <Popover open onOpenChange={() => undefined}>\n <PopoverContent\n anchor={anchor}\n align=\"start\"\n side=\"bottom\"\n sideOffset={6}\n initialFocus={false}\n finalFocus={false}\n data-slot=\"rich-text-mention-popup\"\n className=\"w-72 gap-0 p-1\"\n >\n <ScrollArea className=\"max-h-60\" viewportRef={refViewport}>\n <div\n role=\"listbox\"\n id={listId}\n aria-label={labels.mentionList}\n className={cn('flex flex-col gap-px', state.items.length === 0 && 'hidden')}\n >\n {state.items.map((item, index) => (\n // biome-ignore lint/a11y/useKeyWithClickEvents: the editor keeps focus and drives the list with its own keys\n // biome-ignore lint/a11y/useFocusableInteractive: options are named by aria-activedescendant on the editor, never focused\n <div\n key={item.id}\n id={optionId(listId, index)}\n role=\"option\"\n aria-selected={index === state.active}\n data-slot=\"rich-text-mention-option\"\n data-highlighted={index === state.active ? '' : undefined}\n className={cn(popupItemClass('base-ui'), 'h-auto min-h-8 flex-col items-start gap-0')}\n onMouseDown={(event) => event.preventDefault()}\n onMouseMove={() => setState((previous) => (previous.active === index ? previous : { ...previous, active: index }))}\n onClick={() => onPick(item)}\n >\n <span className=\"truncate\">{item.label}</span>\n {item.secondary ? <span className=\"truncate text-xs text-foreground/70\">{item.secondary}</span> : null}\n </div>\n ))}\n </div>\n {state.status === 'loading' && state.items.length === 0 ? (\n <div className=\"flex items-center gap-2 px-2 py-1.5 text-sm text-muted-foreground\">\n <IconLoader2 className=\"size-4 animate-spin motion-reduce:animate-none\" aria-hidden />\n {labels.mentionSearching}\n </div>\n ) : null}\n {state.status === 'ready' && state.items.length === 0 ? (\n <div className=\"px-2 py-1.5 text-sm text-muted-foreground\">{labels.mentionEmpty}</div>\n ) : null}\n {state.status === 'error' ? <div className=\"px-2 py-1.5 text-sm text-destructive\">{labels.mentionError}</div> : null}\n {state.hasMore ? (\n <div key={state.items.length} ref={watchSentinel} className=\"flex items-center gap-2 px-2 py-1.5 text-xs text-muted-foreground\">\n {state.more ? labels.mentionLoadingMore : null}\n </div>\n ) : null}\n </ScrollArea>\n <div role=\"status\" className=\"sr-only\">\n {announcement}\n </div>\n </PopoverContent>\n </Popover>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { ComponentPropsWithoutRef, Ref } from 'react';
2
+ import { IRichTextEditorHandle, IRichTextField, IRichTextLabels, IRichTextMention } from './types';
3
+ export interface IRichTextEditorProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'defaultValue' | 'onChange' | 'id'> {
4
+ /** The HTML, when the parent owns it. Pass back what `onChange` gave. An empty editor is `""`. */
5
+ value?: string;
6
+ /** The HTML to start from when the editor owns its own value. */
7
+ defaultValue?: string;
8
+ /** Called with the HTML after every edit. Only the elements in `RICH_TEXT_ELEMENTS` can appear, and `""` when there is no text. */
9
+ onChange?: (html: string) => void;
10
+ placeholder?: string;
11
+ disabled?: boolean;
12
+ /** Shows the content and keeps it selectable and copyable, with no toolbar. */
13
+ readOnly?: boolean;
14
+ /** Draws the error border and sets `aria-invalid`. The message is the form's to draw: point `aria-describedby` at it. */
15
+ invalid?: boolean;
16
+ /**
17
+ * Lines the box is tall when empty.
18
+ * @default 6
19
+ */
20
+ minRows?: number;
21
+ /**
22
+ * Lines it grows to before it scrolls inside.
23
+ * @default 20
24
+ */
25
+ maxRows?: number;
26
+ /** The most characters of text (a mention counts as the text it is written as). An edit past it is refused. Off by default. */
27
+ maxLength?: number;
28
+ /** Turns @ into a search the app answers. Without it, @ is an ordinary character. */
29
+ mention?: IRichTextMention;
30
+ /** Adds an "Insert a field" menu that writes `{{id}}` at the caret. */
31
+ fields?: IRichTextField[];
32
+ /**
33
+ * Whether the formatting toolbar shows.
34
+ * @default true
35
+ */
36
+ toolbar?: boolean;
37
+ /** Replaces any of the strings the editor shows (the defaults are English). */
38
+ labels?: Partial<IRichTextLabels>;
39
+ /** On the editable element, so a form can name it. */
40
+ id?: string;
41
+ autoFocus?: boolean;
42
+ /** Called with the files when the writer pastes only files (a screenshot). Without it, such a paste does nothing. */
43
+ onPasteFiles?: (files: File[]) => void;
44
+ /** Gives `insertText` and `focus`, for buttons outside the editor. */
45
+ ref?: Ref<IRichTextEditorHandle>;
46
+ }
47
+ /**
48
+ * A small rich-text field: paragraphs, bold, italic, underline, bulleted and numbered lists and links, with optional
49
+ * @mentions the app answers asynchronously. The value is HTML in and out, and only the elements in `RICH_TEXT_ELEMENTS`
50
+ * can come out (no class, no style), whatever is pasted. A mention is a chip while editing and plain text in the HTML.
51
+ * The server should still sanitise: this is a convenience for the writer, not a security boundary.
52
+ */
53
+ export declare const RichTextEditor: ({ value, defaultValue, onChange, placeholder, disabled, readOnly, invalid, minRows, maxRows, maxLength, mention, fields, toolbar, labels, id, autoFocus, onPasteFiles, ref, className, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: IRichTextEditorProps) => import('react').JSX.Element;
@@ -0,0 +1,244 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { ScrollArea } from "../../ui/scroll-area/scroll-area.js";
3
+ import { RICH_TEXT_BODY_CLASS } from "../rich-text-content/rich-text-content.js";
4
+ import { defaultMentionToText, isDocumentEmpty, serializeDocument } from "./serialize.js";
5
+ import { buildExtensions } from "./extensions.js";
6
+ import { MentionList } from "./mention-list.js";
7
+ import { isLive, syncEditable, syncValue } from "./sync.js";
8
+ import { RichTextToolbar } from "./toolbar.js";
9
+ import { DEFAULT_RICH_TEXT_LABELS } from "./types.js";
10
+ import { useCallback, useEffect, useId, useImperativeHandle, useMemo, useReducer, useRef, useState } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ import { EditorContent, useEditor, useEditorState } from "@tiptap/react";
13
+ //#region ../ui/src/components/edyn/rich-text-editor/rich-text-editor.tsx
14
+ var LINE = 1.5;
15
+ /**
16
+ * A small rich-text field: paragraphs, bold, italic, underline, bulleted and numbered lists and links, with optional
17
+ * @mentions the app answers asynchronously. The value is HTML in and out, and only the elements in `RICH_TEXT_ELEMENTS`
18
+ * can come out (no class, no style), whatever is pasted. A mention is a chip while editing and plain text in the HTML.
19
+ * The server should still sanitise: this is a convenience for the writer, not a security boundary.
20
+ */
21
+ var RichTextEditor = ({ value, defaultValue = "", onChange, placeholder, disabled = false, readOnly = false, invalid = false, minRows = 6, maxRows = 20, maxLength, mention, fields, toolbar = true, labels, id, autoFocus = false, onPasteFiles, ref, className, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }) => {
22
+ const isInvalid = invalid || ariaInvalid === true || ariaInvalid === "true";
23
+ const text = useMemo(() => ({
24
+ ...DEFAULT_RICH_TEXT_LABELS,
25
+ ...labels
26
+ }), [labels]);
27
+ const listId = useId();
28
+ const [suggestion, setSuggestion] = useState(null);
29
+ const [activeOption, setActiveOption] = useState(void 0);
30
+ const [linkOpen, setLinkOpen] = useState(false);
31
+ const refLinkRange = useRef(null);
32
+ const refKeys = useRef(null);
33
+ const refKnown = useRef(value ?? defaultValue);
34
+ const refPending = useRef([]);
35
+ const [, wake] = useReducer((count) => count + 1, 0);
36
+ const editable = !disabled && !readOnly;
37
+ const showToolbar = toolbar && !readOnly;
38
+ const latest = useRef({
39
+ mention,
40
+ maxLength,
41
+ onChange,
42
+ onPasteFiles,
43
+ toolbar: showToolbar
44
+ });
45
+ latest.current = {
46
+ mention,
47
+ maxLength,
48
+ onChange,
49
+ onPasteFiles,
50
+ toolbar: showToolbar
51
+ };
52
+ const host = useMemo(() => ({
53
+ mentionEnabled: () => latest.current.mention !== void 0,
54
+ maxLength: () => latest.current.maxLength,
55
+ toText: () => latest.current.mention?.toText ?? defaultMentionToText,
56
+ onSuggestion: (props) => {
57
+ if (props === null) setActiveOption(void 0);
58
+ setSuggestion(props === null ? null : {
59
+ query: props.query,
60
+ rect: props.clientRect ?? null,
61
+ command: props.command
62
+ });
63
+ },
64
+ onSuggestionKey: ({ event }) => refKeys.current?.(event) ?? false
65
+ }), []);
66
+ const extensions = useMemo(() => buildExtensions(host), [host]);
67
+ const emit = useCallback((current) => {
68
+ const html = isDocumentEmpty(current.state.doc) ? "" : serializeDocument(current.state.doc, host.toText());
69
+ refKnown.current = html;
70
+ return html;
71
+ }, [host]);
72
+ const attributes = useMemo(() => {
73
+ const entries = {
74
+ role: "textbox",
75
+ "aria-multiline": "true",
76
+ "aria-autocomplete": mention ? "list" : void 0,
77
+ "aria-controls": suggestion ? listId : void 0,
78
+ "aria-activedescendant": suggestion ? activeOption : void 0,
79
+ "aria-label": ariaLabel,
80
+ "aria-labelledby": ariaLabelledBy,
81
+ "aria-describedby": ariaDescribedBy,
82
+ "aria-invalid": isInvalid ? "true" : void 0,
83
+ "aria-readonly": readOnly ? "true" : void 0,
84
+ "aria-disabled": disabled ? "true" : void 0,
85
+ "aria-placeholder": placeholder,
86
+ tabindex: readOnly && !disabled ? "0" : void 0,
87
+ id,
88
+ "data-slot": "rich-text-editor-content",
89
+ class: cn(RICH_TEXT_BODY_CLASS, "min-h-(--rich-text-min) px-2.5 py-2 outline-none")
90
+ };
91
+ return Object.fromEntries(Object.entries(entries).filter((entry) => entry[1] !== void 0));
92
+ }, [
93
+ activeOption,
94
+ ariaDescribedBy,
95
+ ariaLabel,
96
+ ariaLabelledBy,
97
+ disabled,
98
+ id,
99
+ isInvalid,
100
+ listId,
101
+ mention,
102
+ placeholder,
103
+ readOnly,
104
+ suggestion
105
+ ]);
106
+ const editor = useEditor({
107
+ extensions,
108
+ content: value ?? defaultValue,
109
+ editable,
110
+ autofocus: autoFocus ? "end" : false,
111
+ editorProps: {
112
+ attributes,
113
+ handlePaste: (_view, event) => {
114
+ const data = event.clipboardData;
115
+ if (!(data !== null && data.files.length > 0 && data.getData("text/plain") === "" && data.getData("text/html") === "") || !latest.current.onPasteFiles) return false;
116
+ latest.current.onPasteFiles(Array.from(data.files));
117
+ return true;
118
+ },
119
+ handleKeyDown: (view, event) => {
120
+ if ((event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && event.key.toLowerCase() === "k" && latest.current.toolbar) {
121
+ event.preventDefault();
122
+ refLinkRange.current = {
123
+ from: view.state.selection.from,
124
+ to: view.state.selection.to
125
+ };
126
+ setLinkOpen(true);
127
+ return true;
128
+ }
129
+ return false;
130
+ }
131
+ },
132
+ onBlur: () => {
133
+ setSuggestion(null);
134
+ setActiveOption(void 0);
135
+ },
136
+ onUpdate: ({ editor: current }) => {
137
+ latest.current.onChange?.(emit(current));
138
+ }
139
+ });
140
+ const ready = isLive(editor);
141
+ useEffect(() => {
142
+ editor.on("create", wake);
143
+ wake();
144
+ return () => {
145
+ editor.off("create", wake);
146
+ };
147
+ }, [editor]);
148
+ const whenLive = useCallback((action) => {
149
+ if (isLive(editor)) action(editor);
150
+ else refPending.current.push(action);
151
+ }, [editor]);
152
+ useImperativeHandle(ref, () => ({
153
+ insertText: (text) => {
154
+ whenLive((live) => {
155
+ live.chain().focus().insertContent({
156
+ type: "text",
157
+ text
158
+ }).run();
159
+ });
160
+ },
161
+ focus: () => {
162
+ whenLive((live) => {
163
+ live.commands.focus();
164
+ });
165
+ }
166
+ }), [whenLive]);
167
+ useEffect(() => {
168
+ if (!ready || !isLive(editor)) return;
169
+ for (const action of refPending.current.splice(0)) action(editor);
170
+ }, [editor, ready]);
171
+ useEffect(() => {
172
+ if (ready) syncEditable(editor, editable);
173
+ }, [
174
+ editable,
175
+ editor,
176
+ ready
177
+ ]);
178
+ useEffect(() => {
179
+ if (ready) syncValue(editor, value, refKnown);
180
+ }, [
181
+ editor,
182
+ ready,
183
+ value
184
+ ]);
185
+ const empty = useEditorState({
186
+ editor,
187
+ selector: ({ editor: current }) => isDocumentEmpty(current.state.doc)
188
+ });
189
+ const pick = useCallback((item) => {
190
+ suggestion?.command(item);
191
+ }, [suggestion]);
192
+ return /* @__PURE__ */ jsxs("div", {
193
+ "data-slot": "rich-text-editor",
194
+ "data-invalid": isInvalid ? "" : void 0,
195
+ "data-disabled": disabled ? "" : void 0,
196
+ "data-readonly": readOnly ? "" : void 0,
197
+ className: cn("flex w-full min-w-0 flex-col rounded-lg border border-control bg-transparent text-base transition-colors focus-within:outline-solid data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:bg-input/50 data-disabled:opacity-50 data-invalid:border-destructive data-invalid:ring-3 data-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:data-disabled:bg-input/80 dark:data-invalid:ring-destructive/40", className),
198
+ ...props,
199
+ children: [
200
+ showToolbar ? /* @__PURE__ */ jsx(RichTextToolbar, {
201
+ editor,
202
+ labels: text,
203
+ fields,
204
+ disabled,
205
+ linkOpen,
206
+ linkRange: refLinkRange,
207
+ onLinkOpenChange: (next) => {
208
+ if (next && !linkOpen) refLinkRange.current = {
209
+ from: editor.state.selection.from,
210
+ to: editor.state.selection.to
211
+ };
212
+ setLinkOpen(next);
213
+ }
214
+ }) : null,
215
+ /* @__PURE__ */ jsxs(ScrollArea, {
216
+ className: "relative",
217
+ style: {
218
+ maxHeight: `${maxRows * LINE + 1}rem`,
219
+ ["--rich-text-min"]: `${minRows * LINE + 1}rem`
220
+ },
221
+ focusable: false,
222
+ children: [empty && placeholder ? /* @__PURE__ */ jsx("p", {
223
+ "aria-hidden": true,
224
+ className: "pointer-events-none absolute start-2.5 top-2 text-muted-foreground",
225
+ children: placeholder
226
+ }) : null, /* @__PURE__ */ jsx(EditorContent, { editor })]
227
+ }),
228
+ suggestion && mention ? /* @__PURE__ */ jsx(MentionList, {
229
+ query: suggestion.query,
230
+ rect: suggestion.rect,
231
+ config: mention,
232
+ labels: text,
233
+ listId,
234
+ onPick: pick,
235
+ keyHandler: refKeys,
236
+ onActiveChange: setActiveOption
237
+ }) : null
238
+ ]
239
+ });
240
+ };
241
+ //#endregion
242
+ export { RichTextEditor };
243
+
244
+ //# sourceMappingURL=rich-text-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../../../../../../ui/src/components/edyn/rich-text-editor/rich-text-editor.tsx"],"sourcesContent":["import { RICH_TEXT_BODY_CLASS } from '@edyn/ui/components/edyn/rich-text-content/rich-text-content';\nimport { ScrollArea } from '@edyn/ui/components/ui/scroll-area';\nimport { cn } from '@edyn/ui/lib/utils';\nimport type { Editor } from '@tiptap/core';\nimport { EditorContent, useEditor, useEditorState } from '@tiptap/react';\nimport type { SuggestionProps } from '@tiptap/suggestion';\nimport {\n type ComponentPropsWithoutRef,\n type Ref,\n useCallback,\n useEffect,\n useId,\n useImperativeHandle,\n useMemo,\n useReducer,\n useRef,\n useState,\n} from 'react';\nimport { buildExtensions, type IEditorHost } from './extensions';\nimport { MentionList } from './mention-list';\nimport { defaultMentionToText, isDocumentEmpty, serializeDocument } from './serialize';\nimport { isLive, syncEditable, syncValue } from './sync';\nimport { type ILinkRange, RichTextToolbar } from './toolbar';\nimport {\n DEFAULT_RICH_TEXT_LABELS,\n type IRichTextEditorHandle,\n type IRichTextField,\n type IRichTextLabels,\n type IRichTextMention,\n type IRichTextMentionItem,\n} from './types';\n\nconst LINE = 1.5;\n\ninterface ISuggestionState {\n query: string;\n rect: (() => DOMRect | null) | null;\n command: (item: IRichTextMentionItem) => void;\n}\n\nexport interface IRichTextEditorProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'defaultValue' | 'onChange' | 'id'> {\n /** The HTML, when the parent owns it. Pass back what `onChange` gave. An empty editor is `\"\"`. */\n value?: string;\n /** The HTML to start from when the editor owns its own value. */\n defaultValue?: string;\n /** Called with the HTML after every edit. Only the elements in `RICH_TEXT_ELEMENTS` can appear, and `\"\"` when there is no text. */\n onChange?: (html: string) => void;\n placeholder?: string;\n disabled?: boolean;\n /** Shows the content and keeps it selectable and copyable, with no toolbar. */\n readOnly?: boolean;\n /** Draws the error border and sets `aria-invalid`. The message is the form's to draw: point `aria-describedby` at it. */\n invalid?: boolean;\n /**\n * Lines the box is tall when empty.\n * @default 6\n */\n minRows?: number;\n /**\n * Lines it grows to before it scrolls inside.\n * @default 20\n */\n maxRows?: number;\n /** The most characters of text (a mention counts as the text it is written as). An edit past it is refused. Off by default. */\n maxLength?: number;\n /** Turns @ into a search the app answers. Without it, @ is an ordinary character. */\n mention?: IRichTextMention;\n /** Adds an \"Insert a field\" menu that writes `{{id}}` at the caret. */\n fields?: IRichTextField[];\n /**\n * Whether the formatting toolbar shows.\n * @default true\n */\n toolbar?: boolean;\n /** Replaces any of the strings the editor shows (the defaults are English). */\n labels?: Partial<IRichTextLabels>;\n /** On the editable element, so a form can name it. */\n id?: string;\n autoFocus?: boolean;\n /** Called with the files when the writer pastes only files (a screenshot). Without it, such a paste does nothing. */\n onPasteFiles?: (files: File[]) => void;\n /** Gives `insertText` and `focus`, for buttons outside the editor. */\n ref?: Ref<IRichTextEditorHandle>;\n}\n\n/**\n * A small rich-text field: paragraphs, bold, italic, underline, bulleted and numbered lists and links, with optional\n * @mentions the app answers asynchronously. The value is HTML in and out, and only the elements in `RICH_TEXT_ELEMENTS`\n * can come out (no class, no style), whatever is pasted. A mention is a chip while editing and plain text in the HTML.\n * The server should still sanitise: this is a convenience for the writer, not a security boundary.\n */\nexport const RichTextEditor = ({\n value,\n defaultValue = '',\n onChange,\n placeholder,\n disabled = false,\n readOnly = false,\n invalid = false,\n minRows = 6,\n maxRows = 20,\n maxLength,\n mention,\n fields,\n toolbar = true,\n labels,\n id,\n autoFocus = false,\n onPasteFiles,\n ref,\n className,\n 'aria-invalid': ariaInvalid,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n ...props\n}: IRichTextEditorProps) => {\n const isInvalid = invalid || ariaInvalid === true || ariaInvalid === 'true';\n const text = useMemo(() => ({ ...DEFAULT_RICH_TEXT_LABELS, ...labels }), [labels]);\n const listId = useId();\n const [suggestion, setSuggestion] = useState<ISuggestionState | null>(null);\n const [activeOption, setActiveOption] = useState<string | undefined>(undefined);\n const [linkOpen, setLinkOpen] = useState(false);\n const refLinkRange = useRef<ILinkRange | null>(null);\n const refKeys = useRef<((event: KeyboardEvent) => boolean) | null>(null);\n // What the editor and the parent agree on. It starts as the value the editor is built from, so a controlled value\n // that has not changed asks for nothing at mount.\n const refKnown = useRef<string | undefined>(value ?? defaultValue);\n const refPending = useRef<Array<(live: Editor) => void>>([]);\n const [, wake] = useReducer((count: number) => count + 1, 0);\n const editable = !disabled && !readOnly;\n const showToolbar = toolbar && !readOnly;\n\n const latest = useRef({ mention, maxLength, onChange, onPasteFiles, toolbar: showToolbar });\n latest.current = { mention, maxLength, onChange, onPasteFiles, toolbar: showToolbar };\n\n const host = useMemo<IEditorHost>(\n () => ({\n mentionEnabled: () => latest.current.mention !== undefined,\n maxLength: () => latest.current.maxLength,\n toText: () => latest.current.mention?.toText ?? defaultMentionToText,\n onSuggestion: (props: SuggestionProps<IRichTextMentionItem, IRichTextMentionItem> | null) => {\n if (props === null) {\n setActiveOption(undefined);\n }\n setSuggestion(props === null ? null : { query: props.query, rect: props.clientRect ?? null, command: props.command });\n },\n onSuggestionKey: ({ event }) => refKeys.current?.(event) ?? false,\n }),\n [],\n );\n const extensions = useMemo(() => buildExtensions(host), [host]);\n\n const emit = useCallback(\n (current: Editor): string => {\n const html = isDocumentEmpty(current.state.doc) ? '' : serializeDocument(current.state.doc, host.toText());\n refKnown.current = html;\n return html;\n },\n [host],\n );\n\n const attributes = useMemo(() => {\n const entries: Record<string, string | undefined> = {\n role: 'textbox',\n 'aria-multiline': 'true',\n 'aria-autocomplete': mention ? 'list' : undefined,\n 'aria-controls': suggestion ? listId : undefined,\n 'aria-activedescendant': suggestion ? activeOption : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-invalid': isInvalid ? 'true' : undefined,\n 'aria-readonly': readOnly ? 'true' : undefined,\n 'aria-disabled': disabled ? 'true' : undefined,\n 'aria-placeholder': placeholder,\n tabindex: readOnly && !disabled ? '0' : undefined,\n id,\n 'data-slot': 'rich-text-editor-content',\n class: cn(RICH_TEXT_BODY_CLASS, 'min-h-(--rich-text-min) px-2.5 py-2 outline-none'),\n };\n return Object.fromEntries(Object.entries(entries).filter((entry): entry is [string, string] => entry[1] !== undefined));\n }, [activeOption, ariaDescribedBy, ariaLabel, ariaLabelledBy, disabled, id, isInvalid, listId, mention, placeholder, readOnly, suggestion]);\n\n const editor = useEditor({\n extensions,\n content: value ?? defaultValue,\n editable,\n autofocus: autoFocus ? 'end' : false,\n editorProps: {\n attributes,\n handlePaste: (_view, event) => {\n const data = event.clipboardData;\n const onlyFiles = data !== null && data.files.length > 0 && data.getData('text/plain') === '' && data.getData('text/html') === '';\n if (!onlyFiles || !latest.current.onPasteFiles) {\n return false;\n }\n latest.current.onPasteFiles(Array.from(data.files));\n return true;\n },\n handleKeyDown: (view, event) => {\n if ((event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && event.key.toLowerCase() === 'k' && latest.current.toolbar) {\n event.preventDefault();\n refLinkRange.current = { from: view.state.selection.from, to: view.state.selection.to };\n setLinkOpen(true);\n return true;\n }\n return false;\n },\n },\n onBlur: () => {\n setSuggestion(null);\n setActiveOption(undefined);\n },\n onUpdate: ({ editor: current }) => {\n latest.current.onChange?.(emit(current));\n },\n });\n\n // useEditor can replace the editor (it destroys one that was not mounted in time, and in development a second render\n // makes a second), so readiness is read off the editor in hand: wake up when this one finishes initialising.\n const ready = isLive(editor);\n useEffect(() => {\n editor.on('create', wake);\n wake();\n return () => {\n editor.off('create', wake);\n };\n }, [editor]);\n\n const whenLive = useCallback(\n (action: (live: Editor) => void) => {\n if (isLive(editor)) {\n action(editor);\n } else {\n refPending.current.push(action);\n }\n },\n [editor],\n );\n\n useImperativeHandle(\n ref,\n () => ({\n insertText: (text) => {\n whenLive((live) => {\n live.chain().focus().insertContent({ type: 'text', text }).run();\n });\n },\n focus: () => {\n whenLive((live) => {\n live.commands.focus();\n });\n },\n }),\n [whenLive],\n );\n\n useEffect(() => {\n if (!ready || !isLive(editor)) {\n return;\n }\n for (const action of refPending.current.splice(0)) {\n action(editor);\n }\n }, [editor, ready]);\n\n useEffect(() => {\n if (ready) {\n syncEditable(editor, editable);\n }\n }, [editable, editor, ready]);\n\n useEffect(() => {\n if (ready) {\n syncValue(editor, value, refKnown);\n }\n }, [editor, ready, value]);\n\n const empty = useEditorState({ editor, selector: ({ editor: current }) => isDocumentEmpty(current.state.doc) });\n\n const pick = useCallback(\n (item: IRichTextMentionItem) => {\n suggestion?.command(item);\n },\n [suggestion],\n );\n\n return (\n <div\n data-slot=\"rich-text-editor\"\n data-invalid={isInvalid ? '' : undefined}\n data-disabled={disabled ? '' : undefined}\n data-readonly={readOnly ? '' : undefined}\n className={cn(\n 'flex w-full min-w-0 flex-col rounded-lg border border-control bg-transparent text-base transition-colors focus-within:outline-solid data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:bg-input/50 data-disabled:opacity-50 data-invalid:border-destructive data-invalid:ring-3 data-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:data-disabled:bg-input/80 dark:data-invalid:ring-destructive/40',\n className,\n )}\n {...props}\n >\n {showToolbar ? (\n <RichTextToolbar\n editor={editor}\n labels={text}\n fields={fields}\n disabled={disabled}\n linkOpen={linkOpen}\n linkRange={refLinkRange}\n onLinkOpenChange={(next) => {\n if (next && !linkOpen) {\n refLinkRange.current = { from: editor.state.selection.from, to: editor.state.selection.to };\n }\n setLinkOpen(next);\n }}\n />\n ) : null}\n <ScrollArea\n className=\"relative\"\n style={{ maxHeight: `${maxRows * LINE + 1}rem`, ['--rich-text-min' as string]: `${minRows * LINE + 1}rem` }}\n focusable={false}\n >\n {empty && placeholder ? (\n <p aria-hidden className=\"pointer-events-none absolute start-2.5 top-2 text-muted-foreground\">\n {placeholder}\n </p>\n ) : null}\n <EditorContent editor={editor} />\n </ScrollArea>\n {suggestion && mention ? (\n <MentionList\n query={suggestion.query}\n rect={suggestion.rect}\n config={mention}\n labels={text}\n listId={listId}\n onPick={pick}\n keyHandler={refKeys}\n onActiveChange={setActiveOption}\n />\n ) : null}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * The editor's schema decides what HTML can come out, whatever goes in. These tests drive the real extension set
3
+ * through a headless ProseMirror editor and read what it serialises, so the allow-list the docs promise is the one
4
+ * that is checked. Typing, focus and the mention list need a real browser: e2e/rich-text-editor.spec.ts.
5
+ */
6
+ export {};
@@ -0,0 +1,13 @@
1
+ import { Node as TProseNode } from '@tiptap/pm/model';
2
+ import { IRichTextMention } from './types';
3
+ export type TMentionToText = NonNullable<IRichTextMention['toText']>;
4
+ export declare const defaultMentionToText: TMentionToText;
5
+ /**
6
+ * The document as HTML. Not `editor.getHTML()`: that would write a mention as its chip element, and
7
+ * the HTML must carry the plain text the app chose (`@person@example.test`) and nothing else.
8
+ */
9
+ export declare const serializeDocument: (doc: TProseNode, toText: TMentionToText) => string;
10
+ /** No text and no mention. A document of blank paragraphs, spaces and line breaks is empty too. */
11
+ export declare const isDocumentEmpty: (doc: TProseNode) => boolean;
12
+ /** Characters the HTML text will have: a mention counts as the text it is written as. */
13
+ export declare const countCharacters: (doc: TProseNode, toText: TMentionToText) => number;