@hprint/core 0.0.1-alpha.0

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.
Files changed (40) hide show
  1. package/dist/ContextMenu.d.ts +25 -0
  2. package/dist/ContextMenu.d.ts.map +1 -0
  3. package/dist/Editor.d.ts +35 -0
  4. package/dist/Editor.d.ts.map +1 -0
  5. package/dist/Instance.d.ts +77 -0
  6. package/dist/Instance.d.ts.map +1 -0
  7. package/dist/ServersPlugin.d.ts +71 -0
  8. package/dist/ServersPlugin.d.ts.map +1 -0
  9. package/dist/index.css +1 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +569 -0
  13. package/dist/index.mjs +22130 -0
  14. package/dist/interface/Editor.d.ts +35 -0
  15. package/dist/interface/Editor.d.ts.map +1 -0
  16. package/dist/objects/CustomRect.d.ts +3 -0
  17. package/dist/objects/CustomRect.d.ts.map +1 -0
  18. package/dist/objects/CustomTextbox.d.ts +3 -0
  19. package/dist/objects/CustomTextbox.d.ts.map +1 -0
  20. package/dist/plugin.d.ts +30 -0
  21. package/dist/plugin.d.ts.map +1 -0
  22. package/dist/utils/fabric-history.d.ts +2 -0
  23. package/dist/utils/fabric-history.d.ts.map +1 -0
  24. package/dist/utils/utils.d.ts +61 -0
  25. package/dist/utils/utils.d.ts.map +1 -0
  26. package/package.json +46 -0
  27. package/src/ContextMenu.js +277 -0
  28. package/src/Editor.ts +215 -0
  29. package/src/Instance.ts +79 -0
  30. package/src/ServersPlugin.ts +387 -0
  31. package/src/index.ts +11 -0
  32. package/src/interface/Editor.ts +56 -0
  33. package/src/objects/CustomRect.js +21 -0
  34. package/src/objects/CustomTextbox.js +165 -0
  35. package/src/plugin.ts +88 -0
  36. package/src/styles/contextMenu.css +60 -0
  37. package/src/utils/fabric-history.js +232 -0
  38. package/src/utils/utils.ts +165 -0
  39. package/tsconfig.json +10 -0
  40. package/vite.config.ts +29 -0
