@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.
- package/LICENSE +21 -0
- package/README.md +10 -2
- package/dist/catalog.js +18 -0
- package/dist/catalog.js.map +1 -0
- package/dist/code/code-editor-block.js +6 -0
- package/dist/code/code-editor-block.js.map +1 -0
- package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js +116 -0
- package/dist/code/code-editor-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/code/index.js +69 -0
- package/dist/code/index.js.map +1 -0
- package/dist/code/languages.js +51 -0
- package/dist/code/languages.js.map +1 -0
- package/dist/code/view.js +255 -0
- package/dist/code/view.js.map +1 -0
- package/dist/collaboration/binding.js +381 -0
- package/dist/collaboration/binding.js.map +1 -0
- package/dist/collaboration/cursors.js +150 -0
- package/dist/collaboration/cursors.js.map +1 -0
- package/dist/collaboration/index.js +108 -0
- package/dist/collaboration/index.js.map +1 -0
- package/dist/collaboration/order.js +88 -0
- package/dist/collaboration/order.js.map +1 -0
- package/dist/collaboration/table.js +234 -0
- package/dist/collaboration/table.js.map +1 -0
- package/dist/collaboration/text.js +91 -0
- package/dist/collaboration/text.js.map +1 -0
- package/dist/components/block-handle.js +6 -0
- package/dist/components/block-handle.js.map +1 -0
- package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js +474 -0
- package/dist/components/block-handle.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/bubble-toolbar.js +6 -0
- package/dist/components/bubble-toolbar.js.map +1 -0
- package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js +381 -0
- package/dist/components/bubble-toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/comment-button.js +6 -0
- package/dist/components/comment-button.js.map +1 -0
- package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js +83 -0
- package/dist/components/comment-button.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/comment-card.js +6 -0
- package/dist/components/comment-card.js.map +1 -0
- package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js +104 -0
- package/dist/components/comment-card.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/menu-escape.js +25 -0
- package/dist/components/menu-escape.js.map +1 -0
- package/dist/components/search-bar.js +6 -0
- package/dist/components/search-bar.js.map +1 -0
- package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js +192 -0
- package/dist/components/search-bar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/slash-menu.js +6 -0
- package/dist/components/slash-menu.js.map +1 -0
- package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js +164 -0
- package/dist/components/slash-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/core/align.js +94 -0
- package/dist/core/align.js.map +1 -0
- package/dist/core/block-selection.js +155 -0
- package/dist/core/block-selection.js.map +1 -0
- package/dist/core/blocks.js +132 -0
- package/dist/core/blocks.js.map +1 -0
- package/dist/core/changes.js +85 -0
- package/dist/core/changes.js.map +1 -0
- package/dist/core/colors.js +16 -0
- package/dist/core/colors.js.map +1 -0
- package/dist/core/commands.js +379 -0
- package/dist/core/commands.js.map +1 -0
- package/dist/core/comments.js +186 -0
- package/dist/core/comments.js.map +1 -0
- package/dist/core/compare.js +106 -0
- package/dist/core/compare.js.map +1 -0
- package/dist/core/convert.js +28 -0
- package/dist/core/convert.js.map +1 -0
- package/dist/core/diff.js +141 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/editor.js +138 -0
- package/dist/core/editor.js.map +1 -0
- package/dist/core/extension.js +99 -0
- package/dist/core/extension.js.map +1 -0
- package/dist/core/file-drop.js +56 -0
- package/dist/core/file-drop.js.map +1 -0
- package/dist/core/highlight.js +120 -0
- package/dist/core/highlight.js.map +1 -0
- package/dist/core/input-rules.js +61 -0
- package/dist/core/input-rules.js.map +1 -0
- package/dist/core/keymap.js +38 -0
- package/dist/core/keymap.js.map +1 -0
- package/dist/core/model.js +196 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/output.js +263 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/plugins.js +102 -0
- package/dist/core/plugins.js.map +1 -0
- package/dist/core/prepare.js +102 -0
- package/dist/core/prepare.js.map +1 -0
- package/dist/core/schema.js +158 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/search.js +197 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/selection.js +16 -0
- package/dist/core/selection.js.map +1 -0
- package/dist/core/slash.js +83 -0
- package/dist/core/slash.js.map +1 -0
- package/dist/core/transaction.js +311 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/define-block.js +177 -0
- package/dist/define-block.js.map +1 -0
- package/dist/document.js +157 -0
- package/dist/document.js.map +1 -0
- package/dist/dom.js +48 -0
- package/dist/dom.js.map +1 -0
- package/dist/editor.js +33 -0
- package/dist/editor.js.map +1 -0
- package/dist/editor.vue_vue_type_script_setup_true_lang.js +564 -0
- package/dist/editor.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/editor2.js +6 -0
- package/dist/editor2.js.map +1 -0
- package/dist/extensions/align/align-menu.js +6 -0
- package/dist/extensions/align/align-menu.js.map +1 -0
- package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js +82 -0
- package/dist/extensions/align/align-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/align/align-sub.js +6 -0
- package/dist/extensions/align/align-sub.js.map +1 -0
- package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js +57 -0
- package/dist/extensions/align/align-sub.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/align/commands.js +40 -0
- package/dist/extensions/align/commands.js.map +1 -0
- package/dist/extensions/align/index.js +27 -0
- package/dist/extensions/align/index.js.map +1 -0
- package/dist/extensions/attachment/attachment-block.js +6 -0
- package/dist/extensions/attachment/attachment-block.js.map +1 -0
- package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js +119 -0
- package/dist/extensions/attachment/attachment-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/attachment/files.js +66 -0
- package/dist/extensions/attachment/files.js.map +1 -0
- package/dist/extensions/attachment/index.js +132 -0
- package/dist/extensions/attachment/index.js.map +1 -0
- package/dist/extensions/blocks.js +170 -0
- package/dist/extensions/blocks.js.map +1 -0
- package/dist/extensions/callout/callout-block.js +6 -0
- package/dist/extensions/callout/callout-block.js.map +1 -0
- package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/extensions/callout/callout-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/callout/html.js +61 -0
- package/dist/extensions/callout/html.js.map +1 -0
- package/dist/extensions/callout/index.js +52 -0
- package/dist/extensions/callout/index.js.map +1 -0
- package/dist/extensions/callout/tones.js +12 -0
- package/dist/extensions/callout/tones.js.map +1 -0
- package/dist/extensions/code-block/code-block-view.js +6 -0
- package/dist/extensions/code-block/code-block-view.js.map +1 -0
- package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js +87 -0
- package/dist/extensions/code-block/code-block-view.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/code-block/index.js +98 -0
- package/dist/extensions/code-block/index.js.map +1 -0
- package/dist/extensions/code-block/languages.js +116 -0
- package/dist/extensions/code-block/languages.js.map +1 -0
- package/dist/extensions/colors/color-menu.js +6 -0
- package/dist/extensions/colors/color-menu.js.map +1 -0
- package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js +113 -0
- package/dist/extensions/colors/color-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/colors/index.js +49 -0
- package/dist/extensions/colors/index.js.map +1 -0
- package/dist/extensions/colors/palette.js +26 -0
- package/dist/extensions/colors/palette.js.map +1 -0
- package/dist/extensions/editor-kit.js +35 -0
- package/dist/extensions/editor-kit.js.map +1 -0
- package/dist/extensions/embed/embed-block.js +6 -0
- package/dist/extensions/embed/embed-block.js.map +1 -0
- package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js +210 -0
- package/dist/extensions/embed/embed-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/embed/index.js +118 -0
- package/dist/extensions/embed/index.js.map +1 -0
- package/dist/extensions/embed/targets.js +79 -0
- package/dist/extensions/embed/targets.js.map +1 -0
- package/dist/extensions/formatting.js +153 -0
- package/dist/extensions/formatting.js.map +1 -0
- package/dist/extensions/image/image-block.js +6 -0
- package/dist/extensions/image/image-block.js.map +1 -0
- package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js +206 -0
- package/dist/extensions/image/image-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/image/index.js +125 -0
- package/dist/extensions/image/index.js.map +1 -0
- package/dist/extensions/image/uploads.js +96 -0
- package/dist/extensions/image/uploads.js.map +1 -0
- package/dist/extensions/list-html.js +58 -0
- package/dist/extensions/list-html.js.map +1 -0
- package/dist/extensions/lists.js +182 -0
- package/dist/extensions/lists.js.map +1 -0
- package/dist/extensions/mention/index.js +56 -0
- package/dist/extensions/mention/index.js.map +1 -0
- package/dist/extensions/mention/mention-menu.js +6 -0
- package/dist/extensions/mention/mention-menu.js.map +1 -0
- package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js +132 -0
- package/dist/extensions/mention/mention-menu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/mention/state.js +113 -0
- package/dist/extensions/mention/state.js.map +1 -0
- package/dist/extensions/table/commands.js +356 -0
- package/dist/extensions/table/commands.js.map +1 -0
- package/dist/extensions/table/html.js +134 -0
- package/dist/extensions/table/html.js.map +1 -0
- package/dist/extensions/table/index.js +56 -0
- package/dist/extensions/table/index.js.map +1 -0
- package/dist/extensions/table/plugin.js +81 -0
- package/dist/extensions/table/plugin.js.map +1 -0
- package/dist/extensions/table/schema.js +258 -0
- package/dist/extensions/table/schema.js.map +1 -0
- package/dist/extensions/table/table-controls.js +6 -0
- package/dist/extensions/table/table-controls.js.map +1 -0
- package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js +698 -0
- package/dist/extensions/table/table-controls.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/extensions/table/view.js +19 -0
- package/dist/extensions/table/view.js.map +1 -0
- package/dist/extensions/toggle/index.js +87 -0
- package/dist/extensions/toggle/index.js.map +1 -0
- package/dist/extensions/toggle/state.js +120 -0
- package/dist/extensions/toggle/state.js.map +1 -0
- package/dist/extensions/toggle/toggle-block.js +6 -0
- package/dist/extensions/toggle/toggle-block.js.map +1 -0
- package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js +42 -0
- package/dist/extensions/toggle/toggle-block.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/locale.js +522 -0
- package/dist/locale.js.map +1 -0
- package/dist/markdown/index.js +24 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/style.css +1 -0
- package/dist/style.d.ts +1 -0
- package/dist/style.js +8 -0
- package/dist/types/catalog.d.ts +8 -0
- package/dist/types/code/code-editor-block.vue.d.ts +17 -0
- package/dist/types/code/index.d.ts +18 -0
- package/dist/types/code/languages.d.ts +7 -0
- package/dist/types/code/types.d.ts +37 -0
- package/dist/types/code/view.d.ts +12 -0
- package/dist/types/collaboration/binding.d.ts +32 -0
- package/dist/types/collaboration/cursors.d.ts +26 -0
- package/dist/types/collaboration/index.d.ts +47 -0
- package/dist/types/collaboration/order.d.ts +20 -0
- package/dist/types/collaboration/table.d.ts +25 -0
- package/dist/types/collaboration/text.d.ts +9 -0
- package/dist/types/components/block-handle.vue.d.ts +3 -0
- package/dist/types/components/bubble-toolbar.vue.d.ts +7 -0
- package/dist/types/components/comment-button.vue.d.ts +3 -0
- package/dist/types/components/comment-card.vue.d.ts +3 -0
- package/dist/types/components/menu-escape.d.ts +7 -0
- package/dist/types/components/search-bar.vue.d.ts +22 -0
- package/dist/types/components/slash-menu.vue.d.ts +11 -0
- package/dist/types/core/align.d.ts +16 -0
- package/dist/types/core/block-selection.d.ts +22 -0
- package/dist/types/core/blocks.d.ts +52 -0
- package/dist/types/core/changes.d.ts +14 -0
- package/dist/types/core/colors.d.ts +4 -0
- package/dist/types/core/commands.d.ts +61 -0
- package/dist/types/core/comments.d.ts +44 -0
- package/dist/types/core/compare.d.ts +10 -0
- package/dist/types/core/convert.d.ts +12 -0
- package/dist/types/core/diff.d.ts +49 -0
- package/dist/types/core/editor.d.ts +64 -0
- package/dist/types/core/extension.d.ts +167 -0
- package/dist/types/core/file-drop.d.ts +19 -0
- package/dist/types/core/highlight.d.ts +32 -0
- package/dist/types/core/input-rules.d.ts +21 -0
- package/dist/types/core/keymap.d.ts +8 -0
- package/dist/types/core/model.d.ts +158 -0
- package/dist/types/core/output.d.ts +64 -0
- package/dist/types/core/plugins.d.ts +24 -0
- package/dist/types/core/prepare.d.ts +23 -0
- package/dist/types/core/schema.d.ts +38 -0
- package/dist/types/core/search.d.ts +83 -0
- package/dist/types/core/selection.d.ts +8 -0
- package/dist/types/core/slash.d.ts +21 -0
- package/dist/types/core/transaction.d.ts +97 -0
- package/dist/types/define-block.d.ts +66 -0
- package/dist/types/document.d.ts +46 -0
- package/dist/types/dom.d.ts +17 -0
- package/dist/types/editor.d.ts +252 -0
- package/dist/types/editor.vue.d.ts +210 -0
- package/dist/types/extensions/align/align-menu.vue.d.ts +3 -0
- package/dist/types/extensions/align/align-sub.vue.d.ts +7 -0
- package/dist/types/extensions/align/commands.d.ts +13 -0
- package/dist/types/extensions/align/index.d.ts +7 -0
- package/dist/types/extensions/attachment/attachment-block.vue.d.ts +17 -0
- package/dist/types/extensions/attachment/files.d.ts +6 -0
- package/dist/types/extensions/attachment/index.d.ts +20 -0
- package/dist/types/extensions/blocks.d.ts +8 -0
- package/dist/types/extensions/callout/callout-block.vue.d.ts +14 -0
- package/dist/types/extensions/callout/html.d.ts +4 -0
- package/dist/types/extensions/callout/index.d.ts +3 -0
- package/dist/types/extensions/callout/tones.d.ts +8 -0
- package/dist/types/extensions/code-block/code-block-view.vue.d.ts +16 -0
- package/dist/types/extensions/code-block/index.d.ts +19 -0
- package/dist/types/extensions/code-block/languages.d.ts +9 -0
- package/dist/types/extensions/colors/color-menu.vue.d.ts +3 -0
- package/dist/types/extensions/colors/index.d.ts +7 -0
- package/dist/types/extensions/colors/palette.d.ts +7 -0
- package/dist/types/extensions/editor-kit.d.ts +37 -0
- package/dist/types/extensions/embed/embed-block.vue.d.ts +16 -0
- package/dist/types/extensions/embed/index.d.ts +18 -0
- package/dist/types/extensions/embed/targets.d.ts +32 -0
- package/dist/types/extensions/formatting.d.ts +8 -0
- package/dist/types/extensions/image/image-block.vue.d.ts +16 -0
- package/dist/types/extensions/image/index.d.ts +16 -0
- package/dist/types/extensions/image/uploads.d.ts +35 -0
- package/dist/types/extensions/list-html.d.ts +11 -0
- package/dist/types/extensions/lists.d.ts +6 -0
- package/dist/types/extensions/mention/index.d.ts +11 -0
- package/dist/types/extensions/mention/mention-menu.vue.d.ts +7 -0
- package/dist/types/extensions/mention/state.d.ts +28 -0
- package/dist/types/extensions/table/commands.d.ts +69 -0
- package/dist/types/extensions/table/html.d.ts +4 -0
- package/dist/types/extensions/table/index.d.ts +8 -0
- package/dist/types/extensions/table/plugin.d.ts +4 -0
- package/dist/types/extensions/table/schema.d.ts +23 -0
- package/dist/types/extensions/table/table-controls.vue.d.ts +3 -0
- package/dist/types/extensions/table/view.d.ts +13 -0
- package/dist/types/extensions/toggle/index.d.ts +7 -0
- package/dist/types/extensions/toggle/state.d.ts +17 -0
- package/dist/types/extensions/toggle/toggle-block.vue.d.ts +14 -0
- package/dist/types/index.d.ts +266 -0
- package/dist/types/locale.d.ts +195 -0
- package/dist/types/markdown/index.d.ts +14 -0
- package/dist/types/use-comments.d.ts +17 -0
- package/dist/use-comments.js +141 -0
- package/dist/use-comments.js.map +1 -0
- 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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"}
|