@dou-ui/editor 0.0.0-stage → 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -2
  3. package/dist/catalog.js +18 -0
  4. package/dist/catalog.js.map +1 -0
  5. package/dist/code/code-editor-block.js +6 -0
  6. package/dist/code/code-editor-block.js.map +1 -0
  7. package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js +116 -0
  8. package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  9. package/dist/code/index.js +69 -0
  10. package/dist/code/index.js.map +1 -0
  11. package/dist/code/languages.js +51 -0
  12. package/dist/code/languages.js.map +1 -0
  13. package/dist/code/view.js +255 -0
  14. package/dist/code/view.js.map +1 -0
  15. package/dist/collaboration/binding.js +381 -0
  16. package/dist/collaboration/binding.js.map +1 -0
  17. package/dist/collaboration/cursors.js +150 -0
  18. package/dist/collaboration/cursors.js.map +1 -0
  19. package/dist/collaboration/index.js +108 -0
  20. package/dist/collaboration/index.js.map +1 -0
  21. package/dist/collaboration/order.js +88 -0
  22. package/dist/collaboration/order.js.map +1 -0
  23. package/dist/collaboration/table.js +234 -0
  24. package/dist/collaboration/table.js.map +1 -0
  25. package/dist/collaboration/text.js +91 -0
  26. package/dist/collaboration/text.js.map +1 -0
  27. package/dist/components/block-handle.js +6 -0
  28. package/dist/components/block-handle.js.map +1 -0
  29. package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js +474 -0
  30. package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js.map +1 -0
  31. package/dist/components/bubble-toolbar.js +6 -0
  32. package/dist/components/bubble-toolbar.js.map +1 -0
  33. package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js +381 -0
  34. package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  35. package/dist/components/comment-button.js +6 -0
  36. package/dist/components/comment-button.js.map +1 -0
  37. package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js +83 -0
  38. package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js.map +1 -0
  39. package/dist/components/comment-card.js +6 -0
  40. package/dist/components/comment-card.js.map +1 -0
  41. package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js +104 -0
  42. package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js.map +1 -0
  43. package/dist/components/menu-escape.js +25 -0
  44. package/dist/components/menu-escape.js.map +1 -0
  45. package/dist/components/search-bar.js +6 -0
  46. package/dist/components/search-bar.js.map +1 -0
  47. package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js +192 -0
  48. package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  49. package/dist/components/slash-menu.js +6 -0
  50. package/dist/components/slash-menu.js.map +1 -0
  51. package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js +164 -0
  52. package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  53. package/dist/core/align.js +94 -0
  54. package/dist/core/align.js.map +1 -0
  55. package/dist/core/block-selection.js +155 -0
  56. package/dist/core/block-selection.js.map +1 -0
  57. package/dist/core/blocks.js +132 -0
  58. package/dist/core/blocks.js.map +1 -0
  59. package/dist/core/changes.js +85 -0
  60. package/dist/core/changes.js.map +1 -0
  61. package/dist/core/colors.js +16 -0
  62. package/dist/core/colors.js.map +1 -0
  63. package/dist/core/commands.js +379 -0
  64. package/dist/core/commands.js.map +1 -0
  65. package/dist/core/comments.js +186 -0
  66. package/dist/core/comments.js.map +1 -0
  67. package/dist/core/compare.js +106 -0
  68. package/dist/core/compare.js.map +1 -0
  69. package/dist/core/convert.js +28 -0
  70. package/dist/core/convert.js.map +1 -0
  71. package/dist/core/diff.js +141 -0
  72. package/dist/core/diff.js.map +1 -0
  73. package/dist/core/editor.js +138 -0
  74. package/dist/core/editor.js.map +1 -0
  75. package/dist/core/extension.js +99 -0
  76. package/dist/core/extension.js.map +1 -0
  77. package/dist/core/file-drop.js +56 -0
  78. package/dist/core/file-drop.js.map +1 -0
  79. package/dist/core/highlight.js +120 -0
  80. package/dist/core/highlight.js.map +1 -0
  81. package/dist/core/input-rules.js +61 -0
  82. package/dist/core/input-rules.js.map +1 -0
  83. package/dist/core/keymap.js +38 -0
  84. package/dist/core/keymap.js.map +1 -0
  85. package/dist/core/model.js +196 -0
  86. package/dist/core/model.js.map +1 -0
  87. package/dist/core/output.js +263 -0
  88. package/dist/core/output.js.map +1 -0
  89. package/dist/core/plugins.js +102 -0
  90. package/dist/core/plugins.js.map +1 -0
  91. package/dist/core/prepare.js +102 -0
  92. package/dist/core/prepare.js.map +1 -0
  93. package/dist/core/schema.js +158 -0
  94. package/dist/core/schema.js.map +1 -0
  95. package/dist/core/search.js +197 -0
  96. package/dist/core/search.js.map +1 -0
  97. package/dist/core/selection.js +16 -0
  98. package/dist/core/selection.js.map +1 -0
  99. package/dist/core/slash.js +83 -0
  100. package/dist/core/slash.js.map +1 -0
  101. package/dist/core/transaction.js +311 -0
  102. package/dist/core/transaction.js.map +1 -0
  103. package/dist/define-block.js +177 -0
  104. package/dist/define-block.js.map +1 -0
  105. package/dist/document.js +157 -0
  106. package/dist/document.js.map +1 -0
  107. package/dist/dom.js +48 -0
  108. package/dist/dom.js.map +1 -0
  109. package/dist/editor.js +33 -0
  110. package/dist/editor.js.map +1 -0
  111. package/dist/editor.vue_vue_type_script_setup_true_lang.js +564 -0
  112. package/dist/editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
  113. package/dist/editor2.js +6 -0
  114. package/dist/editor2.js.map +1 -0
  115. package/dist/extensions/align/align-menu.js +6 -0
  116. package/dist/extensions/align/align-menu.js.map +1 -0
  117. package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js +82 -0
  118. package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  119. package/dist/extensions/align/align-sub.js +6 -0
  120. package/dist/extensions/align/align-sub.js.map +1 -0
  121. package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js +57 -0
  122. package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js.map +1 -0
  123. package/dist/extensions/align/commands.js +40 -0
  124. package/dist/extensions/align/commands.js.map +1 -0
  125. package/dist/extensions/align/index.js +27 -0
  126. package/dist/extensions/align/index.js.map +1 -0
  127. package/dist/extensions/attachment/attachment-block.js +6 -0
  128. package/dist/extensions/attachment/attachment-block.js.map +1 -0
  129. package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js +119 -0
  130. package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  131. package/dist/extensions/attachment/files.js +66 -0
  132. package/dist/extensions/attachment/files.js.map +1 -0
  133. package/dist/extensions/attachment/index.js +132 -0
  134. package/dist/extensions/attachment/index.js.map +1 -0
  135. package/dist/extensions/blocks.js +170 -0
  136. package/dist/extensions/blocks.js.map +1 -0
  137. package/dist/extensions/callout/callout-block.js +6 -0
  138. package/dist/extensions/callout/callout-block.js.map +1 -0
  139. package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js +71 -0
  140. package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  141. package/dist/extensions/callout/html.js +61 -0
  142. package/dist/extensions/callout/html.js.map +1 -0
  143. package/dist/extensions/callout/index.js +52 -0
  144. package/dist/extensions/callout/index.js.map +1 -0
  145. package/dist/extensions/callout/tones.js +12 -0
  146. package/dist/extensions/callout/tones.js.map +1 -0
  147. package/dist/extensions/code-block/code-block-view.js +6 -0
  148. package/dist/extensions/code-block/code-block-view.js.map +1 -0
  149. package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js +87 -0
  150. package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js.map +1 -0
  151. package/dist/extensions/code-block/index.js +98 -0
  152. package/dist/extensions/code-block/index.js.map +1 -0
  153. package/dist/extensions/code-block/languages.js +116 -0
  154. package/dist/extensions/code-block/languages.js.map +1 -0
  155. package/dist/extensions/colors/color-menu.js +6 -0
  156. package/dist/extensions/colors/color-menu.js.map +1 -0
  157. package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js +113 -0
  158. package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  159. package/dist/extensions/colors/index.js +49 -0
  160. package/dist/extensions/colors/index.js.map +1 -0
  161. package/dist/extensions/colors/palette.js +26 -0
  162. package/dist/extensions/colors/palette.js.map +1 -0
  163. package/dist/extensions/editor-kit.js +35 -0
  164. package/dist/extensions/editor-kit.js.map +1 -0
  165. package/dist/extensions/embed/embed-block.js +6 -0
  166. package/dist/extensions/embed/embed-block.js.map +1 -0
  167. package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js +210 -0
  168. package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  169. package/dist/extensions/embed/index.js +118 -0
  170. package/dist/extensions/embed/index.js.map +1 -0
  171. package/dist/extensions/embed/targets.js +79 -0
  172. package/dist/extensions/embed/targets.js.map +1 -0
  173. package/dist/extensions/formatting.js +153 -0
  174. package/dist/extensions/formatting.js.map +1 -0
  175. package/dist/extensions/image/image-block.js +6 -0
  176. package/dist/extensions/image/image-block.js.map +1 -0
  177. package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js +206 -0
  178. package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  179. package/dist/extensions/image/index.js +125 -0
  180. package/dist/extensions/image/index.js.map +1 -0
  181. package/dist/extensions/image/uploads.js +96 -0
  182. package/dist/extensions/image/uploads.js.map +1 -0
  183. package/dist/extensions/list-html.js +58 -0
  184. package/dist/extensions/list-html.js.map +1 -0
  185. package/dist/extensions/lists.js +182 -0
  186. package/dist/extensions/lists.js.map +1 -0
  187. package/dist/extensions/mention/index.js +56 -0
  188. package/dist/extensions/mention/index.js.map +1 -0
  189. package/dist/extensions/mention/mention-menu.js +6 -0
  190. package/dist/extensions/mention/mention-menu.js.map +1 -0
  191. package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js +132 -0
  192. package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
  193. package/dist/extensions/mention/state.js +113 -0
  194. package/dist/extensions/mention/state.js.map +1 -0
  195. package/dist/extensions/table/commands.js +356 -0
  196. package/dist/extensions/table/commands.js.map +1 -0
  197. package/dist/extensions/table/html.js +134 -0
  198. package/dist/extensions/table/html.js.map +1 -0
  199. package/dist/extensions/table/index.js +56 -0
  200. package/dist/extensions/table/index.js.map +1 -0
  201. package/dist/extensions/table/plugin.js +81 -0
  202. package/dist/extensions/table/plugin.js.map +1 -0
  203. package/dist/extensions/table/schema.js +258 -0
  204. package/dist/extensions/table/schema.js.map +1 -0
  205. package/dist/extensions/table/table-controls.js +6 -0
  206. package/dist/extensions/table/table-controls.js.map +1 -0
  207. package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js +698 -0
  208. package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js.map +1 -0
  209. package/dist/extensions/table/view.js +19 -0
  210. package/dist/extensions/table/view.js.map +1 -0
  211. package/dist/extensions/toggle/index.js +87 -0
  212. package/dist/extensions/toggle/index.js.map +1 -0
  213. package/dist/extensions/toggle/state.js +120 -0
  214. package/dist/extensions/toggle/state.js.map +1 -0
  215. package/dist/extensions/toggle/toggle-block.js +6 -0
  216. package/dist/extensions/toggle/toggle-block.js.map +1 -0
  217. package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js +42 -0
  218. package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
  219. package/dist/index.js +49 -0
  220. package/dist/index.js.map +1 -0
  221. package/dist/locale.js +522 -0
  222. package/dist/locale.js.map +1 -0
  223. package/dist/markdown/index.js +24 -0
  224. package/dist/markdown/index.js.map +1 -0
  225. package/dist/style.css +1 -0
  226. package/dist/style.d.ts +1 -0
  227. package/dist/style.js +8 -0
  228. package/dist/types/catalog.d.ts +8 -0
  229. package/dist/types/code/code-editor-block.vue.d.ts +17 -0
  230. package/dist/types/code/index.d.ts +18 -0
  231. package/dist/types/code/languages.d.ts +7 -0
  232. package/dist/types/code/types.d.ts +37 -0
  233. package/dist/types/code/view.d.ts +12 -0
  234. package/dist/types/collaboration/binding.d.ts +32 -0
  235. package/dist/types/collaboration/cursors.d.ts +26 -0
  236. package/dist/types/collaboration/index.d.ts +47 -0
  237. package/dist/types/collaboration/order.d.ts +20 -0
  238. package/dist/types/collaboration/table.d.ts +25 -0
  239. package/dist/types/collaboration/text.d.ts +9 -0
  240. package/dist/types/components/block-handle.vue.d.ts +3 -0
  241. package/dist/types/components/bubble-toolbar.vue.d.ts +7 -0
  242. package/dist/types/components/comment-button.vue.d.ts +3 -0
  243. package/dist/types/components/comment-card.vue.d.ts +3 -0
  244. package/dist/types/components/menu-escape.d.ts +7 -0
  245. package/dist/types/components/search-bar.vue.d.ts +22 -0
  246. package/dist/types/components/slash-menu.vue.d.ts +11 -0
  247. package/dist/types/core/align.d.ts +16 -0
  248. package/dist/types/core/block-selection.d.ts +22 -0
  249. package/dist/types/core/blocks.d.ts +52 -0
  250. package/dist/types/core/changes.d.ts +14 -0
  251. package/dist/types/core/colors.d.ts +4 -0
  252. package/dist/types/core/commands.d.ts +61 -0
  253. package/dist/types/core/comments.d.ts +44 -0
  254. package/dist/types/core/compare.d.ts +10 -0
  255. package/dist/types/core/convert.d.ts +12 -0
  256. package/dist/types/core/diff.d.ts +49 -0
  257. package/dist/types/core/editor.d.ts +64 -0
  258. package/dist/types/core/extension.d.ts +167 -0
  259. package/dist/types/core/file-drop.d.ts +19 -0
  260. package/dist/types/core/highlight.d.ts +32 -0
  261. package/dist/types/core/input-rules.d.ts +21 -0
  262. package/dist/types/core/keymap.d.ts +8 -0
  263. package/dist/types/core/model.d.ts +158 -0
  264. package/dist/types/core/output.d.ts +64 -0
  265. package/dist/types/core/plugins.d.ts +24 -0
  266. package/dist/types/core/prepare.d.ts +23 -0
  267. package/dist/types/core/schema.d.ts +38 -0
  268. package/dist/types/core/search.d.ts +83 -0
  269. package/dist/types/core/selection.d.ts +8 -0
  270. package/dist/types/core/slash.d.ts +21 -0
  271. package/dist/types/core/transaction.d.ts +97 -0
  272. package/dist/types/define-block.d.ts +66 -0
  273. package/dist/types/document.d.ts +46 -0
  274. package/dist/types/dom.d.ts +17 -0
  275. package/dist/types/editor.d.ts +252 -0
  276. package/dist/types/editor.vue.d.ts +210 -0
  277. package/dist/types/extensions/align/align-menu.vue.d.ts +3 -0
  278. package/dist/types/extensions/align/align-sub.vue.d.ts +7 -0
  279. package/dist/types/extensions/align/commands.d.ts +13 -0
  280. package/dist/types/extensions/align/index.d.ts +7 -0
  281. package/dist/types/extensions/attachment/attachment-block.vue.d.ts +17 -0
  282. package/dist/types/extensions/attachment/files.d.ts +6 -0
  283. package/dist/types/extensions/attachment/index.d.ts +20 -0
  284. package/dist/types/extensions/blocks.d.ts +8 -0
  285. package/dist/types/extensions/callout/callout-block.vue.d.ts +14 -0
  286. package/dist/types/extensions/callout/html.d.ts +4 -0
  287. package/dist/types/extensions/callout/index.d.ts +3 -0
  288. package/dist/types/extensions/callout/tones.d.ts +8 -0
  289. package/dist/types/extensions/code-block/code-block-view.vue.d.ts +16 -0
  290. package/dist/types/extensions/code-block/index.d.ts +19 -0
  291. package/dist/types/extensions/code-block/languages.d.ts +9 -0
  292. package/dist/types/extensions/colors/color-menu.vue.d.ts +3 -0
  293. package/dist/types/extensions/colors/index.d.ts +7 -0
  294. package/dist/types/extensions/colors/palette.d.ts +7 -0
  295. package/dist/types/extensions/editor-kit.d.ts +37 -0
  296. package/dist/types/extensions/embed/embed-block.vue.d.ts +16 -0
  297. package/dist/types/extensions/embed/index.d.ts +18 -0
  298. package/dist/types/extensions/embed/targets.d.ts +32 -0
  299. package/dist/types/extensions/formatting.d.ts +8 -0
  300. package/dist/types/extensions/image/image-block.vue.d.ts +16 -0
  301. package/dist/types/extensions/image/index.d.ts +16 -0
  302. package/dist/types/extensions/image/uploads.d.ts +35 -0
  303. package/dist/types/extensions/list-html.d.ts +11 -0
  304. package/dist/types/extensions/lists.d.ts +6 -0
  305. package/dist/types/extensions/mention/index.d.ts +11 -0
  306. package/dist/types/extensions/mention/mention-menu.vue.d.ts +7 -0
  307. package/dist/types/extensions/mention/state.d.ts +28 -0
  308. package/dist/types/extensions/table/commands.d.ts +69 -0
  309. package/dist/types/extensions/table/html.d.ts +4 -0
  310. package/dist/types/extensions/table/index.d.ts +8 -0
  311. package/dist/types/extensions/table/plugin.d.ts +4 -0
  312. package/dist/types/extensions/table/schema.d.ts +23 -0
  313. package/dist/types/extensions/table/table-controls.vue.d.ts +3 -0
  314. package/dist/types/extensions/table/view.d.ts +13 -0
  315. package/dist/types/extensions/toggle/index.d.ts +7 -0
  316. package/dist/types/extensions/toggle/state.d.ts +17 -0
  317. package/dist/types/extensions/toggle/toggle-block.vue.d.ts +14 -0
  318. package/dist/types/index.d.ts +266 -0
  319. package/dist/types/locale.d.ts +195 -0
  320. package/dist/types/markdown/index.d.ts +14 -0
  321. package/dist/types/use-comments.d.ts +17 -0
  322. package/dist/use-comments.js +141 -0
  323. package/dist/use-comments.js.map +1 -0
  324. package/package.json +98 -4
