vuewrite 0.0.1 → 0.0.3
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.md +16 -9
- package/dist/vuewrite.d.ts +63 -9
- package/dist/vuewrite.js +84 -12
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -11,26 +11,33 @@ You can watch the demo [here](https://vuewrite.easix.ru)
|
|
|
11
11
|
|
|
12
12
|
```vue
|
|
13
13
|
<template>
|
|
14
|
-
<TextEditor
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
<TextEditor
|
|
15
|
+
ref="textEditorRef"
|
|
16
|
+
v-model="modelValue"
|
|
17
|
+
single
|
|
18
|
+
class="text-editor"
|
|
19
|
+
:decorator="decorator"
|
|
20
|
+
@keydown="onKeyDown"
|
|
21
|
+
/>
|
|
20
22
|
</template>
|
|
21
23
|
|
|
22
24
|
<script lang="ts">
|
|
25
|
+
import { TextEditor, TextEditorRef } from 'vuewrite'
|
|
26
|
+
|
|
27
|
+
const textEditorRef = shallowRef<TextEditorRef>()
|
|
28
|
+
const modelValue = ref("")
|
|
23
29
|
|
|
24
30
|
const onKeyDown = (e: KeyboardEvent) => {
|
|
31
|
+
if (!textEditorRef.value) return
|
|
25
32
|
if ((e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey) {
|
|
26
33
|
if (e.code === "KeyB") {
|
|
27
|
-
|
|
34
|
+
textEditorRef.value.toggleStyle("bold")
|
|
28
35
|
}
|
|
29
36
|
if (e.code === "KeyI") {
|
|
30
|
-
|
|
37
|
+
textEditorRef.value.toggleStyle("italic")
|
|
31
38
|
}
|
|
32
39
|
if (e.code === "KeyU") {
|
|
33
|
-
|
|
40
|
+
textEditorRef.value.toggleStyle("underline")
|
|
34
41
|
}
|
|
35
42
|
}
|
|
36
43
|
}
|
package/dist/vuewrite.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ComponentOptionsMixin } from 'vue';
|
|
2
|
+
import { ComputedRef } from 'vue';
|
|
2
3
|
import { DefineComponent } from 'vue';
|
|
3
4
|
import { ExtractPropTypes } from 'vue';
|
|
4
5
|
import { HTMLAttributes } from 'vue';
|
|
5
6
|
import { PropType } from 'vue';
|
|
6
7
|
import { PublicProps } from 'vue';
|
|
8
|
+
import { Ref } from 'vue';
|
|
7
9
|
|
|
8
10
|
declare type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
9
11
|
|
|
@@ -39,24 +41,64 @@ export declare type Style = {
|
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
export declare const TextEditor: __VLS_WithTemplateSlots<DefineComponent<__VLS_TypePropsToRuntimeProps<{
|
|
42
|
-
store: TextEditorStore;
|
|
43
|
-
placeholder?: string | undefined;
|
|
44
44
|
decorator?: Decorator | undefined;
|
|
45
|
+
single?: boolean | undefined;
|
|
46
|
+
modelValue?: string | string[] | undefined;
|
|
47
|
+
styles?: Style[] | Style[][] | undefined;
|
|
48
|
+
autofocus?: boolean | undefined;
|
|
45
49
|
}>, {
|
|
46
|
-
|
|
50
|
+
currentStyles: ComputedRef<Map<string, Style>>;
|
|
51
|
+
currentBlock: ComputedRef< {
|
|
52
|
+
id: string;
|
|
53
|
+
text: string;
|
|
54
|
+
type?: string | undefined;
|
|
55
|
+
styles: {
|
|
56
|
+
start: number;
|
|
57
|
+
end: number;
|
|
58
|
+
style: string;
|
|
59
|
+
meta?: any;
|
|
60
|
+
}[];
|
|
61
|
+
} | null>;
|
|
62
|
+
isCollapsed: ComputedRef<boolean>;
|
|
63
|
+
selection: {
|
|
64
|
+
anchor: {
|
|
65
|
+
blockId: string;
|
|
66
|
+
offset: number;
|
|
67
|
+
};
|
|
68
|
+
focus: {
|
|
69
|
+
blockId: string;
|
|
70
|
+
offset: number;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
isFocused: Ref<boolean>;
|
|
74
|
+
toggleStyle: (style: string) => void;
|
|
75
|
+
applyStyle: (_style: string, meta?: any) => void;
|
|
76
|
+
removeStyle: (_style: string) => void;
|
|
77
|
+
insertText: (data: string) => void;
|
|
78
|
+
selectAll: () => void;
|
|
47
79
|
}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
|
|
48
80
|
keydown: (...args: any[]) => void;
|
|
81
|
+
"update:modelValue": (...args: any[]) => void;
|
|
82
|
+
"update:styles": (...args: any[]) => void;
|
|
49
83
|
}, string, PublicProps, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{
|
|
50
|
-
store: TextEditorStore;
|
|
51
|
-
placeholder?: string | undefined;
|
|
52
84
|
decorator?: Decorator | undefined;
|
|
85
|
+
single?: boolean | undefined;
|
|
86
|
+
modelValue?: string | string[] | undefined;
|
|
87
|
+
styles?: Style[] | Style[][] | undefined;
|
|
88
|
+
autofocus?: boolean | undefined;
|
|
53
89
|
}>>> & {
|
|
54
90
|
onKeydown?: ((...args: any[]) => any) | undefined;
|
|
91
|
+
"onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
|
|
92
|
+
"onUpdate:styles"?: ((...args: any[]) => any) | undefined;
|
|
55
93
|
}, {}, {}>, {
|
|
56
94
|
placeholder?(_: {}): any;
|
|
57
95
|
}>;
|
|
58
96
|
|
|
59
|
-
export declare
|
|
97
|
+
export declare type TextEditorRef = Pick<TextEditorStore, "currentStyles" | "currentBlock" | "isCollapsed" | "selection" | "toggleStyle" | "applyStyle" | "removeStyle" | "insertText" | "selectAll"> & {
|
|
98
|
+
isFocused: boolean;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
declare class TextEditorStore {
|
|
60
102
|
blocks: {
|
|
61
103
|
id: string;
|
|
62
104
|
text: string;
|
|
@@ -78,9 +120,20 @@ export declare class TextEditorStore {
|
|
|
78
120
|
offset: number;
|
|
79
121
|
};
|
|
80
122
|
};
|
|
81
|
-
|
|
123
|
+
_isCollapsed: ComputedRef<boolean>;
|
|
82
124
|
get isCollapsed(): boolean;
|
|
83
|
-
|
|
125
|
+
isFocused: Ref<boolean>;
|
|
126
|
+
_currentBlock: ComputedRef< {
|
|
127
|
+
id: string;
|
|
128
|
+
text: string;
|
|
129
|
+
type?: string | undefined;
|
|
130
|
+
styles: {
|
|
131
|
+
start: number;
|
|
132
|
+
end: number;
|
|
133
|
+
style: string;
|
|
134
|
+
meta?: any;
|
|
135
|
+
}[];
|
|
136
|
+
} | null>;
|
|
84
137
|
get currentBlock(): {
|
|
85
138
|
id: string;
|
|
86
139
|
text: string;
|
|
@@ -105,13 +158,14 @@ export declare class TextEditorStore {
|
|
|
105
158
|
}, number, number];
|
|
106
159
|
private moveStyles;
|
|
107
160
|
deleteSelected(): void;
|
|
108
|
-
|
|
161
|
+
_currentStyles: ComputedRef<Map<string, Style>>;
|
|
109
162
|
get currentStyles(): Map<string, Style>;
|
|
110
163
|
applyStyle(_style: string, meta?: any): void;
|
|
111
164
|
removeStyleAt(block: Block, _start: number, _end: number, _style?: string): void;
|
|
112
165
|
removeStyle(_style: string): void;
|
|
113
166
|
removeAllStyles(): void;
|
|
114
167
|
toggleStyle(style: string): void;
|
|
168
|
+
selectAll(): void;
|
|
115
169
|
}
|
|
116
170
|
|
|
117
171
|
export { }
|
package/dist/vuewrite.js
CHANGED
|
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
-
import { getCurrentScope, onScopeDispose, unref, watch, reactive, computed, defineComponent, getCurrentInstance, openBlock, createBlock, resolveDynamicComponent, createElementBlock, normalizeProps, mergeProps, h, nextTick, useSlots,
|
|
7
|
+
import { getCurrentScope, onScopeDispose, unref, watch, reactive, computed, ref, defineComponent, getCurrentInstance, openBlock, createBlock, resolveDynamicComponent, createElementBlock, normalizeProps, mergeProps, h, nextTick, useSlots, onMounted, Fragment, renderList, renderSlot, createCommentVNode } from "vue";
|
|
8
8
|
function tryOnScopeDispose(fn) {
|
|
9
9
|
if (getCurrentScope()) {
|
|
10
10
|
onScopeDispose(fn);
|
|
@@ -150,6 +150,7 @@ class TextEditorStore {
|
|
|
150
150
|
__publicField(this, "_isCollapsed", computed(() => {
|
|
151
151
|
return this.selection.anchor.blockId === this.selection.focus.blockId && this.selection.anchor.offset === this.selection.focus.offset;
|
|
152
152
|
}));
|
|
153
|
+
__publicField(this, "isFocused", ref(false));
|
|
153
154
|
__publicField(this, "_currentBlock", computed(() => {
|
|
154
155
|
if (this.selection.anchor.blockId !== this.selection.focus.blockId)
|
|
155
156
|
return null;
|
|
@@ -158,6 +159,8 @@ class TextEditorStore {
|
|
|
158
159
|
__publicField(this, "_currentStyles", computed(() => {
|
|
159
160
|
const [start, end, startIndex, endIndex] = this.startAndEnd;
|
|
160
161
|
const styles = /* @__PURE__ */ new Map();
|
|
162
|
+
if (startIndex < 0)
|
|
163
|
+
return styles;
|
|
161
164
|
for (let i = startIndex; i <= endIndex; i++) {
|
|
162
165
|
for (let style of this.blocks[i].styles) {
|
|
163
166
|
if (i === startIndex && start.offset < style.start)
|
|
@@ -372,6 +375,11 @@ class TextEditorStore {
|
|
|
372
375
|
this.applyStyle(style);
|
|
373
376
|
}
|
|
374
377
|
}
|
|
378
|
+
selectAll() {
|
|
379
|
+
this.selection.anchor = { blockId: this.blocks[0].id, offset: 0 };
|
|
380
|
+
const lastBlock = this.blocks[this.blocks.length - 1];
|
|
381
|
+
this.selection.focus = { blockId: lastBlock.id, offset: lastBlock.text.length };
|
|
382
|
+
}
|
|
375
383
|
}
|
|
376
384
|
let uidCounter = 0;
|
|
377
385
|
const uid = () => (uidCounter++).toString();
|
|
@@ -516,24 +524,65 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
|
|
|
516
524
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
517
525
|
__name: "TextEditor",
|
|
518
526
|
props: {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
527
|
+
decorator: { type: Function },
|
|
528
|
+
single: { type: Boolean },
|
|
529
|
+
modelValue: {},
|
|
530
|
+
styles: {},
|
|
531
|
+
autofocus: { type: Boolean }
|
|
522
532
|
},
|
|
523
|
-
emits: ["keydown"],
|
|
533
|
+
emits: ["keydown", "update:modelValue", "update:styles"],
|
|
524
534
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
525
535
|
const props = __props;
|
|
526
536
|
const emit = __emit;
|
|
527
537
|
const slots = useSlots();
|
|
528
538
|
const textEditorRef = ref();
|
|
529
|
-
const store =
|
|
539
|
+
const store = new TextEditorStore();
|
|
540
|
+
let modelValue = "";
|
|
541
|
+
watch(() => props.modelValue, (newValue) => {
|
|
542
|
+
if (newValue === void 0 || newValue === null || newValue === modelValue)
|
|
543
|
+
return;
|
|
544
|
+
if (!Array.isArray(newValue)) {
|
|
545
|
+
store.blocks[0].text = newValue;
|
|
546
|
+
return;
|
|
547
|
+
} else {
|
|
548
|
+
for (let i = store.blocks.length; i < newValue.length; i++) {
|
|
549
|
+
store.blocks.push({ id: uid(), text: "", styles: [] });
|
|
550
|
+
}
|
|
551
|
+
store.blocks.length = newValue.length;
|
|
552
|
+
for (let i = 0; i < newValue.length; i++) {
|
|
553
|
+
store.blocks[i].text = newValue[i];
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}, { immediate: true });
|
|
557
|
+
let styles;
|
|
558
|
+
watch(() => props.styles, (newStyles) => {
|
|
559
|
+
if (!newStyles || newStyles.length === 0 || newStyles === styles)
|
|
560
|
+
return;
|
|
561
|
+
for (let i = 0; i < store.blocks.length; i++) {
|
|
562
|
+
if (newStyles.length <= i)
|
|
563
|
+
break;
|
|
564
|
+
const style = props.single ? newStyles : newStyles[i];
|
|
565
|
+
store.blocks[i].styles = style;
|
|
566
|
+
}
|
|
567
|
+
}, { immediate: true });
|
|
568
|
+
watch(() => store.blocks, () => {
|
|
569
|
+
if (props.single) {
|
|
570
|
+
modelValue = store.blocks[0].text;
|
|
571
|
+
styles = store.blocks[0].styles;
|
|
572
|
+
} else {
|
|
573
|
+
modelValue = store.blocks.map((item) => item.text);
|
|
574
|
+
styles = store.blocks.map((item) => item.styles);
|
|
575
|
+
}
|
|
576
|
+
emit("update:modelValue", modelValue);
|
|
577
|
+
emit("update:styles", styles);
|
|
578
|
+
}, { deep: true });
|
|
530
579
|
const onKeyDown = (e) => {
|
|
531
580
|
emit("keydown", e);
|
|
532
581
|
if (e.defaultPrevented)
|
|
533
582
|
return;
|
|
534
583
|
if (e.code === "Enter") {
|
|
535
584
|
e.preventDefault();
|
|
536
|
-
if (e.shiftKey) {
|
|
585
|
+
if (e.shiftKey || props.single) {
|
|
537
586
|
store.insertText("\n");
|
|
538
587
|
} else {
|
|
539
588
|
store.addNewLine();
|
|
@@ -563,7 +612,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
563
612
|
const offset = anchor === sel.focusNode ? 0 : calcOffsetToNode(focus, sel.focusNode) + sel.focusOffset;
|
|
564
613
|
store.selection.focus = { blockId: focus.getAttribute("data-block-id"), offset };
|
|
565
614
|
}
|
|
566
|
-
|
|
615
|
+
if (store.isFocused.value !== !!focus || !!anchor) {
|
|
616
|
+
store.isFocused.value = !!focus || !!anchor;
|
|
617
|
+
}
|
|
618
|
+
if (!anchor && !focus)
|
|
619
|
+
cachedSelection = JSON.parse(JSON.stringify(store.selection));
|
|
567
620
|
});
|
|
568
621
|
let postRendered = false;
|
|
569
622
|
const onPostRender = () => {
|
|
@@ -577,6 +630,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
577
630
|
});
|
|
578
631
|
};
|
|
579
632
|
const applySelection = () => {
|
|
633
|
+
if (!store.isFocused.value) {
|
|
634
|
+
cachedSelection = JSON.parse(JSON.stringify(store.selection));
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
580
637
|
if (isEqual(store.selection, cachedSelection))
|
|
581
638
|
return;
|
|
582
639
|
const nativeSelection = window.getSelection();
|
|
@@ -599,8 +656,24 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
599
656
|
cachedSelection = JSON.parse(JSON.stringify(store.selection));
|
|
600
657
|
};
|
|
601
658
|
watch(() => store.selection, applySelection, { deep: true, flush: "post" });
|
|
659
|
+
onMounted(() => {
|
|
660
|
+
var _a;
|
|
661
|
+
if (props.autofocus) {
|
|
662
|
+
(_a = textEditorRef.value) == null ? void 0 : _a.focus();
|
|
663
|
+
applySelection();
|
|
664
|
+
}
|
|
665
|
+
});
|
|
602
666
|
__expose({
|
|
603
|
-
store
|
|
667
|
+
currentStyles: store._currentStyles,
|
|
668
|
+
currentBlock: store._currentBlock,
|
|
669
|
+
isCollapsed: store._isCollapsed,
|
|
670
|
+
selection: store.selection,
|
|
671
|
+
isFocused: store.isFocused,
|
|
672
|
+
toggleStyle: store.toggleStyle.bind(store),
|
|
673
|
+
applyStyle: store.applyStyle.bind(store),
|
|
674
|
+
removeStyle: store.removeStyle.bind(store),
|
|
675
|
+
insertText: store.insertText.bind(store),
|
|
676
|
+
selectAll: store.selectAll.bind(store)
|
|
604
677
|
});
|
|
605
678
|
return (_ctx, _cache) => {
|
|
606
679
|
return openBlock(), createElementBlock("div", {
|
|
@@ -616,7 +689,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
616
689
|
key: block.id,
|
|
617
690
|
block,
|
|
618
691
|
slots: unref(slots),
|
|
619
|
-
decorator:
|
|
692
|
+
decorator: props.decorator,
|
|
620
693
|
onPostrender: onPostRender
|
|
621
694
|
}, null, 8, ["block", "slots", "decorator"]);
|
|
622
695
|
}), 128)),
|
|
@@ -626,6 +699,5 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
626
699
|
}
|
|
627
700
|
});
|
|
628
701
|
export {
|
|
629
|
-
_sfc_main as TextEditor
|
|
630
|
-
TextEditorStore
|
|
702
|
+
_sfc_main as TextEditor
|
|
631
703
|
};
|
package/package.json
CHANGED