@@ -0,0 +1,35 @@
1
+ import { default as Editor } from '@hprint/core';
2
+ export interface IEditor extends Editor {
3
+ }
4
+ export type IEditorHooksType = 'hookImportBefore' | 'hookImportAfter' | 'hookSaveBefore' | 'hookSaveAfter' | 'hookTransform';
5
+ export declare class IPluginTempl {
6
+ constructor(canvas: fabric.Canvas, editor: IEditor, options?: IPluginOption);
7
+ static pluginName: string;
8
+ static events: string[];
9
+ static apis: string[];
10
+ hotkeyEvent?: (name: string, e: KeyboardEvent) => void;
11
+ hookImportBefore?: (...args: unknown[]) => Promise<unknown>;
12
+ hookImportAfter?: (...args: unknown[]) => Promise<unknown>;
13
+ hookSaveBefore?: (...args: unknown[]) => Promise<unknown>;
14
+ hookSaveAfter?: (...args: unknown[]) => Promise<unknown>;
15
+ hookTransform?: (...args: unknown[]) => Promise<unknown>;
16
+ [propName: string]: any;
17
+ canvas?: fabric.Canvas;
18
+ editor?: IEditor;
19
+ }
20
+ export declare interface IPluginOption {
21
+ [propName: string]: unknown | undefined;
22
+ }
23
+ declare class IPluginClass2 extends IPluginTempl {
24
+ constructor();
25
+ }
26
+ export declare interface IPluginClass {
27
+ new (canvas: fabric.Canvas, editor: Editor, options?: IPluginOption): IPluginClass2;
28
+ }
29
+ export declare interface IPluginMenu {
30
+ text: string;
31
+ command?: () => void;
32
+ child?: IPluginMenu[];
33
+ }
34
+ export {};
35
+ //# sourceMappingURL=Editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Editor.d.ts","sourceRoot":"","sources":["../../src/interface/Editor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAIvC,MAAM,WAAW,OAAQ,SAAQ,MAAM;CAAG;AAG1C,MAAM,MAAM,gBAAgB,GACtB,kBAAkB,GAClB,iBAAiB,GACjB,gBAAgB,GAChB,eAAe,GACf,eAAe,CAAC;AAGtB,MAAM,CAAC,OAAO,OAAO,YAAY;gBAEzB,MAAM,EAAE,MAAM,CAAC,MAAM,EACrB,MAAM,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,aAAa;IAE3B,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;IACvD,gBAAgB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,eAAe,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,cAAc,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,aAAa,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,WAAW,aAAa;IAClC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC3C;AAED,OAAO,OAAO,aAAc,SAAQ,YAAY;;CAE/C;AAED,MAAM,CAAC,OAAO,WAAW,YAAY;IACjC,KACI,MAAM,EAAE,MAAM,CAAC,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,aAAa,GACxB,aAAa,CAAC;CACpB;AAED,MAAM,CAAC,OAAO,WAAW,WAAW;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;CACzB"}
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof fabric.Rect;
2
+ export default _default;
3
+ //# sourceMappingURL=CustomRect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomRect.d.ts","sourceRoot":"","sources":["../../src/objects/CustomRect.js"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ declare const _default: typeof fabric.Rect;
2
+ export default _default;
3
+ //# sourceMappingURL=CustomTextbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CustomTextbox.d.ts","sourceRoot":"","sources":["../../src/objects/CustomTextbox.js"],"names":[],"mappings":""}
@@ -0,0 +1,30 @@
1
+ import { default as Editor } from './Editor';
2
+ type IEditor = Editor;
3
+ declare class FontPlugin {
4
+ canvas: fabric.Canvas;
5
+ editor: IEditor;
6
+ static pluginName: string;
7
+ static apis: string[];
8
+ static events: string[];
9
+ hotkeys: string[];
10
+ repoSrc: string;
11
+ constructor(canvas: fabric.Canvas, editor: IEditor, config: {
12
+ repoSrc: string;
13
+ });
14
+ hookImportBefore(json: string): void;
15
+ downFontByJSON(): void;
16
+ _createFontCSS(): void;
17
+ contextMenu(): ({
18
+ text: string;
19
+ hotkey: string;
20
+ subitems: {
21
+ text: string;
22
+ hotkey: string;
23
+ onclick: () => any;
24
+ }[];
25
+ } | null)[] | undefined;
26
+ hotkeyEvent(eventName: string, { type }: KeyboardEvent): void;
27
+ destroy(): void;
28
+ }
29
+ export default FontPlugin;
30
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,KAAK,OAAO,GAAG,MAAM,CAAC;AAEtB,cAAM,UAAU;IACL,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAEvB,MAAM,CAAC,UAAU,SAAgB;IAEjC,MAAM,CAAC,IAAI,WAAsB;IAEjC,MAAM,CAAC,MAAM,WAAgC;IAEtC,OAAO,EAAE,MAAM,EAAE,CAA0B;IAElD,OAAO,EAAE,MAAM,CAAC;gBAGZ,MAAM,EAAE,MAAM,CAAC,MAAM,EACrB,MAAM,EAAE,OAAO,EACf,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE;IAU/B,gBAAgB,CAAC,IAAI,EAAE,MAAM;IAK7B,cAAc;IAKd,cAAc;IAMd,WAAW;;;;;;;;;IA0BX,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,aAAa;IAUtD,OAAO;CAGV;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=fabric-history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fabric-history.d.ts","sourceRoot":"","sources":["../../src/utils/fabric-history.js"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ import { useClipboard } from '@vueuse/core';
2
+ /**
3
+ * @description: 图片文件转字符串
4
+ * @param {Blob|File} file 文件
5
+ * @return {String}
6
+ */
7
+ export declare function getImgStr(file: File | Blob): Promise<FileReader['result']>;
8
+ /**
9
+ * @description: 选择文件
10
+ * @param {Object} options accept = '', capture = '', multiple = false
11
+ * @return {Promise}
12
+ */
13
+ export declare function selectFiles(options: {
14
+ accept?: string;
15
+ capture?: string;
16
+ multiple?: boolean;
17
+ }): Promise<FileList | null>;
18
+ /**
19
+ * @description: 创建图片元素
20
+ * @param {String} str 图片地址或者base64图片
21
+ * @return {Promise} element 图片元素
22
+ */
23
+ export declare function insertImgFile(str: string): Promise<unknown>;
24
+ /**
25
+ * Copying text to the clipboard
26
+ * @param source Copy source
27
+ * @param options Copy options
28
+ * @returns Promise that resolves when the text is copied successfully, or rejects when the copy fails.
29
+ */
30
+ export declare const clipboardText: (source: string, options?: Parameters<typeof useClipboard>[0]) => Promise<void>;
31
+ export declare function downFile(fileStr: string, fileType: string): void;
32
+ export declare function drawImg(ctx: CanvasRenderingContext2D, left: number, top: number, img: HTMLImageElement, wSize: number, hSize: number, angle: number | undefined): void;
33
+ export declare function shiftAngle(start: fabric.Point, end: fabric.Point): {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ /**
38
+ * 类型工具
39
+ */
40
+ export declare const isImage: (thing: unknown) => thing is fabric.Image;
41
+ export declare const isGroup: (thing: unknown) => thing is fabric.Group;
42
+ export declare const isIText: (thing: unknown) => thing is fabric.IText;
43
+ export declare const isActiveSelection: (thing: unknown) => thing is fabric.ActiveSelection;
44
+ export declare function blobToBase64(blob: Blob): Promise<unknown>;
45
+ export declare function base64ToBlob(base64Data: string): (string | Blob)[] | null;
46
+ declare const _default: {
47
+ getImgStr: typeof getImgStr;
48
+ downFile: typeof downFile;
49
+ selectFiles: typeof selectFiles;
50
+ insertImgFile: typeof insertImgFile;
51
+ clipboardText: (source: string, options?: Parameters<typeof useClipboard>[0]) => Promise<void>;
52
+ drawImg: typeof drawImg;
53
+ isImage: (thing: unknown) => thing is fabric.Image;
54
+ isGroup: (thing: unknown) => thing is fabric.Group;
55
+ isIText: (thing: unknown) => thing is fabric.IText;
56
+ isActiveSelection: (thing: unknown) => thing is fabric.ActiveSelection;
57
+ blobToBase64: typeof blobToBase64;
58
+ base64ToBlob: typeof base64ToBlob;
59
+ };
60
+ export default _default;
61
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAA4B,MAAM,cAAc,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAE1E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAQ3B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,oBAUxC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACtB,QAAQ,MAAM,EACd,UAAU,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,kBAG/C,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAOzD;AAED,wBAAgB,OAAO,CACnB,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,gBAAgB,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GAAG,SAAS,QAQ5B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK;;;EAgBhE;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAExD,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAExD,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAExD,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC1B,OAAO,OAAO,KACf,KAAK,IAAI,MAAM,CAAC,eAElB,CAAC;AAEF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,oBAQtC;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,4BAa9C;;;;;;4BA9FW,MAAM,YACJ,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC;;qBAoDjB,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAAK;qBAI/B,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAAK;qBAI/B,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,KAAK;+BAKnD,OAAO,KACf,KAAK,IAAI,MAAM,CAAC,eAAe;;;;AA6BlC,wBAaE"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@hprint/core",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "vite build",
17
+ "clean": "node -e \"require('fs').rmSync('dist', {recursive: true, force: true})\"",
18
+ "type-check": "tsc --noEmit",
19
+ "watch": "vite build --watch",
20
+ "test": "echo \"Error: no test specified\" && exit 1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/fabric": "~5.3.10",
24
+ "@types/fontfaceobserver": "~2.1.3",
25
+ "@types/qs": "^6.14.0",
26
+ "rimraf": "^6.1.2",
27
+ "vite": "^7.2.4",
28
+ "vite-plugin-dts": "^4.5.4"
29
+ },
30
+ "keywords": [],
31
+ "author": "george-hong",
32
+ "license": "ISC",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "packageManager": "pnpm@10.20.0",
37
+ "dependencies": {
38
+ "@hprint/shared": "workspace:*",
39
+ "@vueuse/core": "~10.11.1",
40
+ "events": "^3.3.0",
41
+ "fabric": "~5.3.0",
42
+ "hotkeys-js": "~3.8.9",
43
+ "tapable": "~2.3.0",
44
+ "uuid": "~8.3.2"
45
+ }
46
+ }
@@ -0,0 +1,277 @@
1
+ import './styles/contextMenu.css';
2
+
3
+ class ContextMenu {
4
+ constructor(container, items) {
5
+ this.container = container;
6
+ this.dom = null;
7
+ this.shown = false;
8
+ this.root = true;
9
+ this.parent = null;
10
+ this.submenus = [];
11
+ this.items = items;
12
+
13
+ this._onclick = (e) => {
14
+ if (
15
+ this.dom &&
16
+ e.target != this.dom &&
17
+ e.target.parentElement != this.dom &&
18
+ !e.target.classList.contains('item') &&
19
+ !e.target.parentElement.classList.contains('item')
20
+ ) {
21
+ this.hideAll();
22
+ }
23
+ };
24
+
25
+ this._oncontextmenu = (e) => {
26
+ e.preventDefault();
27
+ if (
28
+ e.target != this.dom &&
29
+ e.target.parentElement != this.dom &&
30
+ !e.target.classList.contains('item') &&
31
+ !e.target.parentElement.classList.contains('item')
32
+ ) {
33
+ this.hideAll();
34
+ this.show(e.clientX, e.clientY);
35
+ }
36
+ };
37
+
38
+ this._oncontextmenu_keydown = (e) => {
39
+ if (e.keyCode != 93) return;
40
+ e.preventDefault();
41
+
42
+ this.hideAll();
43
+ this.show(e.clientX, e.clientY);
44
+ };
45
+
46
+ this._onblur = () => {
47
+ this.hideAll();
48
+ };
49
+ }
50
+
51
+ getMenuDom() {
52
+ const menu = document.createElement('div');
53
+ menu.classList.add('context');
54
+
55
+ for (const item of this.items) {
56
+ menu.appendChild(this.itemToDomEl(item));
57
+ }
58
+
59
+ return menu;
60
+ }
61
+
62
+ itemToDomEl(data) {
63
+ const item = document.createElement('div');
64
+
65
+ if (data === null) {
66
+ item.classList = 'separator';
67
+ return item;
68
+ }
69
+
70
+ if (
71
+ data.hasOwnProperty('color') &&
72
+ /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(
73
+ data.color.toString()
74
+ )
75
+ ) {
76
+ item.style.cssText = `color: ${data.color}`;
77
+ }
78
+
79
+ item.classList.add('item');
80
+
81
+ const label = document.createElement('span');
82
+ label.classList = 'label';
83
+ label.innerText = data.hasOwnProperty('text')
84
+ ? data['text'].toString()
85
+ : '';
86
+ item.appendChild(label);
87
+
88
+ if (data.hasOwnProperty('disabled') && data['disabled']) {
89
+ item.classList.add('disabled');
90
+ } else {
91
+ item.classList.add('enabled');
92
+ }
93
+
94
+ const hotkey = document.createElement('span');
95
+ hotkey.classList = 'hotkey';
96
+ hotkey.innerText = data.hasOwnProperty('hotkey')
97
+ ? data['hotkey'].toString()
98
+ : '';
99
+ item.appendChild(hotkey);
100
+
101
+ if (
102
+ data.hasOwnProperty('subitems') &&
103
+ Array.isArray(data['subitems']) &&
104
+ data['subitems'].length > 0
105
+ ) {
106
+ const menu = new ContextMenu(this.container, data['subitems']);
107
+ menu.root = false;
108
+ menu.parent = this;
109
+
110
+ const openSubItems = () => {
111
+ if (data.hasOwnProperty('disabled') && data['disabled'] == true)
112
+ return;
113
+
114
+ this.hideSubMenus();
115
+
116
+ const x =
117
+ this.dom.offsetLeft +
118
+ this.dom.clientWidth +
119
+ item.offsetLeft;
120
+ const y = this.dom.offsetTop + item.offsetTop;
121
+
122
+ if (!menu.shown) {
123
+ menu.show(x, y);
124
+ } else {
125
+ menu.hide();
126
+ }
127
+ };
128
+
129
+ this.submenus.push(menu);
130
+
131
+ item.classList.add('has-subitems');
132
+ item.addEventListener('click', openSubItems);
133
+ item.addEventListener('mousemove', openSubItems);
134
+ } else if (
135
+ data.hasOwnProperty('submenu') &&
136
+ data['submenu'] instanceof ContextMenu
137
+ ) {
138
+ const menu = data['submenu'];
139
+ menu.root = false;
140
+ menu.parent = this;
141
+
142
+ const openSubItems = () => {
143
+ if (data.hasOwnProperty('disabled') && data['disabled'] == true)
144
+ return;
145
+
146
+ this.hideSubMenus();
147
+
148
+ const x =
149
+ this.dom.offsetLeft +
150
+ this.dom.clientWidth +
151
+ item.offsetLeft;
152
+ const y = this.dom.offsetTop + item.offsetTop;
153
+
154
+ if (!menu.shown) {
155
+ menu.show(x, y);
156
+ } else {
157
+ menu.hide();
158
+ }
159
+ };
160
+
161
+ this.submenus.push(menu);
162
+
163
+ item.classList.add('has-subitems');
164
+ item.addEventListener('click', openSubItems);
165
+ item.addEventListener('mousemove', openSubItems);
166
+ } else {
167
+ item.addEventListener('click', () => {
168
+ this.hideSubMenus();
169
+
170
+ if (item.classList.contains('disabled')) return;
171
+
172
+ if (
173
+ data.hasOwnProperty('onclick') &&
174
+ typeof data['onclick'] === 'function'
175
+ ) {
176
+ const event = {
177
+ handled: false,
178
+ item: item,
179
+ label: label,
180
+ hotkey: hotkey,
181
+ items: this.items,
182
+ data: data,
183
+ };
184
+
185
+ data['onclick'](event);
186
+
187
+ if (!event.handled) {
188
+ this.hide();
189
+ }
190
+ } else {
191
+ this.hide();
192
+ }
193
+ });
194
+
195
+ item.addEventListener('mousemove', () => {
196
+ this.hideSubMenus();
197
+ });
198
+ }
199
+
200
+ return item;
201
+ }
202
+
203
+ hideAll() {
204
+ if (this.root && !this.parent) {
205
+ if (this.shown) {
206
+ this.hideSubMenus();
207
+
208
+ this.shown = false;
209
+ this.container.removeChild(this.dom);
210
+
211
+ if (this.parent && this.parent.shown) {
212
+ this.parent.hide();
213
+ }
214
+ }
215
+
216
+ return;
217
+ }
218
+
219
+ this.parent.hide();
220
+ }
221
+
222
+ hide() {
223
+ if (this.dom && this.shown) {
224
+ this.shown = false;
225
+ this.hideSubMenus();
226
+ this.container.removeChild(this.dom);
227
+
228
+ if (this.parent && this.parent.shown) {
229
+ this.parent.hide();
230
+ }
231
+ }
232
+ }
233
+
234
+ hideSubMenus() {
235
+ for (const menu of this.submenus) {
236
+ if (menu.shown) {
237
+ menu.shown = false;
238
+ menu.container.removeChild(menu.dom);
239
+ }
240
+ menu.hideSubMenus();
241
+ }
242
+ }
243
+
244
+ show(x, y) {
245
+ this.dom = this.getMenuDom();
246
+
247
+ this.dom.style.left = `${x}px`;
248
+ this.dom.style.top = `${y}px`;
249
+
250
+ this.shown = true;
251
+ this.container.appendChild(this.dom);
252
+ }
253
+
254
+ install() {
255
+ this.container.addEventListener('contextmenu', this._oncontextmenu);
256
+ this.container.addEventListener('keydown', this._oncontextmenu_keydown);
257
+ this.container.addEventListener('click', this._onclick);
258
+ window.addEventListener('blur', this._onblur);
259
+ }
260
+
261
+ setData(data) {
262
+ this.items = data;
263
+ }
264
+
265
+ uninstall() {
266
+ this.dom = null;
267
+ // this.container.removeEventListener('contextmenu', this._oncontextmenu);
268
+ this.container.removeEventListener(
269
+ 'keydown',
270
+ this._oncontextmenu_keydown
271
+ );
272
+ this.container.removeEventListener('click', this._onclick);
273
+ window.removeEventListener('blur', this._onblur);
274
+ }
275
+ }
276
+
277
+ export default ContextMenu;