@@ -0,0 +1,182 @@
1
+ import { createId } from "../core/model.js";
2
+ import { contentDOM, textBlock } from "../core/schema.js";
3
+ import { defineEditorExtension } from "../core/extension.js";
4
+ import { setBlockType } from "../core/commands.js";
5
+ import { blockInputRule } from "../core/input-rules.js";
6
+ import { numberingPlugin } from "../core/plugins.js";
7
+ import { listItemMarkdown, prepareLists } from "./list-html.js";
8
+ import { ListIcon, ListOrderedIcon, ListTodoIcon } from "@dou-ui/icons";
9
+ // src/extensions/lists.ts
10
+ var SVG = "http://www.w3.org/2000/svg";
11
+ /** The check of `CheckIcon`, drawn at the checkbox's stroke width. */
12
+ function checkMark() {
13
+ const svg = document.createElementNS(SVG, "svg");
14
+ svg.setAttribute("viewBox", "0 0 24 24");
15
+ svg.setAttribute("aria-hidden", "true");
16
+ const path = document.createElementNS(SVG, "path");
17
+ path.setAttribute("d", "m5 12.5 4.5 4.5L19 7.5");
18
+ for (const [name, value] of Object.entries({
19
+ fill: "none",
20
+ stroke: "currentColor",
21
+ "stroke-width": "2",
22
+ "stroke-linecap": "round",
23
+ "stroke-linejoin": "round"
24
+ })) path.setAttribute(name, value);
25
+ svg.append(path);
26
+ return svg;
27
+ }
28
+ /** A to-do item: a real checkbox in front of the text, its state in the node's attributes. */
29
+ var checkItemView = (node, view, getPos) => {
30
+ const dom = document.createElement("div");
31
+ dom.className = "dou-editor__content dou-editor__content--checkItem";
32
+ dom.dataset.contentType = "checkItem";
33
+ const box = document.createElement("span");
34
+ box.className = "dou-editor__check";
35
+ box.contentEditable = "false";
36
+ const button = document.createElement("button");
37
+ button.type = "button";
38
+ button.className = "dou-editor__checkbox";
39
+ button.setAttribute("role", "checkbox");
40
+ button.append(checkMark());
41
+ box.append(button);
42
+ const text = document.createElement("div");
43
+ text.className = "dou-editor__text";
44
+ text.id = `dou-editor-check-${createId()}`;
45
+ button.setAttribute("aria-labelledby", text.id);
46
+ dom.append(box, text);
47
+ let current = node;
48
+ const sync = () => {
49
+ const checked = !!current.attrs.checked;
50
+ dom.dataset.checked = String(checked);
51
+ button.setAttribute("aria-checked", String(checked));
52
+ button.disabled = !view.editable;
53
+ };
54
+ sync();
55
+ button.addEventListener("mousedown", (event) => event.preventDefault());
56
+ button.addEventListener("click", () => {
57
+ const pos = getPos();
58
+ if (pos === void 0 || !view.editable) return;
59
+ view.dispatch(view.state.tr.setNodeAttribute(pos, "checked", !current.attrs.checked));
60
+ });
61
+ return {
62
+ dom,
63
+ contentDOM: text,
64
+ update(next) {
65
+ if (next.type !== current.type) return false;
66
+ current = next;
67
+ sync();
68
+ return true;
69
+ },
70
+ ignoreMutation: (mutation) => box.contains(mutation.target),
71
+ stopEvent: (event) => box.contains(event.target)
72
+ };
73
+ };
74
+ /**
75
+ * Bulleted, numbered and to-do lists. Each item is a block of its own:
76
+ * `Enter` continues the list, `Tab` nests an item. Pasted HTML lists keep
77
+ * their nesting, and task lists become to-do items.
78
+ */
79
+ var lists = defineEditorExtension({
80
+ name: "lists",
81
+ blocks: {
82
+ bulletItem: textBlock("bulletItem", {
83
+ continues: true,
84
+ parseDOM: [{ tag: "div[data-content-type=\"bulletItem\"]" }, { tag: "ul > li" }]
85
+ }),
86
+ numberedItem: textBlock("numberedItem", {
87
+ continues: true,
88
+ parseDOM: [{ tag: "div[data-content-type=\"numberedItem\"]" }, { tag: "ol > li" }]
89
+ }),
90
+ checkItem: textBlock("checkItem", {
91
+ continues: true,
92
+ attrs: { checked: { default: false } },
93
+ parseDOM: [{
94
+ tag: "div[data-content-type=\"checkItem\"]",
95
+ getAttrs: (dom) => ({ checked: dom.dataset.checked === "true" })
96
+ }],
97
+ toDOM: (node) => contentDOM("checkItem", { "data-checked": String(node.attrs.checked) })
98
+ })
99
+ },
100
+ nodeViews: { checkItem: checkItemView },
101
+ plugins: () => [numberingPlugin()],
102
+ prepareHTML: prepareLists,
103
+ output: { blocks: {
104
+ bulletItem: {
105
+ group: "ul",
106
+ html: (_block, { content, children }) => `<li>${content}${children}</li>`,
107
+ markdown: (_block, context) => listItemMarkdown("- ", context)
108
+ },
109
+ numberedItem: {
110
+ group: "ol",
111
+ html: (_block, { content, children }) => `<li>${content}${children}</li>`,
112
+ markdown: (_block, context) => listItemMarkdown(`${context.index}. `, context)
113
+ },
114
+ checkItem: {
115
+ group: "ul class=\"task-list\"",
116
+ html: (block, { content, children }) => `<li><input type="checkbox" disabled${block.props.checked ? " checked" : ""}> ${content}${children}</li>`,
117
+ markdown: (block, context) => listItemMarkdown(block.props.checked ? "- [x] " : "- [ ] ", context)
118
+ }
119
+ } },
120
+ inputRules: () => [
121
+ blockInputRule(/^[-*+]\s$/, "bulletItem"),
122
+ blockInputRule(/^1[.)]\s$/, "numberedItem"),
123
+ blockInputRule(/^\[( |x)?\]\s$/i, "checkItem", (match) => ({ checked: match[1]?.toLowerCase() === "x" }))
124
+ ],
125
+ keymap: () => ({
126
+ "mod+shift+7": setBlockType("numberedItem"),
127
+ "mod+shift+8": setBlockType("bulletItem"),
128
+ "mod+shift+9": setBlockType("checkItem")
129
+ }),
130
+ blockOptions: [
131
+ {
132
+ key: "bulletItem",
133
+ type: "bulletItem",
134
+ label: (m) => m.types.bulletItem,
135
+ hint: (m) => m.hints.bulletItem,
136
+ icon: ListIcon,
137
+ keywords: [
138
+ "ul",
139
+ "bullet",
140
+ "list",
141
+ "lb",
142
+ "liebiao"
143
+ ],
144
+ shortcut: "-"
145
+ },
146
+ {
147
+ key: "numberedItem",
148
+ type: "numberedItem",
149
+ label: (m) => m.types.numberedItem,
150
+ hint: (m) => m.hints.numberedItem,
151
+ icon: ListOrderedIcon,
152
+ keywords: [
153
+ "ol",
154
+ "number",
155
+ "ordered",
156
+ "list",
157
+ "lb",
158
+ "bianhao"
159
+ ],
160
+ shortcut: "1."
161
+ },
162
+ {
163
+ key: "checkItem",
164
+ type: "checkItem",
165
+ label: (m) => m.types.checkItem,
166
+ hint: (m) => m.hints.checkItem,
167
+ icon: ListTodoIcon,
168
+ keywords: [
169
+ "todo",
170
+ "task",
171
+ "check",
172
+ "db",
173
+ "daiban",
174
+ "renwu"
175
+ ],
176
+ shortcut: "[]"
177
+ }
178
+ ]
179
+ });
180
+ export { lists };
181
+
182
+ //# sourceMappingURL=lists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lists.js","names":[],"sources":["../../src/extensions/lists.ts"],"sourcesContent":["import { ListIcon, ListOrderedIcon, ListTodoIcon } from '@dou-ui/icons'\nimport type { NodeViewConstructor } from 'prosemirror-view'\nimport { setBlockType } from '../core/commands'\nimport { defineEditorExtension } from '../core/extension'\nimport { blockInputRule } from '../core/input-rules'\nimport { createId } from '../core/model'\nimport { numberingPlugin } from '../core/plugins'\nimport { contentDOM, textBlock } from '../core/schema'\nimport { listItemMarkdown, prepareLists } from './list-html'\n\nconst SVG = 'http://www.w3.org/2000/svg'\n\n/** The check of `CheckIcon`, drawn at the checkbox's stroke width. */\nfunction checkMark(): SVGSVGElement {\n const svg = document.createElementNS(SVG, 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('aria-hidden', 'true')\n const path = document.createElementNS(SVG, 'path')\n path.setAttribute('d', 'm5 12.5 4.5 4.5L19 7.5')\n for (const [name, value] of Object.entries({\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': '2',\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n }))\n path.setAttribute(name, value)\n svg.append(path)\n return svg\n}\n\n/** A to-do item: a real checkbox in front of the text, its state in the node's attributes. */\nconst checkItemView: NodeViewConstructor = (node, view, getPos) => {\n const dom = document.createElement('div')\n dom.className = 'dou-editor__content dou-editor__content--checkItem'\n dom.dataset.contentType = 'checkItem'\n const box = document.createElement('span')\n box.className = 'dou-editor__check'\n box.contentEditable = 'false'\n const button = document.createElement('button')\n button.type = 'button'\n button.className = 'dou-editor__checkbox'\n button.setAttribute('role', 'checkbox')\n button.append(checkMark())\n box.append(button)\n const text = document.createElement('div')\n text.className = 'dou-editor__text'\n // the item's own text names the checkbox\n text.id = `dou-editor-check-${createId()}`\n button.setAttribute('aria-labelledby', text.id)\n dom.append(box, text)\n\n let current = node\n const sync = () => {\n const checked = !!current.attrs.checked\n dom.dataset.checked = String(checked)\n button.setAttribute('aria-checked', String(checked))\n button.disabled = !view.editable\n }\n sync()\n button.addEventListener('mousedown', (event) => event.preventDefault())\n button.addEventListener('click', () => {\n const pos = getPos()\n if (pos === undefined || !view.editable) return\n view.dispatch(view.state.tr.setNodeAttribute(pos, 'checked', !current.attrs.checked))\n })\n return {\n dom,\n contentDOM: text,\n update(next) {\n if (next.type !== current.type) return false\n current = next\n sync()\n return true\n },\n ignoreMutation: (mutation) => box.contains(mutation.target),\n stopEvent: (event) => box.contains(event.target as Node),\n }\n}\n\n/**\n * Bulleted, numbered and to-do lists. Each item is a block of its own:\n * `Enter` continues the list, `Tab` nests an item. Pasted HTML lists keep\n * their nesting, and task lists become to-do items.\n */\nexport const lists = defineEditorExtension({\n name: 'lists',\n blocks: {\n bulletItem: textBlock('bulletItem', {\n continues: true,\n parseDOM: [{ tag: 'div[data-content-type=\"bulletItem\"]' }, { tag: 'ul > li' }],\n }),\n numberedItem: textBlock('numberedItem', {\n continues: true,\n parseDOM: [{ tag: 'div[data-content-type=\"numberedItem\"]' }, { tag: 'ol > li' }],\n }),\n checkItem: textBlock('checkItem', {\n continues: true,\n attrs: { checked: { default: false } },\n parseDOM: [\n {\n tag: 'div[data-content-type=\"checkItem\"]',\n getAttrs: (dom) => ({ checked: (dom as HTMLElement).dataset.checked === 'true' }),\n },\n ],\n toDOM: (node) => contentDOM('checkItem', { 'data-checked': String(node.attrs.checked) }),\n }),\n },\n nodeViews: { checkItem: checkItemView },\n plugins: () => [numberingPlugin()],\n prepareHTML: prepareLists,\n output: {\n blocks: {\n bulletItem: {\n group: 'ul',\n html: (_block, { content, children }) => `<li>${content}${children}</li>`,\n markdown: (_block, context) => listItemMarkdown('- ', context),\n },\n numberedItem: {\n group: 'ol',\n html: (_block, { content, children }) => `<li>${content}${children}</li>`,\n markdown: (_block, context) => listItemMarkdown(`${context.index}. `, context),\n },\n checkItem: {\n group: 'ul class=\"task-list\"',\n html: (block, { content, children }) =>\n `<li><input type=\"checkbox\" disabled${block.props.checked ? ' checked' : ''}> ${content}${children}</li>`,\n markdown: (block, context) =>\n listItemMarkdown(block.props.checked ? '- [x] ' : '- [ ] ', context),\n },\n },\n },\n inputRules: () => [\n blockInputRule(/^[-*+]\\s$/, 'bulletItem'),\n blockInputRule(/^1[.)]\\s$/, 'numberedItem'),\n blockInputRule(/^\\[( |x)?\\]\\s$/i, 'checkItem', (match) => ({\n checked: match[1]?.toLowerCase() === 'x',\n })),\n ],\n keymap: () => ({\n 'mod+shift+7': setBlockType('numberedItem'),\n 'mod+shift+8': setBlockType('bulletItem'),\n 'mod+shift+9': setBlockType('checkItem'),\n }),\n blockOptions: [\n {\n key: 'bulletItem',\n type: 'bulletItem',\n label: (m) => m.types.bulletItem,\n hint: (m) => m.hints.bulletItem,\n icon: ListIcon,\n keywords: ['ul', 'bullet', 'list', 'lb', 'liebiao'],\n shortcut: '-',\n },\n {\n key: 'numberedItem',\n type: 'numberedItem',\n label: (m) => m.types.numberedItem,\n hint: (m) => m.hints.numberedItem,\n icon: ListOrderedIcon,\n keywords: ['ol', 'number', 'ordered', 'list', 'lb', 'bianhao'],\n shortcut: '1.',\n },\n {\n key: 'checkItem',\n type: 'checkItem',\n label: (m) => m.types.checkItem,\n hint: (m) => m.hints.checkItem,\n icon: ListTodoIcon,\n keywords: ['todo', 'task', 'check', 'db', 'daiban', 'renwu'],\n shortcut: '[]',\n },\n ],\n})\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import { defineEditorExtension } from "../../core/extension.js";
2
+ import { escapeHTML } from "../../core/output.js";
3
+ import { mentionPlugin } from "./state.js";
4
+ import mention_menu_default from "./mention-menu.js";
5
+ import { defineComponent, h } from "vue";
6
+ // src/extensions/mention/index.ts
7
+ /**
8
+ * Mentions: typing `@` suggests people (or anything else) from `search`,
9
+ * and the chosen one goes into the text as a mention, kept in the
10
+ * document as `{ type: 'inline', name: 'mention', props: { id, label } }`.
11
+ */
12
+ var mentionHTML = (props) => `<span data-mention="${escapeHTML(String(props.id ?? ""))}">@${escapeHTML(String(props.label ?? ""))}</span>`;
13
+ function mention(options) {
14
+ const Menu = defineComponent({
15
+ name: "DouEditorMentionMenu",
16
+ setup() {
17
+ return () => h(mention_menu_default, { search: options.search });
18
+ }
19
+ });
20
+ return defineEditorExtension({
21
+ name: "mention",
22
+ inlines: { mention: {
23
+ atom: true,
24
+ selectable: false,
25
+ attrs: {
26
+ id: { default: "" },
27
+ label: { default: "" }
28
+ },
29
+ leafText: (node) => `@${node.attrs.label}`,
30
+ parseDOM: [{
31
+ tag: "span[data-mention]",
32
+ getAttrs: (dom) => ({
33
+ id: dom.dataset.mention ?? "",
34
+ label: (dom.textContent ?? "").replace(/^@/, "")
35
+ })
36
+ }],
37
+ toDOM: (node) => [
38
+ "span",
39
+ {
40
+ class: "dou-editor__mention",
41
+ "data-mention": node.attrs.id
42
+ },
43
+ `@${node.attrs.label}`
44
+ ]
45
+ } },
46
+ output: { inlines: { mention: {
47
+ html: mentionHTML,
48
+ markdown: mentionHTML
49
+ } } },
50
+ plugins: () => [mentionPlugin()],
51
+ overlays: [Menu]
52
+ });
53
+ }
54
+ export { mention };
55
+
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["MentionMenu"],"sources":["../../../src/extensions/mention/index.ts"],"sourcesContent":["import { defineComponent, h } from 'vue'\nimport { escapeHTML } from '../../core/output'\nimport { defineEditorExtension, type EditorExtension } from '../../core/extension'\nimport MentionMenu from './mention-menu.vue'\nimport { mentionPlugin, type MentionItem } from './state'\n\nexport interface MentionOptions {\n /**\n * Finds who matches what was typed after the `@` (empty right after it).\n * Can return a Promise; only the answer to the latest query is shown.\n */\n search: (query: string) => MentionItem[] | Promise<MentionItem[]>\n}\n\n/**\n * Mentions: typing `@` suggests people (or anything else) from `search`,\n * and the chosen one goes into the text as a mention, kept in the\n * document as `{ type: 'inline', name: 'mention', props: { id, label } }`.\n */\nconst mentionHTML = (props: Record<string, unknown>) =>\n `<span data-mention=\"${escapeHTML(String(props.id ?? ''))}\">@${escapeHTML(String(props.label ?? ''))}</span>`\n\nexport function mention(options: MentionOptions): EditorExtension {\n const Menu = defineComponent({\n name: 'DouEditorMentionMenu',\n setup() {\n return () => h(MentionMenu, { search: options.search })\n },\n })\n return defineEditorExtension({\n name: 'mention',\n inlines: {\n mention: {\n atom: true,\n selectable: false,\n attrs: { id: { default: '' }, label: { default: '' } },\n leafText: (node) => `@${node.attrs.label}`,\n parseDOM: [\n {\n tag: 'span[data-mention]',\n getAttrs: (dom) => ({\n id: (dom as HTMLElement).dataset.mention ?? '',\n label: ((dom as HTMLElement).textContent ?? '').replace(/^@/, ''),\n }),\n },\n ],\n toDOM: (node) => [\n 'span',\n { class: 'dou-editor__mention', 'data-mention': node.attrs.id },\n `@${node.attrs.label}`,\n ],\n },\n },\n output: {\n // Markdown has no mention: the same HTML keeps the id when it is read back\n inlines: { mention: { html: mentionHTML, markdown: mentionHTML } },\n },\n plugins: () => [mentionPlugin()],\n overlays: [Menu],\n })\n}\n\nexport type { MentionItem }\n"],"mappings":";;;;;;;;;;;AAmBA,IAAM,eAAe,UACnB,uBAAuB,WAAW,OAAO,MAAM,MAAM,EAAE,CAAC,EAAE,KAAK,WAAW,OAAO,MAAM,SAAS,EAAE,CAAC,EAAE;AAEvG,SAAgB,QAAQ,SAA0C;CAChE,MAAM,OAAO,gBAAgB;EAC3B,MAAM;EACN,QAAQ;GACN,aAAa,EAAEA,sBAAa,EAAE,QAAQ,QAAQ,OAAO,CAAC;EACxD;CACF,CAAC;CACD,OAAO,sBAAsB;EAC3B,MAAM;EACN,SAAS,EACP,SAAS;GACP,MAAM;GACN,YAAY;GACZ,OAAO;IAAE,IAAI,EAAE,SAAS,GAAG;IAAG,OAAO,EAAE,SAAS,GAAG;GAAE;GACrD,WAAW,SAAS,IAAI,KAAK,MAAM;GACnC,UAAU,CACR;IACE,KAAK;IACL,WAAW,SAAS;KAClB,IAAK,IAAoB,QAAQ,WAAW;KAC5C,QAAS,IAAoB,eAAe,GAAA,CAAI,QAAQ,MAAM,EAAE;IAClE;GACF,CACF;GACA,QAAQ,SAAS;IACf;IACA;KAAE,OAAO;KAAuB,gBAAgB,KAAK,MAAM;IAAG;IAC9D,IAAI,KAAK,MAAM;GACjB;EACF,EACF;EACA,QAAQ,EAEN,SAAS,EAAE,SAAS;GAAE,MAAM;GAAa,UAAU;EAAY,EAAE,EACnE;EACA,eAAe,CAAC,cAAc,CAAC;EAC/B,UAAU,CAAC,IAAI;CACjB,CAAC;AACH"}
@@ -0,0 +1,6 @@
1
+ import mention_menu_vue_vue_type_script_setup_true_lang_default from "./mention-menu.vue_vue_type_script_setup_true_lang.js";
2
+ // src/extensions/mention/mention-menu.vue
3
+ var mention_menu_default = mention_menu_vue_vue_type_script_setup_true_lang_default;
4
+ export { mention_menu_default as default };
5
+
6
+ //# sourceMappingURL=mention-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention-menu.js","names":["_sfc_main"],"sources":["../../../src/extensions/mention/mention-menu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DouAvatar, useFloating, useLayer, useZIndex, type VirtualElement } from 'dou-ui'\nimport { computed, nextTick, shallowRef, useId, useTemplateRef, watch } from 'vue'\nimport { useEditor } from '../../editor'\nimport {\n chooseMention,\n closeMention,\n getMention,\n setMentionActive,\n setMentionItems,\n type MentionItem,\n} from './state'\n\nconst props = defineProps<{\n search: (query: string) => MentionItem[] | Promise<MentionItem[]>\n}>()\n\nconst { view, state, messages } = useEditor()\nconst id = `dou-editor-mention-${useId()}`\nconst panel = useTemplateRef<HTMLElement>('panel')\n\nconst mention = computed(() => getMention(state.value))\nconst open = computed(() => !!mention.value)\nconst loading = shallowRef(false)\n\n// the list hangs below the `@` that opened it\nconst anchor: VirtualElement = {\n getBoundingClientRect() {\n const current = view.value\n const from = mention.value?.from\n if (!current || from === undefined) return new DOMRect()\n const { left, top, bottom } = current.coordsAtPos(from)\n return new DOMRect(left, top, 0, bottom - top)\n },\n}\nconst { style, placement, update } = useFloating(() => (open.value ? anchor : null), panel, {\n open,\n placement: 'bottom-start',\n offset: 6,\n})\nconst { next } = useZIndex()\nconst zIndex = shallowRef(0)\nwatch(open, (value) => value && (zIndex.value = next()), { immediate: true })\n// a press outside the editor and the list closes it; inside, the cursor move does\nuseLayer({\n open,\n elements: () => [panel.value, view.value?.dom],\n onOutside: () => view.value && closeMention(view.value),\n onEscape: () => view.value && closeMention(view.value),\n})\n\n// search again as the query changes; a slower, older answer never wins\nlet run = 0\nwatch(\n () => (mention.value ? mention.value.query : null),\n async (query) => {\n const current = ++run\n if (query === null) {\n loading.value = false\n return\n }\n loading.value = true\n let items: MentionItem[] = []\n try {\n items = await props.search(query)\n } catch {\n // a failed search shows nothing\n }\n if (current !== run || !view.value || !getMention(view.value.state)) return\n loading.value = false\n setMentionItems(view.value, items.slice(0, 50))\n void nextTick(update)\n },\n { immediate: true },\n)\n\n// the editor keeps focus; the active item is announced through it\nwatch(\n () => [open.value, mention.value?.active, mention.value?.items] as const,\n () => {\n const dom = view.value?.dom\n if (!dom) return\n const items = mention.value?.items ?? []\n const option = open.value && items.length ? `${id}-${mention.value!.active}` : null\n if (open.value) dom.setAttribute('aria-controls', id)\n else dom.removeAttribute('aria-controls')\n if (option) dom.setAttribute('aria-activedescendant', option)\n else dom.removeAttribute('aria-activedescendant')\n if (option) document.getElementById(option)?.scrollIntoView({ block: 'nearest' })\n },\n { flush: 'post' },\n)\n\nfunction choose(item: MentionItem) {\n if (view.value) chooseMention(view.value, item)\n}\n\nfunction hover(index: number) {\n if (view.value && mention.value?.active !== index) setMentionActive(view.value, index)\n}\n</script>\n\n<template>\n <Transition name=\"dou-floating\">\n <div\n v-if=\"open\"\n :id=\"id\"\n ref=\"panel\"\n role=\"listbox\"\n :aria-label=\"messages.mention.label\"\n class=\"dou-dropdown dou-floating dou-editor-mention\"\n :style=\"[style, { zIndex }]\"\n :data-placement=\"placement\"\n @mousedown.prevent\n >\n <div\n v-for=\"(item, index) in mention!.items\"\n :id=\"`${id}-${index}`\"\n :key=\"item.id\"\n role=\"option\"\n :aria-selected=\"index === mention!.active\"\n :class=\"[\n 'dou-dropdown__item',\n 'dou-editor-mention__item',\n { 'is-active': index === mention!.active },\n ]\"\n @pointermove=\"hover(index)\"\n @click=\"choose(item)\"\n >\n <DouAvatar :src=\"item.avatar\" :name=\"item.label\" size=\"sm\" />\n <span class=\"dou-editor-mention__text\">\n <span class=\"dou-editor-mention__label\">{{ item.label }}</span>\n <span v-if=\"item.description\" class=\"dou-editor-mention__description\">{{\n item.description\n }}</span>\n </span>\n </div>\n <div v-if=\"!mention!.items.length\" class=\"dou-editor-slash__empty\">\n {{ loading ? messages.mention.searching : messages.mention.noResults }}\n </div>\n </div>\n </Transition>\n</template>\n"],"mappings":""}
@@ -0,0 +1,132 @@
1
+ import { useEditor } from "../../editor.js";
2
+ import { chooseMention, closeMention, getMention, setMentionActive, setMentionItems } from "./state.js";
3
+ import { DouAvatar, useFloating, useLayer, useZIndex } from "dou-ui";
4
+ import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, openBlock, renderList, shallowRef, toDisplayString, unref, useId, useTemplateRef, watch, withCtx, withModifiers } from "vue";
5
+ // src/extensions/mention/mention-menu.vue?vue&type=script&setup=true&lang.ts
6
+ var _hoisted_1 = ["aria-label", "data-placement"];
7
+ var _hoisted_2 = [
8
+ "id",
9
+ "aria-selected",
10
+ "onPointermove",
11
+ "onClick"
12
+ ];
13
+ var _hoisted_3 = { class: "dou-editor-mention__text" };
14
+ var _hoisted_4 = { class: "dou-editor-mention__label" };
15
+ var _hoisted_5 = {
16
+ key: 0,
17
+ class: "dou-editor-mention__description"
18
+ };
19
+ var _hoisted_6 = {
20
+ key: 0,
21
+ class: "dou-editor-slash__empty"
22
+ };
23
+ var mention_menu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
24
+ __name: "mention-menu",
25
+ props: { search: { type: Function } },
26
+ setup(__props) {
27
+ const props = __props;
28
+ const { view, state, messages } = useEditor();
29
+ const id = `dou-editor-mention-${useId()}`;
30
+ const panel = useTemplateRef("panel");
31
+ const mention = computed(() => getMention(state.value));
32
+ const open = computed(() => !!mention.value);
33
+ const loading = shallowRef(false);
34
+ const anchor = { getBoundingClientRect() {
35
+ const current = view.value;
36
+ const from = mention.value?.from;
37
+ if (!current || from === void 0) return new DOMRect();
38
+ const { left, top, bottom } = current.coordsAtPos(from);
39
+ return new DOMRect(left, top, 0, bottom - top);
40
+ } };
41
+ const { style, placement, update } = useFloating(() => open.value ? anchor : null, panel, {
42
+ open,
43
+ placement: "bottom-start",
44
+ offset: 6
45
+ });
46
+ const { next } = useZIndex();
47
+ const zIndex = shallowRef(0);
48
+ watch(open, (value) => value && (zIndex.value = next()), { immediate: true });
49
+ useLayer({
50
+ open,
51
+ elements: () => [panel.value, view.value?.dom],
52
+ onOutside: () => view.value && closeMention(view.value),
53
+ onEscape: () => view.value && closeMention(view.value)
54
+ });
55
+ let run = 0;
56
+ watch(() => mention.value ? mention.value.query : null, async (query) => {
57
+ const current = ++run;
58
+ if (query === null) {
59
+ loading.value = false;
60
+ return;
61
+ }
62
+ loading.value = true;
63
+ let items = [];
64
+ try {
65
+ items = await props.search(query);
66
+ } catch {}
67
+ if (current !== run || !view.value || !getMention(view.value.state)) return;
68
+ loading.value = false;
69
+ setMentionItems(view.value, items.slice(0, 50));
70
+ nextTick(update);
71
+ }, { immediate: true });
72
+ watch(() => [
73
+ open.value,
74
+ mention.value?.active,
75
+ mention.value?.items
76
+ ], () => {
77
+ const dom = view.value?.dom;
78
+ if (!dom) return;
79
+ const items = mention.value?.items ?? [];
80
+ const option = open.value && items.length ? `${id}-${mention.value.active}` : null;
81
+ if (open.value) dom.setAttribute("aria-controls", id);
82
+ else dom.removeAttribute("aria-controls");
83
+ if (option) dom.setAttribute("aria-activedescendant", option);
84
+ else dom.removeAttribute("aria-activedescendant");
85
+ if (option) document.getElementById(option)?.scrollIntoView({ block: "nearest" });
86
+ }, { flush: "post" });
87
+ function choose(item) {
88
+ if (view.value) chooseMention(view.value, item);
89
+ }
90
+ function hover(index) {
91
+ if (view.value && mention.value?.active !== index) setMentionActive(view.value, index);
92
+ }
93
+ return (_ctx, _cache) => {
94
+ return openBlock(), createBlock(Transition, { name: "dou-floating" }, {
95
+ default: withCtx(() => [open.value ? (openBlock(), createElementBlock("div", {
96
+ key: 0,
97
+ id,
98
+ ref_key: "panel",
99
+ ref: panel,
100
+ role: "listbox",
101
+ "aria-label": unref(messages).mention.label,
102
+ class: "dou-dropdown dou-floating dou-editor-mention",
103
+ style: normalizeStyle([unref(style), { zIndex: zIndex.value }]),
104
+ "data-placement": unref(placement),
105
+ onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
106
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(mention.value.items, (item, index) => {
107
+ return openBlock(), createElementBlock("div", {
108
+ id: `${id}-${index}`,
109
+ key: item.id,
110
+ role: "option",
111
+ "aria-selected": index === mention.value.active,
112
+ class: normalizeClass([
113
+ "dou-dropdown__item",
114
+ "dou-editor-mention__item",
115
+ { "is-active": index === mention.value.active }
116
+ ]),
117
+ onPointermove: ($event) => hover(index),
118
+ onClick: ($event) => choose(item)
119
+ }, [createVNode(unref(DouAvatar), {
120
+ src: item.avatar,
121
+ name: item.label,
122
+ size: "sm"
123
+ }, null, 8, ["src", "name"]), createElementVNode("span", _hoisted_3, [createElementVNode("span", _hoisted_4, toDisplayString(item.label), 1), item.description ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(item.description), 1)) : createCommentVNode("", true)])], 42, _hoisted_2);
124
+ }), 128)), !mention.value.items.length ? (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString(loading.value ? unref(messages).mention.searching : unref(messages).mention.noResults), 1)) : createCommentVNode("", true)], 44, _hoisted_1)) : createCommentVNode("", true)]),
125
+ _: 1
126
+ });
127
+ };
128
+ }
129
+ });
130
+ export { mention_menu_vue_vue_type_script_setup_true_lang_default as default };
131
+
132
+ //# sourceMappingURL=mention-menu.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mention-menu.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_Transition","_withCtx","_createElementBlock","_unref","_normalizeStyle","_withModifiers","_Fragment","_renderList","_normalizeClass","_createVNode","_createElementVNode","_toDisplayString","_createCommentVNode"],"sources":["../../../src/extensions/mention/mention-menu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { DouAvatar, useFloating, useLayer, useZIndex, type VirtualElement } from 'dou-ui'\nimport { computed, nextTick, shallowRef, useId, useTemplateRef, watch } from 'vue'\nimport { useEditor } from '../../editor'\nimport {\n chooseMention,\n closeMention,\n getMention,\n setMentionActive,\n setMentionItems,\n type MentionItem,\n} from './state'\n\nconst props = defineProps<{\n search: (query: string) => MentionItem[] | Promise<MentionItem[]>\n}>()\n\nconst { view, state, messages } = useEditor()\nconst id = `dou-editor-mention-${useId()}`\nconst panel = useTemplateRef<HTMLElement>('panel')\n\nconst mention = computed(() => getMention(state.value))\nconst open = computed(() => !!mention.value)\nconst loading = shallowRef(false)\n\n// the list hangs below the `@` that opened it\nconst anchor: VirtualElement = {\n getBoundingClientRect() {\n const current = view.value\n const from = mention.value?.from\n if (!current || from === undefined) return new DOMRect()\n const { left, top, bottom } = current.coordsAtPos(from)\n return new DOMRect(left, top, 0, bottom - top)\n },\n}\nconst { style, placement, update } = useFloating(() => (open.value ? anchor : null), panel, {\n open,\n placement: 'bottom-start',\n offset: 6,\n})\nconst { next } = useZIndex()\nconst zIndex = shallowRef(0)\nwatch(open, (value) => value && (zIndex.value = next()), { immediate: true })\n// a press outside the editor and the list closes it; inside, the cursor move does\nuseLayer({\n open,\n elements: () => [panel.value, view.value?.dom],\n onOutside: () => view.value && closeMention(view.value),\n onEscape: () => view.value && closeMention(view.value),\n})\n\n// search again as the query changes; a slower, older answer never wins\nlet run = 0\nwatch(\n () => (mention.value ? mention.value.query : null),\n async (query) => {\n const current = ++run\n if (query === null) {\n loading.value = false\n return\n }\n loading.value = true\n let items: MentionItem[] = []\n try {\n items = await props.search(query)\n } catch {\n // a failed search shows nothing\n }\n if (current !== run || !view.value || !getMention(view.value.state)) return\n loading.value = false\n setMentionItems(view.value, items.slice(0, 50))\n void nextTick(update)\n },\n { immediate: true },\n)\n\n// the editor keeps focus; the active item is announced through it\nwatch(\n () => [open.value, mention.value?.active, mention.value?.items] as const,\n () => {\n const dom = view.value?.dom\n if (!dom) return\n const items = mention.value?.items ?? []\n const option = open.value && items.length ? `${id}-${mention.value!.active}` : null\n if (open.value) dom.setAttribute('aria-controls', id)\n else dom.removeAttribute('aria-controls')\n if (option) dom.setAttribute('aria-activedescendant', option)\n else dom.removeAttribute('aria-activedescendant')\n if (option) document.getElementById(option)?.scrollIntoView({ block: 'nearest' })\n },\n { flush: 'post' },\n)\n\nfunction choose(item: MentionItem) {\n if (view.value) chooseMention(view.value, item)\n}\n\nfunction hover(index: number) {\n if (view.value && mention.value?.active !== index) setMentionActive(view.value, index)\n}\n</script>\n\n<template>\n <Transition name=\"dou-floating\">\n <div\n v-if=\"open\"\n :id=\"id\"\n ref=\"panel\"\n role=\"listbox\"\n :aria-label=\"messages.mention.label\"\n class=\"dou-dropdown dou-floating dou-editor-mention\"\n :style=\"[style, { zIndex }]\"\n :data-placement=\"placement\"\n @mousedown.prevent\n >\n <div\n v-for=\"(item, index) in mention!.items\"\n :id=\"`${id}-${index}`\"\n :key=\"item.id\"\n role=\"option\"\n :aria-selected=\"index === mention!.active\"\n :class=\"[\n 'dou-dropdown__item',\n 'dou-editor-mention__item',\n { 'is-active': index === mention!.active },\n ]\"\n @pointermove=\"hover(index)\"\n @click=\"choose(item)\"\n >\n <DouAvatar :src=\"item.avatar\" :name=\"item.label\" size=\"sm\" />\n <span class=\"dou-editor-mention__text\">\n <span class=\"dou-editor-mention__label\">{{ item.label }}</span>\n <span v-if=\"item.description\" class=\"dou-editor-mention__description\">{{\n item.description\n }}</span>\n </span>\n </div>\n <div v-if=\"!mention!.items.length\" class=\"dou-editor-slash__empty\">\n {{ loading ? messages.mention.searching : messages.mention.noResults }}\n </div>\n </div>\n </Transition>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,113 @@
1
+ import { isComposing } from "dou-ui";
2
+ import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
3
+ // src/extensions/mention/state.ts
4
+ var mentionKey = new PluginKey("dou-editor-mention");
5
+ var TRIGGER = "@";
6
+ function getMention(state) {
7
+ return state ? mentionKey.getState(state) ?? null : null;
8
+ }
9
+ function setMeta(view, meta) {
10
+ view.dispatch(view.state.tr.setMeta(mentionKey, meta).setMeta("addToHistory", false));
11
+ }
12
+ var closeMention = (view) => setMeta(view, { close: true });
13
+ var setMentionItems = (view, items) => setMeta(view, { items });
14
+ var setMentionActive = (view, active) => setMeta(view, { active });
15
+ /** Replace the `@query` with a mention and a space after it. */
16
+ function chooseMention(view, item) {
17
+ const current = getMention(view.state);
18
+ if (!current) return;
19
+ const type = view.state.schema.nodes.mention;
20
+ const { head } = view.state.selection;
21
+ const tr = view.state.tr.replaceWith(current.from, head, [type.create({
22
+ id: item.id,
23
+ label: item.label
24
+ }), view.state.schema.text(" ")]);
25
+ tr.setSelection(TextSelection.create(tr.doc, current.from + 2));
26
+ view.dispatch(tr.setMeta(mentionKey, { close: true }).scrollIntoView());
27
+ view.focus();
28
+ }
29
+ function mentionPlugin() {
30
+ let opening = null;
31
+ return new Plugin({
32
+ key: mentionKey,
33
+ appendTransaction(_trs, _old, state) {
34
+ if (opening === null) return null;
35
+ const from = opening;
36
+ opening = null;
37
+ if (state.doc.textBetween(from, from + 1) !== TRIGGER) return null;
38
+ return state.tr.setMeta(mentionKey, { open: from });
39
+ },
40
+ state: {
41
+ init: () => null,
42
+ apply(tr, value, _old, state) {
43
+ const meta = tr.getMeta(mentionKey);
44
+ if (meta?.close) return null;
45
+ if (meta?.open !== void 0) return {
46
+ from: meta.open,
47
+ query: "",
48
+ items: [],
49
+ active: 0
50
+ };
51
+ if (!value) return null;
52
+ const from = tr.mapping.map(value.from);
53
+ const { $head, empty } = state.selection;
54
+ if (!empty || $head.pos <= from || $head.pos - from > 33) return null;
55
+ if (!state.doc.resolve(from).sameParent($head)) return null;
56
+ const text = state.doc.textBetween(from, $head.pos, "\n", "\n");
57
+ if (text[0] !== TRIGGER) return null;
58
+ const query = text.slice(1);
59
+ if (/\s/.test(query)) return null;
60
+ let { items, active } = value;
61
+ if (meta?.items) {
62
+ items = meta.items;
63
+ active = 0;
64
+ }
65
+ if (meta?.active !== void 0) active = meta.active;
66
+ if (from === value.from && query === value.query && items === value.items && active === value.active) return value;
67
+ return {
68
+ from,
69
+ query,
70
+ items,
71
+ active
72
+ };
73
+ }
74
+ },
75
+ props: {
76
+ handleTextInput(view, from, _to, text) {
77
+ if (text !== TRIGGER || getMention(view.state)) return false;
78
+ const $from = view.state.doc.resolve(from);
79
+ if (!$from.parent.isTextblock || $from.parent.type.spec.code) return false;
80
+ const before = $from.parent.textBetween(0, $from.parentOffset, "", "");
81
+ if (/[\w.+-]$/.test(before)) return false;
82
+ opening = from;
83
+ return false;
84
+ },
85
+ handleKeyDown(view, event) {
86
+ const current = getMention(view.state);
87
+ if (!current || isComposing(event)) return false;
88
+ const count = current.items.length;
89
+ switch (event.key) {
90
+ case "Escape":
91
+ closeMention(view);
92
+ return true;
93
+ case "ArrowDown":
94
+ case "ArrowUp":
95
+ if (!count) return false;
96
+ setMentionActive(view, (current.active + (event.key === "ArrowDown" ? 1 : -1) + count) % count);
97
+ return true;
98
+ case "Enter":
99
+ case "Tab": {
100
+ const item = current.items[current.active];
101
+ if (!item) return false;
102
+ chooseMention(view, item);
103
+ return true;
104
+ }
105
+ default: return false;
106
+ }
107
+ }
108
+ }
109
+ });
110
+ }
111
+ export { chooseMention, closeMention, getMention, mentionKey, mentionPlugin, setMentionActive, setMentionItems };
112
+
113
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","names":[],"sources":["../../../src/extensions/mention/state.ts"],"sourcesContent":["import { isComposing } from 'dou-ui'\nimport { Plugin, PluginKey, TextSelection, type EditorState } from 'prosemirror-state'\nimport type { EditorView } from 'prosemirror-view'\n\n/** Someone (or something) that can be mentioned. */\nexport interface MentionItem {\n /** Stored in the document. */\n id: string\n /** Shown after the `@`, and stored too so the mention reads without a lookup. */\n label: string\n /** A second line in the suggestions, e.g. a team or an email. */\n description?: string\n /** An image shown in the suggestions. */\n avatar?: string\n}\n\n/** The suggestions: open from the `@` at `from` to the cursor, with what was typed as the query. */\nexport interface MentionState {\n from: number\n query: string\n items: MentionItem[]\n active: number\n}\n\ninterface MentionMeta {\n open?: number\n close?: true\n items?: MentionItem[]\n active?: number\n}\n\nexport const mentionKey = new PluginKey<MentionState | null>('dou-editor-mention')\n\nconst TRIGGER = '@'\nconst MAX_QUERY = 32\n\nexport function getMention(state: EditorState | null): MentionState | null {\n return state ? (mentionKey.getState(state) ?? null) : null\n}\n\nfunction setMeta(view: EditorView, meta: MentionMeta) {\n view.dispatch(view.state.tr.setMeta(mentionKey, meta).setMeta('addToHistory', false))\n}\n\nexport const closeMention = (view: EditorView) => setMeta(view, { close: true })\nexport const setMentionItems = (view: EditorView, items: MentionItem[]) => setMeta(view, { items })\nexport const setMentionActive = (view: EditorView, active: number) => setMeta(view, { active })\n\n/** Replace the `@query` with a mention and a space after it. */\nexport function chooseMention(view: EditorView, item: MentionItem) {\n const current = getMention(view.state)\n if (!current) return\n const type = view.state.schema.nodes.mention!\n const { head } = view.state.selection\n const tr = view.state.tr.replaceWith(current.from, head, [\n type.create({ id: item.id, label: item.label }),\n view.state.schema.text(' '),\n ])\n tr.setSelection(TextSelection.create(tr.doc, current.from + 2))\n view.dispatch(tr.setMeta(mentionKey, { close: true } satisfies MentionMeta).scrollIntoView())\n view.focus()\n}\n\nexport function mentionPlugin(): Plugin<MentionState | null> {\n let opening: number | null = null\n return new Plugin<MentionState | null>({\n key: mentionKey,\n // the browser types the `@` itself; the suggestions open once it is in\n appendTransaction(_trs, _old, state) {\n if (opening === null) return null\n const from = opening\n opening = null\n if (state.doc.textBetween(from, from + 1) !== TRIGGER) return null\n return state.tr.setMeta(mentionKey, { open: from } satisfies MentionMeta)\n },\n state: {\n init: () => null,\n apply(tr, value, _old, state) {\n const meta = tr.getMeta(mentionKey) as MentionMeta | undefined\n if (meta?.close) return null\n if (meta?.open !== undefined) return { from: meta.open, query: '', items: [], active: 0 }\n if (!value) return null\n const from = tr.mapping.map(value.from)\n const { $head, empty } = state.selection\n // close when the cursor leaves the query, or the `@` is deleted\n if (!empty || $head.pos <= from || $head.pos - from > MAX_QUERY + 1) return null\n if (!state.doc.resolve(from).sameParent($head)) return null\n const text = state.doc.textBetween(from, $head.pos, '\\n', '\\n')\n if (text[0] !== TRIGGER) return null\n const query = text.slice(1)\n if (/\\s/.test(query)) return null\n let { items, active } = value\n if (meta?.items) {\n items = meta.items\n active = 0\n }\n if (meta?.active !== undefined) active = meta.active\n if (\n from === value.from &&\n query === value.query &&\n items === value.items &&\n active === value.active\n )\n return value\n return { from, query, items, active }\n },\n },\n props: {\n handleTextInput(view, from, _to, text) {\n if (text !== TRIGGER || getMention(view.state)) return false\n const $from = view.state.doc.resolve(from)\n if (!$from.parent.isTextblock || $from.parent.type.spec.code) return false\n const before = $from.parent.textBetween(0, $from.parentOffset, '', '')\n // not inside a word or an email address\n if (/[\\w.+-]$/.test(before)) return false\n opening = from\n return false\n },\n handleKeyDown(view, event) {\n const current = getMention(view.state)\n if (!current || isComposing(event)) return false\n const count = current.items.length\n switch (event.key) {\n case 'Escape':\n closeMention(view)\n return true\n case 'ArrowDown':\n case 'ArrowUp':\n if (!count) return false\n setMentionActive(\n view,\n (current.active + (event.key === 'ArrowDown' ? 1 : -1) + count) % count,\n )\n return true\n case 'Enter':\n case 'Tab': {\n const item = current.items[current.active]\n if (!item) return false\n chooseMention(view, item)\n return true\n }\n default:\n return false\n }\n },\n },\n })\n}\n"],"mappings":"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"}