@chenyomi/leafer-htmltext-editor 1.0.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +177 -0
  3. package/dist/TextEditTool/index.d.ts +17 -0
  4. package/dist/TextEditTool/index.d.ts.map +1 -0
  5. package/dist/TextEditTool/index.js +138 -0
  6. package/dist/TextEditTool/utils.d.ts +8 -0
  7. package/dist/TextEditTool/utils.d.ts.map +1 -0
  8. package/dist/TextEditTool/utils.js +173 -0
  9. package/dist/TextEditor.d.ts +27 -0
  10. package/dist/TextEditor.d.ts.map +1 -0
  11. package/dist/TextEditor.js +166 -0
  12. package/dist/esm/TextEditTool/index.d.ts +17 -0
  13. package/dist/esm/TextEditTool/index.d.ts.map +1 -0
  14. package/dist/esm/TextEditTool/index.js +135 -0
  15. package/dist/esm/TextEditTool/utils.d.ts +8 -0
  16. package/dist/esm/TextEditTool/utils.d.ts.map +1 -0
  17. package/dist/esm/TextEditTool/utils.js +165 -0
  18. package/dist/esm/TextEditor.d.ts +27 -0
  19. package/dist/esm/TextEditor.d.ts.map +1 -0
  20. package/dist/esm/TextEditor.js +163 -0
  21. package/dist/esm/fonts/font.d.ts +17 -0
  22. package/dist/esm/fonts/font.d.ts.map +1 -0
  23. package/dist/esm/fonts/font.js +68 -0
  24. package/dist/esm/fonts/utils.d.ts +9 -0
  25. package/dist/esm/fonts/utils.d.ts.map +1 -0
  26. package/dist/esm/fonts/utils.js +170 -0
  27. package/dist/esm/index.d.ts +7 -0
  28. package/dist/esm/index.d.ts.map +1 -0
  29. package/dist/esm/utils.d.ts +3 -0
  30. package/dist/esm/utils.d.ts.map +1 -0
  31. package/dist/esm/utils.js +284 -0
  32. package/dist/fonts/font.d.ts +17 -0
  33. package/dist/fonts/font.d.ts.map +1 -0
  34. package/dist/fonts/font.js +72 -0
  35. package/dist/fonts/utils.d.ts +9 -0
  36. package/dist/fonts/utils.d.ts.map +1 -0
  37. package/dist/fonts/utils.js +180 -0
  38. package/dist/index.d.ts +7 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.esm.js +79 -0
  41. package/dist/index.js +92 -0
  42. package/dist/utils.d.ts +3 -0
  43. package/dist/utils.d.ts.map +1 -0
  44. package/dist/utils.js +289 -0
  45. package/package.json +55 -0
  46. package/src/TextEditTool/index.ts +145 -0
  47. package/src/TextEditTool/utils.ts +216 -0
  48. package/src/TextEditor.ts +200 -0
  49. package/src/fonts/font.ts +86 -0
  50. package/src/fonts/utils.ts +232 -0
  51. package/src/index.ts +92 -0
  52. package/src/utils.ts +331 -0
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FontManager = exports.defaultFonts = exports.fontManager = exports.setHTMLText = exports.updataHtmlText = exports.TextEditor = void 0;
7
+ exports.initTextEditorQuill = initTextEditorQuill;
8
+ const TextEditor_1 = require("./TextEditor");
9
+ var TextEditor_2 = require("./TextEditor");
10
+ Object.defineProperty(exports, "TextEditor", { enumerable: true, get: function () { return TextEditor_2.TextEditor; } });
11
+ require("./TextEditTool");
12
+ const core_1 = require("@leafer-ui/core");
13
+ const quill_1 = require("quill");
14
+ const quill_2 = __importDefault(require("quill"));
15
+ require("quill/dist/quill.core.css");
16
+ var utils_1 = require("./utils");
17
+ Object.defineProperty(exports, "updataHtmlText", { enumerable: true, get: function () { return utils_1.updataHtmlText; } });
18
+ Object.defineProperty(exports, "setHTMLText", { enumerable: true, get: function () { return utils_1.setHTMLText; } });
19
+ var font_1 = require("./fonts/font");
20
+ Object.defineProperty(exports, "fontManager", { enumerable: true, get: function () { return font_1.fontManager; } });
21
+ Object.defineProperty(exports, "defaultFonts", { enumerable: true, get: function () { return font_1.defaultFonts; } });
22
+ Object.defineProperty(exports, "FontManager", { enumerable: true, get: function () { return font_1.FontManager; } });
23
+ core_1.Plugin.add("leafer-htmltext-editor", "editor");
24
+ function initTextEditorQuill(container) {
25
+ const textInner = document.getElementById("textInnerEditor");
26
+ if (!textInner) {
27
+ const el = document.createElement("div");
28
+ el.id = "textInnerEditor";
29
+ el.style.position = "fixed";
30
+ el.style.transformOrigin = "left top";
31
+ el.style.overflowWrap = "break-word";
32
+ el.style.wordBreak = "break-all";
33
+ el.style.visibility = "hidden";
34
+ document.body.appendChild(el);
35
+ }
36
+ TextEditor_1.TextEditor.quill = new quill_2.default("#textInnerEditor", {
37
+ theme: null,
38
+ modules: {
39
+ toolbar: false,
40
+ keyboard: {
41
+ bindings: {
42
+ enter: {
43
+ key: "Enter",
44
+ handler: (range, context) => {
45
+ const [line] = TextEditor_1.TextEditor.quill.getLine(range.index);
46
+ const BlockBlot = quill_2.default.import("blots/block");
47
+ if (!BlockBlot || !BlockBlot.bubbleFormats)
48
+ return;
49
+ const lineFormats = BlockBlot.bubbleFormats(line);
50
+ const delta = new quill_1.Delta()
51
+ .retain(range.index)
52
+ .delete(range.length)
53
+ .insert("\n", lineFormats);
54
+ TextEditor_1.TextEditor.quill.updateContents(delta, quill_2.default.sources.USER);
55
+ TextEditor_1.TextEditor.quill.setSelection(range.index + 1, quill_2.default.sources.SILENT);
56
+ return false;
57
+ },
58
+ },
59
+ },
60
+ },
61
+ },
62
+ });
63
+ const FontAttributor = quill_2.default.import("attributors/class/font");
64
+ FontAttributor.whitelist = [
65
+ "Roboto",
66
+ "RobotoMono",
67
+ "Inter",
68
+ "OpenSans",
69
+ "Montserrat",
70
+ "RobotoCondensed",
71
+ "Arimo",
72
+ "NotoSans",
73
+ "NotoSansSymbols",
74
+ "Merriweather",
75
+ "PlayfairDisplay",
76
+ "NotoSerif",
77
+ "Lato",
78
+ "Spectral",
79
+ "DancingScript",
80
+ "NotoSansSimplifiedChinese",
81
+ "NotoSerifSimplifiedChinese",
82
+ "NotoSansTraditionalChinese",
83
+ "NotoSansHongKong",
84
+ "NotoSerifTraditionalChinese",
85
+ "NotoSerifHongKong",
86
+ "NotoSansJapanese",
87
+ "NotoSansKorean",
88
+ "Poppins",
89
+ ];
90
+ quill_2.default.register(FontAttributor, true);
91
+ return TextEditor_1.TextEditor.quill;
92
+ }
@@ -0,0 +1,3 @@
1
+ export declare const updataHtmlText: (e?: any, base64font?: any, fontObj?: any) => Promise<void>;
2
+ export declare const setHTMLText: (key: string, value?: any, base64font?: any, editor?: any, isInnerEditor?: boolean) => void;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,cAAc,GAAU,IAAI,GAAG,EAAE,aAAa,GAAG,EAAE,UAAU,GAAG,kBAuG5E,CAAC;AAqFF,eAAO,MAAM,WAAW,GACtB,KAAK,MAAM,EACX,QAAQ,GAAG,EACX,aAAa,GAAG,EAChB,SAAS,GAAG,EACZ,gBAAgB,OAAO,SAgIxB,CAAC"}
package/dist/utils.js ADDED
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setHTMLText = exports.updataHtmlText = void 0;
4
+ const TextEditor_1 = require("./TextEditor");
5
+ function getEditorContext() {
6
+ const quill = TextEditor_1.TextEditor.quill;
7
+ return { quill };
8
+ }
9
+ const updataHtmlText = async (e, base64font, fontObj) => {
10
+ const { scaleX, scaleY } = e.worldTransform;
11
+ const zoomScale = Math.max(Math.abs(scaleX), Math.abs(scaleY));
12
+ const dom = document.querySelector('#textInnerEditor');
13
+ if (dom && e.data.textData?.fontFamily) {
14
+ dom.style.fontFamily = e.data.textData.fontFamily;
15
+ }
16
+ if (dom && e.data.textData?.fontSize) {
17
+ dom.style.fontSize = `${e.data.textData.fontSize * zoomScale}px`;
18
+ }
19
+ if (dom && e.data.textData?.lineHeight) {
20
+ dom.style.lineHeight = e.data.textData.lineHeight;
21
+ }
22
+ if (dom && e.data.textData?.letterSpacing) {
23
+ dom.style.letterSpacing = `${e.data.textData.letterSpacing}px`;
24
+ }
25
+ if (dom && e.data.textData?.textShadow) {
26
+ dom.style.textShadow = e.data.textData.textShadow;
27
+ }
28
+ else if (dom) {
29
+ dom.style.textShadow = 'none';
30
+ }
31
+ if (dom && e.data.textData?.alignContent) {
32
+ const qlEditor = dom.querySelector('.ql-editor');
33
+ if (qlEditor) {
34
+ qlEditor.style.alignContent = e.data.textData.alignContent;
35
+ }
36
+ }
37
+ const { quill } = getEditorContext();
38
+ if (!quill)
39
+ return;
40
+ const html = quill.getSemanticHTML();
41
+ if (html === '<p></p>') {
42
+ if (e.text.includes('<style>@font-face')) {
43
+ e.text = e.text.split('</style>')[0] + '</style>';
44
+ }
45
+ else {
46
+ e.text = '';
47
+ }
48
+ return;
49
+ }
50
+ if (e.text.includes('<style>@font-face')) {
51
+ const style = e.text.split('</style>')[0];
52
+ if (fontObj && !style.includes(fontObj.code)) {
53
+ const addStyle = `@font-face {font-family: ${fontObj.code};src: url(${base64font}) format('woff2') } .ql-font-${fontObj?.code?.replace(/\s+/g, '')} {font-family: ${fontObj.name};}`;
54
+ e.text =
55
+ style +
56
+ addStyle +
57
+ '</style>' +
58
+ addFontSizeToP(e, html, e.data.textData.fontSize, e.data.textData.lineHeight, e.data.textData.letterSpacing, e.data.textData.textShadow, e.data.textData?.alignContent);
59
+ }
60
+ else {
61
+ e.text =
62
+ style +
63
+ '</style>' +
64
+ addFontSizeToP(e, html, e.data.textData.fontSize, e.data.textData.lineHeight, e.data.textData.letterSpacing, e.data.textData.textShadow, e.data.textData?.alignContent);
65
+ }
66
+ }
67
+ else if (base64font && fontObj) {
68
+ const style = `<style>@font-face {font-family: ${e.data.textData.fontFamily.split(',')[0]};src: url(${base64font}) format('woff2') } .ql-font-${fontObj?.code?.replace(/\s+/g, '')} {font-family: ${fontObj.name};}</style>`;
69
+ e.text =
70
+ style +
71
+ addFontSizeToP(e, html, e.data.textData.fontSize, e.data.textData.lineHeight, e.data.textData.letterSpacing, e.data.textData.textShadow, e.data.textData?.alignContent);
72
+ }
73
+ else {
74
+ e.text = addFontSizeToP(e, html, e.data.textData.fontSize, e.data.textData.lineHeight, e.data.textData.letterSpacing, e.data.textData.textShadow, e.data.textData?.alignContent);
75
+ }
76
+ };
77
+ exports.updataHtmlText = updataHtmlText;
78
+ const addFontSizeToP = (e, html, fontSize = 16, lineHeight = '1.5', letterSpacing = '0', textShadow = 'none', alignContent = 'start') => {
79
+ const { quill } = getEditorContext();
80
+ if (!quill)
81
+ return html;
82
+ const { scaleX, scaleY } = e.worldTransform;
83
+ const zoomScale = Math.max(Math.abs(scaleX), Math.abs(scaleY));
84
+ const wrapper = document.createElement('div');
85
+ wrapper.innerHTML = html;
86
+ const wrapperStyle = {
87
+ fontSize: `${fontSize}px`,
88
+ lineHeight,
89
+ letterSpacing: `${letterSpacing}px`,
90
+ textShadow
91
+ };
92
+ wrapper.querySelectorAll('p,ol,ul').forEach((p) => {
93
+ Object.assign(p.style, wrapperStyle);
94
+ });
95
+ let str = wrapper.innerHTML;
96
+ if (/<p\b[^>]*><\/p>/.test(str)) {
97
+ str = str.replace(/<p\b([^>]*)><\/p>/g, '<p$1>&nbsp;</p>');
98
+ }
99
+ let height;
100
+ const div = document.querySelector('#textInnerEditor');
101
+ const actualHeight = Number((quill.scroll.domNode.scrollHeight / zoomScale).toFixed(0));
102
+ const actualWidth = Number((quill.scroll.domNode.scrollWidth / zoomScale).toFixed(0));
103
+ if (['center', 'end'].includes(e.data.textData.alignContent)) {
104
+ if (e.parent.height < actualHeight) {
105
+ e.data.textData.alignContent = 'start';
106
+ height = (actualHeight || e.__layout.boxBounds.height) + 'px';
107
+ }
108
+ else {
109
+ height = `${e.parent.height}px`;
110
+ }
111
+ }
112
+ else {
113
+ if (e.parent.height < actualHeight) {
114
+ height = (actualHeight || e.__layout.boxBounds.height) + 'px';
115
+ }
116
+ else {
117
+ height = `${e.parent.height}px`;
118
+ }
119
+ }
120
+ const style = `<style>sub,sup{font-size:63%;}.ql-ui{position:absolute}ol,ul{counter-reset:list-0;padding-left:1.5em;margin:0}ol>li,ul>li{counter-increment:list-0;list-style-type:none;position:relative;padding-left:0;margin:0}ol>li::before{content:counter(list-0,decimal) '. ';position:absolute;left:-1.5em;width:1.2em;text-align:right}ul>li::before{content:'\\u2022';position:absolute;left:-1.5em;width:1.2em;text-align:right}li[data-list]{counter-set:list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}.ql-align-center{text-align:center}.ql-align-right{text-align:right}.ql-align-left{text-align:left}.ql-align-justify{text-align:justify}</style>`;
121
+ let divBox = '';
122
+ if (e.parent.children[0].tag.includes('Shape')) {
123
+ divBox =
124
+ style +
125
+ `<div style="width: ${e.parent.width}px;height: ${e.parent.height}px;overflow-wrap:break-word;word-break:break-all;align-content:center;">${str}</div>`;
126
+ }
127
+ else if (e.data.canChangeBox) {
128
+ divBox =
129
+ style +
130
+ `<div style="width: ${e.parent.width}px;height:${height};overflow-wrap:break-word;word-break:break-all;align-content:${alignContent};">${str}</div>`;
131
+ }
132
+ else {
133
+ if (e.data.textData.italic) {
134
+ divBox = style + str;
135
+ }
136
+ else {
137
+ divBox = style + `<div style="width: ${actualWidth - 10}px">${str}</div>`;
138
+ }
139
+ }
140
+ console.log(divBox, '最终的html内容');
141
+ return divBox;
142
+ };
143
+ const setHTMLText = (key, value, base64font, editor, isInnerEditor) => {
144
+ const { quill } = getEditorContext();
145
+ if (!quill) {
146
+ console.error('Quill editor not initialized');
147
+ return;
148
+ }
149
+ const rangeStr = localStorage.getItem('selection-change');
150
+ const range = rangeStr ? JSON.parse(rangeStr) : null;
151
+ if (range && isInnerEditor) {
152
+ quill.setSelection(range.index, range.length);
153
+ }
154
+ if (!editor || !editor.dateEdit) {
155
+ console.warn('Editor context not available for dateEdit');
156
+ return;
157
+ }
158
+ editor.dateEdit(async (e) => {
159
+ if (key === 'font') {
160
+ const fontSimpleName = value.code.replace(/\s+/g, '');
161
+ if (isInnerEditor) {
162
+ if (range && range.length) {
163
+ quill.formatText(range.index, range.length, key, fontSimpleName);
164
+ }
165
+ else {
166
+ quill.formatText(0, quill.getLength() - 1, key, fontSimpleName);
167
+ }
168
+ (0, exports.updataHtmlText)(e, base64font ?? null, value ?? null);
169
+ }
170
+ else {
171
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
172
+ quill.formatText(0, quill.getLength() - 1, key, fontSimpleName);
173
+ (0, exports.updataHtmlText)(e, base64font ?? null, value ?? null);
174
+ }
175
+ }
176
+ else if (key === 'fontSize') {
177
+ e.data.textData[key] = value;
178
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
179
+ (0, exports.updataHtmlText)(e, base64font ?? null);
180
+ }
181
+ else if (key === 'textCase') {
182
+ if (isInnerEditor && range) {
183
+ const text = quill.getText(range.index, range.length);
184
+ const formats = quill.getFormat(range.index, range.length);
185
+ quill.deleteText(range.index, range.length);
186
+ let convertedText;
187
+ if (text === text.toUpperCase() && /[A-Z]/.test(text)) {
188
+ convertedText = text.toLowerCase();
189
+ }
190
+ else if (text === text.toLowerCase() && /[a-z]/.test(text)) {
191
+ convertedText = text.toUpperCase();
192
+ }
193
+ else {
194
+ convertedText = text.toUpperCase();
195
+ }
196
+ quill.insertText(range.index, convertedText, formats);
197
+ if (range && isInnerEditor) {
198
+ quill.setSelection(range.index, range.length);
199
+ }
200
+ }
201
+ }
202
+ else if (key === 'script') {
203
+ let val = 'sub';
204
+ if (value === 'super')
205
+ val = 'sup';
206
+ if (isInnerEditor) {
207
+ if (range && range.length) {
208
+ quill.formatText(range.index, range.length, key, quill.getFormat(range).script === value ? false : val);
209
+ }
210
+ else {
211
+ quill.formatText(0, quill.getLength() - 1, key, quill.getFormat().script === value ? false : val);
212
+ }
213
+ }
214
+ else {
215
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
216
+ quill.formatText(0, quill.getLength() - 1, key, quill.getFormat().script === value ? false : val);
217
+ (0, exports.updataHtmlText)(e);
218
+ }
219
+ }
220
+ else if (key === 'align') {
221
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
222
+ if (isInnerEditor) {
223
+ quill.format(key, value);
224
+ }
225
+ else {
226
+ quill.formatLine(0, quill.getLength(), key, value);
227
+ }
228
+ (0, exports.updataHtmlText)(e);
229
+ }
230
+ else if (key === 'alignContent') {
231
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
232
+ e.data.textData[key] = value;
233
+ (0, exports.updataHtmlText)(e);
234
+ }
235
+ else if (key === 'color') {
236
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
237
+ quill.formatText(0, quill.getLength() - 1, key, value);
238
+ if (e.tag === 'HTMLText') {
239
+ (0, exports.updataHtmlText)(e);
240
+ }
241
+ else if (e.parent.findOne && e.parent.findOne('HTMLText')) {
242
+ (0, exports.updataHtmlText)(e.parent.findOne('HTMLText'));
243
+ }
244
+ }
245
+ else if (key === 'textShadow') {
246
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
247
+ e.data.textData[key] = value;
248
+ (0, exports.updataHtmlText)(e);
249
+ }
250
+ else if (key === 'list') {
251
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
252
+ if (isInnerEditor) {
253
+ const [line] = quill.getLine(range?.index || 0);
254
+ if (line.formats().list) {
255
+ quill.format(key, false);
256
+ }
257
+ else {
258
+ quill.format(key, value);
259
+ }
260
+ }
261
+ else {
262
+ const [line] = quill.getLine(range?.index || 0);
263
+ if (line.formats().list) {
264
+ quill.formatLine(0, quill.getLength(), key, false);
265
+ }
266
+ else {
267
+ quill.formatLine(0, quill.getLength(), key, value);
268
+ }
269
+ }
270
+ (0, exports.updataHtmlText)(e);
271
+ }
272
+ else {
273
+ if (isInnerEditor) {
274
+ if (range && range.length) {
275
+ quill.formatText(range.index, range.length, key, !quill.getFormat(range)[key]);
276
+ }
277
+ else {
278
+ quill.formatText(0, quill.getLength() - 1, key, !quill.getFormat()[key]);
279
+ }
280
+ }
281
+ else {
282
+ editor.isMultiSelect && editor.isMultiSelect() && quill.clipboard.dangerouslyPasteHTML(e.text);
283
+ quill.formatText(0, quill.getLength() - 1, key, !quill.getFormat()[key]);
284
+ (0, exports.updataHtmlText)(e);
285
+ }
286
+ }
287
+ }, 1);
288
+ };
289
+ exports.setHTMLText = setHTMLText;
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@chenyomi/leafer-htmltext-editor",
3
+ "version": "1.0.0",
4
+ "description": "A text editor plugin for Leafer UI with HTML text support and Quill integration",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "npm run build:cjs && npm run build:esm && npm run build:types",
10
+ "build:cjs": "tsc --module commonjs --outDir dist",
11
+ "build:esm": "tsc --module esnext --outDir dist/esm && mv dist/esm/index.js dist/index.esm.js",
12
+ "build:types": "tsc --declaration --emitDeclarationOnly --outDir dist",
13
+ "prepublishOnly": "npm run build"
14
+ },
15
+ "keywords": [
16
+ "leafer",
17
+ "leafer-ui",
18
+ "text-editor",
19
+ "quill",
20
+ "html-text",
21
+ "wysiwyg",
22
+ "rich-text"
23
+ ],
24
+ "author": "chenyomi",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/yourusername/leafer-htmltext-editor.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/yourusername/leafer-htmltext-editor/issues"
32
+ },
33
+ "homepage": "https://github.com/yourusername/leafer-htmltext-editor#readme",
34
+ "peerDependencies": {
35
+ "@leafer-ui/core": ">=1.0.0",
36
+ "@leafer-in/editor": ">=1.0.0",
37
+ "@leafer-in/interface": ">=1.0.0",
38
+ "@leafer-in/html": ">=1.0.0",
39
+ "leafer-ui": ">=1.0.0",
40
+ "quill": ">=2.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^20.0.0",
44
+ "typescript": "^5.0.0"
45
+ },
46
+ "files": [
47
+ "dist",
48
+ "src",
49
+ "README.md",
50
+ "LICENSE"
51
+ ],
52
+ "publishConfig": {
53
+ "access": "public"
54
+ }
55
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * TextEditTool 插件
3
+ * 用于在编辑器中提供带有圆角的矩形的控制点编辑功能
4
+ *
5
+ * 作者: chenyomi
6
+ */
7
+
8
+ import { DragEvent, PointerEvent, WatchEvent } from 'leafer-ui';
9
+ import { EditTool, Editor, registerEditTool, EditorScaleEvent, EditorMoveEvent } from '@leafer-in/editor';
10
+ import { updataHtmlText } from '../utils';
11
+ import { TextEditor } from '../TextEditor';
12
+
13
+ // 简单的防抖函数实现
14
+ function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void {
15
+ let timeout: ReturnType<typeof setTimeout> | null = null;
16
+ return function(this: any, ...args: Parameters<T>) {
17
+ if (timeout) clearTimeout(timeout);
18
+ timeout = setTimeout(() => func.apply(this, args), wait);
19
+ };
20
+ }
21
+
22
+ @registerEditTool()
23
+ export class TextEditTool extends EditTool {
24
+ public get tag() {
25
+ return 'TextEditTool';
26
+ }
27
+
28
+ public quill: any = null;
29
+ private _dragRAF: number | null = null;
30
+ private updateBoxDebounced: (text: any) => void;
31
+
32
+ constructor(editor: any) {
33
+ super(editor);
34
+ this.eventIds = [];
35
+ this.updateBoxDebounced = debounce((text: any) => {
36
+ updataHtmlText(text);
37
+ }, 300);
38
+ }
39
+
40
+ public addEvent(): void {
41
+ if (!this.editor?.element) return;
42
+
43
+ const text = this.editor.element.findOne('HTMLText');
44
+ if (!text) return;
45
+
46
+ const { scaleX, scaleY } = text.worldTransform;
47
+ const zoomScale = Math.max(Math.abs(scaleX), Math.abs(scaleY));
48
+ const div: any = document.querySelector('#textInnerEditor');
49
+
50
+ if (!div) return;
51
+
52
+ const { style } = div;
53
+
54
+ this.eventIds = [
55
+ this.editor.on_(EditorScaleEvent.SCALE, (e: any) => {
56
+ if (!text.data) text.data = {};
57
+ if (!text.data.canChangeBox) {
58
+ text.data.canChangeBox = true;
59
+ }
60
+ if (text.data.canChangeBox && text.parent) {
61
+ const parentWidth = text.parent.width;
62
+ if (parentWidth !== undefined) {
63
+ style.width = parentWidth * zoomScale + 'px';
64
+ style.height = 'auto';
65
+ }
66
+ }
67
+ this.updateBoxDebounced(text);
68
+ }),
69
+ this.editor.on_(PointerEvent.DOUBLE_TAP, () => {
70
+ if (!text.parent?.locked) {
71
+ this.editor.openInnerEditor(text, true);
72
+ }
73
+ })
74
+ ];
75
+ }
76
+
77
+ public onLoad(): void {
78
+ if (!this.editor?.element) return;
79
+
80
+ const text: any = this.editor.element.findOne('HTMLText');
81
+ if (!text) return;
82
+
83
+ const { scaleX, scaleY } = text.worldTransform;
84
+ const zoomScale = Math.max(Math.abs(scaleX), Math.abs(scaleY));
85
+
86
+ this.addEvent();
87
+ this.editBox.add(this.view);
88
+
89
+ this.quill = TextEditor.quill;
90
+ if (this.quill && text.text) {
91
+ this.quill.clipboard.dangerouslyPasteHTML(text.text);
92
+ }
93
+
94
+ const div: any = document.querySelector('#textInnerEditor');
95
+ if (!div) return;
96
+
97
+ const { style } = div;
98
+
99
+ if (text.data?.canChangeBox && text.parent) {
100
+ const parentWidth = text.parent.width;
101
+ const parentHeight = text.parent.height;
102
+ if (parentWidth !== undefined) {
103
+ style.width = parentWidth * zoomScale + 'px';
104
+ }
105
+ if (parentHeight !== undefined) {
106
+ style.height = parentHeight * zoomScale + 'px';
107
+ }
108
+ } else {
109
+ style.width = 'auto';
110
+ style.height = 'auto';
111
+ }
112
+ }
113
+
114
+ private isUpdatingPoints = false;
115
+ private curveAmount = 0;
116
+
117
+ public updateChangeBoxBound(text: any): void {
118
+ if (text && text.__layout?.boxBounds) {
119
+ text.set({
120
+ width: text.__layout.boxBounds.width,
121
+ height: text.__layout.boxBounds.height
122
+ });
123
+ }
124
+ }
125
+
126
+ public onUpdate(): void {
127
+ if (!this.editor?.element) return;
128
+
129
+ const text = this.editor.element.findOne('HTMLText');
130
+ if (!text) return;
131
+
132
+ const el = this.editor.element;
133
+ console.log('文本bound更新');
134
+
135
+ // 记录上一次的 curveAmount,只有变化时才触发
136
+ if (this.curveAmount === (text as any).curveAmount) return;
137
+ if (this.isUpdatingPoints) return;
138
+ }
139
+
140
+ public onUnload(): void {
141
+ this.editor.off_(this.eventIds);
142
+ }
143
+
144
+ public onDestroy(): void {}
145
+ }