vuewrite 0.0.2 → 0.0.4

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.
@@ -5,6 +5,7 @@ import { ExtractPropTypes } from 'vue';
5
5
  import { HTMLAttributes } from 'vue';
6
6
  import { PropType } from 'vue';
7
7
  import { PublicProps } from 'vue';
8
+ import { Ref } from 'vue';
8
9
 
9
10
  declare type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
10
11
 
@@ -44,6 +45,7 @@ decorator?: Decorator | undefined;
44
45
  single?: boolean | undefined;
45
46
  modelValue?: string | string[] | undefined;
46
47
  styles?: Style[] | Style[][] | undefined;
48
+ autofocus?: boolean | undefined;
47
49
  }>, {
48
50
  currentStyles: ComputedRef<Map<string, Style>>;
49
51
  currentBlock: ComputedRef< {
@@ -68,10 +70,12 @@ blockId: string;
68
70
  offset: number;
69
71
  };
70
72
  };
73
+ isFocused: Ref<boolean>;
71
74
  toggleStyle: (style: string) => void;
72
75
  applyStyle: (_style: string, meta?: any) => void;
73
76
  removeStyle: (_style: string) => void;
74
77
  insertText: (data: string) => void;
78
+ selectAll: () => void;
75
79
  }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {
76
80
  keydown: (...args: any[]) => void;
77
81
  "update:modelValue": (...args: any[]) => void;
@@ -81,6 +85,7 @@ decorator?: Decorator | undefined;
81
85
  single?: boolean | undefined;
82
86
  modelValue?: string | string[] | undefined;
83
87
  styles?: Style[] | Style[][] | undefined;
88
+ autofocus?: boolean | undefined;
84
89
  }>>> & {
85
90
  onKeydown?: ((...args: any[]) => any) | undefined;
86
91
  "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
@@ -89,7 +94,9 @@ onKeydown?: ((...args: any[]) => any) | undefined;
89
94
  placeholder?(_: {}): any;
90
95
  }>;
91
96
 
92
- export declare type TextEditorRef = Pick<TextEditorStore, "currentStyles" | "currentBlock" | "isCollapsed" | "selection" | "toggleStyle" | "applyStyle" | "removeStyle" | "insertText">;
97
+ export declare type TextEditorRef = Pick<TextEditorStore, "currentStyles" | "currentBlock" | "isCollapsed" | "selection" | "toggleStyle" | "applyStyle" | "removeStyle" | "insertText" | "selectAll"> & {
98
+ isFocused: boolean;
99
+ };
93
100
 
94
101
  declare class TextEditorStore {
95
102
  blocks: {
@@ -115,6 +122,7 @@ declare class TextEditorStore {
115
122
  };
116
123
  _isCollapsed: ComputedRef<boolean>;
117
124
  get isCollapsed(): boolean;
125
+ isFocused: Ref<boolean>;
118
126
  _currentBlock: ComputedRef< {
119
127
  id: string;
120
128
  text: string;
@@ -137,6 +145,8 @@ declare class TextEditorStore {
137
145
  meta?: any;
138
146
  }[];
139
147
  } | null;
148
+ _selectedText: ComputedRef<string>;
149
+ get selectedText(): string;
140
150
  moveOffset(newOffset: number): void;
141
151
  onInput(_e: Event): void;
142
152
  addNewLine(): void;
@@ -157,6 +167,7 @@ declare class TextEditorStore {
157
167
  removeStyle(_style: string): void;
158
168
  removeAllStyles(): void;
159
169
  toggleStyle(style: string): void;
170
+ selectAll(): void;
160
171
  }
161
172
 
162
173
  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, ref, Fragment, renderList, renderSlot, createCommentVNode } from "vue";
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,11 +150,24 @@ 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;
156
157
  return this.blocks.find((item) => item.id === this.selection.anchor.blockId) ?? null;
157
158
  }));
