@bitrix24/b24ui-nuxt 2.1.10 → 2.1.12
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/README-AI.md +1 -1
- package/dist/meta.d.mts +98220 -780
- package/dist/meta.mjs +98220 -780
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/components/ContextMenu.d.vue.ts +6 -2
- package/dist/runtime/components/ContextMenu.vue.d.ts +6 -2
- package/dist/runtime/components/ContextMenuContent.vue +2 -2
- package/dist/runtime/components/DropdownMenu.d.vue.ts +6 -2
- package/dist/runtime/components/DropdownMenu.vue.d.ts +6 -2
- package/dist/runtime/components/DropdownMenuContent.vue +2 -2
- package/dist/runtime/components/Editor.d.vue.ts +87 -0
- package/dist/runtime/components/Editor.vue +185 -0
- package/dist/runtime/components/Editor.vue.d.ts +87 -0
- package/dist/runtime/components/EditorDragHandle.d.vue.ts +60 -0
- package/dist/runtime/components/EditorDragHandle.vue +128 -0
- package/dist/runtime/components/EditorDragHandle.vue.d.ts +60 -0
- package/dist/runtime/components/EditorEmojiMenu.d.vue.ts +35 -0
- package/dist/runtime/components/EditorEmojiMenu.vue +70 -0
- package/dist/runtime/components/EditorEmojiMenu.vue.d.ts +35 -0
- package/dist/runtime/components/EditorMentionMenu.d.vue.ts +39 -0
- package/dist/runtime/components/EditorMentionMenu.vue +74 -0
- package/dist/runtime/components/EditorMentionMenu.vue.d.ts +39 -0
- package/dist/runtime/components/EditorSuggestionMenu.d.vue.ts +52 -0
- package/dist/runtime/components/EditorSuggestionMenu.vue +79 -0
- package/dist/runtime/components/EditorSuggestionMenu.vue.d.ts +52 -0
- package/dist/runtime/components/EditorToolbar.d.vue.ts +80 -0
- package/dist/runtime/components/EditorToolbar.vue +239 -0
- package/dist/runtime/components/EditorToolbar.vue.d.ts +80 -0
- package/dist/runtime/components/FormField.vue +2 -2
- package/dist/runtime/components/InputMenu.d.vue.ts +2 -0
- package/dist/runtime/components/InputMenu.vue +14 -1
- package/dist/runtime/components/InputMenu.vue.d.ts +2 -0
- package/dist/runtime/components/Pagination.d.vue.ts +0 -1
- package/dist/runtime/components/Pagination.vue.d.ts +0 -1
- package/dist/runtime/components/Select.d.vue.ts +2 -0
- package/dist/runtime/components/Select.vue +14 -1
- package/dist/runtime/components/Select.vue.d.ts +2 -0
- package/dist/runtime/components/SelectMenu.d.vue.ts +2 -0
- package/dist/runtime/components/SelectMenu.vue +14 -1
- package/dist/runtime/components/SelectMenu.vue.d.ts +2 -0
- package/dist/runtime/components/color-mode/ColorModeSelect.vue +1 -0
- package/dist/runtime/components/locale/LocaleSelect.vue +1 -0
- package/dist/runtime/components/prose/Accordion.vue +1 -1
- package/dist/runtime/composables/defineShortcuts.d.ts +1 -0
- package/dist/runtime/composables/defineShortcuts.js +60 -13
- package/dist/runtime/composables/index.d.ts +1 -0
- package/dist/runtime/composables/index.js +1 -0
- package/dist/runtime/composables/useEditorMenu.d.ts +64 -0
- package/dist/runtime/composables/useEditorMenu.js +448 -0
- package/dist/runtime/composables/useSpeechRecognition.d.ts +122 -0
- package/dist/runtime/composables/useSpeechRecognition.js +166 -0
- package/dist/runtime/dictionary/icons.d.ts +1 -0
- package/dist/runtime/dictionary/icons.js +5 -3
- package/dist/runtime/types/editor.d.ts +69 -0
- package/dist/runtime/types/editor.js +0 -0
- package/dist/runtime/types/index.d.ts +7 -0
- package/dist/runtime/types/index.js +7 -0
- package/dist/runtime/utils/editor.d.ts +69 -0
- package/dist/runtime/utils/editor.js +364 -0
- package/dist/runtime/vue/components/color-mode/ColorModeSelect.vue +1 -0
- package/dist/shared/{b24ui-nuxt.BCphUjPy.mjs → b24ui-nuxt.C8MyFqPm.mjs} +185 -1
- package/dist/unplugin.mjs +1 -1
- package/dist/vite.mjs +1 -1
- package/package.json +16 -3
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defu } from 'defu';
|
|
2
2
|
import { defineNuxtModule, createResolver, addPlugin, hasNuxtModule, addComponentsDir, addImportsDir, installModule } from '@nuxt/kit';
|
|
3
|
-
import { d as defaultOptions, v as version, n as name, a as getDefaultConfig, b as addTemplates } from './shared/b24ui-nuxt.
|
|
3
|
+
import { d as defaultOptions, v as version, n as name, a as getDefaultConfig, b as addTemplates } from './shared/b24ui-nuxt.C8MyFqPm.mjs';
|
|
4
4
|
import 'node:url';
|
|
5
5
|
import 'scule';
|
|
6
6
|
import 'knitwork';
|
|
@@ -101,8 +101,12 @@ export type ContextMenuSlots<A extends ArrayOrNested<ContextMenuItem> = ArrayOrN
|
|
|
101
101
|
index: number;
|
|
102
102
|
}) => any;
|
|
103
103
|
'item-trailing': SlotProps<T>;
|
|
104
|
-
'content-top': (props
|
|
105
|
-
|
|
104
|
+
'content-top': (props: {
|
|
105
|
+
sub: boolean;
|
|
106
|
+
}) => any;
|
|
107
|
+
'content-bottom': (props: {
|
|
108
|
+
sub: boolean;
|
|
109
|
+
}) => any;
|
|
106
110
|
} & DynamicSlots<MergeTypes<T>, 'label' | 'description', {
|
|
107
111
|
active?: boolean;
|
|
108
112
|
index: number;
|
|
@@ -101,8 +101,12 @@ export type ContextMenuSlots<A extends ArrayOrNested<ContextMenuItem> = ArrayOrN
|
|
|
101
101
|
index: number;
|
|
102
102
|
}) => any;
|
|
103
103
|
'item-trailing': SlotProps<T>;
|
|
104
|
-
'content-top': (props
|
|
105
|
-
|
|
104
|
+
'content-top': (props: {
|
|
105
|
+
sub: boolean;
|
|
106
|
+
}) => any;
|
|
107
|
+
'content-bottom': (props: {
|
|
108
|
+
sub: boolean;
|
|
109
|
+
}) => any;
|
|
106
110
|
} & DynamicSlots<MergeTypes<T>, 'label' | 'description', {
|
|
107
111
|
active?: boolean;
|
|
108
112
|
index: number;
|
|
@@ -162,7 +162,7 @@ const groups = computed(
|
|
|
162
162
|
:class="b24ui.content({ class: [b24uiOverride?.content, props.class] })"
|
|
163
163
|
v-bind="contentProps"
|
|
164
164
|
>
|
|
165
|
-
<slot name="content-top" />
|
|
165
|
+
<slot name="content-top" :sub="sub ?? false" />
|
|
166
166
|
|
|
167
167
|
<div role="presentation" data-slot="viewport" :class="b24ui.viewport({ class: b24uiOverride?.viewport })">
|
|
168
168
|
<ContextMenu.Group
|
|
@@ -247,7 +247,7 @@ const groups = computed(
|
|
|
247
247
|
|
|
248
248
|
<slot />
|
|
249
249
|
|
|
250
|
-
<slot name="content-bottom" />
|
|
250
|
+
<slot name="content-bottom" :sub="sub ?? false" />
|
|
251
251
|
</component>
|
|
252
252
|
</ContextMenu.Portal>
|
|
253
253
|
</template>
|
|
@@ -108,8 +108,12 @@ export type DropdownMenuSlots<A extends ArrayOrNested<DropdownMenuItem> = ArrayO
|
|
|
108
108
|
index: number;
|
|
109
109
|
}) => any;
|
|
110
110
|
'item-trailing': SlotProps<T>;
|
|
111
|
-
'content-top': (props
|
|
112
|
-
|
|
111
|
+
'content-top': (props: {
|
|
112
|
+
sub: boolean;
|
|
113
|
+
}) => any;
|
|
114
|
+
'content-bottom': (props: {
|
|
115
|
+
sub: boolean;
|
|
116
|
+
}) => any;
|
|
113
117
|
} & DynamicSlots<MergeTypes<T>, 'label' | 'description', {
|
|
114
118
|
active?: boolean;
|
|
115
119
|
index: number;
|
|
@@ -108,8 +108,12 @@ export type DropdownMenuSlots<A extends ArrayOrNested<DropdownMenuItem> = ArrayO
|
|
|
108
108
|
index: number;
|
|
109
109
|
}) => any;
|
|
110
110
|
'item-trailing': SlotProps<T>;
|
|
111
|
-
'content-top': (props
|
|
112
|
-
|
|
111
|
+
'content-top': (props: {
|
|
112
|
+
sub: boolean;
|
|
113
|
+
}) => any;
|
|
114
|
+
'content-bottom': (props: {
|
|
115
|
+
sub: boolean;
|
|
116
|
+
}) => any;
|
|
113
117
|
} & DynamicSlots<MergeTypes<T>, 'label' | 'description', {
|
|
114
118
|
active?: boolean;
|
|
115
119
|
index: number;
|
|
@@ -172,7 +172,7 @@ const groups = computed(
|
|
|
172
172
|
:class="b24ui.content({ class: [b24uiOverride?.content, props.class] })"
|
|
173
173
|
v-bind="contentProps"
|
|
174
174
|
>
|
|
175
|
-
<slot name="content-top" />
|
|
175
|
+
<slot name="content-top" :sub="sub ?? false" />
|
|
176
176
|
|
|
177
177
|
<div role="presentation" data-slot="viewport" :class="b24ui.viewport({ class: b24uiOverride?.viewport })">
|
|
178
178
|
<DropdownMenu.Group
|
|
@@ -261,7 +261,7 @@ const groups = computed(
|
|
|
261
261
|
|
|
262
262
|
<slot />
|
|
263
263
|
|
|
264
|
-
<slot name="content-bottom" />
|
|
264
|
+
<slot name="content-bottom" :sub="sub ?? false" />
|
|
265
265
|
</component>
|
|
266
266
|
</DropdownMenu.Portal>
|
|
267
267
|
</template>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { Editor as TiptapEditor, EditorOptions, Content } from '@tiptap/vue-3';
|
|
3
|
+
import type { StarterKitOptions } from '@tiptap/starter-kit';
|
|
4
|
+
import type { PlaceholderOptions } from '@tiptap/extension-placeholder';
|
|
5
|
+
import type { MarkdownExtensionOptions } from '@tiptap/markdown';
|
|
6
|
+
import type { ImageOptions } from '@tiptap/extension-image';
|
|
7
|
+
import type { MentionOptions } from '@tiptap/extension-mention';
|
|
8
|
+
import theme from '#build/b24ui/editor';
|
|
9
|
+
import type { EditorHandlers, EditorCustomHandlers } from '../types/editor';
|
|
10
|
+
import type { ComponentConfig } from '../types/tv';
|
|
11
|
+
type Editor = ComponentConfig<typeof theme, AppConfig, 'editor'>;
|
|
12
|
+
export type EditorContentType = 'json' | 'html' | 'markdown';
|
|
13
|
+
export interface EditorProps<T extends Content = Content, H extends EditorCustomHandlers = EditorCustomHandlers> extends Omit<Partial<EditorOptions>, 'content' | 'element'> {
|
|
14
|
+
/**
|
|
15
|
+
* The element or component this component should render as.
|
|
16
|
+
* @defaultValue 'div'
|
|
17
|
+
*/
|
|
18
|
+
as?: any;
|
|
19
|
+
modelValue?: T;
|
|
20
|
+
/**
|
|
21
|
+
* The content type the content is provided as.
|
|
22
|
+
* When not specified, it's automatically inferred: strings are treated as 'html', objects as 'json'.
|
|
23
|
+
*/
|
|
24
|
+
contentType?: EditorContentType;
|
|
25
|
+
/**
|
|
26
|
+
* The starter kit options to configure the editor.
|
|
27
|
+
* @defaultValue { headings: { levels: [1, 2, 3, 4] }, link: { openOnClick: false }, dropcursor: { color: 'var(--ui-color-accent-main-primary)', width: 2 } }
|
|
28
|
+
* @see https://tiptap.dev/docs/editor/extensions/functionality/starterkit
|
|
29
|
+
*/
|
|
30
|
+
starterKit?: Partial<StarterKitOptions>;
|
|
31
|
+
/**
|
|
32
|
+
* The placeholder text to show in empty paragraphs.
|
|
33
|
+
* `{ showOnlyWhenEditable: false, showOnlyCurrent: true }`{lang="ts-type"}
|
|
34
|
+
* Can be a string or PlaceholderOptions from `@tiptap/extension-placeholder`.
|
|
35
|
+
* @see https://tiptap.dev/docs/editor/extensions/functionality/placeholder
|
|
36
|
+
*/
|
|
37
|
+
placeholder?: string | Partial<PlaceholderOptions>;
|
|
38
|
+
/**
|
|
39
|
+
* The markdown extension options to configure markdown parsing and serialization.
|
|
40
|
+
* @see https://tiptap.dev/docs/editor/extensions/functionality/markdown
|
|
41
|
+
*/
|
|
42
|
+
markdown?: Partial<MarkdownExtensionOptions>;
|
|
43
|
+
/**
|
|
44
|
+
* The image extension options to configure image handling.
|
|
45
|
+
* @see https://tiptap.dev/docs/editor/extensions/nodes/image
|
|
46
|
+
*/
|
|
47
|
+
image?: Partial<ImageOptions>;
|
|
48
|
+
/**
|
|
49
|
+
* The mention extension options to configure mention handling.
|
|
50
|
+
* @see https://tiptap.dev/docs/editor/extensions/nodes/mention
|
|
51
|
+
*/
|
|
52
|
+
mention?: Partial<MentionOptions>;
|
|
53
|
+
/**
|
|
54
|
+
* Custom item handlers to override or extend the default handlers.
|
|
55
|
+
* These handlers are provided to all child components (toolbar, suggestion menu, etc.).
|
|
56
|
+
*/
|
|
57
|
+
handlers?: H;
|
|
58
|
+
class?: any;
|
|
59
|
+
b24ui?: Editor['slots'];
|
|
60
|
+
}
|
|
61
|
+
export interface EditorEmits<T extends Content = Content> {
|
|
62
|
+
'update:modelValue': [value: T];
|
|
63
|
+
}
|
|
64
|
+
export interface EditorSlots<H extends EditorCustomHandlers = EditorCustomHandlers> {
|
|
65
|
+
default(props: {
|
|
66
|
+
editor: TiptapEditor;
|
|
67
|
+
handlers: EditorHandlers<H>;
|
|
68
|
+
}): any;
|
|
69
|
+
}
|
|
70
|
+
declare const __VLS_export: <T extends Content, H extends EditorCustomHandlers>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
71
|
+
props: __VLS_PrettifyLocal<EditorProps<T, H> & __VLS_EmitsToProps<__VLS_NormalizeEmits<(evt: "update:modelValue", value: T) => void>>> & import("vue").PublicProps & (typeof globalThis extends {
|
|
72
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
73
|
+
} ? P : {});
|
|
74
|
+
expose: (exposed: import("vue").ShallowUnwrapRef<{
|
|
75
|
+
editor: import("vue").ShallowRef<TiptapEditor | undefined, TiptapEditor | undefined>;
|
|
76
|
+
}>) => void;
|
|
77
|
+
attrs: any;
|
|
78
|
+
slots: EditorSlots<H>;
|
|
79
|
+
emit: (evt: "update:modelValue", value: T) => void;
|
|
80
|
+
}>) => import("vue").VNode & {
|
|
81
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
82
|
+
};
|
|
83
|
+
declare const _default: typeof __VLS_export;
|
|
84
|
+
export default _default;
|
|
85
|
+
type __VLS_PrettifyLocal<T> = {
|
|
86
|
+
[K in keyof T as K]: T[K];
|
|
87
|
+
} & {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import theme from "#build/b24ui/editor";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { computed, provide, useAttrs, watch } from "vue";
|
|
7
|
+
import { defu } from "defu";
|
|
8
|
+
import { Primitive, useForwardProps } from "reka-ui";
|
|
9
|
+
import { mergeAttributes } from "@tiptap/core";
|
|
10
|
+
import HorizontalRule from "@tiptap/extension-horizontal-rule";
|
|
11
|
+
import Image from "@tiptap/extension-image";
|
|
12
|
+
import Mention from "@tiptap/extension-mention";
|
|
13
|
+
import Placeholder from "@tiptap/extension-placeholder";
|
|
14
|
+
import { Markdown } from "@tiptap/markdown";
|
|
15
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
16
|
+
import { useEditor, EditorContent } from "@tiptap/vue-3";
|
|
17
|
+
import { reactiveOmit } from "@vueuse/core";
|
|
18
|
+
import { useAppConfig } from "#imports";
|
|
19
|
+
import { createHandlers } from "../utils/editor";
|
|
20
|
+
import { tv } from "../utils/tv";
|
|
21
|
+
defineOptions({ inheritAttrs: false });
|
|
22
|
+
const props = defineProps({
|
|
23
|
+
as: { type: null, required: false },
|
|
24
|
+
modelValue: { type: null, required: false },
|
|
25
|
+
contentType: { type: String, required: false },
|
|
26
|
+
starterKit: { type: Object, required: false },
|
|
27
|
+
placeholder: { type: [String, Object], required: false },
|
|
28
|
+
markdown: { type: Object, required: false },
|
|
29
|
+
image: { type: Object, required: false },
|
|
30
|
+
mention: { type: Object, required: false },
|
|
31
|
+
handlers: { type: null, required: false },
|
|
32
|
+
class: { type: null, required: false },
|
|
33
|
+
b24ui: { type: null, required: false },
|
|
34
|
+
extensions: { type: Array, required: false },
|
|
35
|
+
injectCSS: { type: Boolean, required: false },
|
|
36
|
+
injectNonce: { type: null, required: false },
|
|
37
|
+
autofocus: { type: [String, Number, Boolean, null], required: false },
|
|
38
|
+
editable: { type: Boolean, required: false },
|
|
39
|
+
textDirection: { type: String, required: false },
|
|
40
|
+
editorProps: { type: Object, required: false },
|
|
41
|
+
parseOptions: { type: Object, required: false },
|
|
42
|
+
coreExtensionOptions: { type: Object, required: false },
|
|
43
|
+
enableInputRules: { type: [Array, Boolean], required: false },
|
|
44
|
+
enablePasteRules: { type: [Array, Boolean], required: false },
|
|
45
|
+
enableCoreExtensions: { type: [Boolean, Object], required: false },
|
|
46
|
+
enableContentCheck: { type: Boolean, required: false },
|
|
47
|
+
emitContentError: { type: Boolean, required: false },
|
|
48
|
+
onBeforeCreate: { type: Function, required: false },
|
|
49
|
+
onCreate: { type: Function, required: false },
|
|
50
|
+
onMount: { type: Function, required: false },
|
|
51
|
+
onUnmount: { type: Function, required: false },
|
|
52
|
+
onContentError: { type: Function, required: false },
|
|
53
|
+
onUpdate: { type: Function, required: false },
|
|
54
|
+
onSelectionUpdate: { type: Function, required: false },
|
|
55
|
+
onTransaction: { type: Function, required: false },
|
|
56
|
+
onFocus: { type: Function, required: false },
|
|
57
|
+
onBlur: { type: Function, required: false },
|
|
58
|
+
onDestroy: { type: Function, required: false },
|
|
59
|
+
onPaste: { type: Function, required: false },
|
|
60
|
+
onDrop: { type: Function, required: false },
|
|
61
|
+
onDelete: { type: Function, required: false }
|
|
62
|
+
});
|
|
63
|
+
const emits = defineEmits(["update:modelValue"]);
|
|
64
|
+
defineSlots();
|
|
65
|
+
const attrs = useAttrs();
|
|
66
|
+
const appConfig = useAppConfig();
|
|
67
|
+
const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.editor || {} })());
|
|
68
|
+
const rootProps = useForwardProps(reactiveOmit(props, "starterKit", "extensions", "editorProps", "contentType", "class", "placeholder", "markdown", "image", "mention", "handlers"));
|
|
69
|
+
const editorProps = computed(() => defu(props.editorProps, {
|
|
70
|
+
attributes: {
|
|
71
|
+
autocomplete: "off",
|
|
72
|
+
autocorrect: "off",
|
|
73
|
+
autocapitalize: "off",
|
|
74
|
+
...attrs,
|
|
75
|
+
class: b24ui.value.base({ class: props.b24ui?.base })
|
|
76
|
+
}
|
|
77
|
+
}));
|
|
78
|
+
const contentType = computed(() => props.contentType || (typeof props.modelValue === "string" ? "html" : "json"));
|
|
79
|
+
const starterKit = computed(() => defu(props.starterKit, {
|
|
80
|
+
horizontalRule: false,
|
|
81
|
+
headings: {
|
|
82
|
+
levels: [1, 2, 3, 4]
|
|
83
|
+
},
|
|
84
|
+
dropcursor: {
|
|
85
|
+
color: "var(--ui-color-accent-main-primary)",
|
|
86
|
+
width: 2
|
|
87
|
+
},
|
|
88
|
+
link: {
|
|
89
|
+
openOnClick: false
|
|
90
|
+
}
|
|
91
|
+
}));
|
|
92
|
+
const placeholder = computed(() => defu(typeof props.placeholder === "string" ? { placeholder: props.placeholder } : props.placeholder, {
|
|
93
|
+
showOnlyWhenEditable: false,
|
|
94
|
+
showOnlyCurrent: true
|
|
95
|
+
}));
|
|
96
|
+
const markdown = computed(() => defu(props.markdown, {
|
|
97
|
+
markedOptions: {
|
|
98
|
+
gfm: true
|
|
99
|
+
}
|
|
100
|
+
}));
|
|
101
|
+
const image = computed(() => defu(props.image, {}));
|
|
102
|
+
const mention = computed(() => defu(props.mention, {
|
|
103
|
+
HTMLAttributes: {
|
|
104
|
+
class: "mention"
|
|
105
|
+
}
|
|
106
|
+
}));
|
|
107
|
+
const extensions = computed(() => [
|
|
108
|
+
contentType.value === "markdown" && Markdown.configure(markdown.value),
|
|
109
|
+
StarterKit.configure(starterKit.value),
|
|
110
|
+
HorizontalRule.extend({
|
|
111
|
+
renderHTML() {
|
|
112
|
+
return [
|
|
113
|
+
"div",
|
|
114
|
+
mergeAttributes(this.options.HTMLAttributes, { "data-type": this.name }),
|
|
115
|
+
["hr"]
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
Image.configure(image.value),
|
|
120
|
+
props.placeholder && Placeholder.configure(placeholder.value),
|
|
121
|
+
Mention.configure(mention.value),
|
|
122
|
+
...props.extensions || []
|
|
123
|
+
].filter((extension) => !!extension));
|
|
124
|
+
const editor = useEditor({
|
|
125
|
+
...rootProps.value,
|
|
126
|
+
content: props.modelValue,
|
|
127
|
+
contentType: contentType.value,
|
|
128
|
+
extensions: extensions.value,
|
|
129
|
+
editorProps: editorProps.value,
|
|
130
|
+
onUpdate: ({ editor: editor2 }) => {
|
|
131
|
+
let value;
|
|
132
|
+
try {
|
|
133
|
+
if (contentType.value === "html") {
|
|
134
|
+
value = editor2.getHTML();
|
|
135
|
+
} else if (contentType.value === "json") {
|
|
136
|
+
value = editor2.getJSON();
|
|
137
|
+
} else if (contentType.value === "markdown") {
|
|
138
|
+
value = editor2.getMarkdown();
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
value = editor2.getText();
|
|
142
|
+
}
|
|
143
|
+
emits("update:modelValue", value);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
watch(() => props.modelValue, (newVal) => {
|
|
147
|
+
if (!editor.value || newVal == null) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const currentContent = contentType.value === "html" ? editor.value.getHTML() : contentType.value === "json" ? JSON.stringify(editor.value.getJSON()) : contentType.value === "markdown" ? editor.value.getMarkdown() : editor.value.getText();
|
|
151
|
+
const newContent = contentType.value === "json" && typeof newVal === "object" ? JSON.stringify(newVal) : String(newVal);
|
|
152
|
+
if (currentContent !== newContent) {
|
|
153
|
+
const currentSelection = editor.value.state.selection;
|
|
154
|
+
const currentPos = currentSelection.from;
|
|
155
|
+
editor.value.commands.setContent(newVal);
|
|
156
|
+
const newDoc = editor.value.state.doc;
|
|
157
|
+
if (currentPos <= newDoc.content.size) {
|
|
158
|
+
editor.value.commands.setTextSelection(currentPos);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
const handlers = computed(() => ({
|
|
163
|
+
...createHandlers(),
|
|
164
|
+
...props.handlers
|
|
165
|
+
}));
|
|
166
|
+
provide("editorHandlers", handlers);
|
|
167
|
+
defineExpose({
|
|
168
|
+
editor
|
|
169
|
+
});
|
|
170
|
+
</script>
|
|
171
|
+
|
|
172
|
+
<template>
|
|
173
|
+
<Primitive :as="as" data-slot="root" :class="b24ui.root({ class: [props.b24ui?.root, props.class] })">
|
|
174
|
+
<template v-if="editor">
|
|
175
|
+
<slot :editor="editor" :handlers="handlers" />
|
|
176
|
+
|
|
177
|
+
<EditorContent
|
|
178
|
+
role="presentation"
|
|
179
|
+
:editor="editor"
|
|
180
|
+
data-slot="content"
|
|
181
|
+
:class="b24ui.content({ class: props.b24ui?.content })"
|
|
182
|
+
/>
|
|
183
|
+
</template>
|
|
184
|
+
</Primitive>
|
|
185
|
+
</template>
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { Editor as TiptapEditor, EditorOptions, Content } from '@tiptap/vue-3';
|
|
3
|
+
import type { StarterKitOptions } from '@tiptap/starter-kit';
|
|
4
|
+
import type { PlaceholderOptions } from '@tiptap/extension-placeholder';
|
|
5
|
+
import type { MarkdownExtensionOptions } from '@tiptap/markdown';
|
|
6
|
+
import type { ImageOptions } from '@tiptap/extension-image';
|
|
7
|
+
import type { MentionOptions } from '@tiptap/extension-mention';
|
|
8
|
+
import theme from '#build/b24ui/editor';
|
|
9
|
+
import type { EditorHandlers, EditorCustomHandlers } from '../types/editor';
|
|
10
|
+
import type { ComponentConfig } from '../types/tv';
|
|
11
|
+
type Editor = ComponentConfig<typeof theme, AppConfig, 'editor'>;
|
|
12
|
+
export type EditorContentType = 'json' | 'html' | 'markdown';
|
|
13
|
+
export interface EditorProps<T extends Content = Content, H extends EditorCustomHandlers = EditorCustomHandlers> extends Omit<Partial<EditorOptions>, 'content' | 'element'> {
|
|
14
|
+
/**
|
|
15
|
+
* The element or component this component should render as.
|
|
16
|
+
* @defaultValue 'div'
|
|
17
|
+
*/
|
|
18
|
+
as?: any;
|
|
19
|
+
modelValue?: T;
|
|
20
|
+
/**
|
|
21
|
+
* The content type the content is provided as.
|
|
22
|
+
* When not specified, it's automatically inferred: strings are treated as 'html', objects as 'json'.
|
|
23
|
+
*/
|
|
24
|
+
contentType?: EditorContentType;
|
|
25
|
+
/**
|
|
26
|
+
* The starter kit options to configure the editor.
|
|
27
|
+
* @defaultValue { headings: { levels: [1, 2, 3, 4] }, link: { openOnClick: false }, dropcursor: { color: 'var(--ui-color-accent-main-primary)', width: 2 } }
|
|
28
|
+
* @see https://tiptap.dev/docs/editor/extensions/functionality/starterkit
|
|
29
|
+
*/
|
|
30
|
+
starterKit?: Partial<StarterKitOptions>;
|
|
31
|
+
/**
|
|
32
|
+
* The placeholder text to show in empty paragraphs.
|
|
33
|
+
* `{ showOnlyWhenEditable: false, showOnlyCurrent: true }`{lang="ts-type"}
|
|
34
|
+
* Can be a string or PlaceholderOptions from `@tiptap/extension-placeholder`.
|
|
35
|
+
* @see https://tiptap.dev/docs/editor/extensions/functionality/placeholder
|
|
36
|
+
*/
|
|
37
|
+
placeholder?: string | Partial<PlaceholderOptions>;
|
|
38
|
+
/**
|
|
39
|
+
* The markdown extension options to configure markdown parsing and serialization.
|
|
40
|
+
* @see https://tiptap.dev/docs/editor/extensions/functionality/markdown
|
|
41
|
+
*/
|
|
42
|
+
markdown?: Partial<MarkdownExtensionOptions>;
|
|
43
|
+
/**
|
|
44
|
+
* The image extension options to configure image handling.
|
|
45
|
+
* @see https://tiptap.dev/docs/editor/extensions/nodes/image
|
|
46
|
+
*/
|
|
47
|
+
image?: Partial<ImageOptions>;
|
|
48
|
+
/**
|
|
49
|
+
* The mention extension options to configure mention handling.
|
|
50
|
+
* @see https://tiptap.dev/docs/editor/extensions/nodes/mention
|
|
51
|
+
*/
|
|
52
|
+
mention?: Partial<MentionOptions>;
|
|
53
|
+
/**
|
|
54
|
+
* Custom item handlers to override or extend the default handlers.
|
|
55
|
+
* These handlers are provided to all child components (toolbar, suggestion menu, etc.).
|
|
56
|
+
*/
|
|
57
|
+
handlers?: H;
|
|
58
|
+
class?: any;
|
|
59
|
+
b24ui?: Editor['slots'];
|
|
60
|
+
}
|
|
61
|
+
export interface EditorEmits<T extends Content = Content> {
|
|
62
|
+
'update:modelValue': [value: T];
|
|
63
|
+
}
|
|
64
|
+
export interface EditorSlots<H extends EditorCustomHandlers = EditorCustomHandlers> {
|
|
65
|
+
default(props: {
|
|
66
|
+
editor: TiptapEditor;
|
|
67
|
+
handlers: EditorHandlers<H>;
|
|
68
|
+
}): any;
|
|
69
|
+
}
|
|
70
|
+
declare const __VLS_export: <T extends Content, H extends EditorCustomHandlers>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
71
|
+
props: __VLS_PrettifyLocal<EditorProps<T, H> & __VLS_EmitsToProps<__VLS_NormalizeEmits<(evt: "update:modelValue", value: T) => void>>> & import("vue").PublicProps & (typeof globalThis extends {
|
|
72
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
73
|
+
} ? P : {});
|
|
74
|
+
expose: (exposed: import("vue").ShallowUnwrapRef<{
|
|
75
|
+
editor: import("vue").ShallowRef<TiptapEditor | undefined, TiptapEditor | undefined>;
|
|
76
|
+
}>) => void;
|
|
77
|
+
attrs: any;
|
|
78
|
+
slots: EditorSlots<H>;
|
|
79
|
+
emit: (evt: "update:modelValue", value: T) => void;
|
|
80
|
+
}>) => import("vue").VNode & {
|
|
81
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
82
|
+
};
|
|
83
|
+
declare const _default: typeof __VLS_export;
|
|
84
|
+
export default _default;
|
|
85
|
+
type __VLS_PrettifyLocal<T> = {
|
|
86
|
+
[K in keyof T as K]: T[K];
|
|
87
|
+
} & {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { AppConfig } from '@nuxt/schema';
|
|
2
|
+
import type { Editor, JSONContent } from '@tiptap/vue-3';
|
|
3
|
+
import type { DragHandleProps } from '@tiptap/extension-drag-handle-vue-3';
|
|
4
|
+
import theme from '#build/b24ui/editor-drag-handle';
|
|
5
|
+
import type { ButtonProps, IconComponent, LinkPropsKeys } from '../types';
|
|
6
|
+
import type { FloatingUIOptions } from '../types/editor';
|
|
7
|
+
import type { ComponentConfig } from '../types/tv';
|
|
8
|
+
type EditorDragHandle = ComponentConfig<typeof theme, AppConfig, 'editorDragHandle'>;
|
|
9
|
+
export interface EditorDragHandleProps extends Omit<DragHandleProps, 'editor' | 'element' | 'onNodeChange' | 'computePositionConfig' | 'class'>, Omit<ButtonProps, LinkPropsKeys | 'icon' | 'color'> {
|
|
10
|
+
/**
|
|
11
|
+
* @defaultValue icons.drag
|
|
12
|
+
* @IconComponent
|
|
13
|
+
*/
|
|
14
|
+
icon?: IconComponent;
|
|
15
|
+
/**
|
|
16
|
+
* @defaultValue 'air-tertiary-no-accent'
|
|
17
|
+
*/
|
|
18
|
+
color?: ButtonProps['color'];
|
|
19
|
+
/**
|
|
20
|
+
* The options for positioning the drag handle. Those are passed to Floating UI and include options for the placement, offset, flip, shift, size, autoPlacement, hide, and inline middleware.
|
|
21
|
+
* @defaultValue { strategy: 'absolute', placement: 'left-start' }
|
|
22
|
+
* @see https://floating-ui.com/docs/computePosition#options
|
|
23
|
+
*/
|
|
24
|
+
options?: FloatingUIOptions;
|
|
25
|
+
editor: Editor;
|
|
26
|
+
b24ui?: EditorDragHandle['slots'] & ButtonProps['b24ui'];
|
|
27
|
+
}
|
|
28
|
+
export interface EditorDragHandleSlots {
|
|
29
|
+
default(props: {
|
|
30
|
+
b24ui: EditorDragHandle['b24ui'];
|
|
31
|
+
onClick: () => {
|
|
32
|
+
node: JSONContent;
|
|
33
|
+
pos: number;
|
|
34
|
+
} | undefined;
|
|
35
|
+
}): any;
|
|
36
|
+
}
|
|
37
|
+
export interface EditorDragHandleEmits {
|
|
38
|
+
nodeChange: [{
|
|
39
|
+
node: JSONContent;
|
|
40
|
+
pos: number;
|
|
41
|
+
}];
|
|
42
|
+
}
|
|
43
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<EditorDragHandleProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
44
|
+
nodeChange: (args_0: {
|
|
45
|
+
node: JSONContent;
|
|
46
|
+
pos: number;
|
|
47
|
+
}) => any;
|
|
48
|
+
}, string, import("vue").PublicProps, Readonly<EditorDragHandleProps> & Readonly<{
|
|
49
|
+
onNodeChange?: ((args_0: {
|
|
50
|
+
node: JSONContent;
|
|
51
|
+
pos: number;
|
|
52
|
+
}) => any) | undefined;
|
|
53
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, EditorDragHandleSlots>;
|
|
54
|
+
declare const _default: typeof __VLS_export;
|
|
55
|
+
export default _default;
|
|
56
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
57
|
+
new (): {
|
|
58
|
+
$slots: S;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import theme from "#build/b24ui/editor-drag-handle";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script setup>
|
|
6
|
+
import { computed, ref } from "vue";
|
|
7
|
+
import DragHandle from "@tiptap/extension-drag-handle-vue-3";
|
|
8
|
+
import { useForwardProps } from "reka-ui";
|
|
9
|
+
import { reactiveOmit, reactivePick } from "@vueuse/core";
|
|
10
|
+
import { defu } from "defu";
|
|
11
|
+
import { useAppConfig } from "#imports";
|
|
12
|
+
import { buildFloatingUIMiddleware } from "../utils/editor";
|
|
13
|
+
import { transformUI } from "../utils";
|
|
14
|
+
import { tv } from "../utils/tv";
|
|
15
|
+
import icons from "../dictionary/icons";
|
|
16
|
+
import B24Button from "./Button.vue";
|
|
17
|
+
defineOptions({ inheritAttrs: false });
|
|
18
|
+
const props = defineProps({
|
|
19
|
+
icon: { type: [Function, Object], required: false },
|
|
20
|
+
color: { type: null, required: false, default: "air-tertiary-no-accent" },
|
|
21
|
+
options: { type: Object, required: false },
|
|
22
|
+
editor: { type: Object, required: true },
|
|
23
|
+
b24ui: { type: void 0, required: false },
|
|
24
|
+
pluginKey: { type: [Object, String], required: false },
|
|
25
|
+
onElementDragStart: { type: Function, required: false },
|
|
26
|
+
onElementDragEnd: { type: Function, required: false },
|
|
27
|
+
getReferencedVirtualElement: { type: Function, required: false },
|
|
28
|
+
label: { type: String, required: false },
|
|
29
|
+
activeColor: { type: null, required: false },
|
|
30
|
+
depth: { type: null, required: false },
|
|
31
|
+
activeDepth: { type: null, required: false },
|
|
32
|
+
size: { type: null, required: false, default: "sm" },
|
|
33
|
+
rounded: { type: Boolean, required: false },
|
|
34
|
+
block: { type: Boolean, required: false },
|
|
35
|
+
loadingAuto: { type: Boolean, required: false },
|
|
36
|
+
normalCase: { type: Boolean, required: false },
|
|
37
|
+
useWait: { type: Boolean, required: false },
|
|
38
|
+
useClock: { type: Boolean, required: false },
|
|
39
|
+
useDropdown: { type: Boolean, required: false },
|
|
40
|
+
onClick: { type: [Function, Array], required: false },
|
|
41
|
+
class: { type: null, required: false },
|
|
42
|
+
avatar: { type: Object, required: false },
|
|
43
|
+
loading: { type: Boolean, required: false },
|
|
44
|
+
as: { type: null, required: false },
|
|
45
|
+
type: { type: null, required: false },
|
|
46
|
+
disabled: { type: Boolean, required: false },
|
|
47
|
+
isAction: { type: Boolean, required: false },
|
|
48
|
+
exactActiveClass: { type: String, required: false },
|
|
49
|
+
viewTransition: { type: Boolean, required: false }
|
|
50
|
+
});
|
|
51
|
+
defineSlots();
|
|
52
|
+
const emit = defineEmits(["nodeChange"]);
|
|
53
|
+
const dragHandleProps = useForwardProps(reactivePick(props, "pluginKey", "onElementDragEnd", "onElementDragStart", "getReferencedVirtualElement"));
|
|
54
|
+
const buttonProps = useForwardProps(reactiveOmit(props, "icon", "options", "editor", "pluginKey", "onElementDragEnd", "onElementDragStart", "getReferencedVirtualElement", "class", "b24ui"));
|
|
55
|
+
const appConfig = useAppConfig();
|
|
56
|
+
const b24ui = computed(() => tv({ extend: tv(theme), ...appConfig.b24ui?.editorDragHandle || {} })());
|
|
57
|
+
const floatingUIOptions = computed(() => defu(props.options, {
|
|
58
|
+
strategy: "absolute",
|
|
59
|
+
placement: "left-start",
|
|
60
|
+
offset: ({ rects }) => {
|
|
61
|
+
const blockHeight = rects.reference.height;
|
|
62
|
+
const handleHeight = rects.floating.height;
|
|
63
|
+
if (blockHeight > 40) {
|
|
64
|
+
return {
|
|
65
|
+
alignmentAxis: 0,
|
|
66
|
+
mainAxis: 8
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
alignmentAxis: (blockHeight - handleHeight) / 2,
|
|
71
|
+
mainAxis: 8
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
flip: {},
|
|
75
|
+
shift: {},
|
|
76
|
+
size: false,
|
|
77
|
+
autoPlacement: false,
|
|
78
|
+
hide: false,
|
|
79
|
+
inline: false
|
|
80
|
+
}));
|
|
81
|
+
const middleware = computed(() => buildFloatingUIMiddleware(floatingUIOptions.value));
|
|
82
|
+
const computePositionConfig = computed(() => ({
|
|
83
|
+
placement: floatingUIOptions.value.placement,
|
|
84
|
+
strategy: floatingUIOptions.value.strategy,
|
|
85
|
+
middleware: middleware.value
|
|
86
|
+
}));
|
|
87
|
+
const currentNodePos = ref();
|
|
88
|
+
function onNodeChange({ pos }) {
|
|
89
|
+
currentNodePos.value = pos;
|
|
90
|
+
}
|
|
91
|
+
function onClick() {
|
|
92
|
+
if (!props.editor) return;
|
|
93
|
+
const pos = currentNodePos.value;
|
|
94
|
+
if (pos == null) return;
|
|
95
|
+
const node = props.editor.state.doc.nodeAt(pos);
|
|
96
|
+
if (node) {
|
|
97
|
+
const selectedNode = { node: node.toJSON(), pos };
|
|
98
|
+
emit("nodeChange", selectedNode);
|
|
99
|
+
props.editor.chain().setNodeSelection(pos).run();
|
|
100
|
+
return selectedNode;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<template>
|
|
106
|
+
<DragHandle
|
|
107
|
+
v-bind="dragHandleProps"
|
|
108
|
+
:compute-position-config="computePositionConfig"
|
|
109
|
+
:editor="editor"
|
|
110
|
+
:on-node-change="onNodeChange"
|
|
111
|
+
data-slot="root"
|
|
112
|
+
:class="b24ui.root({ class: [props.b24ui?.root, props.class] })"
|
|
113
|
+
@click="onClick"
|
|
114
|
+
>
|
|
115
|
+
<slot :b24ui="b24ui" :on-click="onClick">
|
|
116
|
+
<B24Button
|
|
117
|
+
v-bind="{
|
|
118
|
+
...buttonProps,
|
|
119
|
+
icon: props.icon || icons.drag,
|
|
120
|
+
...$attrs
|
|
121
|
+
}"
|
|
122
|
+
data-slot="handle"
|
|
123
|
+
:class="b24ui.handle({ class: [props.b24ui?.handle, props.class] })"
|
|
124
|
+
:b24ui="transformUI(b24ui, props.b24ui)"
|
|
125
|
+
/>
|
|
126
|
+
</slot>
|
|
127
|
+
</DragHandle>
|
|
128
|
+
</template>
|