@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,104 @@
1
+ import { editorKey } from "../editor.js";
2
+ import { commentRange } from "../core/comments.js";
3
+ import { DouCommentThread, useFloating, useLayer, useZIndex } from "dou-ui";
4
+ import { Transition, computed, createBlock, createCommentVNode, createElementBlock, defineComponent, inject, nextTick, normalizeStyle, openBlock, shallowRef, unref, watch, withCtx } from "vue";
5
+ // src/components/comment-card.vue?vue&type=script&setup=true&lang.ts
6
+ var _hoisted_1 = ["aria-label", "data-placement"];
7
+ var comment_card_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
8
+ __name: "comment-card",
9
+ setup(__props) {
10
+ const { view, state, comments, messages } = inject(editorKey);
11
+ const panel = shallowRef(null);
12
+ const thread = computed(() => {
13
+ const id = comments.active.value;
14
+ return id && comments.threads.value.find((item) => item.id === id) || null;
15
+ });
16
+ const range = computed(() => {
17
+ if (comments.pending.value) return comments.pending.value;
18
+ const current = state.value;
19
+ return thread.value && current ? commentRange(current.doc, thread.value.id) : null;
20
+ });
21
+ const open = computed(() => !!range.value);
22
+ const anchor = { getBoundingClientRect() {
23
+ const current = view.value;
24
+ const at = range.value;
25
+ if (!current || !at) return new DOMRect();
26
+ const start = current.coordsAtPos(at.from, 1);
27
+ const end = current.coordsAtPos(at.to, -1);
28
+ const right = Math.abs(start.top - end.top) < 4 ? end.right : Math.max(start.left + 1, current.dom.getBoundingClientRect().right);
29
+ return new DOMRect(start.left, start.top, Math.max(1, right - start.left), end.bottom - start.top);
30
+ } };
31
+ const { style, placement, update } = useFloating(() => open.value ? anchor : null, panel, {
32
+ open,
33
+ placement: "bottom-start",
34
+ offset: 6
35
+ });
36
+ const { next } = useZIndex();
37
+ const zIndex = shallowRef(0);
38
+ watch(open, (value) => value && (zIndex.value = next()), { immediate: true });
39
+ watch(state, () => open.value && void nextTick(update));
40
+ const threadView = shallowRef(null);
41
+ /** Focus was in the card, or went nowhere when what held it went away. */
42
+ const focusLost = () => {
43
+ const el = document.activeElement;
44
+ return !el || el === document.body || !!panel.value?.contains(el);
45
+ };
46
+ function close() {
47
+ const refocus = focusLost();
48
+ comments.close();
49
+ if (refocus) view.value?.focus();
50
+ }
51
+ useLayer({
52
+ open,
53
+ elements: () => [panel.value, view.value?.dom],
54
+ onEscape: () => close(),
55
+ onOutside: () => comments.close()
56
+ });
57
+ watch(() => comments.focusRequest.value, async () => {
58
+ await nextTick();
59
+ await nextTick();
60
+ threadView.value?.focus();
61
+ });
62
+ function onClosed() {
63
+ const el = document.activeElement;
64
+ if (!el || el === document.body) view.value?.focus();
65
+ }
66
+ return (_ctx, _cache) => {
67
+ return openBlock(), createBlock(Transition, {
68
+ name: "dou-floating",
69
+ onAfterLeave: onClosed
70
+ }, {
71
+ default: withCtx(() => [open.value ? (openBlock(), createElementBlock("div", {
72
+ key: 0,
73
+ ref_key: "panel",
74
+ ref: panel,
75
+ class: "dou-floating dou-editor-comment",
76
+ role: "dialog",
77
+ "aria-label": unref(messages).commentThread,
78
+ style: normalizeStyle([unref(style), { zIndex: zIndex.value }]),
79
+ "data-placement": unref(placement)
80
+ }, [(openBlock(), createBlock(unref(DouCommentThread), {
81
+ ref_key: "threadView",
82
+ ref: threadView,
83
+ key: thread.value?.id ?? "new",
84
+ thread: thread.value,
85
+ author: unref(comments).author.value,
86
+ readonly: !unref(comments).writable.value,
87
+ autofocus: !thread.value,
88
+ onAction: unref(comments).apply,
89
+ onCancel: close
90
+ }, null, 8, [
91
+ "thread",
92
+ "author",
93
+ "readonly",
94
+ "autofocus",
95
+ "onAction"
96
+ ]))], 12, _hoisted_1)) : createCommentVNode("", true)]),
97
+ _: 1
98
+ });
99
+ };
100
+ }
101
+ });
102
+ export { comment_card_vue_vue_type_script_setup_true_lang_default as default };
103
+
104
+ //# sourceMappingURL=comment-card.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comment-card.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_Transition","_withCtx","_createElementBlock","_unref","_normalizeStyle","_createCommentVNode"],"sources":["../../src/components/comment-card.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n DouCommentThread,\n useFloating,\n useLayer,\n useZIndex,\n type CommentThread,\n type VirtualElement,\n} from 'dou-ui'\nimport { computed, inject, nextTick, shallowRef, watch } from 'vue'\nimport { commentRange, type CommentRange } from '../core/comments'\nimport { editorKey } from '../editor'\n\n/*\n * The thread a piece of text is about, beside that text: opened by a\n * click on highlighted text or `v-model:activeComment`, or empty while\n * a new thread's first comment is being written.\n */\n\nconst { view, state, comments, messages } = inject(editorKey)!\nconst panel = shallowRef<HTMLElement | null>(null)\n\nconst thread = computed<CommentThread | null>(() => {\n const id = comments.active.value\n return (id && comments.threads.value.find((item) => item.id === id)) || null\n})\n\nconst range = computed<CommentRange | null>(() => {\n if (comments.pending.value) return comments.pending.value\n const current = state.value\n return thread.value && current ? commentRange(current.doc, thread.value.id) : null\n})\n\nconst open = computed(() => !!range.value)\n\nconst anchor: VirtualElement = {\n getBoundingClientRect() {\n const current = view.value\n const at = range.value\n if (!current || !at) return new DOMRect()\n const start = current.coordsAtPos(at.from, 1)\n const end = current.coordsAtPos(at.to, -1)\n // text over several lines: from its start to the editor's right edge\n const right =\n Math.abs(start.top - end.top) < 4\n ? end.right\n : Math.max(start.left + 1, current.dom.getBoundingClientRect().right)\n return new DOMRect(\n start.left,\n start.top,\n Math.max(1, right - start.left),\n end.bottom - start.top,\n )\n },\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 })\nwatch(state, () => open.value && void nextTick(update))\n\nconst threadView = shallowRef<InstanceType<typeof DouCommentThread> | null>(null)\n\n/** Focus was in the card, or went nowhere when what held it went away. */\nconst focusLost = () => {\n const el = document.activeElement\n return !el || el === document.body || !!panel.value?.contains(el)\n}\n\nfunction close() {\n const refocus = focusLost()\n comments.close()\n if (refocus) view.value?.focus()\n}\n\n// the card is a layer: Escape goes to a menu opened from it first, and a\n// press on such a menu isn't outside. A press on the editor's text is the\n// editor's: it opens the thread there, or closes the card.\nuseLayer({\n open,\n elements: () => [panel.value, view.value?.dom],\n onEscape: () => close(),\n onOutside: () => comments.close(),\n})\n\n// asked to (by the keyboard shortcut, or a thread just started): the reply box\nwatch(\n () => comments.focusRequest.value,\n async () => {\n await nextTick()\n await nextTick()\n threadView.value?.focus()\n },\n)\n\n// a thread resolved or removed from the card: back to the text, once\n// the card is gone (focus can sit in it while it fades out)\nfunction onClosed() {\n const el = document.activeElement\n if (!el || el === document.body) view.value?.focus()\n}\n</script>\n\n<template>\n <Transition name=\"dou-floating\" @after-leave=\"onClosed\">\n <div\n v-if=\"open\"\n ref=\"panel\"\n class=\"dou-floating dou-editor-comment\"\n role=\"dialog\"\n :aria-label=\"messages.commentThread\"\n :style=\"[style, { zIndex }]\"\n :data-placement=\"placement\"\n >\n <DouCommentThread\n ref=\"threadView\"\n :key=\"thread?.id ?? 'new'\"\n :thread=\"thread\"\n :author=\"comments.author.value\"\n :readonly=\"!comments.writable.value\"\n :autofocus=\"!thread\"\n @action=\"comments.apply\"\n @cancel=\"close\"\n />\n </div>\n </Transition>\n</template>\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { isComposing } from "dou-ui";
2
+ import { onScopeDispose, watch } from "vue";
3
+ // src/components/menu-escape.ts
4
+ /**
5
+ * Menus opened from the editor's own controls leave the focus in the text,
6
+ * where the editor would take `Escape` for itself (selecting the block).
7
+ * While one is open, `Escape` closes it first.
8
+ */
9
+ function useMenuEscape(open, close) {
10
+ const onKeydown = (event) => {
11
+ if (event.key !== "Escape" || isComposing(event)) return;
12
+ event.preventDefault();
13
+ event.stopPropagation();
14
+ close();
15
+ };
16
+ const listen = (on) => {
17
+ if (on) window.addEventListener("keydown", onKeydown, true);
18
+ else window.removeEventListener("keydown", onKeydown, true);
19
+ };
20
+ watch(open, (value) => listen(value));
21
+ onScopeDispose(() => listen(false));
22
+ }
23
+ export { useMenuEscape };
24
+
25
+ //# sourceMappingURL=menu-escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menu-escape.js","names":[],"sources":["../../src/components/menu-escape.ts"],"sourcesContent":["import { isComposing } from 'dou-ui'\nimport { onScopeDispose, watch, type Ref } from 'vue'\n\n/**\n * Menus opened from the editor's own controls leave the focus in the text,\n * where the editor would take `Escape` for itself (selecting the block).\n * While one is open, `Escape` closes it first.\n */\nexport function useMenuEscape(open: Ref<boolean>, close: () => void) {\n const onKeydown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || isComposing(event)) return\n event.preventDefault()\n event.stopPropagation()\n close()\n }\n const listen = (on: boolean) => {\n if (on) window.addEventListener('keydown', onKeydown, true)\n else window.removeEventListener('keydown', onKeydown, true)\n }\n watch(open, (value) => listen(value))\n onScopeDispose(() => listen(false))\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,cAAc,MAAoB,OAAmB;CACnE,MAAM,aAAa,UAAyB;EAC1C,IAAI,MAAM,QAAQ,YAAY,YAAY,KAAK,GAAG;EAClD,MAAM,eAAe;EACrB,MAAM,gBAAgB;EACtB,MAAM;CACR;CACA,MAAM,UAAU,OAAgB;EAC9B,IAAI,IAAI,OAAO,iBAAiB,WAAW,WAAW,IAAI;OACrD,OAAO,oBAAoB,WAAW,WAAW,IAAI;CAC5D;CACA,MAAM,OAAO,UAAU,OAAO,KAAK,CAAC;CACpC,qBAAqB,OAAO,KAAK,CAAC;AACpC"}
@@ -0,0 +1,6 @@
1
+ import search_bar_vue_vue_type_script_setup_true_lang_default from "./search-bar.vue_vue_type_script_setup_true_lang.js";
2
+ // src/components/search-bar.vue
3
+ var search_bar_default = search_bar_vue_vue_type_script_setup_true_lang_default;
4
+ export { search_bar_default as default };
5
+
6
+ //# sourceMappingURL=search-bar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-bar.js","names":["_sfc_main"],"sources":["../../src/components/search-bar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { LetterCaseIcon, RegexIcon, WholeWordIcon } from '@dou-ui/icons'\nimport { DouFindReplace, DouToggle, DouTooltip, useLocale } from 'dou-ui'\nimport type { Node as PMNode } from 'prosemirror-model'\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport {\n currentMatch,\n matchAfter,\n previewMatch,\n replaceMatches,\n searchDoc,\n searchKey,\n selectMatch,\n setSearch,\n type EditorSearchQuery,\n} from '../core/search'\nimport { useEditor } from '../editor'\n\ndefineOptions({ name: 'DouEditorSearchBar' })\n\nconst props = defineProps<{ readonly: boolean; skip: (node: PMNode) => boolean }>()\nconst emit = defineEmits<{ close: [] }>()\nconst replacing = defineModel<boolean>('replacing', { default: false })\n\nconst { view, state } = useEditor()\nconst { t } = useLocale()\nconst panel = ref<InstanceType<typeof DouFindReplace>>()\n\nconst search = computed(() => (state.value ? searchKey.getState(state.value) : undefined))\nconst query = computed<EditorSearchQuery>(() => search.value?.query ?? { search: '' })\nconst current = computed(() => (state.value ? currentMatch(state.value) : -1))\n\nconst options = [\n { key: 'caseSensitive', icon: LetterCaseIcon },\n { key: 'wholeWord', icon: WholeWordIcon },\n { key: 'regexp', icon: RegexIcon },\n] as const\n\n/** Puts in a new query; a new search selects the first match from the cursor on, as a preview. */\nfunction setQuery(patch: Partial<EditorSearchQuery>) {\n const v = view.value\n if (!v) return\n const next = { ...query.value, ...patch }\n v.dispatch(setSearch(v.state.tr, { query: next }))\n if (Object.keys(patch).every((key) => key === 'replace')) return\n const preview = previewMatch(v.state)\n if (preview) v.dispatch(preview)\n}\n\nfunction go(step: 1 | -1) {\n const v = view.value\n const s = v && searchKey.getState(v.state)\n if (!v || !s?.matches.length) return\n const list = s.matches\n const i = currentMatch(v.state)\n const from = i >= 0 ? i : matchAfter(list, v.state.selection.from)\n const to = i >= 0 ? (i + step + list.length) % list.length : step > 0 ? from : from - 1\n v.dispatch(selectMatch(v.state, list[(to + list.length) % list.length]!))\n}\n\nfunction replaceOne() {\n const v = view.value\n if (!v || props.readonly) return\n const i = currentMatch(v.state)\n if (i < 0) return go(1)\n const m = searchKey.getState(v.state)!.matches[i]!\n const tr = replaceMatches(v.state, [m], query.value)\n // on to the next match after the replacement\n const end = tr.mapping.map(m.to)\n v.dispatch(tr)\n const list = searchKey.getState(v.state)!.matches\n if (list.length) v.dispatch(selectMatch(v.state, list[matchAfter(list, end)]!))\n}\n\nfunction replaceAll() {\n const v = view.value\n const s = v && searchKey.getState(v.state)\n if (!v || !s?.matches.length || props.readonly) return\n // every match, also past the number counted\n const list = s.capped\n ? searchDoc(v.state.doc, query.value, { limit: Infinity, skip: props.skip }).matches\n : s.matches\n v.dispatch(replaceMatches(v.state, list, query.value))\n}\n\n// The panel floats over the text: scrolling to a match keeps it below the panel, and a\n// match the panel still covers, as one in the first line, moves the panel down past it.\nconst shift = ref(0)\nlet margin: unknown\nonMounted(() => {\n const v = view.value\n const el = panel.value?.$el as HTMLElement | undefined\n if (!v || !el) return\n margin = v.someProp('scrollMargin')\n const top = el.getBoundingClientRect().height + 16\n v.setProps({ scrollMargin: { top, bottom: 8, left: 8, right: 8 } })\n})\nonBeforeUnmount(() => {\n const v = view.value\n if (v && !v.isDestroyed) v.setProps({ scrollMargin: margin as number | undefined })\n})\nwatch(\n [current, state],\n () => {\n const v = view.value\n const el = panel.value?.$el as HTMLElement | undefined\n const m = current.value >= 0 ? search.value?.matches[current.value] : undefined\n if (!v || !el || !m) return (shift.value = 0)\n let start, end\n try {\n start = v.coordsAtPos(m.from)\n end = v.coordsAtPos(m.to)\n } catch {\n return (shift.value = 0)\n }\n const box = el.getBoundingClientRect()\n const top = box.top - shift.value\n const bottom = box.bottom - shift.value\n const covered =\n start.top < bottom &&\n end.bottom > top &&\n Math.min(start.left, end.left) < box.right &&\n Math.max(start.right, end.right) > box.left\n shift.value = covered ? Math.ceil(Math.max(start.bottom, end.bottom) - top + 6) : 0\n },\n { flush: 'post' },\n)\n\ndefineExpose({\n focus: (which?: 'find' | 'replace') => panel.value?.focus(which),\n next: () => go(1),\n previous: () => go(-1),\n})\n</script>\n\n<template>\n <DouFindReplace\n ref=\"panel\"\n :style=\"shift ? { transform: `translateY(${shift}px)` } : undefined\"\n v-model:replacing=\"replacing\"\n :search=\"query.search\"\n :replace=\"query.replace ?? ''\"\n :count=\"search?.matches.length ?? 0\"\n :current=\"current\"\n :capped=\"search?.capped\"\n :error=\"search?.error && t('findReplace.invalidRegexp')\"\n :readonly=\"readonly\"\n @update:search=\"setQuery({ search: $event })\"\n @update:replace=\"setQuery({ replace: $event })\"\n @next=\"go(1)\"\n @previous=\"go(-1)\"\n @replace-one=\"replaceOne\"\n @replace-all=\"replaceAll\"\n @close=\"emit('close')\"\n >\n <template #options>\n <DouTooltip\n v-for=\"option in options\"\n :key=\"option.key\"\n :content=\"t(`findReplace.${option.key}`)\"\n >\n <DouToggle\n :pressed=\"!!query[option.key]\"\n size=\"sm\"\n :aria-label=\"t(`findReplace.${option.key}`)\"\n @update:pressed=\"setQuery({ [option.key]: $event })\"\n >\n <component :is=\"option.icon\" />\n </DouToggle>\n </DouTooltip>\n </template>\n </DouFindReplace>\n</template>\n"],"mappings":""}
@@ -0,0 +1,192 @@
1
+ import { useEditor } from "../editor.js";
2
+ import { currentMatch, matchAfter, previewMatch, replaceMatches, searchDoc, searchKey, selectMatch, setSearch } from "../core/search.js";
3
+ import { DouFindReplace, DouToggle, DouTooltip, useLocale } from "dou-ui";
4
+ import { Fragment, computed, createBlock, createElementBlock, createVNode, defineComponent, mergeModels, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, renderList, resolveDynamicComponent, unref, useModel, watch, withCtx } from "vue";
5
+ import { LetterCaseIcon, RegexIcon, WholeWordIcon } from "@dou-ui/icons";
6
+ // src/components/search-bar.vue?vue&type=script&setup=true&lang.ts
7
+ var search_bar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
8
+ name: "DouEditorSearchBar",
9
+ __name: "search-bar",
10
+ props: /*@__PURE__*/ mergeModels({
11
+ readonly: { type: Boolean },
12
+ skip: { type: Function }
13
+ }, {
14
+ "replacing": {
15
+ type: Boolean,
16
+ default: false
17
+ },
18
+ "replacingModifiers": {}
19
+ }),
20
+ emits: /*@__PURE__*/ mergeModels(["close"], ["update:replacing"]),
21
+ setup(__props, { expose: __expose, emit: __emit }) {
22
+ const props = __props;
23
+ const emit = __emit;
24
+ const replacing = useModel(__props, "replacing");
25
+ const { view, state } = useEditor();
26
+ const { t } = useLocale();
27
+ const panel = ref();
28
+ const search = computed(() => state.value ? searchKey.getState(state.value) : void 0);
29
+ const query = computed(() => search.value?.query ?? { search: "" });
30
+ const current = computed(() => state.value ? currentMatch(state.value) : -1);
31
+ const options = [
32
+ {
33
+ key: "caseSensitive",
34
+ icon: LetterCaseIcon
35
+ },
36
+ {
37
+ key: "wholeWord",
38
+ icon: WholeWordIcon
39
+ },
40
+ {
41
+ key: "regexp",
42
+ icon: RegexIcon
43
+ }
44
+ ];
45
+ /** Puts in a new query; a new search selects the first match from the cursor on, as a preview. */
46
+ function setQuery(patch) {
47
+ const v = view.value;
48
+ if (!v) return;
49
+ const next = {
50
+ ...query.value,
51
+ ...patch
52
+ };
53
+ v.dispatch(setSearch(v.state.tr, { query: next }));
54
+ if (Object.keys(patch).every((key) => key === "replace")) return;
55
+ const preview = previewMatch(v.state);
56
+ if (preview) v.dispatch(preview);
57
+ }
58
+ function go(step) {
59
+ const v = view.value;
60
+ const s = v && searchKey.getState(v.state);
61
+ if (!v || !s?.matches.length) return;
62
+ const list = s.matches;
63
+ const i = currentMatch(v.state);
64
+ const from = i >= 0 ? i : matchAfter(list, v.state.selection.from);
65
+ const to = i >= 0 ? (i + step + list.length) % list.length : step > 0 ? from : from - 1;
66
+ v.dispatch(selectMatch(v.state, list[(to + list.length) % list.length]));
67
+ }
68
+ function replaceOne() {
69
+ const v = view.value;
70
+ if (!v || props.readonly) return;
71
+ const i = currentMatch(v.state);
72
+ if (i < 0) return go(1);
73
+ const m = searchKey.getState(v.state).matches[i];
74
+ const tr = replaceMatches(v.state, [m], query.value);
75
+ const end = tr.mapping.map(m.to);
76
+ v.dispatch(tr);
77
+ const list = searchKey.getState(v.state).matches;
78
+ if (list.length) v.dispatch(selectMatch(v.state, list[matchAfter(list, end)]));
79
+ }
80
+ function replaceAll() {
81
+ const v = view.value;
82
+ const s = v && searchKey.getState(v.state);
83
+ if (!v || !s?.matches.length || props.readonly) return;
84
+ const list = s.capped ? searchDoc(v.state.doc, query.value, {
85
+ limit: Infinity,
86
+ skip: props.skip
87
+ }).matches : s.matches;
88
+ v.dispatch(replaceMatches(v.state, list, query.value));
89
+ }
90
+ const shift = ref(0);
91
+ let margin;
92
+ onMounted(() => {
93
+ const v = view.value;
94
+ const el = panel.value?.$el;
95
+ if (!v || !el) return;
96
+ margin = v.someProp("scrollMargin");
97
+ const top = el.getBoundingClientRect().height + 16;
98
+ v.setProps({ scrollMargin: {
99
+ top,
100
+ bottom: 8,
101
+ left: 8,
102
+ right: 8
103
+ } });
104
+ });
105
+ onBeforeUnmount(() => {
106
+ const v = view.value;
107
+ if (v && !v.isDestroyed) v.setProps({ scrollMargin: margin });
108
+ });
109
+ watch([current, state], () => {
110
+ const v = view.value;
111
+ const el = panel.value?.$el;
112
+ const m = current.value >= 0 ? search.value?.matches[current.value] : void 0;
113
+ if (!v || !el || !m) return shift.value = 0;
114
+ let start, end;
115
+ try {
116
+ start = v.coordsAtPos(m.from);
117
+ end = v.coordsAtPos(m.to);
118
+ } catch {
119
+ return shift.value = 0;
120
+ }
121
+ const box = el.getBoundingClientRect();
122
+ const top = box.top - shift.value;
123
+ const bottom = box.bottom - shift.value;
124
+ const covered = start.top < bottom && end.bottom > top && Math.min(start.left, end.left) < box.right && Math.max(start.right, end.right) > box.left;
125
+ shift.value = covered ? Math.ceil(Math.max(start.bottom, end.bottom) - top + 6) : 0;
126
+ }, { flush: "post" });
127
+ __expose({
128
+ focus: (which) => panel.value?.focus(which),
129
+ next: () => go(1),
130
+ previous: () => go(-1)
131
+ });
132
+ return (_ctx, _cache) => {
133
+ return openBlock(), createBlock(unref(DouFindReplace), {
134
+ ref_key: "panel",
135
+ ref: panel,
136
+ style: normalizeStyle(shift.value ? { transform: `translateY(${shift.value}px)` } : void 0),
137
+ replacing: replacing.value,
138
+ "onUpdate:replacing": _cache[0] || (_cache[0] = ($event) => replacing.value = $event),
139
+ search: query.value.search,
140
+ replace: query.value.replace ?? "",
141
+ count: search.value?.matches.length ?? 0,
142
+ current: current.value,
143
+ capped: search.value?.capped,
144
+ error: search.value?.error && unref(t)("findReplace.invalidRegexp"),
145
+ readonly: __props.readonly,
146
+ "onUpdate:search": _cache[1] || (_cache[1] = ($event) => setQuery({ search: $event })),
147
+ "onUpdate:replace": _cache[2] || (_cache[2] = ($event) => setQuery({ replace: $event })),
148
+ onNext: _cache[3] || (_cache[3] = ($event) => go(1)),
149
+ onPrevious: _cache[4] || (_cache[4] = ($event) => go(-1)),
150
+ onReplaceOne: replaceOne,
151
+ onReplaceAll: replaceAll,
152
+ onClose: _cache[5] || (_cache[5] = ($event) => emit("close"))
153
+ }, {
154
+ options: withCtx(() => [(openBlock(), createElementBlock(Fragment, null, renderList(options, (option) => {
155
+ return createVNode(unref(DouTooltip), {
156
+ key: option.key,
157
+ content: unref(t)(`findReplace.${option.key}`)
158
+ }, {
159
+ default: withCtx(() => [createVNode(unref(DouToggle), {
160
+ pressed: !!query.value[option.key],
161
+ size: "sm",
162
+ "aria-label": unref(t)(`findReplace.${option.key}`),
163
+ "onUpdate:pressed": ($event) => setQuery({ [option.key]: $event })
164
+ }, {
165
+ default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(option.icon)))]),
166
+ _: 2
167
+ }, 1032, [
168
+ "pressed",
169
+ "aria-label",
170
+ "onUpdate:pressed"
171
+ ])]),
172
+ _: 2
173
+ }, 1032, ["content"]);
174
+ }), 64))]),
175
+ _: 1
176
+ }, 8, [
177
+ "style",
178
+ "replacing",
179
+ "search",
180
+ "replace",
181
+ "count",
182
+ "current",
183
+ "capped",
184
+ "error",
185
+ "readonly"
186
+ ]);
187
+ };
188
+ }
189
+ });
190
+ export { search_bar_vue_vue_type_script_setup_true_lang_default as default };
191
+
192
+ //# sourceMappingURL=search-bar.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-bar.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createBlock","_unref","_normalizeStyle","_withCtx","_createElementBlock","_Fragment","_renderList","_createVNode","_resolveDynamicComponent"],"sources":["../../src/components/search-bar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { LetterCaseIcon, RegexIcon, WholeWordIcon } from '@dou-ui/icons'\nimport { DouFindReplace, DouToggle, DouTooltip, useLocale } from 'dou-ui'\nimport type { Node as PMNode } from 'prosemirror-model'\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\nimport {\n currentMatch,\n matchAfter,\n previewMatch,\n replaceMatches,\n searchDoc,\n searchKey,\n selectMatch,\n setSearch,\n type EditorSearchQuery,\n} from '../core/search'\nimport { useEditor } from '../editor'\n\ndefineOptions({ name: 'DouEditorSearchBar' })\n\nconst props = defineProps<{ readonly: boolean; skip: (node: PMNode) => boolean }>()\nconst emit = defineEmits<{ close: [] }>()\nconst replacing = defineModel<boolean>('replacing', { default: false })\n\nconst { view, state } = useEditor()\nconst { t } = useLocale()\nconst panel = ref<InstanceType<typeof DouFindReplace>>()\n\nconst search = computed(() => (state.value ? searchKey.getState(state.value) : undefined))\nconst query = computed<EditorSearchQuery>(() => search.value?.query ?? { search: '' })\nconst current = computed(() => (state.value ? currentMatch(state.value) : -1))\n\nconst options = [\n { key: 'caseSensitive', icon: LetterCaseIcon },\n { key: 'wholeWord', icon: WholeWordIcon },\n { key: 'regexp', icon: RegexIcon },\n] as const\n\n/** Puts in a new query; a new search selects the first match from the cursor on, as a preview. */\nfunction setQuery(patch: Partial<EditorSearchQuery>) {\n const v = view.value\n if (!v) return\n const next = { ...query.value, ...patch }\n v.dispatch(setSearch(v.state.tr, { query: next }))\n if (Object.keys(patch).every((key) => key === 'replace')) return\n const preview = previewMatch(v.state)\n if (preview) v.dispatch(preview)\n}\n\nfunction go(step: 1 | -1) {\n const v = view.value\n const s = v && searchKey.getState(v.state)\n if (!v || !s?.matches.length) return\n const list = s.matches\n const i = currentMatch(v.state)\n const from = i >= 0 ? i : matchAfter(list, v.state.selection.from)\n const to = i >= 0 ? (i + step + list.length) % list.length : step > 0 ? from : from - 1\n v.dispatch(selectMatch(v.state, list[(to + list.length) % list.length]!))\n}\n\nfunction replaceOne() {\n const v = view.value\n if (!v || props.readonly) return\n const i = currentMatch(v.state)\n if (i < 0) return go(1)\n const m = searchKey.getState(v.state)!.matches[i]!\n const tr = replaceMatches(v.state, [m], query.value)\n // on to the next match after the replacement\n const end = tr.mapping.map(m.to)\n v.dispatch(tr)\n const list = searchKey.getState(v.state)!.matches\n if (list.length) v.dispatch(selectMatch(v.state, list[matchAfter(list, end)]!))\n}\n\nfunction replaceAll() {\n const v = view.value\n const s = v && searchKey.getState(v.state)\n if (!v || !s?.matches.length || props.readonly) return\n // every match, also past the number counted\n const list = s.capped\n ? searchDoc(v.state.doc, query.value, { limit: Infinity, skip: props.skip }).matches\n : s.matches\n v.dispatch(replaceMatches(v.state, list, query.value))\n}\n\n// The panel floats over the text: scrolling to a match keeps it below the panel, and a\n// match the panel still covers, as one in the first line, moves the panel down past it.\nconst shift = ref(0)\nlet margin: unknown\nonMounted(() => {\n const v = view.value\n const el = panel.value?.$el as HTMLElement | undefined\n if (!v || !el) return\n margin = v.someProp('scrollMargin')\n const top = el.getBoundingClientRect().height + 16\n v.setProps({ scrollMargin: { top, bottom: 8, left: 8, right: 8 } })\n})\nonBeforeUnmount(() => {\n const v = view.value\n if (v && !v.isDestroyed) v.setProps({ scrollMargin: margin as number | undefined })\n})\nwatch(\n [current, state],\n () => {\n const v = view.value\n const el = panel.value?.$el as HTMLElement | undefined\n const m = current.value >= 0 ? search.value?.matches[current.value] : undefined\n if (!v || !el || !m) return (shift.value = 0)\n let start, end\n try {\n start = v.coordsAtPos(m.from)\n end = v.coordsAtPos(m.to)\n } catch {\n return (shift.value = 0)\n }\n const box = el.getBoundingClientRect()\n const top = box.top - shift.value\n const bottom = box.bottom - shift.value\n const covered =\n start.top < bottom &&\n end.bottom > top &&\n Math.min(start.left, end.left) < box.right &&\n Math.max(start.right, end.right) > box.left\n shift.value = covered ? Math.ceil(Math.max(start.bottom, end.bottom) - top + 6) : 0\n },\n { flush: 'post' },\n)\n\ndefineExpose({\n focus: (which?: 'find' | 'replace') => panel.value?.focus(which),\n next: () => go(1),\n previous: () => go(-1),\n})\n</script>\n\n<template>\n <DouFindReplace\n ref=\"panel\"\n :style=\"shift ? { transform: `translateY(${shift}px)` } : undefined\"\n v-model:replacing=\"replacing\"\n :search=\"query.search\"\n :replace=\"query.replace ?? ''\"\n :count=\"search?.matches.length ?? 0\"\n :current=\"current\"\n :capped=\"search?.capped\"\n :error=\"search?.error && t('findReplace.invalidRegexp')\"\n :readonly=\"readonly\"\n @update:search=\"setQuery({ search: $event })\"\n @update:replace=\"setQuery({ replace: $event })\"\n @next=\"go(1)\"\n @previous=\"go(-1)\"\n @replace-one=\"replaceOne\"\n @replace-all=\"replaceAll\"\n @close=\"emit('close')\"\n >\n <template #options>\n <DouTooltip\n v-for=\"option in options\"\n :key=\"option.key\"\n :content=\"t(`findReplace.${option.key}`)\"\n >\n <DouToggle\n :pressed=\"!!query[option.key]\"\n size=\"sm\"\n :aria-label=\"t(`findReplace.${option.key}`)\"\n @update:pressed=\"setQuery({ [option.key]: $event })\"\n >\n <component :is=\"option.icon\" />\n </DouToggle>\n </DouTooltip>\n </template>\n </DouFindReplace>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAoBA,MAAM,QAAQ;EACd,MAAM,OAAO;EACb,MAAM,YAAYA,SAAoB,SAAC,WAA+B;EAEtE,MAAM,EAAE,MAAM,UAAU,UAAU;EAClC,MAAM,EAAE,MAAM,UAAU;EACxB,MAAM,QAAQ,IAAyC;EAEvD,MAAM,SAAS,eAAgB,MAAM,QAAQ,UAAU,SAAS,MAAM,KAAK,IAAI,KAAA,CAAU;EACzF,MAAM,QAAQ,eAAkC,OAAO,OAAO,SAAS,EAAE,QAAQ,GAAG,CAAC;EACrF,MAAM,UAAU,eAAgB,MAAM,QAAQ,aAAa,MAAM,KAAK,IAAI,EAAG;EAE7E,MAAM,UAAU;GACd;IAAE,KAAK;IAAiB,MAAM;GAAe;GAC7C;IAAE,KAAK;IAAa,MAAM;GAAc;GACxC;IAAE,KAAK;IAAU,MAAM;GAAU;EACnC;;EAGA,SAAS,SAAS,OAAmC;GACnD,MAAM,IAAI,KAAK;GACf,IAAI,CAAC,GAAG;GACR,MAAM,OAAO;IAAE,GAAG,MAAM;IAAO,GAAG;GAAM;GACxC,EAAE,SAAS,UAAU,EAAE,MAAM,IAAI,EAAE,OAAO,KAAK,CAAC,CAAC;GACjD,IAAI,OAAO,KAAK,KAAK,CAAC,CAAC,OAAO,QAAQ,QAAQ,SAAS,GAAG;GAC1D,MAAM,UAAU,aAAa,EAAE,KAAK;GACpC,IAAI,SAAS,EAAE,SAAS,OAAO;EACjC;EAEA,SAAS,GAAG,MAAc;GACxB,MAAM,IAAI,KAAK;GACf,MAAM,IAAI,KAAK,UAAU,SAAS,EAAE,KAAK;GACzC,IAAI,CAAC,KAAK,CAAC,GAAG,QAAQ,QAAQ;GAC9B,MAAM,OAAO,EAAE;GACf,MAAM,IAAI,aAAa,EAAE,KAAK;GAC9B,MAAM,OAAO,KAAK,IAAI,IAAI,WAAW,MAAM,EAAE,MAAM,UAAU,IAAI;GACjE,MAAM,KAAK,KAAK,KAAK,IAAI,OAAO,KAAK,UAAU,KAAK,SAAS,OAAO,IAAI,OAAO,OAAO;GACtF,EAAE,SAAS,YAAY,EAAE,OAAO,MAAM,KAAK,KAAK,UAAU,KAAK,OAAQ,CAAC;EAC1E;EAEA,SAAS,aAAa;GACpB,MAAM,IAAI,KAAK;GACf,IAAI,CAAC,KAAK,MAAM,UAAU;GAC1B,MAAM,IAAI,aAAa,EAAE,KAAK;GAC9B,IAAI,IAAI,GAAG,OAAO,GAAG,CAAC;GACtB,MAAM,IAAI,UAAU,SAAS,EAAE,KAAK,CAAC,CAAE,QAAQ;GAC/C,MAAM,KAAK,eAAe,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,KAAK;GAEnD,MAAM,MAAM,GAAG,QAAQ,IAAI,EAAE,EAAE;GAC/B,EAAE,SAAS,EAAE;GACb,MAAM,OAAO,UAAU,SAAS,EAAE,KAAK,CAAC,CAAE;GAC1C,IAAI,KAAK,QAAQ,EAAE,SAAS,YAAY,EAAE,OAAO,KAAK,WAAW,MAAM,GAAG,EAAG,CAAC;EAChF;EAEA,SAAS,aAAa;GACpB,MAAM,IAAI,KAAK;GACf,MAAM,IAAI,KAAK,UAAU,SAAS,EAAE,KAAK;GACzC,IAAI,CAAC,KAAK,CAAC,GAAG,QAAQ,UAAU,MAAM,UAAU;GAEhD,MAAM,OAAO,EAAE,SACX,UAAU,EAAE,MAAM,KAAK,MAAM,OAAO;IAAE,OAAO;IAAU,MAAM,MAAM;GAAK,CAAC,CAAC,CAAC,UAC3E,EAAE;GACN,EAAE,SAAS,eAAe,EAAE,OAAO,MAAM,MAAM,KAAK,CAAC;EACvD;EAIA,MAAM,QAAQ,IAAI,CAAC;EACnB,IAAI;EACJ,gBAAgB;GACd,MAAM,IAAI,KAAK;GACf,MAAM,KAAK,MAAM,OAAO;GACxB,IAAI,CAAC,KAAK,CAAC,IAAI;GACf,SAAS,EAAE,SAAS,cAAc;GAClC,MAAM,MAAM,GAAG,sBAAsB,CAAC,CAAC,SAAS;GAChD,EAAE,SAAS,EAAE,cAAc;IAAE;IAAK,QAAQ;IAAG,MAAM;IAAG,OAAO;GAAE,EAAE,CAAC;EACpE,CAAC;EACD,sBAAsB;GACpB,MAAM,IAAI,KAAK;GACf,IAAI,KAAK,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,OAA6B,CAAC;EACpF,CAAC;EACD,MACE,CAAC,SAAS,KAAK,SACT;GACJ,MAAM,IAAI,KAAK;GACf,MAAM,KAAK,MAAM,OAAO;GACxB,MAAM,IAAI,QAAQ,SAAS,IAAI,OAAO,OAAO,QAAQ,QAAQ,SAAS,KAAA;GACtE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,OAAQ,MAAM,QAAQ;GAC3C,IAAI,OAAO;GACX,IAAI;IACF,QAAQ,EAAE,YAAY,EAAE,IAAI;IAC5B,MAAM,EAAE,YAAY,EAAE,EAAE;GAC1B,QAAQ;IACN,OAAQ,MAAM,QAAQ;GACxB;GACA,MAAM,MAAM,GAAG,sBAAsB;GACrC,MAAM,MAAM,IAAI,MAAM,MAAM;GAC5B,MAAM,SAAS,IAAI,SAAS,MAAM;GAClC,MAAM,UACJ,MAAM,MAAM,UACZ,IAAI,SAAS,OACb,KAAK,IAAI,MAAM,MAAM,IAAI,IAAI,IAAI,IAAI,SACrC,KAAK,IAAI,MAAM,OAAO,IAAI,KAAK,IAAI,IAAI;GACzC,MAAM,QAAQ,UAAU,KAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI;EACpF,GACA,EAAE,OAAO,OAAO,CAClB;EAEA,SAAa;GACX,QAAQ,UAA+B,MAAM,OAAO,MAAM,KAAK;GAC/D,YAAY,GAAG,CAAC;GAChB,gBAAgB,GAAG,EAAE;EACvB,CAAC;;GAIC,OAAAC,UAAA,GAAAC,YAmCiBC,MAAA,cAAA,GAAA;IAlCX,SAAA;IAAJ,KAAI;IACH,OAAKC,eAAE,MAAA,QAAK,EAAA,WAAA,cAA8B,MAAA,MAAK,KAAA,IAAU,KAAA,CAAS;IAC3D,WAAW,UAAA;IAAA,sBAAA,OAAA,OAAA,OAAA,MAAA,WAAA,UAAS,QAAA;IAC3B,QAAQ,MAAA,MAAM;IACd,SAAS,MAAA,MAAM,WAAO;IACtB,OAAO,OAAA,OAAQ,QAAQ,UAAM;IAC7B,SAAS,QAAA;IACT,QAAQ,OAAA,OAAQ;IAChB,OAAO,OAAA,OAAQ,SAASD,MAAA,CAAA,CAAC,CAAA,2BAAA;IACzB,UAAU,QAAA;IACV,mBAAa,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,EAAA,QAAW,OAAM,CAAA;IACxC,oBAAc,OAAA,OAAA,OAAA,MAAA,WAAE,SAAQ,EAAA,SAAY,OAAM,CAAA;IAC1C,QAAI,OAAA,OAAA,OAAA,MAAA,WAAE,GAAE,CAAA;IACR,YAAQ,OAAA,OAAA,OAAA,MAAA,WAAE,GAAE,EAAA;IACZ,cAAa;IACb,cAAa;IACb,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;IAED,SAAOE,cAEW,EAD3BJ,UAAA,GAAAK,mBAaaC,UAAA,MAAAC,WAZM,UAAV,WAAM;KADf,OAAAC,YAaaN,MAAA,UAAA,GAAA;MAXV,KAAK,OAAO;MACZ,SAASA,MAAA,CAAA,CAAC,CAAA,eAAgB,OAAO,KAAG;;MAErC,SAAAE,cAOY,CAPZI,YAOYN,MAAA,SAAA,GAAA;OANT,SAAO,CAAA,CAAI,MAAA,MAAM,OAAO;OACzB,MAAK;OACJ,cAAYA,MAAA,CAAA,CAAC,CAAA,eAAgB,OAAO,KAAG;OACvC,qBAAc,WAAE,SAAQ,GAAI,OAAO,MAAM,OAAM,CAAA;;OAEhD,SAAAE,cAA+B,EAA/BJ,UAAA,GAAAC,YAA+BQ,wBAAf,OAAO,IAAI,CAAA,E"}
@@ -0,0 +1,6 @@
1
+ import slash_menu_vue_vue_type_script_setup_true_lang_default from "./slash-menu.vue_vue_type_script_setup_true_lang.js";
2
+ // src/components/slash-menu.vue
3
+ var slash_menu_default = slash_menu_vue_vue_type_script_setup_true_lang_default;
4
+ export { slash_menu_default as default };
5
+
6
+ //# sourceMappingURL=slash-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-menu.js","names":["_sfc_main"],"sources":["../../src/components/slash-menu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { isComposing, useFloating, useLayer, useZIndex, type VirtualElement } from 'dou-ui'\nimport { computed, inject, nextTick, shallowRef, useId, useTemplateRef, watch } from 'vue'\nimport { filterOptions } from '../catalog'\nimport { labelOf, type BlockOption } from '../core/extension'\nimport { insertBlockAfter, setBlockType } from '../core/commands'\nimport { slashKey, type SlashState } from '../core/slash'\nimport { editorKey } from '../editor'\n\nconst props = defineProps<{ slash: SlashState | null }>()\n\nconst { view, messages, kit } = inject(editorKey)!\nconst id = `dou-editor-slash-${useId()}`\nconst panel = useTemplateRef<HTMLElement>('panel')\nconst active = shallowRef(0)\n\nconst options = computed(() =>\n props.slash ? filterOptions(kit.blockOptions, props.slash.query, messages.value) : [],\n)\nconst open = computed(() => !!props.slash)\n\n// the menu hangs below the `/` that opened it\nconst anchor: VirtualElement = {\n getBoundingClientRect() {\n const current = view.value\n const from = props.slash?.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})\n// stacked like DouUI's own popups, above whatever opened before\nconst { next } = useZIndex()\nconst zIndex = shallowRef(0)\nwatch(open, (value) => value && (zIndex.value = next()), { immediate: true })\n// a press outside the editor and the menu closes it; inside, the cursor move does\nuseLayer({\n open,\n elements: () => [panel.value, view.value?.dom],\n onOutside: () => close(),\n onEscape: () => close(),\n})\n\nwatch(\n () => props.slash?.query,\n () => {\n active.value = 0\n void nextTick(update)\n },\n)\n\n// the editor keeps focus; the active option is announced through it\nwatch(\n [open, active, options],\n () => {\n const dom = view.value?.dom\n if (!dom) return\n const option = open.value && options.value.length ? `${id}-${active.value}` : null\n if (open.value) {\n dom.setAttribute('aria-controls', id)\n dom.setAttribute('aria-expanded', 'true')\n } else {\n dom.removeAttribute('aria-controls')\n dom.removeAttribute('aria-expanded')\n }\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(option: BlockOption) {\n const current = view.value\n const slash = props.slash\n if (!current || !slash) return\n // remove the `/query`, then turn the block, or add one below when it has text\n const removed = current.state.tr.delete(slash.from, current.state.selection.from)\n current.dispatch(removed.setMeta(slashKey, null))\n const { $from } = current.state.selection\n if ($from.parent.content.size > 0) insertBlockAfter($from.pos)(current.state, current.dispatch)\n if (option.type !== 'paragraph' || $from.parent.content.size === 0)\n setBlockType(option.type, option.props)(current.state, current.dispatch)\n current.focus()\n}\n\nfunction close() {\n const current = view.value\n if (current) current.dispatch(current.state.tr.setMeta(slashKey, null))\n}\n\n/** Keys the editor passes on while the menu is open. */\nfunction onKey(event: KeyboardEvent): boolean {\n if (isComposing(event)) return false\n const count = options.value.length\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n if (!count) return false\n active.value = (active.value + (event.key === 'ArrowDown' ? 1 : -1) + count) % count\n return true\n case 'Enter':\n case 'Tab': {\n const option = options.value[active.value]\n if (!option) {\n close()\n return false\n }\n choose(option)\n return true\n }\n default:\n return false\n }\n}\n\ndefineExpose({ onKey })\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.blockTypes\"\n class=\"dou-dropdown dou-floating dou-editor-slash\"\n :style=\"[style, { zIndex }]\"\n :data-placement=\"placement\"\n @mousedown.prevent\n >\n <div\n v-for=\"(option, index) in options\"\n :id=\"`${id}-${index}`\"\n :key=\"option.key\"\n role=\"option\"\n :aria-selected=\"index === active\"\n :class=\"['dou-dropdown__item', 'dou-editor-slash__item', { 'is-active': index === active }]\"\n @pointermove=\"active = index\"\n @click=\"choose(option)\"\n >\n <span class=\"dou-editor-slash__icon\"><component :is=\"option.icon\" /></span>\n <span class=\"dou-editor-slash__text\">\n <span class=\"dou-editor-slash__label\">{{ labelOf(option.label, messages) }}</span>\n <span v-if=\"option.hint\" class=\"dou-editor-slash__hint\">{{\n labelOf(option.hint, messages)\n }}</span>\n </span>\n <kbd v-if=\"option.shortcut\" class=\"dou-editor-slash__shortcut\">{{ option.shortcut }}</kbd>\n </div>\n <div v-if=\"!options.length\" class=\"dou-editor-slash__empty\">{{ messages.noResults }}</div>\n </div>\n </Transition>\n</template>\n"],"mappings":""}
@@ -0,0 +1,164 @@
1
+ import { labelOf } from "../core/extension.js";
2
+ import { filterOptions } from "../catalog.js";
3
+ import { insertBlockAfter, setBlockType } from "../core/commands.js";
4
+ import { slashKey } from "../core/slash.js";
5
+ import { editorKey } from "../editor.js";
6
+ import { isComposing, useFloating, useLayer, useZIndex } from "dou-ui";
7
+ import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, inject, nextTick, normalizeClass, normalizeStyle, openBlock, renderList, resolveDynamicComponent, shallowRef, toDisplayString, unref, useId, useTemplateRef, watch, withCtx, withModifiers } from "vue";
8
+ // src/components/slash-menu.vue?vue&type=script&setup=true&lang.ts
9
+ var _hoisted_1 = ["aria-label", "data-placement"];
10
+ var _hoisted_2 = [
11
+ "id",
12
+ "aria-selected",
13
+ "onPointermove",
14
+ "onClick"
15
+ ];
16
+ var _hoisted_3 = { class: "dou-editor-slash__icon" };
17
+ var _hoisted_4 = { class: "dou-editor-slash__text" };
18
+ var _hoisted_5 = { class: "dou-editor-slash__label" };
19
+ var _hoisted_6 = {
20
+ key: 0,
21
+ class: "dou-editor-slash__hint"
22
+ };
23
+ var _hoisted_7 = {
24
+ key: 0,
25
+ class: "dou-editor-slash__shortcut"
26
+ };
27
+ var _hoisted_8 = {
28
+ key: 0,
29
+ class: "dou-editor-slash__empty"
30
+ };
31
+ var slash_menu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
32
+ __name: "slash-menu",
33
+ props: { slash: {} },
34
+ setup(__props, { expose: __expose }) {
35
+ const props = __props;
36
+ const { view, messages, kit } = inject(editorKey);
37
+ const id = `dou-editor-slash-${useId()}`;
38
+ const panel = useTemplateRef("panel");
39
+ const active = shallowRef(0);
40
+ const options = computed(() => props.slash ? filterOptions(kit.blockOptions, props.slash.query, messages.value) : []);
41
+ const open = computed(() => !!props.slash);
42
+ const anchor = { getBoundingClientRect() {
43
+ const current = view.value;
44
+ const from = props.slash?.from;
45
+ if (!current || from === void 0) return new DOMRect();
46
+ const { left, top, bottom } = current.coordsAtPos(from);
47
+ return new DOMRect(left, top, 0, bottom - top);
48
+ } };
49
+ const { style, placement, update } = useFloating(() => open.value ? anchor : null, panel, {
50
+ open,
51
+ placement: "bottom-start",
52
+ offset: 6
53
+ });
54
+ const { next } = useZIndex();
55
+ const zIndex = shallowRef(0);
56
+ watch(open, (value) => value && (zIndex.value = next()), { immediate: true });
57
+ useLayer({
58
+ open,
59
+ elements: () => [panel.value, view.value?.dom],
60
+ onOutside: () => close(),
61
+ onEscape: () => close()
62
+ });
63
+ watch(() => props.slash?.query, () => {
64
+ active.value = 0;
65
+ nextTick(update);
66
+ });
67
+ watch([
68
+ open,
69
+ active,
70
+ options
71
+ ], () => {
72
+ const dom = view.value?.dom;
73
+ if (!dom) return;
74
+ const option = open.value && options.value.length ? `${id}-${active.value}` : null;
75
+ if (open.value) {
76
+ dom.setAttribute("aria-controls", id);
77
+ dom.setAttribute("aria-expanded", "true");
78
+ } else {
79
+ dom.removeAttribute("aria-controls");
80
+ dom.removeAttribute("aria-expanded");
81
+ }
82
+ if (option) dom.setAttribute("aria-activedescendant", option);
83
+ else dom.removeAttribute("aria-activedescendant");
84
+ if (option) document.getElementById(option)?.scrollIntoView({ block: "nearest" });
85
+ }, { flush: "post" });
86
+ function choose(option) {
87
+ const current = view.value;
88
+ const slash = props.slash;
89
+ if (!current || !slash) return;
90
+ const removed = current.state.tr.delete(slash.from, current.state.selection.from);
91
+ current.dispatch(removed.setMeta(slashKey, null));
92
+ const { $from } = current.state.selection;
93
+ if ($from.parent.content.size > 0) insertBlockAfter($from.pos)(current.state, current.dispatch);
94
+ if (option.type !== "paragraph" || $from.parent.content.size === 0) setBlockType(option.type, option.props)(current.state, current.dispatch);
95
+ current.focus();
96
+ }
97
+ function close() {
98
+ const current = view.value;
99
+ if (current) current.dispatch(current.state.tr.setMeta(slashKey, null));
100
+ }
101
+ /** Keys the editor passes on while the menu is open. */
102
+ function onKey(event) {
103
+ if (isComposing(event)) return false;
104
+ const count = options.value.length;
105
+ switch (event.key) {
106
+ case "ArrowDown":
107
+ case "ArrowUp":
108
+ if (!count) return false;
109
+ active.value = (active.value + (event.key === "ArrowDown" ? 1 : -1) + count) % count;
110
+ return true;
111
+ case "Enter":
112
+ case "Tab": {
113
+ const option = options.value[active.value];
114
+ if (!option) {
115
+ close();
116
+ return false;
117
+ }
118
+ choose(option);
119
+ return true;
120
+ }
121
+ default: return false;
122
+ }
123
+ }
124
+ __expose({ onKey });
125
+ return (_ctx, _cache) => {
126
+ return openBlock(), createBlock(Transition, { name: "dou-floating" }, {
127
+ default: withCtx(() => [open.value ? (openBlock(), createElementBlock("div", {
128
+ key: 0,
129
+ id,
130
+ ref_key: "panel",
131
+ ref: panel,
132
+ role: "listbox",
133
+ "aria-label": unref(messages).blockTypes,
134
+ class: "dou-dropdown dou-floating dou-editor-slash",
135
+ style: normalizeStyle([unref(style), { zIndex: zIndex.value }]),
136
+ "data-placement": unref(placement),
137
+ onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"]))
138
+ }, [(openBlock(true), createElementBlock(Fragment, null, renderList(options.value, (option, index) => {
139
+ return openBlock(), createElementBlock("div", {
140
+ id: `${id}-${index}`,
141
+ key: option.key,
142
+ role: "option",
143
+ "aria-selected": index === active.value,
144
+ class: normalizeClass([
145
+ "dou-dropdown__item",
146
+ "dou-editor-slash__item",
147
+ { "is-active": index === active.value }
148
+ ]),
149
+ onPointermove: ($event) => active.value = index,
150
+ onClick: ($event) => choose(option)
151
+ }, [
152
+ createElementVNode("span", _hoisted_3, [(openBlock(), createBlock(resolveDynamicComponent(option.icon)))]),
153
+ createElementVNode("span", _hoisted_4, [createElementVNode("span", _hoisted_5, toDisplayString(unref(labelOf)(option.label, unref(messages))), 1), option.hint ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(unref(labelOf)(option.hint, unref(messages))), 1)) : createCommentVNode("", true)]),
154
+ option.shortcut ? (openBlock(), createElementBlock("kbd", _hoisted_7, toDisplayString(option.shortcut), 1)) : createCommentVNode("", true)
155
+ ], 42, _hoisted_2);
156
+ }), 128)), !options.value.length ? (openBlock(), createElementBlock("div", _hoisted_8, toDisplayString(unref(messages).noResults), 1)) : createCommentVNode("", true)], 44, _hoisted_1)) : createCommentVNode("", true)]),
157
+ _: 1
158
+ });
159
+ };
160
+ }
161
+ });
162
+ export { slash_menu_vue_vue_type_script_setup_true_lang_default as default };
163
+
164
+ //# sourceMappingURL=slash-menu.vue_vue_type_script_setup_true_lang.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-menu.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_Transition","_withCtx","_createElementBlock","_unref","_normalizeStyle","_withModifiers","_Fragment","_renderList","_normalizeClass","_createElementVNode","_resolveDynamicComponent","_toDisplayString","_createCommentVNode"],"sources":["../../src/components/slash-menu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { isComposing, useFloating, useLayer, useZIndex, type VirtualElement } from 'dou-ui'\nimport { computed, inject, nextTick, shallowRef, useId, useTemplateRef, watch } from 'vue'\nimport { filterOptions } from '../catalog'\nimport { labelOf, type BlockOption } from '../core/extension'\nimport { insertBlockAfter, setBlockType } from '../core/commands'\nimport { slashKey, type SlashState } from '../core/slash'\nimport { editorKey } from '../editor'\n\nconst props = defineProps<{ slash: SlashState | null }>()\n\nconst { view, messages, kit } = inject(editorKey)!\nconst id = `dou-editor-slash-${useId()}`\nconst panel = useTemplateRef<HTMLElement>('panel')\nconst active = shallowRef(0)\n\nconst options = computed(() =>\n props.slash ? filterOptions(kit.blockOptions, props.slash.query, messages.value) : [],\n)\nconst open = computed(() => !!props.slash)\n\n// the menu hangs below the `/` that opened it\nconst anchor: VirtualElement = {\n getBoundingClientRect() {\n const current = view.value\n const from = props.slash?.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})\n// stacked like DouUI's own popups, above whatever opened before\nconst { next } = useZIndex()\nconst zIndex = shallowRef(0)\nwatch(open, (value) => value && (zIndex.value = next()), { immediate: true })\n// a press outside the editor and the menu closes it; inside, the cursor move does\nuseLayer({\n open,\n elements: () => [panel.value, view.value?.dom],\n onOutside: () => close(),\n onEscape: () => close(),\n})\n\nwatch(\n () => props.slash?.query,\n () => {\n active.value = 0\n void nextTick(update)\n },\n)\n\n// the editor keeps focus; the active option is announced through it\nwatch(\n [open, active, options],\n () => {\n const dom = view.value?.dom\n if (!dom) return\n const option = open.value && options.value.length ? `${id}-${active.value}` : null\n if (open.value) {\n dom.setAttribute('aria-controls', id)\n dom.setAttribute('aria-expanded', 'true')\n } else {\n dom.removeAttribute('aria-controls')\n dom.removeAttribute('aria-expanded')\n }\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(option: BlockOption) {\n const current = view.value\n const slash = props.slash\n if (!current || !slash) return\n // remove the `/query`, then turn the block, or add one below when it has text\n const removed = current.state.tr.delete(slash.from, current.state.selection.from)\n current.dispatch(removed.setMeta(slashKey, null))\n const { $from } = current.state.selection\n if ($from.parent.content.size > 0) insertBlockAfter($from.pos)(current.state, current.dispatch)\n if (option.type !== 'paragraph' || $from.parent.content.size === 0)\n setBlockType(option.type, option.props)(current.state, current.dispatch)\n current.focus()\n}\n\nfunction close() {\n const current = view.value\n if (current) current.dispatch(current.state.tr.setMeta(slashKey, null))\n}\n\n/** Keys the editor passes on while the menu is open. */\nfunction onKey(event: KeyboardEvent): boolean {\n if (isComposing(event)) return false\n const count = options.value.length\n switch (event.key) {\n case 'ArrowDown':\n case 'ArrowUp':\n if (!count) return false\n active.value = (active.value + (event.key === 'ArrowDown' ? 1 : -1) + count) % count\n return true\n case 'Enter':\n case 'Tab': {\n const option = options.value[active.value]\n if (!option) {\n close()\n return false\n }\n choose(option)\n return true\n }\n default:\n return false\n }\n}\n\ndefineExpose({ onKey })\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.blockTypes\"\n class=\"dou-dropdown dou-floating dou-editor-slash\"\n :style=\"[style, { zIndex }]\"\n :data-placement=\"placement\"\n @mousedown.prevent\n >\n <div\n v-for=\"(option, index) in options\"\n :id=\"`${id}-${index}`\"\n :key=\"option.key\"\n role=\"option\"\n :aria-selected=\"index === active\"\n :class=\"['dou-dropdown__item', 'dou-editor-slash__item', { 'is-active': index === active }]\"\n @pointermove=\"active = index\"\n @click=\"choose(option)\"\n >\n <span class=\"dou-editor-slash__icon\"><component :is=\"option.icon\" /></span>\n <span class=\"dou-editor-slash__text\">\n <span class=\"dou-editor-slash__label\">{{ labelOf(option.label, messages) }}</span>\n <span v-if=\"option.hint\" class=\"dou-editor-slash__hint\">{{\n labelOf(option.hint, messages)\n }}</span>\n </span>\n <kbd v-if=\"option.shortcut\" class=\"dou-editor-slash__shortcut\">{{ option.shortcut }}</kbd>\n </div>\n <div v-if=\"!options.length\" class=\"dou-editor-slash__empty\">{{ messages.noResults }}</div>\n </div>\n </Transition>\n</template>\n"],"mappings":"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"}