159
+ __publicField(this, "_selectedText", computed(() => {
160
+ if (this.isCollapsed)
161
+ return "";
162
+ const [start, end, startIndex, endIndex] = this.startAndEnd;
163
+ if (startIndex === endIndex) {
164
+ return this.blocks[startIndex].text.slice(start.offset, end.offset);
165
+ }
166
+ const startText = this.blocks[startIndex].text.slice(start.offset);
167
+ const endText = this.blocks[endIndex].text.slice(0, end.offset);
168
+ const arr = [startText, ...this.blocks.slice(startIndex + 1, endIndex).map((block) => block.text), endText];
169
+ return arr.join("\n");
170
+ }));
158
171
  __publicField(this, "_currentStyles", computed(() => {
159
172
  const [start, end, startIndex, endIndex] = this.startAndEnd;
160
173
  const styles = /* @__PURE__ */ new Map();
@@ -178,6 +191,9 @@ class TextEditorStore {
178
191
  get currentBlock() {
179
192
  return this._currentBlock.value;
180
193
  }
194
+ get selectedText() {
195
+ return this._selectedText.value;
196
+ }
181
197
  moveOffset(newOffset) {
182
198
  const delta = newOffset - this.selection.anchor.offset;
183
199
  if (delta === 0)
@@ -374,6 +390,11 @@ class TextEditorStore {
374
390
  this.applyStyle(style);
375
391
  }
376
392
  }
393
+ selectAll() {
394
+ this.selection.anchor = { blockId: this.blocks[0].id, offset: 0 };
395
+ const lastBlock = this.blocks[this.blocks.length - 1];
396
+ this.selection.focus = { blockId: lastBlock.id, offset: lastBlock.text.length };
397
+ }
377
398
  }
378
399
  let uidCounter = 0;
379
400
  const uid = () => (uidCounter++).toString();
@@ -521,7 +542,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
521
542
  decorator: { type: Function },
522
543
  single: { type: Boolean },
523
544
  modelValue: {},
524
- styles: {}
545
+ styles: {},
546
+ autofocus: { type: Boolean }
525
547
  },
526
548
  emits: ["keydown", "update:modelValue", "update:styles"],
527
549
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -605,7 +627,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
605
627
  const offset = anchor === sel.focusNode ? 0 : calcOffsetToNode(focus, sel.focusNode) + sel.focusOffset;
606
628
  store.selection.focus = { blockId: focus.getAttribute("data-block-id"), offset };
607
629
  }
608
- cachedSelection = JSON.parse(JSON.stringify(store.selection));
630
+ if (store.isFocused.value !== !!focus || !!anchor) {
631
+ store.isFocused.value = !!focus || !!anchor;
632
+ }
633
+ if (!anchor && !focus)
634
+ cachedSelection = JSON.parse(JSON.stringify(store.selection));
609
635
  });
610
636
  let postRendered = false;
611
637
  const onPostRender = () => {
@@ -619,6 +645,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
619
645
  });
620
646
  };
621
647
  const applySelection = () => {
648
+ if (!store.isFocused.value) {
649
+ cachedSelection = JSON.parse(JSON.stringify(store.selection));
650
+ return;
651
+ }
622
652
  if (isEqual(store.selection, cachedSelection))
623
653
  return;
624
654
  const nativeSelection = window.getSelection();
@@ -641,15 +671,41 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
641
671
  cachedSelection = JSON.parse(JSON.stringify(store.selection));
642
672
  };
643
673
  watch(() => store.selection, applySelection, { deep: true, flush: "post" });
674
+ onMounted(() => {
675
+ var _a;
676
+ if (props.autofocus) {
677
+ (_a = textEditorRef.value) == null ? void 0 : _a.focus();
678
+ applySelection();
679
+ }
680
+ });
681
+ const onCopy = (e) => {
682
+ e.preventDefault();
683
+ navigator.clipboard.writeText(store.selectedText);
684
+ };
685
+ const onCut = (e) => {
686
+ e.preventDefault();
687
+ navigator.clipboard.writeText(store.selectedText);
688
+ store.deleteSelected();
689
+ };
690
+ const onPaste = (e) => {
691
+ var _a;
692
+ e.preventDefault();
693
+ const text = (_a = e.clipboardData) == null ? void 0 : _a.getData("Text");
694
+ if (!text)
695
+ return;
696
+ store.insertText(text);
697
+ };
644
698
  __expose({
645
699
  currentStyles: store._currentStyles,
646
700
  currentBlock: store._currentBlock,
647
701
  isCollapsed: store._isCollapsed,
648
702
  selection: store.selection,
703
+ isFocused: store.isFocused,
649
704
  toggleStyle: store.toggleStyle.bind(store),
650
705
  applyStyle: store.applyStyle.bind(store),
651
706
  removeStyle: store.removeStyle.bind(store),
652
- insertText: store.insertText.bind(store)
707
+ insertText: store.insertText.bind(store),
708
+ selectAll: store.selectAll.bind(store)
653
709
  });
654
710
  return (_ctx, _cache) => {
655
711
  return openBlock(), createElementBlock("div", {
@@ -658,7 +714,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
658
714
  contenteditable: "",
659
715
  onInput: _cache[0] || (_cache[0] = //@ts-ignore
660
716
  (...args) => unref(store).onInput && unref(store).onInput(...args)),
661
- onKeydown: onKeyDown
717
+ onKeydown: onKeyDown,
718
+ onCopy,
719
+ onPaste,
720
+ onCut
662
721
  }, [
663
722
  (openBlock(true), createElementBlock(Fragment, null, renderList(unref(store).blocks, (block) => {
664
723
  return openBlock(), createBlock(_sfc_main$1, {
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.2",
5
+ "version": "0.0.4",
6
6
  "type": "module",
7
7
  "license": "MIT",
8
8
  "author": "den59k",