mce 0.11.2 → 0.11.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 CHANGED
@@ -45,8 +45,8 @@ npm i mce
45
45
  svg(),
46
46
  ],
47
47
  theme: 'system',
48
- language: 'en',
49
48
  viewMode: 'edgeless',
49
+ clipboard: true,
50
50
  checkerboard: true,
51
51
  pixelGrid: true,
52
52
  camera: true,
@@ -60,6 +60,7 @@ npm i mce
60
60
  zoomToFitOffset: { left: 0, top: 0, right: 0, bottom: 0 },
61
61
  localDb: false,
62
62
  customUpload: async (blob) => URL.createObjectURL(blob),
63
+ customContextMenu: (menu) => menu,
63
64
  defaultFont: { family: 'SourceHanSansCN-Normal', src: '/SourceHanSansCN-Normal.woff' },
64
65
  doc: {
65
66
  children: [
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ref, computed, watch, markRaw, reactive, warn, shallowRef, inject, provide, defineComponent, createVNode, mergeProps, createElementVNode, toValue, getCurrentInstance, onScopeDispose, createElementBlock, openBlock, Fragment, renderList, unref, normalizeStyle, normalizeClass, onBeforeUnmount, readonly, toRef, onMounted, useId, onDeactivated, onActivated, useAttrs, createBlock, resolveDynamicComponent, useTemplateRef, renderSlot, Teleport, createCommentVNode, mergeModels, useModel, resolveComponent, withCtx, createTextVNode, toDisplayString, createSlots, normalizeProps, guardReactiveProps, withDirectives, vShow, vModelText, nextTick, withModifiers, onBeforeMount, isRef } from "vue";
1
+ import { ref, computed, watch, markRaw, reactive, warn, shallowRef, onBeforeUnmount, onMounted, inject, provide, defineComponent, createVNode, mergeProps, createElementVNode, toValue, getCurrentInstance, onScopeDispose, createElementBlock, openBlock, Fragment, renderList, unref, normalizeStyle, normalizeClass, readonly, toRef, useId, onDeactivated, onActivated, useAttrs, createBlock, resolveDynamicComponent, useTemplateRef, renderSlot, Teleport, createCommentVNode, mergeModels, useModel, resolveComponent, withCtx, createTextVNode, toDisplayString, createSlots, normalizeProps, guardReactiveProps, withDirectives, vShow, vModelText, nextTick, withModifiers, onBeforeMount, isRef } from "vue";
2
2
  import { useFileDialog, useEventListener, isClient, useResizeObserver as useResizeObserver$1, useLocalStorage, onClickOutside, useMouse, useDebounceFn } from "@vueuse/core";
3
3
  import { Node as Node$1, Element2D, Timeline, Engine, Camera2D, DrawboardEffect, IN_BROWSER, clamp, assets, TimelineNode, Transform2D, DEG_TO_RAD, render, Animation } from "modern-canvas";
4
4
  import { getObjectValueByPath, setObjectValueByPath, Observable, Reactivable, idGenerator, property, normalizeElement, normalizeTextContent } from "modern-idoc";
@@ -110,7 +110,6 @@ const _0_config_base = defineMixin((editor, options) => {
110
110
  config
111
111
  } = editor;
112
112
  registerConfig("theme", "system");
113
- registerConfig("language", "en");
114
113
  registerConfig("viewMode", "edgeless");
115
114
  registerConfig("checkerboard", false);
116
115
  registerConfig("pixelGrid", false);
@@ -1362,6 +1361,18 @@ function findChildrenWithProvide(key, vnode) {
1362
1361
  }
1363
1362
  return [];
1364
1363
  }
1364
+ function isInputEvent(event) {
1365
+ if (!event)
1366
+ return false;
1367
+ let path = event.path;
1368
+ if (!path && event.composedPath)
1369
+ path = event.composedPath();
1370
+ if (!path)
1371
+ return false;
1372
+ return path?.some(
1373
+ (el) => ["INPUT", "TEXTAREA", "SELECT"].includes(el?.tagName) || el?.contentEditable === "true"
1374
+ );
1375
+ }
1365
1376
  function propsFactory(props, source) {
1366
1377
  return (defaults) => {
1367
1378
  return Object.keys(props).reduce((obj, prop) => {
@@ -1391,18 +1402,6 @@ function uuidv4() {
1391
1402
  const defaultHotkeys = [
1392
1403
  { command: "cancel", key: "Esc", editable: false }
1393
1404
  ];
1394
- function isInputEvent(event) {
1395
- if (!event)
1396
- return false;
1397
- let path = event.path;
1398
- if (!path && event.composedPath)
1399
- path = event.composedPath();
1400
- if (!path)
1401
- return false;
1402
- return path?.some(
1403
- (el) => ["INPUT", "TEXTAREA", "SELECT"].includes(el?.tagName) || el?.contentEditable === "true"
1404
- );
1405
- }
1406
1405
  const _1_hotkey = defineMixin((editor) => {
1407
1406
  const {
1408
1407
  registerConfig
@@ -1962,15 +1961,15 @@ const _2_load = defineMixin((editor) => {
1962
1961
  };
1963
1962
  const load = async (source) => {
1964
1963
  state.value = "loading";
1965
- let result;
1964
+ const items = [];
1966
1965
  try {
1967
1966
  for (const loader of loaders.value.values()) {
1968
1967
  if (await loader.test(source)) {
1969
1968
  const res = await loader.load(source);
1970
1969
  if (Array.isArray(res)) {
1971
- result = { id: idGenerator(), children: res };
1970
+ items.push(...res);
1972
1971
  } else {
1973
- result = res;
1972
+ items.push(res);
1974
1973
  }
1975
1974
  break;
1976
1975
  }
@@ -1978,10 +1977,10 @@ const _2_load = defineMixin((editor) => {
1978
1977
  } finally {
1979
1978
  state.value = void 0;
1980
1979
  }
1981
- if (!result) {
1980
+ if (!items.length) {
1982
1981
  throw new Error(`Failed to load source "${source}"`);
1983
1982
  }
1984
- return result;
1983
+ return items;
1985
1984
  };
1986
1985
  const openFileDialog = (options = {}) => {
1987
1986
  const {
@@ -2549,7 +2548,11 @@ const _4_4_doc = defineMixin((editor, options) => {
2549
2548
  }
2550
2549
  }
2551
2550
  if (source && typeof source !== "string") {
2552
- doc2.set(source);
2551
+ if (Array.isArray(source)) {
2552
+ doc2.set({ children: source });
2553
+ } else {
2554
+ doc2.set(source);
2555
+ }
2553
2556
  }
2554
2557
  });
2555
2558
  doc2.on("update", throttle((update, origin) => emit("updateDoc", update, origin), 200));
@@ -2559,8 +2562,17 @@ const _4_4_doc = defineMixin((editor, options) => {
2559
2562
  let id;
2560
2563
  if (typeof source === "string") {
2561
2564
  id = source;
2562
- } else {
2563
- id = source?.id;
2565
+ } else if (source) {
2566
+ if (Array.isArray(source) && source.length === 1) {
2567
+ source = source[0];
2568
+ }
2569
+ if (!Array.isArray(source)) {
2570
+ if (source.meta?.inEditorIs === "Doc") {
2571
+ id = source.id;
2572
+ } else {
2573
+ source = [source];
2574
+ }
2575
+ }
2564
2576
  }
2565
2577
  const _doc = new Doc(id);
2566
2578
  state.value = "loading";
@@ -2576,11 +2588,7 @@ const _4_4_doc = defineMixin((editor, options) => {
2576
2588
  return _doc;
2577
2589
  };
2578
2590
  const loadDoc = async (source) => {
2579
- let loaded = await load(source);
2580
- if (loaded.meta?.inEditorIs !== "Doc") {
2581
- loaded = { id: idGenerator(), children: [loaded] };
2582
- }
2583
- const _doc = await setDoc(loaded);
2591
+ const _doc = await setDoc(await load(source));
2584
2592
  emit("loadDoc", _doc, source);
2585
2593
  return _doc;
2586
2594
  };
@@ -4054,7 +4062,12 @@ const _clipboard = definePlugin((editor, options) => {
4054
4062
  const useClipboard = options.clipboard !== false && SUPPORTS_CLIPBOARD;
4055
4063
  const copy = async (data) => {
4056
4064
  if (data === void 0) {
4057
- data = selection.value.length === 1 ? [selection.value[0].toJSON()] : selection.value.map((v) => v.toJSON());
4065
+ data = selection.value.map((v) => {
4066
+ const json = v.toJSON();
4067
+ delete json.style.left;
4068
+ delete json.style.top;
4069
+ return json;
4070
+ });
4058
4071
  }
4059
4072
  if (useClipboard) {
4060
4073
  if (Array.isArray(data)) {
@@ -4077,46 +4090,69 @@ const _clipboard = definePlugin((editor, options) => {
4077
4090
  }
4078
4091
  }
4079
4092
  };
4080
- async function cut() {
4093
+ const cut = async () => {
4081
4094
  await copy();
4082
4095
  exec("delete");
4083
- }
4084
- let lock = false;
4085
- function pasteLock() {
4086
- if (!lock) {
4087
- lock = true;
4088
- }
4096
+ };
4097
+ let locked = false;
4098
+ function getPasteLock() {
4089
4099
  return {
4090
- lock,
4091
- unlock: () => lock = false
4100
+ locked,
4101
+ lock: () => locked = true,
4102
+ unlock: () => locked = false
4092
4103
  };
4093
4104
  }
4094
- async function onPaste(source) {
4095
- const items = source ?? await navigator.clipboard.read();
4105
+ async function _paste(items) {
4096
4106
  const elements = [];
4097
4107
  for (const item of items) {
4098
4108
  for (const type of item.types) {
4099
4109
  const blob = await item.getType(type);
4100
4110
  if (await canLoad(blob)) {
4101
- elements.push(await load(blob));
4111
+ elements.push(...await load(blob));
4102
4112
  } else {
4103
4113
  console.warn(`Unhandled clipboard ${blob.type}`, await blob.text());
4104
4114
  }
4105
4115
  }
4106
4116
  }
4107
- addElement(elements, {
4108
- inPointerPosition: true,
4109
- active: true,
4110
- regenId: true
4111
- });
4117
+ if (elements.length) {
4118
+ addElement(elements, {
4119
+ inPointerPosition: true,
4120
+ active: true,
4121
+ regenId: true
4122
+ });
4123
+ }
4112
4124
  }
4113
- async function paste() {
4114
- await new Promise((r) => setTimeout(r, 0));
4115
- const { lock: lock2, unlock } = pasteLock();
4116
- if (!lock2) {
4117
- try {
4125
+ const paste = async (source) => {
4126
+ if (source) {
4127
+ const { unlock, lock } = getPasteLock();
4128
+ lock();
4129
+ let items = [];
4130
+ if (source instanceof DataTransfer) {
4131
+ for (const item of source.items) {
4132
+ switch (item.kind) {
4133
+ case "file": {
4134
+ const file = item.getAsFile();
4135
+ if (file) {
4136
+ items.push(new ClipboardItem({ [file.type]: file }));
4137
+ }
4138
+ break;
4139
+ }
4140
+ }
4141
+ }
4142
+ } else {
4143
+ items = source;
4144
+ }
4145
+ if (items.length) {
4146
+ await _paste(items);
4147
+ } else {
4148
+ unlock();
4149
+ }
4150
+ } else {
4151
+ await new Promise((r) => setTimeout(r, 100));
4152
+ const { locked: locked2, unlock } = getPasteLock();
4153
+ if (!locked2) {
4118
4154
  if (useClipboard) {
4119
- await onPaste();
4155
+ await _paste(await navigator.clipboard.read());
4120
4156
  } else if (copiedData.value) {
4121
4157
  if (Array.isArray(copiedData.value)) {
4122
4158
  addElement(copiedData.value?.map((el) => cloneDeep(el)) ?? [], {
@@ -4126,13 +4162,11 @@ const _clipboard = definePlugin((editor, options) => {
4126
4162
  });
4127
4163
  }
4128
4164
  }
4129
- } finally {
4165
+ } else {
4130
4166
  unlock();
4131
4167
  }
4132
- } else {
4133
- unlock();
4134
4168
  }
4135
- }
4169
+ };
4136
4170
  async function duplicate() {
4137
4171
  await copy();
4138
4172
  await paste();
@@ -4156,31 +4190,16 @@ const _clipboard = definePlugin((editor, options) => {
4156
4190
  ],
4157
4191
  setup: () => {
4158
4192
  if (useClipboard) {
4159
- window.addEventListener("paste", async (e) => {
4160
- const items = e.clipboardData?.items;
4161
- if (items?.length) {
4162
- pasteLock();
4163
- const clipboardItems = [];
4164
- for (const item of items) {
4165
- switch (item.kind) {
4166
- case "file": {
4167
- const file = item.getAsFile();
4168
- if (file) {
4169
- clipboardItems.push(
4170
- new ClipboardItem({
4171
- [file.type]: file
4172
- })
4173
- );
4174
- }
4175
- break;
4176
- }
4177
- }
4178
- }
4179
- await onPaste(
4180
- clipboardItems.length ? clipboardItems : void 0
4181
- );
4193
+ async function onPaste(e) {
4194
+ if (isInputEvent(e)) {
4195
+ return;
4182
4196
  }
4183
- });
4197
+ if (e.clipboardData) {
4198
+ await paste(e.clipboardData);
4199
+ }
4200
+ }
4201
+ window.addEventListener("paste", onPaste);
4202
+ onBeforeUnmount(() => window.removeEventListener("paste", onPaste));
4184
4203
  }
4185
4204
  }
4186
4205
  };
@@ -4609,7 +4628,7 @@ const _import = definePlugin((editor) => {
4609
4628
  } = editor;
4610
4629
  const _import2 = async (options = {}) => {
4611
4630
  const files = await openFileDialog({ multiple: true });
4612
- return addElement(await Promise.all(files.map((file) => load(file))), {
4631
+ return addElement((await Promise.all(files.map((file) => load(file)))).flat(), {
4613
4632
  ...options,
4614
4633
  sizeToFit: true,
4615
4634
  positionToFit: true
@@ -4622,7 +4641,35 @@ const _import = definePlugin((editor) => {
4622
4641
  ],
4623
4642
  hotkeys: [
4624
4643
  { command: "import", key: "CmdOrCtrl+i" }
4625
- ]
4644
+ ],
4645
+ setup: () => {
4646
+ const {
4647
+ drawboardPointer,
4648
+ drawboardDom,
4649
+ exec
4650
+ } = editor;
4651
+ function onDragover(e) {
4652
+ e.preventDefault();
4653
+ drawboardPointer.value = { x: e.clientX, y: e.clientY };
4654
+ if (e.dataTransfer) {
4655
+ e.dataTransfer.dropEffect = "copy";
4656
+ }
4657
+ }
4658
+ async function onDrop(e) {
4659
+ e.preventDefault();
4660
+ if (e.dataTransfer) {
4661
+ await exec("paste", e.dataTransfer);
4662
+ }
4663
+ }
4664
+ onMounted(() => {
4665
+ drawboardDom.value?.addEventListener("dragover", onDragover);
4666
+ drawboardDom.value?.addEventListener("drop", onDrop);
4667
+ });
4668
+ onBeforeUnmount(() => {
4669
+ drawboardDom.value?.removeEventListener("dragover", onDragover);
4670
+ drawboardDom.value?.removeEventListener("drop", onDrop);
4671
+ });
4672
+ }
4626
4673
  };
4627
4674
  });
4628
4675
  const _layerOrder = definePlugin((editor) => {
@@ -4988,10 +5035,7 @@ const _new = definePlugin((editor) => {
4988
5035
  setDoc
4989
5036
  } = editor;
4990
5037
  function _new2() {
4991
- setDoc({
4992
- id: idGenerator(),
4993
- children: []
4994
- });
5038
+ setDoc([]);
4995
5039
  }
4996
5040
  return {
4997
5041
  name: "mce:new",
@@ -8872,6 +8916,7 @@ export {
8872
8916
  imageExts,
8873
8917
  imageMimeTypeExtMap,
8874
8918
  isClickInsideElement,
8919
+ isInputEvent,
8875
8920
  isMac,
8876
8921
  isOverlappingAabb,
8877
8922
  isOverlappingObb,
@@ -3,13 +3,11 @@ declare global {
3
3
  interface Options extends Partial<Config> {
4
4
  }
5
5
  type Theme = 'system' | 'light' | 'dark';
6
- type Language = string;
7
6
  type ViewMode = 'frame' | 'edgeless';
8
7
  type TypographyStrategy = 'autoHeight' | 'autoWidth' | 'fixedWidthHeight' | 'autoFontSize';
9
8
  type HandleShape = 'rect' | 'circle';
10
9
  interface Config {
11
10
  theme: Theme;
12
- language: Language;
13
11
  viewMode: ViewMode;
14
12
  checkerboard: boolean;
15
13
  pixelGrid: boolean;
@@ -13,7 +13,7 @@ declare global {
13
13
  registerLoader: (value: Loader | Loader[]) => void;
14
14
  unregisterLoader: (name: string) => void;
15
15
  canLoad: (source: any) => Promise<boolean>;
16
- load: <T = NormalizedElement>(source: any) => Promise<T>;
16
+ load: <T = NormalizedElement>(source: any) => Promise<T[]>;
17
17
  openFileDialog: (options?: {
18
18
  multiple?: boolean;
19
19
  }) => Promise<File[]>;
@@ -1,10 +1,10 @@
1
- import type { Document } from 'modern-idoc';
1
+ import type { Document, Element } from 'modern-idoc';
2
2
  import { Doc } from '../models';
3
3
  declare global {
4
4
  namespace Mce {
5
5
  interface Editor {
6
6
  getDoc: () => JsonData;
7
- setDoc: (doc: Document | string) => Promise<Doc>;
7
+ setDoc: (doc: Document | Element[] | string) => Promise<Doc>;
8
8
  loadDoc: (source: any) => Promise<Doc>;
9
9
  clearDoc: () => void;
10
10
  }
@@ -7,10 +7,11 @@ declare global {
7
7
  paste: [event: KeyboardEvent];
8
8
  duplicate: [event: KeyboardEvent];
9
9
  }
10
+ type PasteSource = DataTransfer | ClipboardItem[];
10
11
  interface Commands {
11
12
  copy: (data?: any) => Promise<void>;
12
13
  cut: () => Promise<void>;
13
- paste: () => Promise<void>;
14
+ paste: (source?: PasteSource) => Promise<void>;
14
15
  duplicate: () => Promise<void>;
15
16
  }
16
17
  interface Editor {
@@ -10,3 +10,4 @@ export declare function convertToUnit(str: number, unit?: string): string;
10
10
  export declare function convertToUnit(str: string | number | null | undefined, unit?: string): string | undefined;
11
11
  export declare function templateRef(): TemplateRef;
12
12
  export declare function findChildrenWithProvide(key: InjectionKey<any> | symbol, vnode?: VNodeChild): ComponentInternalInstance[];
13
+ export declare function isInputEvent(event?: Event): boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mce",
3
3
  "type": "module",
4
- "version": "0.11.2",
4
+ "version": "0.11.3",
5
5
  "description": "The headless canvas editor framework. only the ESM.",
6
6
  "author": "wxm",
7
7
  "license": "MIT",