vuewrite 0.0.5 → 0.0.7

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.
@@ -43,9 +43,14 @@ export declare type Style = {
43
43
  export declare const TextEditor: __VLS_WithTemplateSlots<DefineComponent<__VLS_TypePropsToRuntimeProps<{
44
44
  decorator?: Decorator | undefined;
45
45
  single?: boolean | undefined;
46
- modelValue?: string | string[] | undefined;
47
- styles?: Style[] | Style[][] | undefined;
46
+ modelValue?: string | {
47
+ text: string;
48
+ styles?: Style[] | undefined;
49
+ type?: string | undefined;
50
+ }[] | undefined;
51
+ styles?: Style[] | undefined;
48
52
  autofocus?: boolean | undefined;
53
+ autoselect?: boolean | undefined;
49
54
  }>, {
50
55
  currentStyles: ComputedRef<Map<string, Style>>;
51
56
  currentBlock: ComputedRef< {
@@ -75,7 +80,11 @@ toggleStyle: (style: string) => void;
75
80
  applyStyle: (_style: string, meta?: any) => void;
76
81
  removeStyle: (_style: string) => void;
77
82
  insertText: (data: string) => void;
83
+ insertBlock: (blockData: Partial<Block>) => void;
84
+ addNewLine: () => void;
85
+ removeNewLine: () => void;
78
86
  selectAll: () => void;
87
+ getClientRects: (selection: TextEditorSelection) => DOMRectList;
79
88
  }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
80
89
  keydown: (...args: any[]) => void;
81
90
  "update:modelValue": (...args: any[]) => void;
@@ -83,9 +92,14 @@ keydown: (...args: any[]) => void;
83
92
  }, string, PublicProps, Readonly<ExtractPropTypes<__VLS_TypePropsToRuntimeProps<{
84
93
  decorator?: Decorator | undefined;
85
94
  single?: boolean | undefined;
86
- modelValue?: string | string[] | undefined;
87
- styles?: Style[] | Style[][] | undefined;
95
+ modelValue?: string | {
96
+ text: string;
97
+ styles?: Style[] | undefined;
98
+ type?: string | undefined;
99
+ }[] | undefined;
100
+ styles?: Style[] | undefined;
88
101
  autofocus?: boolean | undefined;
102
+ autoselect?: boolean | undefined;
89
103
  }>>> & {
90
104
  onKeydown?: ((...args: any[]) => any) | undefined;
91
105
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
@@ -94,8 +108,20 @@ onKeydown?: ((...args: any[]) => any) | undefined;
94
108
  placeholder?(_: {}): any;
95
109
  }>;
96
110
 
97
- export declare type TextEditorRef = Pick<TextEditorStore, "currentStyles" | "currentBlock" | "isCollapsed" | "selection" | "toggleStyle" | "applyStyle" | "removeStyle" | "insertText" | "selectAll"> & {
111
+ export declare type TextEditorRef = Pick<TextEditorStore, "currentStyles" | "currentBlock" | "isCollapsed" | "selection" | "toggleStyle" | "applyStyle" | "removeStyle" | "insertText" | "insertBlock" | "addNewLine" | "removeNewLine" | "selectAll"> & {
98
112
  isFocused: boolean;
113
+ getClientRects: (selection: TextEditorSelection) => DOMRectList;
114
+ };
115
+
116
+ declare type TextEditorSelection = {
117
+ anchor: {
118
+ blockId: string;
119
+ offset: number;
120
+ };
121
+ focus: {
122
+ blockId: string;
123
+ offset: number;
124
+ };
99
125
  };
100
126
 
101
127
  declare class TextEditorStore {
@@ -148,9 +174,11 @@ declare class TextEditorStore {
148
174
  _selectedText: ComputedRef<string>;
149
175
  get selectedText(): string;
150
176
  moveOffset(newOffset: number): void;
177
+ removeNewLine(): void;
151
178
  onInput(_e: Event): void;
152
179
  addNewLine(): void;
153
180
  insertText(data: string): void;
181
+ insertBlock(blockData: Partial<Block>): void;
154
182
  get startAndEnd(): [{
155
183
  blockId: string;
156
184
  offset: number;
package/dist/vuewrite.js CHANGED
@@ -202,6 +202,16 @@ class TextEditorStore {
202
202
  this.selection.anchor.offset = newOffset;
203
203
  this.selection.focus.offset = newOffset;
204
204
  }
205
+ removeNewLine() {
206
+ const blockIndex = this.blocks.findIndex((item) => item.id === this.selection.anchor.blockId);
207
+ if (blockIndex < 1)
208
+ return;
209
+ this.selection.anchor.blockId = this.blocks[blockIndex - 1].id;
210
+ this.selection.focus.blockId = this.blocks[blockIndex - 1].id;
211
+ this.selection.anchor.offset = this.blocks[blockIndex - 1].text.length;
212
+ this.selection.focus.offset = this.blocks[blockIndex - 1].text.length;
213
+ this.blocks.splice(blockIndex, 1);
214
+ }
205
215
  onInput(_e) {
206
216
  const ev = _e;
207
217
  ev.preventDefault();
@@ -212,16 +222,9 @@ class TextEditorStore {
212
222
  if (!block)
213
223
  return;
214
224
  if ((ev.inputType === "deleteContentBackward" || ev.inputType === "deleteContentForward") && collapsed) {
215
- const blockIndex = this.blocks.findIndex((item) => item.id === this.selection.anchor.blockId);
216
225
  if (ev.inputType === "deleteContentBackward") {
217
226
  if (this.selection.anchor.offset === 0) {
218
- if (blockIndex > 0) {
219
- this.selection.anchor.blockId = this.blocks[blockIndex - 1].id;
220
- this.selection.focus.blockId = this.blocks[blockIndex - 1].id;
221
- this.selection.anchor.offset = this.blocks[blockIndex - 1].text.length;
222
- this.selection.focus.offset = this.blocks[blockIndex - 1].text.length;
223
- this.blocks.splice(blockIndex, 1);
224
- }
227
+ this.removeNewLine();
225
228
  } else {
226
229
  const offset = Math.max(0, this.selection.focus.offset - 1);
227
230
  block.text = block.text.slice(0, offset) + block.text.slice(this.selection.focus.offset);
@@ -229,6 +232,7 @@ class TextEditorStore {
229
232
  }
230
233
  }
231
234
  if (ev.inputType === "deleteContentForward") {
235
+ const blockIndex = this.blocks.findIndex((item) => item.id === this.selection.anchor.blockId);
232
236
  if (this.selection.anchor.offset === this.blocks[blockIndex].text.length) {
233
237
  this.blocks.splice(blockIndex + 1, 1);
234
238
  } else {
@@ -260,6 +264,20 @@ class TextEditorStore {
260
264
  block.text = block.text.slice(0, this.selection.focus.offset) + data + block.text.slice(this.selection.focus.offset);
261
265
  this.moveOffset(clamp(this.selection.focus.offset + data.length, 0, block.text.length));
262
266
  }
267
+ insertBlock(blockData) {
268
+ if (!this.currentBlock)
269
+ return;
270
+ this.deleteSelected();
271
+ if (this.currentBlock.text !== "") {
272
+ this.addNewLine();
273
+ }
274
+ if (this.currentBlock.text !== "") {
275
+ const selection = JSON.parse(JSON.stringify(this.selection));
276
+ this.addNewLine();
277
+ Object.assign(this.selection, selection);
278
+ }
279
+ Object.assign(this.currentBlock, blockData);
280
+ }
263
281
  get startAndEnd() {
264
282
  const a = this.blocks.findIndex((block) => block.id === this.selection.anchor.blockId);
265
283
  const f = this.blocks.findIndex((block) => block.id === this.selection.focus.blockId);
@@ -415,7 +433,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
415
433
  return props.slots[props.block.type] ?? null;
416
434
  });
417
435
  const blockProps = {
418
- "data-block-id": props.block.id
436
+ "data-vw-block-id": props.block.id
419
437
  };
420
438
  const renderBlockPart = (text, styles) => {
421
439
  if (!props.decorator)
@@ -543,7 +561,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
543
561
  single: { type: Boolean },
544
562
  modelValue: {},
545
563
  styles: {},
546
- autofocus: { type: Boolean }
564
+ autofocus: { type: Boolean },
565
+ autoselect: { type: Boolean }
547
566
  },
548
567
  emits: ["keydown", "update:modelValue", "update:styles"],
549
568
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -565,7 +584,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
565
584
  }
566
585
  store.blocks.length = newValue.length;
567
586
  for (let i = 0; i < newValue.length; i++) {
568
- store.blocks[i].text = newValue[i];
587
+ store.blocks[i].text = newValue[i].text;
588
+ store.blocks[i].type = newValue[i].type;
589
+ store.blocks[i].text = newValue[i].text;
569
590
  }
570
591
  }
571
592
  }, { immediate: true });
@@ -576,20 +597,23 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
576
597
  for (let i = 0; i < store.blocks.length; i++) {
577
598
  if (newStyles.length <= i)
578
599
  break;
579
- const style = props.single ? newStyles : newStyles[i];
580
- store.blocks[i].styles = style;
600
+ store.blocks[i].styles = newStyles;
581
601
  }
582
602
  }, { immediate: true });
583
603
  watch(() => store.blocks, () => {
584
604
  if (props.single) {
585
605
  modelValue = store.blocks[0].text;
586
606
  styles = store.blocks[0].styles;
607
+ emit("update:styles", styles);
608
+ emit("update:modelValue", modelValue);
587
609
  } else {
588
- modelValue = store.blocks.map((item) => item.text);
589
- styles = store.blocks.map((item) => item.styles);
610
+ modelValue = store.blocks.map((item) => ({
611
+ type: item.type,
612
+ text: item.text,
613
+ styles: item.styles.length === 0 ? void 0 : item.styles
614
+ }));
615
+ emit("update:modelValue", modelValue);
590
616
  }
591
- emit("update:modelValue", modelValue);
592
- emit("update:styles", styles);
593
617
  }, { deep: true });
594
618
  const onKeyDown = (e) => {
595
619
  emit("keydown", e);
@@ -604,8 +628,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
604
628
  }
605
629
  }
606
630
  if (e.code === "Backspace") {
607
- if (store.isCollapsed && store.currentBlock === store.blocks[0] && store.selection.anchor.offset === 0) {
631
+ if (store.isCollapsed && store.selection.anchor.offset === 0) {
608
632
  e.preventDefault();
633
+ if (store.currentBlock && store.currentBlock.type) {
634
+ delete store.currentBlock.type;
635
+ } else {
636
+ store.removeNewLine();
637
+ }
609
638
  }
610
639
  }
611
640
  if ((e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey) {
@@ -617,15 +646,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
617
646
  let cachedSelection = {};
618
647
  useEventListener(document, "selectionchange", () => {
619
648
  const sel = window.getSelection();
620
- const anchor = findParent(sel.anchorNode, (el) => el.hasAttribute("data-block-id"));
649
+ const anchor = findParent(sel.anchorNode, (el) => el.hasAttribute("data-vw-block-id"));
621
650
  if (anchor) {
622
651
  const offset = anchor === sel.anchorNode ? 0 : calcOffsetToNode(anchor, sel.anchorNode) + sel.anchorOffset;
623
- store.selection.anchor = { blockId: anchor.getAttribute("data-block-id"), offset };
652
+ store.selection.anchor = { blockId: anchor.getAttribute("data-vw-block-id"), offset };
624
653
  }
625
- const focus = findParent(sel.focusNode, (el) => el.hasAttribute("data-block-id"));
654
+ const focus = findParent(sel.focusNode, (el) => el.hasAttribute("data-vw-block-id"));
626
655
  if (focus) {
627
656
  const offset = anchor === sel.focusNode ? 0 : calcOffsetToNode(focus, sel.focusNode) + sel.focusOffset;
628
- store.selection.focus = { blockId: focus.getAttribute("data-block-id"), offset };
657
+ store.selection.focus = { blockId: focus.getAttribute("data-vw-block-id"), offset };
629
658
  }
630
659
  if (store.isFocused.value !== !!focus || !!anchor) {
631
660
  store.isFocused.value = !!focus || !!anchor;
@@ -644,6 +673,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
644
673
  postRendered = false;
645
674
  });
646
675
  };
676
+ const getNode = (blockId) => {
677
+ for (let item of textEditorRef.value.children) {
678
+ if (item.getAttribute("data-vw-block-id") === blockId) {
679
+ return item;
680
+ }
681
+ }
682
+ return null;
683
+ };
647
684
  const applySelection = () => {
648
685
  if (!store.isFocused.value) {
649
686
  cachedSelection = JSON.parse(JSON.stringify(store.selection));
@@ -651,17 +688,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
651
688
  }
652
689
  if (isEqual(store.selection, cachedSelection))
653
690
  return;
691
+ const anchor = getNode(store.selection.anchor.blockId);
692
+ const focus = getNode(store.selection.focus.blockId);
654
693
  const nativeSelection = window.getSelection();
655
- let anchor = null;
656
- let focus = null;
657
- for (let item of textEditorRef.value.children) {
658
- if (item.getAttribute("data-block-id") === store.selection.anchor.blockId) {
659
- anchor = item;
660
- }
661
- if (item.getAttribute("data-block-id") === store.selection.focus.blockId) {
662
- focus = item;
663
- }
664
- }
665
694
  if (anchor && focus) {
666
695
  nativeSelection.setBaseAndExtent(
667
696
  ...calcNodeByOffset(anchor, store.selection.anchor.offset),
@@ -672,11 +701,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
672
701
  };
673
702
  watch(() => store.selection, applySelection, { deep: true, flush: "post" });
674
703
  onMounted(() => {
675
- var _a;
704
+ var _a, _b;
676
705
  if (props.autofocus) {
677
706
  (_a = textEditorRef.value) == null ? void 0 : _a.focus();
678
707
  applySelection();
679
708
  }
709
+ if (props.autoselect) {
710
+ (_b = textEditorRef.value) == null ? void 0 : _b.focus();
711
+ store.selectAll();
712
+ }
680
713
  });
681
714
  const onCopy = (e) => {
682
715
  e.preventDefault();
@@ -695,6 +728,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
695
728
  return;
696
729
  store.insertText(text);
697
730
  };
731
+ const getClientRects = (selection) => {
732
+ const anchor = getNode(selection.anchor.blockId);
733
+ const focus = getNode(selection.focus.blockId);
734
+ if (anchor && focus) {
735
+ const range = new Range();
736
+ range.setStart(...calcNodeByOffset(anchor, selection.anchor.offset));
737
+ range.setEnd(...calcNodeByOffset(focus, selection.focus.offset));
738
+ return range.getClientRects();
739
+ }
740
+ return new DOMRectList();
741
+ };
698
742
  __expose({
699
743
  currentStyles: store._currentStyles,
700
744
  currentBlock: store._currentBlock,
@@ -705,7 +749,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
705
749
  applyStyle: store.applyStyle.bind(store),
706
750
  removeStyle: store.removeStyle.bind(store),
707
751
  insertText: store.insertText.bind(store),
708
- selectAll: store.selectAll.bind(store)
752
+ insertBlock: store.insertBlock.bind(store),
753
+ addNewLine: store.addNewLine.bind(store),
754
+ removeNewLine: store.removeNewLine.bind(store),
755
+ selectAll: store.selectAll.bind(store),
756
+ getClientRects
709
757
  });
710
758
  return (_ctx, _cache) => {
711
759
  return openBlock(), createElementBlock("div", {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vuewrite",
3
3
  "description": "Rich Text Editor based on Vue3 reactivity",
4
4
  "private": false,
5
- "version": "0.0.5",
5
+ "version": "0.0.7",
6
6
  "type": "module",
7
7
  "license": "MIT",
8
8
  "author": "den59k",