@opentiny/fluent-editor 4.1.1 → 4.1.2

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.
@@ -60,6 +60,37 @@ function replaceDeltaImage(delta, imageUrls, imagePlaceholder) {
60
60
  return newDelta;
61
61
  }, new Delta());
62
62
  }
63
+ const WHITE_SPACE_CHAR_RE = /^[\u3000\u0020]$/;
64
+ const WHITE_SPACE_OR_NBSP_RE = /^[\u3000\u0020\u00A0]$/;
65
+ function replaceStrWhiteSpace(str) {
66
+ let textWithWhiteSpace = "";
67
+ let beginHasChar = false;
68
+ let consecutiveSpaces = 0;
69
+ for (const char of str) {
70
+ if (WHITE_SPACE_CHAR_RE.test(char)) {
71
+ consecutiveSpaces += 1;
72
+ if (!beginHasChar) {
73
+ textWithWhiteSpace += " ";
74
+ } else if (consecutiveSpaces === 1) {
75
+ textWithWhiteSpace += char;
76
+ } else {
77
+ textWithWhiteSpace += " ";
78
+ }
79
+ } else {
80
+ consecutiveSpaces = 0;
81
+ textWithWhiteSpace += char;
82
+ beginHasChar = true;
83
+ }
84
+ }
85
+ return textWithWhiteSpace;
86
+ }
87
+ function shouldInsertNbspOnSpace(prefix) {
88
+ if (!prefix) {
89
+ return true;
90
+ }
91
+ const lastChar = prefix.slice(-1);
92
+ return WHITE_SPACE_OR_NBSP_RE.test(lastChar);
93
+ }
63
94
  function splitWithBreak(insertContent) {
64
95
  const lines = [];
65
96
  const insertStr = insertContent;
@@ -131,7 +162,9 @@ export {
131
162
  isNullOrUndefined,
132
163
  omit,
133
164
  replaceDeltaImage,
165
+ replaceStrWhiteSpace,
134
166
  sanitize,
167
+ shouldInsertNbspOnSpace,
135
168
  splitWithBreak
136
169
  };
137
170
  //# sourceMappingURL=editor.utils.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.utils.es.js","sources":["../../../src/config/editor.utils.ts"],"sourcesContent":["import Quill from 'quill'\n\nconst Delta = Quill.import('delta')\n\n// color hex to rgba\nexport function hexToRgbA(hex: string) {\n let color\n if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {\n color = hex.substring(1).split('')\n if (color.length === 3) {\n color = [color[0], color[0], color[1], color[1], color[2], color[2]]\n }\n color = `0x${color.join('')}`\n return (\n `rgba(${\n [(color >> 16) & 255, (color >> 8) & 255, color & 255].join(',')\n },1)`\n )\n }\n}\n\n/**\n * 将File格式的图片转换成Url格式\n * @param imageFile File格式的图片\n */\nexport function imageFileToUrl(imageFile) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(imageFile)\n reader.onload = function (e) {\n resolve(e.target.result)\n }\n reader.onerror = reject\n }).catch((error) => {\n console.error('Error reading file:', error)\n })\n}\n\n/**\n * 将Url格式的图片转换成File格式\n * @param imageUrl 图片的URL\n */\nexport function imageUrlToFile(imageUrl, isErrorImage?: boolean) {\n return new Promise((resolve, reject) => {\n fetch(imageUrl, {\n method: 'get',\n mode: 'no-cors',\n })\n .then(res => res.blob())\n .then((blob) => {\n if (!blob.type.includes('image') || !blob.type) {\n return reject()\n }\n const fileType = blob.type.replace(/^.*\\//, '')\n const fileName = isErrorImage\n ? 'editorx-error-image.png'\n : `image-${new Date().getTime()}.${fileType}`\n const file = new File([blob], fileName, blob)\n resolve(file)\n })\n .catch(reject)\n })\n}\n\nexport function isNullOrUndefined(param) {\n return param === null || param === undefined\n}\n\n/**\n * omit\n * @param obj target Object\n * @param uselessKeys keys of removed properties\n * @return new Object without useless properties\n */\nexport function omit(obj, uselessKeys) {\n return (\n obj\n && Object.keys(obj).reduce((acc, key) => {\n return uselessKeys.includes(key) ? acc : { ...acc, [key]: obj[key] }\n }, {})\n )\n}\n\n/**\n * 将delta中的图片替换成制定的图片数组,用于图片上传到服务器的场景\n * @param delta 原始delta\n * @param imageUrls 图片数组\n * @param imagePlaceholder 标识是否是占位图的数组,与图片数组一一对应\n * @return 替换之后的delta\n */\nexport function replaceDeltaImage(delta, imageUrls, imagePlaceholder) {\n let imageIndex = 0\n return delta.reduce((newDelta, op) => {\n if (op.insert.image && !op.insert.image.hasExisted) {\n const attributes = imagePlaceholder[imageIndex]\n ? { ...op.attributes, width: 'auto', height: 225 } // 占位图片应该固定大小\n : op.attributes\n newDelta.insert({ image: imageUrls[imageIndex] }, attributes)\n imageIndex++\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nexport function splitWithBreak(insertContent: string) {\n const lines = []\n const insertStr = insertContent\n let start = 0\n for (let i = 0; i < insertContent.length; i++) {\n if (insertStr.charAt(i) === '\\n') {\n if (i === 0) {\n lines.push('\\n')\n }\n else {\n lines.push(insertStr.substring(start, i))\n lines.push('\\n')\n }\n start = i + 1\n }\n }\n\n const tailStr = insertStr.substring(start)\n if (tailStr) {\n lines.push(tailStr)\n }\n\n return lines\n}\n\n/**\n * getEventComposedPath\n * compatibility fixed for Event.path/Event.composedPath\n * Event.path is only for chrome/opera\n * Event.composedPath is for Safari, FF\n * Neither for Micro Edge\n * @return an array of event.path\n */\nexport function getEventComposedPath(evt) {\n let path\n // chrome, opera, safari, firefox\n path = evt.path || (evt.composedPath && evt.composedPath())\n\n // other: edge\n if (path === undefined && evt.target) {\n path = []\n let target = evt.target\n path.push(target)\n\n while (target && target.parentNode) {\n target = target.parentNode\n path.push(target)\n }\n }\n\n return path\n}\n\nexport function sanitize(url, protocols) {\n const anchor = document.createElement('a')\n anchor.href = url\n const protocol = anchor.href.slice(0, anchor.href.indexOf(':'))\n return protocols.includes(protocol)\n}\n\nexport function hadProtocol(url: string) {\n if (!url || !/^(?:f|ht)tps?\\:\\/\\//.test(url)) {\n return false\n }\n return true\n}\n\nexport function isInside(position, dom) {\n const areaPosition = dom.getBoundingClientRect()\n const { pageX, pageY } = position\n // getBoundingClientRect是不考虑窗口滚动的\n const left = pageX - window.scrollX\n const top = pageY - window.scrollY\n const {\n left: areaLeft,\n top: areaTop,\n width: areaWidth,\n height: areaHeight,\n } = areaPosition\n const inside\n = left > areaLeft\n && left < areaLeft + areaWidth\n && top > areaTop\n && top < areaTop + areaHeight\n return inside\n}\n"],"names":[],"mappings":";AAEA,MAAM,QAAQ,MAAM,OAAO,OAAO;AAG3B,SAAS,UAAU,KAAa;AACrC,MAAI;AACJ,MAAI,2BAA2B,KAAK,GAAG,GAAG;AACxC,YAAQ,IAAI,UAAU,CAAC,EAAE,MAAM,EAAE;AACjC,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,IACrE;AACA,YAAQ,KAAK,MAAM,KAAK,EAAE,CAAC;AAC3B,WACE,QACE,CAAE,SAAS,KAAM,KAAM,SAAS,IAAK,KAAK,QAAQ,GAAG,EAAE,KAAK,GAAG,CACjE;AAAA,EAEJ;AACF;AAMO,SAAS,eAAe,WAAW;AACxC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,SAAS,IAAI,WAAA;AACnB,WAAO,cAAc,SAAS;AAC9B,WAAO,SAAS,SAAU,GAAG;AAC3B,cAAQ,EAAE,OAAO,MAAM;AAAA,IACzB;AACA,WAAO,UAAU;AAAA,EACnB,CAAC,EAAE,MAAM,CAAC,UAAU;AAClB,YAAQ,MAAM,uBAAuB,KAAK;AAAA,EAC5C,CAAC;AACH;AAMO,SAAS,eAAe,UAAU,cAAwB;AAC/D,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,UAAU;AAAA,MACd,QAAQ;AAAA,MACR,MAAM;AAAA,IAAA,CACP,EACE,KAAK,CAAA,QAAO,IAAI,MAAM,EACtB,KAAK,CAAC,SAAS;AACd,UAAI,CAAC,KAAK,KAAK,SAAS,OAAO,KAAK,CAAC,KAAK,MAAM;AAC9C,eAAO,OAAA;AAAA,MACT;AACA,YAAM,WAAW,KAAK,KAAK,QAAQ,SAAS,EAAE;AAC9C,YAAM,WAAW,eACb,4BACA,UAAS,oBAAI,QAAO,QAAA,CAAS,IAAI,QAAQ;AAC7C,YAAM,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,UAAU,IAAI;AAC5C,cAAQ,IAAI;AAAA,IACd,CAAC,EACA,MAAM,MAAM;AAAA,EACjB,CAAC;AACH;AAEO,SAAS,kBAAkB,OAAO;AACvC,SAAO,UAAU,QAAQ,UAAU;AACrC;AAQO,SAAS,KAAK,KAAK,aAAa;AACrC,SACE,OACG,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,KAAK,QAAQ;AACvC,WAAO,YAAY,SAAS,GAAG,IAAI,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,GAAG,IAAI,GAAG,EAAA;AAAA,EACnE,GAAG,CAAA,CAAE;AAET;AASO,SAAS,kBAAkB,OAAO,WAAW,kBAAkB;AACpE,MAAI,aAAa;AACjB,SAAO,MAAM,OAAO,CAAC,UAAU,OAAO;AACpC,QAAI,GAAG,OAAO,SAAS,CAAC,GAAG,OAAO,MAAM,YAAY;AAClD,YAAM,aAAa,iBAAiB,UAAU,IAC1C,EAAE,GAAG,GAAG,YAAY,OAAO,QAAQ,QAAQ,IAAA,IAC3C,GAAG;AACP,eAAS,OAAO,EAAE,OAAO,UAAU,UAAU,EAAA,GAAK,UAAU;AAC5D;AAAA,IACF,OACK;AACH,eAAS,OAAO,GAAG,QAAQ,GAAG,UAAU;AAAA,IAC1C;AACA,WAAO;AAAA,EACT,GAAG,IAAI,OAAO;AAChB;AAEO,SAAS,eAAe,eAAuB;AACpD,QAAM,QAAQ,CAAA;AACd,QAAM,YAAY;AAClB,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,QAAI,UAAU,OAAO,CAAC,MAAM,MAAM;AAChC,UAAI,MAAM,GAAG;AACX,cAAM,KAAK,IAAI;AAAA,MACjB,OACK;AACH,cAAM,KAAK,UAAU,UAAU,OAAO,CAAC,CAAC;AACxC,cAAM,KAAK,IAAI;AAAA,MACjB;AACA,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AAEA,QAAM,UAAU,UAAU,UAAU,KAAK;AACzC,MAAI,SAAS;AACX,UAAM,KAAK,OAAO;AAAA,EACpB;AAEA,SAAO;AACT;AAUO,SAAS,qBAAqB,KAAK;AACxC,MAAI;AAEJ,SAAO,IAAI,QAAS,IAAI,gBAAgB,IAAI,aAAA;AAG5C,MAAI,SAAS,UAAa,IAAI,QAAQ;AACpC,WAAO,CAAA;AACP,QAAI,SAAS,IAAI;AACjB,SAAK,KAAK,MAAM;AAEhB,WAAO,UAAU,OAAO,YAAY;AAClC,eAAS,OAAO;AAChB,WAAK,KAAK,MAAM;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,SAAS,KAAK,WAAW;AACvC,QAAM,SAAS,SAAS,cAAc,GAAG;AACzC,SAAO,OAAO;AACd,QAAM,WAAW,OAAO,KAAK,MAAM,GAAG,OAAO,KAAK,QAAQ,GAAG,CAAC;AAC9D,SAAO,UAAU,SAAS,QAAQ;AACpC;AAEO,SAAS,YAAY,KAAa;AACvC,MAAI,CAAC,OAAO,CAAC,sBAAsB,KAAK,GAAG,GAAG;AAC5C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,SAAS,UAAU,KAAK;AACtC,QAAM,eAAe,IAAI,sBAAA;AACzB,QAAM,EAAE,OAAO,MAAA,IAAU;AAEzB,QAAM,OAAO,QAAQ,OAAO;AAC5B,QAAM,MAAM,QAAQ,OAAO;AAC3B,QAAM;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,EAAA,IACN;AACJ,QAAM,SACF,OAAO,YACJ,OAAO,WAAW,aAClB,MAAM,WACN,MAAM,UAAU;AACvB,SAAO;AACT;"}
1
+ {"version":3,"file":"editor.utils.es.js","sources":["../../../src/config/editor.utils.ts"],"sourcesContent":["import Quill from 'quill'\n\nconst Delta = Quill.import('delta')\n\n// color hex to rgba\nexport function hexToRgbA(hex: string) {\n let color\n if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {\n color = hex.substring(1).split('')\n if (color.length === 3) {\n color = [color[0], color[0], color[1], color[1], color[2], color[2]]\n }\n color = `0x${color.join('')}`\n return (\n `rgba(${\n [(color >> 16) & 255, (color >> 8) & 255, color & 255].join(',')\n },1)`\n )\n }\n}\n\n/**\n * 将File格式的图片转换成Url格式\n * @param imageFile File格式的图片\n */\nexport function imageFileToUrl(imageFile) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(imageFile)\n reader.onload = function (e) {\n resolve(e.target.result)\n }\n reader.onerror = reject\n }).catch((error) => {\n console.error('Error reading file:', error)\n })\n}\n\n/**\n * 将Url格式的图片转换成File格式\n * @param imageUrl 图片的URL\n */\nexport function imageUrlToFile(imageUrl, isErrorImage?: boolean) {\n return new Promise((resolve, reject) => {\n fetch(imageUrl, {\n method: 'get',\n mode: 'no-cors',\n })\n .then(res => res.blob())\n .then((blob) => {\n if (!blob.type.includes('image') || !blob.type) {\n return reject()\n }\n const fileType = blob.type.replace(/^.*\\//, '')\n const fileName = isErrorImage\n ? 'editorx-error-image.png'\n : `image-${new Date().getTime()}.${fileType}`\n const file = new File([blob], fileName, blob)\n resolve(file)\n })\n .catch(reject)\n })\n}\n\nexport function isNullOrUndefined(param) {\n return param === null || param === undefined\n}\n\n/**\n * omit\n * @param obj target Object\n * @param uselessKeys keys of removed properties\n * @return new Object without useless properties\n */\nexport function omit(obj, uselessKeys) {\n return (\n obj\n && Object.keys(obj).reduce((acc, key) => {\n return uselessKeys.includes(key) ? acc : { ...acc, [key]: obj[key] }\n }, {})\n )\n}\n\n/**\n * 将delta中的图片替换成制定的图片数组,用于图片上传到服务器的场景\n * @param delta 原始delta\n * @param imageUrls 图片数组\n * @param imagePlaceholder 标识是否是占位图的数组,与图片数组一一对应\n * @return 替换之后的delta\n */\nexport function replaceDeltaImage(delta, imageUrls, imagePlaceholder) {\n let imageIndex = 0\n return delta.reduce((newDelta, op) => {\n if (op.insert.image && !op.insert.image.hasExisted) {\n const attributes = imagePlaceholder[imageIndex]\n ? { ...op.attributes, width: 'auto', height: 225 } // 占位图片应该固定大小\n : op.attributes\n newDelta.insert({ image: imageUrls[imageIndex] }, attributes)\n imageIndex++\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nconst WHITE_SPACE_CHAR_RE = /^[\\u3000\\u0020]$/\nconst WHITE_SPACE_OR_NBSP_RE = /^[\\u3000\\u0020\\u00A0]$/\n\n/**\n * 将连续空格转为不间断空格,以便 innerHTML 输出 &nbsp; 并在预览中保留间距。\n * - 行首空白:全部转为 \\u00A0\n * - 正文内连续空白:保留第一个普通空格,其余转为 \\u00A0\n */\nexport function replaceStrWhiteSpace(str: string) {\n let textWithWhiteSpace = ''\n let beginHasChar = false\n let consecutiveSpaces = 0\n for (const char of str) {\n if (WHITE_SPACE_CHAR_RE.test(char)) {\n consecutiveSpaces += 1\n if (!beginHasChar) {\n textWithWhiteSpace += '\\u00A0'\n }\n else if (consecutiveSpaces === 1) {\n textWithWhiteSpace += char\n }\n else {\n textWithWhiteSpace += '\\u00A0'\n }\n }\n else {\n consecutiveSpaces = 0\n textWithWhiteSpace += char\n beginHasChar = true\n }\n }\n return textWithWhiteSpace\n}\n\n/** 空格键是否应插入不间断空格(与 replaceStrWhiteSpace 规则一致) */\nexport function shouldInsertNbspOnSpace(prefix: string) {\n if (!prefix) {\n return true\n }\n const lastChar = prefix.slice(-1)\n return WHITE_SPACE_OR_NBSP_RE.test(lastChar)\n}\n\nexport function splitWithBreak(insertContent: string) {\n const lines = []\n const insertStr = insertContent\n let start = 0\n for (let i = 0; i < insertContent.length; i++) {\n if (insertStr.charAt(i) === '\\n') {\n if (i === 0) {\n lines.push('\\n')\n }\n else {\n lines.push(insertStr.substring(start, i))\n lines.push('\\n')\n }\n start = i + 1\n }\n }\n\n const tailStr = insertStr.substring(start)\n if (tailStr) {\n lines.push(tailStr)\n }\n\n return lines\n}\n\n/**\n * getEventComposedPath\n * compatibility fixed for Event.path/Event.composedPath\n * Event.path is only for chrome/opera\n * Event.composedPath is for Safari, FF\n * Neither for Micro Edge\n * @return an array of event.path\n */\nexport function getEventComposedPath(evt) {\n let path\n // chrome, opera, safari, firefox\n path = evt.path || (evt.composedPath && evt.composedPath())\n\n // other: edge\n if (path === undefined && evt.target) {\n path = []\n let target = evt.target\n path.push(target)\n\n while (target && target.parentNode) {\n target = target.parentNode\n path.push(target)\n }\n }\n\n return path\n}\n\nexport function sanitize(url, protocols) {\n const anchor = document.createElement('a')\n anchor.href = url\n const protocol = anchor.href.slice(0, anchor.href.indexOf(':'))\n return protocols.includes(protocol)\n}\n\nexport function hadProtocol(url: string) {\n if (!url || !/^(?:f|ht)tps?\\:\\/\\//.test(url)) {\n return false\n }\n return true\n}\n\nexport function isInside(position, dom) {\n const areaPosition = dom.getBoundingClientRect()\n const { pageX, pageY } = position\n // getBoundingClientRect是不考虑窗口滚动的\n const left = pageX - window.scrollX\n const top = pageY - window.scrollY\n const {\n left: areaLeft,\n top: areaTop,\n width: areaWidth,\n height: areaHeight,\n } = areaPosition\n const inside\n = left > areaLeft\n && left < areaLeft + areaWidth\n && top > areaTop\n && top < areaTop + areaHeight\n return inside\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { isNullOrUndefined } from "./editor.utils.es.js";
2
- import { getEventComposedPath, hadProtocol, hexToRgbA, imageFileToUrl, imageUrlToFile, isInside, omit, replaceDeltaImage, sanitize, splitWithBreak } from "./editor.utils.es.js";
2
+ import { getEventComposedPath, hadProtocol, hexToRgbA, imageFileToUrl, imageUrlToFile, isInside, omit, replaceDeltaImage, replaceStrWhiteSpace, sanitize, shouldInsertNbspOnSpace, splitWithBreak } from "./editor.utils.es.js";
3
3
  import { AUDIO_VIDEO_UPLOADER_MIME_TYPES, BIG_DELTA_LIMIT, CHANGE_LANGUAGE_EVENT, COMPRESSED_UPLOADER_MIME_TYPES, DEFAULT_TOOLBAR, DOC_UPLOADER_MIME_TYPES, FILE_UPLOADER_MIME_TYPES, FULL_TOOLBAR, IMAGE_UPLOADER_MIME_TYPES, OTHER_FILE_UPLOADER_MIME_TYPES, PPT_UPLOADER_MIME_TYPES, XSL_UPLOADER_MIME_TYPES, defaultLanguage } from "./editor.config.es.js";
4
4
  import "./types/index.es.js";
5
5
  function inputFile(type, accept) {
@@ -64,7 +64,9 @@ export {
64
64
  namespace,
65
65
  omit,
66
66
  replaceDeltaImage,
67
+ replaceStrWhiteSpace,
67
68
  sanitize,
69
+ shouldInsertNbspOnSpace,
68
70
  splitWithBreak
69
71
  };
70
72
  //# sourceMappingURL=index.es.js.map
package/es/index.es.js CHANGED
@@ -12,7 +12,7 @@ import { FontStyle } from "./attributors/font-style.es.js";
12
12
  import { LineHeightStyle } from "./attributors/line-height.es.js";
13
13
  import { TextIndentStyle } from "./attributors/text-indent.es.js";
14
14
  import { AUDIO_VIDEO_UPLOADER_MIME_TYPES, BIG_DELTA_LIMIT, CHANGE_LANGUAGE_EVENT, COMPRESSED_UPLOADER_MIME_TYPES, DEFAULT_TOOLBAR, DOC_UPLOADER_MIME_TYPES, FILE_UPLOADER_MIME_TYPES, FULL_TOOLBAR, IMAGE_UPLOADER_MIME_TYPES, OTHER_FILE_UPLOADER_MIME_TYPES, PPT_UPLOADER_MIME_TYPES, XSL_UPLOADER_MIME_TYPES, defaultLanguage } from "./config/editor.config.es.js";
15
- import { getEventComposedPath, hadProtocol, hexToRgbA, imageFileToUrl, imageUrlToFile, isInside, isNullOrUndefined, omit, replaceDeltaImage, sanitize, splitWithBreak } from "./config/editor.utils.es.js";
15
+ import { getEventComposedPath, hadProtocol, hexToRgbA, imageFileToUrl, imageUrlToFile, isInside, isNullOrUndefined, omit, replaceDeltaImage, replaceStrWhiteSpace, sanitize, shouldInsertNbspOnSpace, splitWithBreak } from "./config/editor.utils.es.js";
16
16
  import { EmojiBlot } from "./formats/emoji.es.js";
17
17
  import { SoftBreak } from "./formats/soft-break.es.js";
18
18
  import { StrikeBlot } from "./formats/strike.es.js";
@@ -132,9 +132,11 @@ export {
132
132
  namespace,
133
133
  omit,
134
134
  replaceDeltaImage,
135
+ replaceStrWhiteSpace,
135
136
  sanitize,
136
137
  setAlignStyle,
137
138
  shortKey,
139
+ shouldInsertNbspOnSpace,
138
140
  splitWithBreak
139
141
  };
140
142
  //# sourceMappingURL=index.es.js.map
@@ -1,7 +1,7 @@
1
1
  import Quill from "quill";
2
2
  import { ERROR_IMAGE_PLACEHOLDER_EN, ERROR_IMAGE_PLACEHOLDER_CN } from "../config/base64-image.es.js";
3
3
  import { BIG_DELTA_LIMIT } from "../config/editor.config.es.js";
4
- import { isNullOrUndefined, replaceDeltaImage, imageUrlToFile, splitWithBreak, omit, hexToRgbA } from "../config/editor.utils.es.js";
4
+ import { isNullOrUndefined, replaceDeltaImage, imageUrlToFile, splitWithBreak, omit, hexToRgbA, replaceStrWhiteSpace } from "../config/editor.utils.es.js";
5
5
  import { isString } from "../utils/is.es.js";
6
6
  const Clipboard = Quill.import("modules/clipboard");
7
7
  const Delta = Quill.import("delta");
@@ -347,20 +347,6 @@ function rebuildDelta(delta, cellLine) {
347
347
  }, new Delta());
348
348
  return buildedDelta;
349
349
  }
350
- function replaceStrWhiteSpace(str) {
351
- const isWhiteSpace = (value) => /^(\u3000|\u0020){1}$/.test(value);
352
- let textWithWhiteSpace = "";
353
- let beginHasChar = false;
354
- for (const char of str) {
355
- if (isWhiteSpace(char) && !beginHasChar) {
356
- textWithWhiteSpace += " ";
357
- } else {
358
- textWithWhiteSpace += char;
359
- beginHasChar = true;
360
- }
361
- }
362
- return textWithWhiteSpace;
363
- }
364
350
  function replaceDeltaWhiteSpace(delta, rootBgColor) {
365
351
  return delta.reduce((newDelta, op) => {
366
352
  if (rootBgColor && op.attributes && op.attributes.color && !op.attributes.background) {
@@ -1 +1 @@
1
- {"version":3,"file":"custom-clipboard.es.js","sources":["../../../src/modules/custom-clipboard.ts"],"sourcesContent":["import type TypeClipboard from 'quill/modules/clipboard'\nimport type FluentEditor from '../fluent-editor'\nimport Quill from 'quill'\nimport {\n ERROR_IMAGE_PLACEHOLDER_CN,\n ERROR_IMAGE_PLACEHOLDER_EN,\n} from '../config/base64-image'\nimport { BIG_DELTA_LIMIT } from '../config/editor.config'\nimport {\n hexToRgbA,\n imageUrlToFile,\n isNullOrUndefined,\n omit,\n replaceDeltaImage,\n splitWithBreak,\n} from '../config/editor.utils'\nimport { isString } from '../utils/is'\n\nconst Clipboard = Quill.import('modules/clipboard') as typeof TypeClipboard\nconst Delta = Quill.import('delta')\n\nexport class CustomClipboard extends Clipboard {\n declare quill: FluentEditor\n\n constructor(public quill: FluentEditor) {\n super(quill)\n // 解决 quill 组件在中文输入法的情景下开始输入placeholder不消失的问题\n this.quill.root.addEventListener('input', () => {\n if (this.quill.root.innerText !== '\\\\n' && this.quill.root.classList.contains('ql-blank')) {\n this.quill.root.classList.toggle('ql-blank', false)\n }\n else {\n this.quill.options.placeholder = this.quill.getLangText('editor-placeholder')\n }\n })\n }\n\n prepareMatching(container: HTMLElement, nodeMatches) {\n const elementMatchers = []\n const textMatchers = []\n this.matchers.forEach((pair) => {\n const [selector, matcher] = pair\n if (selector === Node.TEXT_NODE) {\n textMatchers.push(matcher)\n }\n else if (selector === Node.ELEMENT_NODE) {\n elementMatchers.push(matcher)\n }\n else if (isString(selector)) {\n // word 的 v:shape 系列标签只能通过 getElementsByTagName 获取\n const vRegex = /v:(.+)/\n const nodeList = Array.from(\n vRegex.test(selector)\n ? container.getElementsByTagName(selector)\n : container.querySelectorAll(selector),\n )\n nodeList.forEach((node) => {\n if (nodeMatches.has(node)) {\n const matches = nodeMatches.get(node)\n matches.push(matcher)\n }\n else {\n nodeMatches.set(node, [matcher])\n }\n })\n }\n })\n return [elementMatchers, textMatchers]\n }\n\n onCaptureCopy(e, isCut = false) {\n if (e.defaultPrevented) {\n return\n }\n e.preventDefault()\n const [range] = this.quill.selection.getRange()\n if (isNullOrUndefined(range)) {\n return\n }\n const { html, text } = this.onCopy(range, isCut)\n\n // 兼容IE11浏览器`\n if (!e.clipboardData) {\n e.clipboardData = {\n types: 'text/plain',\n setData: (_type, value) => {\n // @ts-ignore\n return window.clipboardData.setData('Text', value)\n },\n }\n }\n\n // 复制代码时移除utf8中产生的不间断空格\\u00A0\n let plainText = text\n if (html.startsWith('<pre>')) {\n plainText = text.replace(/\\u00A0/g, ' ')\n }\n\n e.clipboardData.setData('text/html', html)\n e.clipboardData.setData('text/plain', plainText)\n if (isCut) {\n this.quill.deleteText(range, Quill.sources.USER)\n }\n }\n\n onCapturePaste(e: ClipboardEvent) {\n if (e.defaultPrevented || !this.quill.isEnabled()) {\n return\n }\n e.preventDefault()\n const range = this.quill.getSelection(true)\n if (isNullOrUndefined(range)) {\n return\n }\n\n // 兼容IE11浏览器\n if (!e.clipboardData) {\n // @ts-ignore\n e.clipboardData = {\n types: 'text/plain',\n getData: () => {\n // @ts-ignore\n return window.clipboardData.getData('Text')\n },\n }\n }\n\n const html = e.clipboardData.getData('text/html')\n const text = e.clipboardData.getData('text/plain')\n const files = Array.from(e.clipboardData.files || [])\n const msExcelCheck = /<meta.*?Microsoft Excel\\s[\\d].*?>/\n\n if (html.search(msExcelCheck) === -1 && files.length > 0) {\n this.quill.uploader.upload(range, files)\n }\n else {\n const msWordCheck1\n = /<meta\\s*name=\"?generator\"?\\s*content=\"?microsoft\\s*word\\s*\\d+\"?\\/?>/i\n const msWordCheck2 = /xmlns:o=\"urn:schemas-microsoft-com/i\n const result = { html, text, files, rtf: null }\n if (html.search(msExcelCheck) !== -1) {\n result.html = renderStyles(html)\n }\n if (msWordCheck1.test(html) || msWordCheck2.test(html)) {\n // TODO: 当word文档包含heading时text/rtf读取为空,无法获取hex图片,待修复。可参考ckeditor5/issues/2493\n result.rtf = e.clipboardData.getData('text/rtf')\n }\n this.onPaste(range, result)\n }\n }\n\n onPaste(range, { html, text, files: clipboardFiles, rtf }) {\n const hexImages = this.extractImageDataFromRtf(rtf)\n const rootBgColor = getComputedStyle(this.quill.root).backgroundColor\n const formats = this.quill.getFormat(range.index)\n let pastedDelta = this.convert({ text, html }, formats)\n pastedDelta = replaceDeltaWhiteSpace(pastedDelta, rootBgColor)\n const deltaLength = pastedDelta.ops.length\n\n let loadingTipsContainer\n if (deltaLength > BIG_DELTA_LIMIT) {\n loadingTipsContainer = this.quill.addContainer('ql-loading-tips')\n loadingTipsContainer.innerHTML = this.quill.getLangText('pasting')\n }\n\n const linePos = { index: range.index, length: range.length, fix: 0 }\n const [line, offset] = this.quill.getLine(range.index)\n\n const handlePasteContent = (content: any) => {\n const pastedContent = content\n\n const oldDelta = new Delta().retain(linePos.index).delete(linePos.length)\n const delta = oldDelta.concat(pastedContent)\n\n setTimeout(() => {\n this.quill.updateContents(delta, Quill.sources.USER)\n // 光标位置应该在粘贴内容之后:原光标位置 + 粘贴内容长度\n const newSelectionIndex = linePos.index + (pastedContent.length ? pastedContent.length() : 0)\n this.quill.setSelection(\n newSelectionIndex,\n Quill.sources.SILENT,\n )\n this.quill.scrollSelectionIntoView()\n if (loadingTipsContainer) {\n loadingTipsContainer.remove()\n }\n })\n }\n\n ;(async () => {\n try {\n const [files, placeholders, originalUrls, imageIndexs] = this.flipFilesArray(\n await this.extractFilesFromDelta(\n pastedDelta,\n clipboardFiles,\n hexImages,\n ),\n )\n\n if (files.length === 0) {\n handlePasteContent(pastedDelta)\n }\n else {\n if (this.quill.options.editorPaste && this.quill.options.editorPaste.observers.length !== 0) {\n // 设置editorPaste回调的情况\n this.quill.options.editorPaste.emit({\n files,\n callback: ({ code, message, data }) => {\n if (code === 0) {\n const { imageUrls } = data\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n handlePasteContent(pastedDelta)\n }\n else {\n console.error('error message:', message)\n }\n },\n })\n }\n else {\n // 没有originalUrls 也没有文件粘贴\n if (files[0] !== undefined || originalUrls.length === 0) {\n // 没有设置editorPaste回调的情况下,File格式的占位图需要手动转换成url格式,插入到编辑器中\n const imageUrls = await this.files2urls(\n files,\n placeholders,\n originalUrls,\n pastedDelta,\n imageIndexs,\n )\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n }\n handlePasteContent(pastedDelta)\n }\n }\n }\n catch (_e) {\n throw new Error('Paste failed.')\n }\n })()\n }\n\n files2urls(files: File[], placeholders, originalUrls, pastedDelta, imageIndexs) {\n return Promise.all(\n files.map(async (imageFile, index) => {\n const netImgExp = /^((http|https)\\:)?\\/\\/([\\s\\S]+)$/\n if (\n !placeholders[index]\n && originalUrls[index]\n && netImgExp.test(originalUrls[index])\n ) {\n // 不是占位图的普通url图片直接返回url\n return new Promise((resolve) => {\n resolve(originalUrls[index])\n })\n }\n else {\n const range = this.getImgSelection(pastedDelta, imageIndexs[index])\n const urls = await this.quill.uploader.getFileUrls([imageFile], range)\n return urls[0] || undefined\n }\n }),\n )\n }\n\n flipFilesArray(filesArr) {\n const files = []\n const placeholders = []\n const originalUrls = []\n const imageIndexs = []\n filesArr.forEach((item: any) => {\n if (item) {\n const [file, placeholder, originalUrl, imageIndex] = item\n files.push(file)\n placeholders.push(placeholder)\n originalUrls.push(originalUrl)\n if (imageIndex === 0 || imageIndex) {\n imageIndexs.push(imageIndex)\n }\n }\n })\n return [files, placeholders, originalUrls, imageIndexs]\n }\n\n // 将图片从hex转为base64\n convertHexToBase64(hexString) {\n return btoa(\n hexString\n .match(/\\w{2}/g)\n .map((char) => {\n return String.fromCharCode(Number.parseInt(char, 16))\n })\n .join(''),\n )\n }\n\n // 匹配rtf中的图片,存储为{hex, type}对象数组\n extractImageDataFromRtf(rtfData) {\n if (!rtfData) {\n return []\n }\n\n const regexPictureHeader\n = /{\\\\pict[\\s\\S]+?\\\\bliptag-?\\d+(\\\\blipupi-?\\d+)?({\\\\\\*\\\\blipuid\\s?[\\da-fA-F]+)?[\\s}]*?/\n const regexPicture = new RegExp(\n `(?:(${regexPictureHeader.source}))([\\\\da-fA-F\\\\s]+)\\\\}`,\n 'g',\n )\n const images = rtfData.match(regexPicture)\n const result = []\n\n if (images) {\n for (const image of images) {\n let imageType = ''\n\n if (image.includes('\\\\pngblip')) {\n imageType = 'image/png'\n }\n else if (image.includes('\\\\jpegblip')) {\n imageType = 'image/jpeg'\n }\n\n if (imageType) {\n result.push({\n hex: image\n .replace(regexPictureHeader, '')\n .replace(/[^\\da-fA-F]/g, ''),\n type: imageType,\n })\n }\n }\n }\n\n return result\n }\n\n extractFilesFromDelta(delta, clipboardFiles, hexImages?) {\n let index = -1\n return Promise.all(\n delta.map(async (op) => {\n index++\n const image = op.insert.image\n if (!image || image.hasExisted) {\n return\n }\n\n let file\n let isPlaceholderImage = false\n let imageIndex\n try {\n // hex 图片存在则为 file:/// 协议本地图片,使用 hex 图片转为 base64 读取\n const hexImage = hexImages.length && hexImages.shift()\n const newImage\n = hexImage\n && `data:${hexImage.type};base64,${this.convertHexToBase64(\n hexImage.hex,\n )}`\n imageIndex = index\n file = await imageUrlToFile(newImage || image.src || image)\n }\n catch (_err) {\n if (clipboardFiles.length !== 0) {\n // 跨域获取图片失败时从剪切板获取图片\n const clipboardFile = clipboardFiles[0]\n const imageType\n = clipboardFile.type?.indexOf('image') === -1\n ? 'image/png'\n : clipboardFile.type\n const blob = clipboardFile.slice(0, clipboardFile.size, imageType)\n file = new File([blob], `image-CORS-${new Date().getTime()}.png`, {\n type: imageType,\n })\n }\n else if (image.src.startsWith('http')) {\n // 什么都不做\n }\n else {\n // 剪切板中无图片,用失败占位图替换\n const errorImagePlaceholderJpg\n = this.quill.getLangText('img-error') === 'Image Copy Error'\n ? ERROR_IMAGE_PLACEHOLDER_EN\n : ERROR_IMAGE_PLACEHOLDER_CN\n file = await imageUrlToFile(errorImagePlaceholderJpg, true)\n isPlaceholderImage = true\n }\n }\n\n return [file, isPlaceholderImage, image, imageIndex]\n }),\n )\n }\n\n getImgSelection(delta, imageIndex) {\n let length = 0\n delta.ops.every((op, index) => {\n if (index === imageIndex) {\n return false\n }\n if (typeof op.insert === 'string') {\n length += op.insert.length\n }\n else if (typeof op.insert === 'object') {\n // 对于图片、提及等对象类型的 insert,长度为 1\n length += 1\n }\n return true\n })\n const range = {\n index: length,\n length: 0,\n }\n return range\n }\n}\n\nfunction rebuildDelta(delta, cellLine) {\n const { cell: cellId, colspan, row: rowId, rowspan } = cellLine\n const buildedDelta = delta.reduce((newDelta, op) => {\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n\n lines.forEach((text) => {\n if (text === '\\n') {\n // 对换行增加 table-cell-line 格式,以避免表格断开\n newDelta.insert('\\n', {\n ...op.attributes,\n 'table-cell-line': { row: rowId, cell: cellId, rowspan, colspan },\n })\n }\n else {\n text = text.endsWith('\\r') ? text.slice(0, -1) : text\n newDelta.insert(\n text,\n omit(op.attributes, ['table', 'table-cell-line']),\n )\n }\n })\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n\n return newDelta\n }, new Delta())\n\n return buildedDelta\n}\n\nfunction replaceStrWhiteSpace(str) {\n const isWhiteSpace = value => /^(\\u3000|\\u0020){1}$/.test(value) // 空白字符\n let textWithWhiteSpace = ''\n let beginHasChar = false\n for (const char of str) {\n if (isWhiteSpace(char) && !beginHasChar) {\n textWithWhiteSpace += '\\u00A0'\n }\n else {\n textWithWhiteSpace += char\n beginHasChar = true\n }\n }\n return textWithWhiteSpace\n}\n\nfunction replaceDeltaWhiteSpace(delta, rootBgColor?) {\n return delta.reduce((newDelta, op) => {\n // fix: 当粘贴文字颜色和编辑器背景色一致且自身无背景色的情况下移除文字颜色样式,避免误导用户粘贴无效\n if (\n rootBgColor\n && op.attributes\n && op.attributes.color\n && !op.attributes.background\n ) {\n const originColor = op.attributes.color\n const fontColor\n = originColor.indexOf('#') === 0 ? hexToRgbA(originColor) : originColor\n if (\n fontColor === rootBgColor\n || (fontColor === 'rgba(255,255,255,1)'\n && rootBgColor === 'rgba(0, 0, 0, 0)')\n ) {\n delete op.attributes.color\n }\n }\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n let insertWithWhiteSpace = ''\n lines.forEach((text) => {\n insertWithWhiteSpace += replaceStrWhiteSpace(text)\n })\n newDelta.insert(insertWithWhiteSpace, op.attributes)\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nfunction renderStyles(html) {\n let htmlString = html\n // Trim unnecessary parts.\n htmlString = htmlString.substring(\n htmlString.indexOf('<html '),\n htmlString.length,\n )\n htmlString = htmlString.substring(\n 0,\n htmlString.lastIndexOf('</html>') + '</html>'.length,\n )\n\n // Add temporary iframe.\n const iframe = document.createElement('iframe')\n iframe.style.display = 'none'\n document.body.appendChild(iframe)\n\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document\n iframeDoc.open()\n iframeDoc.write(htmlString)\n iframeDoc.close()\n\n let collection\n let pointer\n const rules\n = iframeDoc.styleSheets[iframeDoc.styleSheets.length - 1].cssRules\n\n // Convert internal styles to inline style of respective node.\n for (let idx = 0; idx < rules.length; idx++) {\n if ((rules[idx] as CSSStyleRule).selectorText === '') {\n continue\n }\n collection = iframeDoc.body.querySelectorAll(\n (rules[idx] as CSSStyleRule).selectorText,\n )\n\n for (pointer = 0; pointer < collection.length; pointer++) {\n collection[pointer].style.cssText += (\n rules[idx] as CSSStyleRule\n ).style.cssText\n }\n }\n\n // @ts-ignore\n const convertedString = iframeDoc.firstChild.outerHTML\n // Remove temporary iframe.\n iframe.parentNode.removeChild(iframe)\n\n return convertedString\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"custom-clipboard.es.js","sources":["../../../src/modules/custom-clipboard.ts"],"sourcesContent":["import type TypeClipboard from 'quill/modules/clipboard'\nimport type FluentEditor from '../fluent-editor'\nimport Quill from 'quill'\nimport {\n ERROR_IMAGE_PLACEHOLDER_CN,\n ERROR_IMAGE_PLACEHOLDER_EN,\n} from '../config/base64-image'\nimport { BIG_DELTA_LIMIT } from '../config/editor.config'\nimport {\n hexToRgbA,\n imageUrlToFile,\n isNullOrUndefined,\n omit,\n replaceDeltaImage,\n replaceStrWhiteSpace,\n splitWithBreak,\n} from '../config/editor.utils'\nimport { isString } from '../utils/is'\n\nconst Clipboard = Quill.import('modules/clipboard') as typeof TypeClipboard\nconst Delta = Quill.import('delta')\n\nexport class CustomClipboard extends Clipboard {\n declare quill: FluentEditor\n\n constructor(public quill: FluentEditor) {\n super(quill)\n // 解决 quill 组件在中文输入法的情景下开始输入placeholder不消失的问题\n this.quill.root.addEventListener('input', () => {\n if (this.quill.root.innerText !== '\\\\n' && this.quill.root.classList.contains('ql-blank')) {\n this.quill.root.classList.toggle('ql-blank', false)\n }\n else {\n this.quill.options.placeholder = this.quill.getLangText('editor-placeholder')\n }\n })\n }\n\n prepareMatching(container: HTMLElement, nodeMatches) {\n const elementMatchers = []\n const textMatchers = []\n this.matchers.forEach((pair) => {\n const [selector, matcher] = pair\n if (selector === Node.TEXT_NODE) {\n textMatchers.push(matcher)\n }\n else if (selector === Node.ELEMENT_NODE) {\n elementMatchers.push(matcher)\n }\n else if (isString(selector)) {\n // word 的 v:shape 系列标签只能通过 getElementsByTagName 获取\n const vRegex = /v:(.+)/\n const nodeList = Array.from(\n vRegex.test(selector)\n ? container.getElementsByTagName(selector)\n : container.querySelectorAll(selector),\n )\n nodeList.forEach((node) => {\n if (nodeMatches.has(node)) {\n const matches = nodeMatches.get(node)\n matches.push(matcher)\n }\n else {\n nodeMatches.set(node, [matcher])\n }\n })\n }\n })\n return [elementMatchers, textMatchers]\n }\n\n onCaptureCopy(e, isCut = false) {\n if (e.defaultPrevented) {\n return\n }\n e.preventDefault()\n const [range] = this.quill.selection.getRange()\n if (isNullOrUndefined(range)) {\n return\n }\n const { html, text } = this.onCopy(range, isCut)\n\n // 兼容IE11浏览器`\n if (!e.clipboardData) {\n e.clipboardData = {\n types: 'text/plain',\n setData: (_type, value) => {\n // @ts-ignore\n return window.clipboardData.setData('Text', value)\n },\n }\n }\n\n // 复制代码时移除utf8中产生的不间断空格\\u00A0\n let plainText = text\n if (html.startsWith('<pre>')) {\n plainText = text.replace(/\\u00A0/g, ' ')\n }\n\n e.clipboardData.setData('text/html', html)\n e.clipboardData.setData('text/plain', plainText)\n if (isCut) {\n this.quill.deleteText(range, Quill.sources.USER)\n }\n }\n\n onCapturePaste(e: ClipboardEvent) {\n if (e.defaultPrevented || !this.quill.isEnabled()) {\n return\n }\n e.preventDefault()\n const range = this.quill.getSelection(true)\n if (isNullOrUndefined(range)) {\n return\n }\n\n // 兼容IE11浏览器\n if (!e.clipboardData) {\n // @ts-ignore\n e.clipboardData = {\n types: 'text/plain',\n getData: () => {\n // @ts-ignore\n return window.clipboardData.getData('Text')\n },\n }\n }\n\n const html = e.clipboardData.getData('text/html')\n const text = e.clipboardData.getData('text/plain')\n const files = Array.from(e.clipboardData.files || [])\n const msExcelCheck = /<meta.*?Microsoft Excel\\s[\\d].*?>/\n\n if (html.search(msExcelCheck) === -1 && files.length > 0) {\n this.quill.uploader.upload(range, files)\n }\n else {\n const msWordCheck1\n = /<meta\\s*name=\"?generator\"?\\s*content=\"?microsoft\\s*word\\s*\\d+\"?\\/?>/i\n const msWordCheck2 = /xmlns:o=\"urn:schemas-microsoft-com/i\n const result = { html, text, files, rtf: null }\n if (html.search(msExcelCheck) !== -1) {\n result.html = renderStyles(html)\n }\n if (msWordCheck1.test(html) || msWordCheck2.test(html)) {\n // TODO: 当word文档包含heading时text/rtf读取为空,无法获取hex图片,待修复。可参考ckeditor5/issues/2493\n result.rtf = e.clipboardData.getData('text/rtf')\n }\n this.onPaste(range, result)\n }\n }\n\n onPaste(range, { html, text, files: clipboardFiles, rtf }) {\n const hexImages = this.extractImageDataFromRtf(rtf)\n const rootBgColor = getComputedStyle(this.quill.root).backgroundColor\n const formats = this.quill.getFormat(range.index)\n let pastedDelta = this.convert({ text, html }, formats)\n pastedDelta = replaceDeltaWhiteSpace(pastedDelta, rootBgColor)\n const deltaLength = pastedDelta.ops.length\n\n let loadingTipsContainer\n if (deltaLength > BIG_DELTA_LIMIT) {\n loadingTipsContainer = this.quill.addContainer('ql-loading-tips')\n loadingTipsContainer.innerHTML = this.quill.getLangText('pasting')\n }\n\n const linePos = { index: range.index, length: range.length, fix: 0 }\n const [line, offset] = this.quill.getLine(range.index)\n\n const handlePasteContent = (content: any) => {\n const pastedContent = content\n\n const oldDelta = new Delta().retain(linePos.index).delete(linePos.length)\n const delta = oldDelta.concat(pastedContent)\n\n setTimeout(() => {\n this.quill.updateContents(delta, Quill.sources.USER)\n // 光标位置应该在粘贴内容之后:原光标位置 + 粘贴内容长度\n const newSelectionIndex = linePos.index + (pastedContent.length ? pastedContent.length() : 0)\n this.quill.setSelection(\n newSelectionIndex,\n Quill.sources.SILENT,\n )\n this.quill.scrollSelectionIntoView()\n if (loadingTipsContainer) {\n loadingTipsContainer.remove()\n }\n })\n }\n\n ;(async () => {\n try {\n const [files, placeholders, originalUrls, imageIndexs] = this.flipFilesArray(\n await this.extractFilesFromDelta(\n pastedDelta,\n clipboardFiles,\n hexImages,\n ),\n )\n\n if (files.length === 0) {\n handlePasteContent(pastedDelta)\n }\n else {\n if (this.quill.options.editorPaste && this.quill.options.editorPaste.observers.length !== 0) {\n // 设置editorPaste回调的情况\n this.quill.options.editorPaste.emit({\n files,\n callback: ({ code, message, data }) => {\n if (code === 0) {\n const { imageUrls } = data\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n handlePasteContent(pastedDelta)\n }\n else {\n console.error('error message:', message)\n }\n },\n })\n }\n else {\n // 没有originalUrls 也没有文件粘贴\n if (files[0] !== undefined || originalUrls.length === 0) {\n // 没有设置editorPaste回调的情况下,File格式的占位图需要手动转换成url格式,插入到编辑器中\n const imageUrls = await this.files2urls(\n files,\n placeholders,\n originalUrls,\n pastedDelta,\n imageIndexs,\n )\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n }\n handlePasteContent(pastedDelta)\n }\n }\n }\n catch (_e) {\n throw new Error('Paste failed.')\n }\n })()\n }\n\n files2urls(files: File[], placeholders, originalUrls, pastedDelta, imageIndexs) {\n return Promise.all(\n files.map(async (imageFile, index) => {\n const netImgExp = /^((http|https)\\:)?\\/\\/([\\s\\S]+)$/\n if (\n !placeholders[index]\n && originalUrls[index]\n && netImgExp.test(originalUrls[index])\n ) {\n // 不是占位图的普通url图片直接返回url\n return new Promise((resolve) => {\n resolve(originalUrls[index])\n })\n }\n else {\n const range = this.getImgSelection(pastedDelta, imageIndexs[index])\n const urls = await this.quill.uploader.getFileUrls([imageFile], range)\n return urls[0] || undefined\n }\n }),\n )\n }\n\n flipFilesArray(filesArr) {\n const files = []\n const placeholders = []\n const originalUrls = []\n const imageIndexs = []\n filesArr.forEach((item: any) => {\n if (item) {\n const [file, placeholder, originalUrl, imageIndex] = item\n files.push(file)\n placeholders.push(placeholder)\n originalUrls.push(originalUrl)\n if (imageIndex === 0 || imageIndex) {\n imageIndexs.push(imageIndex)\n }\n }\n })\n return [files, placeholders, originalUrls, imageIndexs]\n }\n\n // 将图片从hex转为base64\n convertHexToBase64(hexString) {\n return btoa(\n hexString\n .match(/\\w{2}/g)\n .map((char) => {\n return String.fromCharCode(Number.parseInt(char, 16))\n })\n .join(''),\n )\n }\n\n // 匹配rtf中的图片,存储为{hex, type}对象数组\n extractImageDataFromRtf(rtfData) {\n if (!rtfData) {\n return []\n }\n\n const regexPictureHeader\n = /{\\\\pict[\\s\\S]+?\\\\bliptag-?\\d+(\\\\blipupi-?\\d+)?({\\\\\\*\\\\blipuid\\s?[\\da-fA-F]+)?[\\s}]*?/\n const regexPicture = new RegExp(\n `(?:(${regexPictureHeader.source}))([\\\\da-fA-F\\\\s]+)\\\\}`,\n 'g',\n )\n const images = rtfData.match(regexPicture)\n const result = []\n\n if (images) {\n for (const image of images) {\n let imageType = ''\n\n if (image.includes('\\\\pngblip')) {\n imageType = 'image/png'\n }\n else if (image.includes('\\\\jpegblip')) {\n imageType = 'image/jpeg'\n }\n\n if (imageType) {\n result.push({\n hex: image\n .replace(regexPictureHeader, '')\n .replace(/[^\\da-fA-F]/g, ''),\n type: imageType,\n })\n }\n }\n }\n\n return result\n }\n\n extractFilesFromDelta(delta, clipboardFiles, hexImages?) {\n let index = -1\n return Promise.all(\n delta.map(async (op) => {\n index++\n const image = op.insert.image\n if (!image || image.hasExisted) {\n return\n }\n\n let file\n let isPlaceholderImage = false\n let imageIndex\n try {\n // hex 图片存在则为 file:/// 协议本地图片,使用 hex 图片转为 base64 读取\n const hexImage = hexImages.length && hexImages.shift()\n const newImage\n = hexImage\n && `data:${hexImage.type};base64,${this.convertHexToBase64(\n hexImage.hex,\n )}`\n imageIndex = index\n file = await imageUrlToFile(newImage || image.src || image)\n }\n catch (_err) {\n if (clipboardFiles.length !== 0) {\n // 跨域获取图片失败时从剪切板获取图片\n const clipboardFile = clipboardFiles[0]\n const imageType\n = clipboardFile.type?.indexOf('image') === -1\n ? 'image/png'\n : clipboardFile.type\n const blob = clipboardFile.slice(0, clipboardFile.size, imageType)\n file = new File([blob], `image-CORS-${new Date().getTime()}.png`, {\n type: imageType,\n })\n }\n else if (image.src.startsWith('http')) {\n // 什么都不做\n }\n else {\n // 剪切板中无图片,用失败占位图替换\n const errorImagePlaceholderJpg\n = this.quill.getLangText('img-error') === 'Image Copy Error'\n ? ERROR_IMAGE_PLACEHOLDER_EN\n : ERROR_IMAGE_PLACEHOLDER_CN\n file = await imageUrlToFile(errorImagePlaceholderJpg, true)\n isPlaceholderImage = true\n }\n }\n\n return [file, isPlaceholderImage, image, imageIndex]\n }),\n )\n }\n\n getImgSelection(delta, imageIndex) {\n let length = 0\n delta.ops.every((op, index) => {\n if (index === imageIndex) {\n return false\n }\n if (typeof op.insert === 'string') {\n length += op.insert.length\n }\n else if (typeof op.insert === 'object') {\n // 对于图片、提及等对象类型的 insert,长度为 1\n length += 1\n }\n return true\n })\n const range = {\n index: length,\n length: 0,\n }\n return range\n }\n}\n\nfunction rebuildDelta(delta, cellLine) {\n const { cell: cellId, colspan, row: rowId, rowspan } = cellLine\n const buildedDelta = delta.reduce((newDelta, op) => {\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n\n lines.forEach((text) => {\n if (text === '\\n') {\n // 对换行增加 table-cell-line 格式,以避免表格断开\n newDelta.insert('\\n', {\n ...op.attributes,\n 'table-cell-line': { row: rowId, cell: cellId, rowspan, colspan },\n })\n }\n else {\n text = text.endsWith('\\r') ? text.slice(0, -1) : text\n newDelta.insert(\n text,\n omit(op.attributes, ['table', 'table-cell-line']),\n )\n }\n })\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n\n return newDelta\n }, new Delta())\n\n return buildedDelta\n}\n\nfunction replaceDeltaWhiteSpace(delta, rootBgColor?) {\n return delta.reduce((newDelta, op) => {\n // fix: 当粘贴文字颜色和编辑器背景色一致且自身无背景色的情况下移除文字颜色样式,避免误导用户粘贴无效\n if (\n rootBgColor\n && op.attributes\n && op.attributes.color\n && !op.attributes.background\n ) {\n const originColor = op.attributes.color\n const fontColor\n = originColor.indexOf('#') === 0 ? hexToRgbA(originColor) : originColor\n if (\n fontColor === rootBgColor\n || (fontColor === 'rgba(255,255,255,1)'\n && rootBgColor === 'rgba(0, 0, 0, 0)')\n ) {\n delete op.attributes.color\n }\n }\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n let insertWithWhiteSpace = ''\n lines.forEach((text) => {\n insertWithWhiteSpace += replaceStrWhiteSpace(text)\n })\n newDelta.insert(insertWithWhiteSpace, op.attributes)\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nfunction renderStyles(html) {\n let htmlString = html\n // Trim unnecessary parts.\n htmlString = htmlString.substring(\n htmlString.indexOf('<html '),\n htmlString.length,\n )\n htmlString = htmlString.substring(\n 0,\n htmlString.lastIndexOf('</html>') + '</html>'.length,\n )\n\n // Add temporary iframe.\n const iframe = document.createElement('iframe')\n iframe.style.display = 'none'\n document.body.appendChild(iframe)\n\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document\n iframeDoc.open()\n iframeDoc.write(htmlString)\n iframeDoc.close()\n\n let collection\n let pointer\n const rules\n = iframeDoc.styleSheets[iframeDoc.styleSheets.length - 1].cssRules\n\n // Convert internal styles to inline style of respective node.\n for (let idx = 0; idx < rules.length; idx++) {\n if ((rules[idx] as CSSStyleRule).selectorText === '') {\n continue\n }\n collection = iframeDoc.body.querySelectorAll(\n (rules[idx] as CSSStyleRule).selectorText,\n )\n\n for (pointer = 0; pointer < collection.length; pointer++) {\n collection[pointer].style.cssText += (\n rules[idx] as CSSStyleRule\n ).style.cssText\n }\n }\n\n // @ts-ignore\n const convertedString = iframeDoc.firstChild.outerHTML\n // Remove temporary iframe.\n iframe.parentNode.removeChild(iframe)\n\n return convertedString\n}\n"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ import { ColorPicker, Picker } from "../modules/toolbar/better-picker.es.js";
8
8
  import { FormatPainter } from "../tools/format-painter.es.js";
9
9
  import { fullscreenHandler } from "../tools/fullscreen.es.js";
10
10
  import { Screenshot } from "../tools/screenshot.es.js";
11
- import { isNullOrUndefined } from "../config/editor.utils.es.js";
11
+ import { shouldInsertNbspOnSpace, isNullOrUndefined } from "../config/editor.utils.es.js";
12
12
  import { LinkTooltip } from "../modules/link/modules/tooltip.es.js";
13
13
  const OriginSnowTheme = FluentEditor.import("themes/snow");
14
14
  const IconPicker = FluentEditor.import("ui/icon-picker");
@@ -17,7 +17,24 @@ OriginSnowTheme.DEFAULTS = {
17
17
  "i18n": true,
18
18
  "keyboard": {
19
19
  bindings: {
20
- ...shortKey
20
+ ...shortKey,
21
+ preserveWhiteSpace: {
22
+ key: " ",
23
+ collapsed: true,
24
+ handler(range, context) {
25
+ if (!shouldInsertNbspOnSpace(context.prefix)) {
26
+ return true;
27
+ }
28
+ this.quill.insertText(
29
+ range.index,
30
+ " ",
31
+ context.format,
32
+ FluentEditor.sources.USER
33
+ );
34
+ this.quill.setSelection(range.index + 1, FluentEditor.sources.SILENT);
35
+ return false;
36
+ }
37
+ }
21
38
  }
22
39
  },
23
40
  "toolbar": {
@@ -1 +1 @@
1
- {"version":3,"file":"snow.es.js","sources":["../../../src/themes/snow.ts"],"sourcesContent":["import type { ThemeOptions } from 'quill/core/theme'\nimport type TypeToolbar from 'quill/modules/toolbar'\nimport type TypeIconPicker from 'quill/ui/icon-picker'\nimport { I18N_LOCALE_CHANGE } from 'quill-i18n'\nimport { inputFile, isNullOrUndefined } from '../config'\nimport FluentEditor from '../core/fluent-editor'\nimport { CustomImageSpec } from '../modules/custom-image/specs/custom-image-spec'\nimport { LinkTooltip } from '../modules/link'\nimport { shortKey } from '../modules/shortcut-key'\nimport { ColorPicker, Picker } from '../modules/toolbar/better-picker'\nimport { FormatPainter } from '../tools/format-painter'\nimport { fullscreenHandler } from '../tools/fullscreen'\nimport { Screenshot } from '../tools/screenshot'\n\nconst OriginSnowTheme = FluentEditor.import('themes/snow') as any\nconst IconPicker = FluentEditor.import('ui/icon-picker') as typeof TypeIconPicker\n\nOriginSnowTheme.DEFAULTS = {\n modules: {\n 'i18n': true,\n 'keyboard': {\n bindings: {\n ...shortKey,\n },\n },\n 'toolbar': {\n handlers: {\n ...(OriginSnowTheme.DEFAULTS as Record<string, any>).modules.toolbar.handlers,\n 'formula': function () {\n if (!this.quill.isEnabled()) return\n const mathlive = this.quill.getModule('mathlive')\n if (!mathlive) {\n this.quill.theme.tooltip.edit('formula')\n }\n else {\n mathlive.createDialog()\n }\n },\n 'undo': function () {\n this.quill.history.undo()\n },\n 'redo': function () {\n this.quill.history.redo()\n },\n 'file': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('file')\n inputFile.call(this, 'file', accept)\n },\n 'image': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('image')\n inputFile.call(this, 'image', accept)\n },\n 'video': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('video')\n inputFile.call(this, 'video', accept)\n },\n 'ai': function () {},\n 'emoji': function () {},\n 'fullscreen': fullscreenHandler,\n [FormatPainter.toolName]: FormatPainter,\n [Screenshot.toolName]: Screenshot,\n 'line-height': function (value) {\n this.quill.format('line-height', value)\n },\n 'divider': function () {\n const range = this.quill.getSelection(true)\n this.quill.insertText(range.index, '\\n', FluentEditor.sources.USER)\n this.quill.insertEmbed(range.index + 1, 'divider', true, FluentEditor.sources.USER)\n this.quill.setSelection(range.index + 2, FluentEditor.sources.SILENT)\n },\n },\n },\n 'image': {\n specs: [CustomImageSpec],\n overlay: {\n style: {\n border: '1px dashed rgb(68, 68, 68)',\n },\n },\n },\n 'shortcut-key': false,\n 'file': true,\n },\n}\n\nconst ALIGNS = [false, 'center', 'right']\nconst COLORS = [\n '',\n 'rgb(255, 255, 255)',\n 'rgb(0, 0, 0)',\n 'rgb(72, 83, 104)',\n 'rgb(41, 114, 244)',\n 'rgb(0, 163, 245)',\n 'rgb(49, 155, 98)',\n 'rgb(222, 60, 54)',\n 'rgb(248, 136, 37)',\n 'rgb(245, 196, 0)',\n 'rgb(153, 56, 215)',\n\n 'rgb(242, 242, 242)',\n 'rgb(127, 127, 127)',\n 'rgb(243, 245, 247)',\n 'rgb(229, 239, 255)',\n 'rgb(229, 246, 255)',\n 'rgb(234, 250, 241)',\n 'rgb(254, 233, 232)',\n 'rgb(254, 243, 235)',\n 'rgb(254, 249, 227)',\n 'rgb(253, 235, 255)',\n\n 'rgb(216, 216, 216)',\n 'rgb(89, 89, 89)',\n 'rgb(197, 202, 211)',\n 'rgb(199, 220, 255)',\n 'rgb(199, 236, 255)',\n 'rgb(195, 234, 213)',\n 'rgb(255, 201, 199)',\n 'rgb(255, 220, 196)',\n 'rgb(255, 238, 173)',\n 'rgb(242, 199, 255)',\n\n 'rgb(191, 191, 191)',\n 'rgb(63, 63, 63)',\n 'rgb(128, 139, 158)',\n 'rgb(153, 190, 255)',\n 'rgb(153, 221, 255)',\n 'rgb(152, 215, 182)',\n 'rgb(255, 156, 153)',\n 'rgb(255, 186, 132)',\n 'rgb(255, 226, 112)',\n 'rgb(213, 142, 255)',\n\n 'rgb(165, 165, 165)',\n 'rgb(38, 38, 38)',\n 'rgb(53, 59, 69)',\n 'rgb(20, 80, 184)',\n 'rgb(18, 116, 165)',\n 'rgb(39, 124, 79)',\n 'rgb(158, 30, 26)',\n 'rgb(184, 96, 20)',\n 'rgb(163, 130, 0)',\n 'rgb(94, 34, 129)',\n\n 'rgb(147, 147, 147)',\n 'rgb(13, 13, 13)',\n 'rgb(36, 39, 46)',\n 'rgb(12, 48, 110)',\n 'rgb(10, 65, 92)',\n 'rgb(24, 78, 50)',\n 'rgb(88, 17, 14)',\n 'rgb(92, 48, 10)',\n 'rgb(102, 82, 0)',\n 'rgb(59, 21, 81)',\n\n 'custom',\n]\nconst FONTS = [false, 'serif', 'monospace']\nconst HEADERS = ['1', '2', '3', false]\nconst SIZES = ['small', false, 'large', 'huge']\nconst LINEHEIGHT = [false, '1.2', '1.5', '2']\n\nclass SnowTheme extends OriginSnowTheme {\n constructor(public quill: FluentEditor, options: ThemeOptions) {\n super(quill, options)\n\n this.quill.on(I18N_LOCALE_CHANGE, () => {\n this.i18nTextToolbar()\n })\n }\n\n i18nTextToolbar() {\n const toolbar = this.quill.getModule('toolbar') as TypeToolbar\n ColorPicker.clearText = this.quill.getLangText('clear-color')\n ColorPicker.customText = this.quill.getLangText('custom-color')\n\n if (!toolbar || !this.pickers) return\n\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) {\n // EasyColorPicker have not dts. abd origin quill ColorPicker dts not complete. use any to resovle ts type error\n const colorPicker = picker as any\n colorPicker.options.remove()\n Array.from(colorPicker.select.options).forEach((option: any) => {\n if (option.hasAttribute('custom')) {\n option.remove()\n }\n })\n colorPicker.buildOptions()\n colorPicker.createUsedColor()\n }\n\n if (picker.select && picker.select.classList.contains('ql-header')) {\n const getLabelKey = (v: string | null) => (v ? `header-${v}` : 'header-normal')\n\n // 更新 label 文本\n const labelValue = picker.label.getAttribute('data-value')\n picker.label.setAttribute('data-label', this.quill.getLangText(getLabelKey(labelValue)))\n\n const select = picker.select as HTMLSelectElement\n Array.from(select.options).forEach((option) => {\n const value = option.getAttribute('value')\n option.textContent = this.quill.getLangText(getLabelKey(value))\n })\n\n picker.options.remove()\n picker.buildOptions()\n }\n })\n }\n\n buildPickers(selects: NodeListOf<HTMLSelectElement>, icons: Record<string, string | Record<string, string>>) {\n this.pickers = Array.from(selects).map((select) => {\n if (select.classList.contains('ql-align')) {\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillSelect(select, ALIGNS)\n }\n return new IconPicker(select, icons.align as Record<string, string>)\n }\n\n if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n const format = select.classList.contains('ql-background') ? 'background' : 'color'\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillColorSelect(select, COLORS, format, format === 'background' ? '#ffffff' : '#000000')\n }\n return new ColorPicker(select, icons[format] as string, {\n expandIcon: '<i class=\"icon\" />',\n closeAfterChange: false,\n })\n }\n\n if (isNullOrUndefined(select.querySelector('option'))) {\n if (select.classList.contains('ql-font')) {\n fillSelect(select, FONTS)\n }\n else if (select.classList.contains('ql-header')) {\n fillSelect(select, HEADERS)\n }\n else if (select.classList.contains('ql-size')) {\n fillSelect(select, SIZES)\n }\n else if (select.classList.contains('ql-line-height')) {\n fillSelect(select, LINEHEIGHT)\n }\n }\n return new Picker(select)\n })\n\n const update = () => {\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) return\n picker.update()\n })\n }\n this.quill.on(FluentEditor.events.EDITOR_CHANGE, update)\n }\n\n extendToolbar(toolbar) {\n const icons = FluentEditor.import('ui/icons') as Record<string, any>\n toolbar.container.classList.add('ql-snow')\n this.buildButtons(toolbar.container.querySelectorAll('button'), icons)\n this.buildPickers(toolbar.container.querySelectorAll('select'), icons)\n this.tooltip = new LinkTooltip(this.quill, this.options.bounds)\n }\n}\n\nfunction fillSelect(select, values, defaultValue = false) {\n values.forEach((value) => {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n option.setAttribute('value', value)\n }\n select.appendChild(option)\n })\n}\n\nfunction fillColorSelect(\n select: HTMLSelectElement,\n values: Array<string | boolean>,\n format: string,\n defaultValue: unknown = false,\n) {\n const colorGetter = document.createElement('span') as HTMLElement\n for (const value of values) {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n if (value !== 'custom') {\n colorGetter.style[format] = String(value)\n option.setAttribute('value', colorGetter.style[format])\n }\n else {\n option.setAttribute('value', value)\n }\n }\n select.appendChild(option)\n }\n}\n\nexport default SnowTheme\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snow.es.js","sources":["../../../src/themes/snow.ts"],"sourcesContent":["import type { ThemeOptions } from 'quill/core/theme'\nimport type TypeToolbar from 'quill/modules/toolbar'\nimport type TypeIconPicker from 'quill/ui/icon-picker'\nimport { I18N_LOCALE_CHANGE } from 'quill-i18n'\nimport { inputFile, isNullOrUndefined, shouldInsertNbspOnSpace } from '../config'\nimport FluentEditor from '../core/fluent-editor'\nimport { CustomImageSpec } from '../modules/custom-image/specs/custom-image-spec'\nimport { LinkTooltip } from '../modules/link'\nimport { shortKey } from '../modules/shortcut-key'\nimport { ColorPicker, Picker } from '../modules/toolbar/better-picker'\nimport { FormatPainter } from '../tools/format-painter'\nimport { fullscreenHandler } from '../tools/fullscreen'\nimport { Screenshot } from '../tools/screenshot'\n\nconst OriginSnowTheme = FluentEditor.import('themes/snow') as any\nconst IconPicker = FluentEditor.import('ui/icon-picker') as typeof TypeIconPicker\n\nOriginSnowTheme.DEFAULTS = {\n modules: {\n 'i18n': true,\n 'keyboard': {\n bindings: {\n ...shortKey,\n preserveWhiteSpace: {\n key: ' ',\n collapsed: true,\n handler(this: { quill: FluentEditor }, range, context) {\n if (!shouldInsertNbspOnSpace(context.prefix)) {\n return true\n }\n this.quill.insertText(\n range.index,\n '\\u00A0',\n context.format,\n FluentEditor.sources.USER,\n )\n this.quill.setSelection(range.index + 1, FluentEditor.sources.SILENT)\n return false\n },\n },\n },\n },\n 'toolbar': {\n handlers: {\n ...(OriginSnowTheme.DEFAULTS as Record<string, any>).modules.toolbar.handlers,\n 'formula': function () {\n if (!this.quill.isEnabled()) return\n const mathlive = this.quill.getModule('mathlive')\n if (!mathlive) {\n this.quill.theme.tooltip.edit('formula')\n }\n else {\n mathlive.createDialog()\n }\n },\n 'undo': function () {\n this.quill.history.undo()\n },\n 'redo': function () {\n this.quill.history.redo()\n },\n 'file': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('file')\n inputFile.call(this, 'file', accept)\n },\n 'image': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('image')\n inputFile.call(this, 'image', accept)\n },\n 'video': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('video')\n inputFile.call(this, 'video', accept)\n },\n 'ai': function () {},\n 'emoji': function () {},\n 'fullscreen': fullscreenHandler,\n [FormatPainter.toolName]: FormatPainter,\n [Screenshot.toolName]: Screenshot,\n 'line-height': function (value) {\n this.quill.format('line-height', value)\n },\n 'divider': function () {\n const range = this.quill.getSelection(true)\n this.quill.insertText(range.index, '\\n', FluentEditor.sources.USER)\n this.quill.insertEmbed(range.index + 1, 'divider', true, FluentEditor.sources.USER)\n this.quill.setSelection(range.index + 2, FluentEditor.sources.SILENT)\n },\n },\n },\n 'image': {\n specs: [CustomImageSpec],\n overlay: {\n style: {\n border: '1px dashed rgb(68, 68, 68)',\n },\n },\n },\n 'shortcut-key': false,\n 'file': true,\n },\n}\n\nconst ALIGNS = [false, 'center', 'right']\nconst COLORS = [\n '',\n 'rgb(255, 255, 255)',\n 'rgb(0, 0, 0)',\n 'rgb(72, 83, 104)',\n 'rgb(41, 114, 244)',\n 'rgb(0, 163, 245)',\n 'rgb(49, 155, 98)',\n 'rgb(222, 60, 54)',\n 'rgb(248, 136, 37)',\n 'rgb(245, 196, 0)',\n 'rgb(153, 56, 215)',\n\n 'rgb(242, 242, 242)',\n 'rgb(127, 127, 127)',\n 'rgb(243, 245, 247)',\n 'rgb(229, 239, 255)',\n 'rgb(229, 246, 255)',\n 'rgb(234, 250, 241)',\n 'rgb(254, 233, 232)',\n 'rgb(254, 243, 235)',\n 'rgb(254, 249, 227)',\n 'rgb(253, 235, 255)',\n\n 'rgb(216, 216, 216)',\n 'rgb(89, 89, 89)',\n 'rgb(197, 202, 211)',\n 'rgb(199, 220, 255)',\n 'rgb(199, 236, 255)',\n 'rgb(195, 234, 213)',\n 'rgb(255, 201, 199)',\n 'rgb(255, 220, 196)',\n 'rgb(255, 238, 173)',\n 'rgb(242, 199, 255)',\n\n 'rgb(191, 191, 191)',\n 'rgb(63, 63, 63)',\n 'rgb(128, 139, 158)',\n 'rgb(153, 190, 255)',\n 'rgb(153, 221, 255)',\n 'rgb(152, 215, 182)',\n 'rgb(255, 156, 153)',\n 'rgb(255, 186, 132)',\n 'rgb(255, 226, 112)',\n 'rgb(213, 142, 255)',\n\n 'rgb(165, 165, 165)',\n 'rgb(38, 38, 38)',\n 'rgb(53, 59, 69)',\n 'rgb(20, 80, 184)',\n 'rgb(18, 116, 165)',\n 'rgb(39, 124, 79)',\n 'rgb(158, 30, 26)',\n 'rgb(184, 96, 20)',\n 'rgb(163, 130, 0)',\n 'rgb(94, 34, 129)',\n\n 'rgb(147, 147, 147)',\n 'rgb(13, 13, 13)',\n 'rgb(36, 39, 46)',\n 'rgb(12, 48, 110)',\n 'rgb(10, 65, 92)',\n 'rgb(24, 78, 50)',\n 'rgb(88, 17, 14)',\n 'rgb(92, 48, 10)',\n 'rgb(102, 82, 0)',\n 'rgb(59, 21, 81)',\n\n 'custom',\n]\nconst FONTS = [false, 'serif', 'monospace']\nconst HEADERS = ['1', '2', '3', false]\nconst SIZES = ['small', false, 'large', 'huge']\nconst LINEHEIGHT = [false, '1.2', '1.5', '2']\n\nclass SnowTheme extends OriginSnowTheme {\n constructor(public quill: FluentEditor, options: ThemeOptions) {\n super(quill, options)\n\n this.quill.on(I18N_LOCALE_CHANGE, () => {\n this.i18nTextToolbar()\n })\n }\n\n i18nTextToolbar() {\n const toolbar = this.quill.getModule('toolbar') as TypeToolbar\n ColorPicker.clearText = this.quill.getLangText('clear-color')\n ColorPicker.customText = this.quill.getLangText('custom-color')\n\n if (!toolbar || !this.pickers) return\n\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) {\n // EasyColorPicker have not dts. abd origin quill ColorPicker dts not complete. use any to resovle ts type error\n const colorPicker = picker as any\n colorPicker.options.remove()\n Array.from(colorPicker.select.options).forEach((option: any) => {\n if (option.hasAttribute('custom')) {\n option.remove()\n }\n })\n colorPicker.buildOptions()\n colorPicker.createUsedColor()\n }\n\n if (picker.select && picker.select.classList.contains('ql-header')) {\n const getLabelKey = (v: string | null) => (v ? `header-${v}` : 'header-normal')\n\n // 更新 label 文本\n const labelValue = picker.label.getAttribute('data-value')\n picker.label.setAttribute('data-label', this.quill.getLangText(getLabelKey(labelValue)))\n\n const select = picker.select as HTMLSelectElement\n Array.from(select.options).forEach((option) => {\n const value = option.getAttribute('value')\n option.textContent = this.quill.getLangText(getLabelKey(value))\n })\n\n picker.options.remove()\n picker.buildOptions()\n }\n })\n }\n\n buildPickers(selects: NodeListOf<HTMLSelectElement>, icons: Record<string, string | Record<string, string>>) {\n this.pickers = Array.from(selects).map((select) => {\n if (select.classList.contains('ql-align')) {\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillSelect(select, ALIGNS)\n }\n return new IconPicker(select, icons.align as Record<string, string>)\n }\n\n if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n const format = select.classList.contains('ql-background') ? 'background' : 'color'\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillColorSelect(select, COLORS, format, format === 'background' ? '#ffffff' : '#000000')\n }\n return new ColorPicker(select, icons[format] as string, {\n expandIcon: '<i class=\"icon\" />',\n closeAfterChange: false,\n })\n }\n\n if (isNullOrUndefined(select.querySelector('option'))) {\n if (select.classList.contains('ql-font')) {\n fillSelect(select, FONTS)\n }\n else if (select.classList.contains('ql-header')) {\n fillSelect(select, HEADERS)\n }\n else if (select.classList.contains('ql-size')) {\n fillSelect(select, SIZES)\n }\n else if (select.classList.contains('ql-line-height')) {\n fillSelect(select, LINEHEIGHT)\n }\n }\n return new Picker(select)\n })\n\n const update = () => {\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) return\n picker.update()\n })\n }\n this.quill.on(FluentEditor.events.EDITOR_CHANGE, update)\n }\n\n extendToolbar(toolbar) {\n const icons = FluentEditor.import('ui/icons') as Record<string, any>\n toolbar.container.classList.add('ql-snow')\n this.buildButtons(toolbar.container.querySelectorAll('button'), icons)\n this.buildPickers(toolbar.container.querySelectorAll('select'), icons)\n this.tooltip = new LinkTooltip(this.quill, this.options.bounds)\n }\n}\n\nfunction fillSelect(select, values, defaultValue = false) {\n values.forEach((value) => {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n option.setAttribute('value', value)\n }\n select.appendChild(option)\n })\n}\n\nfunction fillColorSelect(\n select: HTMLSelectElement,\n values: Array<string | boolean>,\n format: string,\n defaultValue: unknown = false,\n) {\n const colorGetter = document.createElement('span') as HTMLElement\n for (const value of values) {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n if (value !== 'custom') {\n colorGetter.style[format] = String(value)\n option.setAttribute('value', colorGetter.style[format])\n }\n else {\n option.setAttribute('value', value)\n }\n }\n select.appendChild(option)\n }\n}\n\nexport default SnowTheme\n"],"names":[],"mappings":"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"}
@@ -62,6 +62,37 @@ function replaceDeltaImage(delta, imageUrls, imagePlaceholder) {
62
62
  return newDelta;
63
63
  }, new Delta());
64
64
  }
65
+ const WHITE_SPACE_CHAR_RE = /^[\u3000\u0020]$/;
66
+ const WHITE_SPACE_OR_NBSP_RE = /^[\u3000\u0020\u00A0]$/;
67
+ function replaceStrWhiteSpace(str) {
68
+ let textWithWhiteSpace = "";
69
+ let beginHasChar = false;
70
+ let consecutiveSpaces = 0;
71
+ for (const char of str) {
72
+ if (WHITE_SPACE_CHAR_RE.test(char)) {
73
+ consecutiveSpaces += 1;
74
+ if (!beginHasChar) {
75
+ textWithWhiteSpace += " ";
76
+ } else if (consecutiveSpaces === 1) {
77
+ textWithWhiteSpace += char;
78
+ } else {
79
+ textWithWhiteSpace += " ";
80
+ }
81
+ } else {
82
+ consecutiveSpaces = 0;
83
+ textWithWhiteSpace += char;
84
+ beginHasChar = true;
85
+ }
86
+ }
87
+ return textWithWhiteSpace;
88
+ }
89
+ function shouldInsertNbspOnSpace(prefix) {
90
+ if (!prefix) {
91
+ return true;
92
+ }
93
+ const lastChar = prefix.slice(-1);
94
+ return WHITE_SPACE_OR_NBSP_RE.test(lastChar);
95
+ }
65
96
  function splitWithBreak(insertContent) {
66
97
  const lines = [];
67
98
  const insertStr = insertContent;
@@ -132,6 +163,8 @@ exports.isInside = isInside;
132
163
  exports.isNullOrUndefined = isNullOrUndefined;
133
164
  exports.omit = omit;
134
165
  exports.replaceDeltaImage = replaceDeltaImage;
166
+ exports.replaceStrWhiteSpace = replaceStrWhiteSpace;
135
167
  exports.sanitize = sanitize;
168
+ exports.shouldInsertNbspOnSpace = shouldInsertNbspOnSpace;
136
169
  exports.splitWithBreak = splitWithBreak;
137
170
  //# sourceMappingURL=editor.utils.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.utils.cjs.js","sources":["../../../src/config/editor.utils.ts"],"sourcesContent":["import Quill from 'quill'\n\nconst Delta = Quill.import('delta')\n\n// color hex to rgba\nexport function hexToRgbA(hex: string) {\n let color\n if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {\n color = hex.substring(1).split('')\n if (color.length === 3) {\n color = [color[0], color[0], color[1], color[1], color[2], color[2]]\n }\n color = `0x${color.join('')}`\n return (\n `rgba(${\n [(color >> 16) & 255, (color >> 8) & 255, color & 255].join(',')\n },1)`\n )\n }\n}\n\n/**\n * 将File格式的图片转换成Url格式\n * @param imageFile File格式的图片\n */\nexport function imageFileToUrl(imageFile) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(imageFile)\n reader.onload = function (e) {\n resolve(e.target.result)\n }\n reader.onerror = reject\n }).catch((error) => {\n console.error('Error reading file:', error)\n })\n}\n\n/**\n * 将Url格式的图片转换成File格式\n * @param imageUrl 图片的URL\n */\nexport function imageUrlToFile(imageUrl, isErrorImage?: boolean) {\n return new Promise((resolve, reject) => {\n fetch(imageUrl, {\n method: 'get',\n mode: 'no-cors',\n })\n .then(res => res.blob())\n .then((blob) => {\n if (!blob.type.includes('image') || !blob.type) {\n return reject()\n }\n const fileType = blob.type.replace(/^.*\\//, '')\n const fileName = isErrorImage\n ? 'editorx-error-image.png'\n : `image-${new Date().getTime()}.${fileType}`\n const file = new File([blob], fileName, blob)\n resolve(file)\n })\n .catch(reject)\n })\n}\n\nexport function isNullOrUndefined(param) {\n return param === null || param === undefined\n}\n\n/**\n * omit\n * @param obj target Object\n * @param uselessKeys keys of removed properties\n * @return new Object without useless properties\n */\nexport function omit(obj, uselessKeys) {\n return (\n obj\n && Object.keys(obj).reduce((acc, key) => {\n return uselessKeys.includes(key) ? acc : { ...acc, [key]: obj[key] }\n }, {})\n )\n}\n\n/**\n * 将delta中的图片替换成制定的图片数组,用于图片上传到服务器的场景\n * @param delta 原始delta\n * @param imageUrls 图片数组\n * @param imagePlaceholder 标识是否是占位图的数组,与图片数组一一对应\n * @return 替换之后的delta\n */\nexport function replaceDeltaImage(delta, imageUrls, imagePlaceholder) {\n let imageIndex = 0\n return delta.reduce((newDelta, op) => {\n if (op.insert.image && !op.insert.image.hasExisted) {\n const attributes = imagePlaceholder[imageIndex]\n ? { ...op.attributes, width: 'auto', height: 225 } // 占位图片应该固定大小\n : op.attributes\n newDelta.insert({ image: imageUrls[imageIndex] }, attributes)\n imageIndex++\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nexport function splitWithBreak(insertContent: string) {\n const lines = []\n const insertStr = insertContent\n let start = 0\n for (let i = 0; i < insertContent.length; i++) {\n if (insertStr.charAt(i) === '\\n') {\n if (i === 0) {\n lines.push('\\n')\n }\n else {\n lines.push(insertStr.substring(start, i))\n lines.push('\\n')\n }\n start = i + 1\n }\n }\n\n const tailStr = insertStr.substring(start)\n if (tailStr) {\n lines.push(tailStr)\n }\n\n return lines\n}\n\n/**\n * getEventComposedPath\n * compatibility fixed for Event.path/Event.composedPath\n * Event.path is only for chrome/opera\n * Event.composedPath is for Safari, FF\n * Neither for Micro Edge\n * @return an array of event.path\n */\nexport function getEventComposedPath(evt) {\n let path\n // chrome, opera, safari, firefox\n path = evt.path || (evt.composedPath && evt.composedPath())\n\n // other: edge\n if (path === undefined && evt.target) {\n path = []\n let target = evt.target\n path.push(target)\n\n while (target && target.parentNode) {\n target = target.parentNode\n path.push(target)\n }\n }\n\n return path\n}\n\nexport function sanitize(url, protocols) {\n const anchor = document.createElement('a')\n anchor.href = url\n const protocol = anchor.href.slice(0, anchor.href.indexOf(':'))\n return protocols.includes(protocol)\n}\n\nexport function hadProtocol(url: string) {\n if (!url || !/^(?:f|ht)tps?\\:\\/\\//.test(url)) {\n return false\n }\n return true\n}\n\nexport function isInside(position, dom) {\n const areaPosition = dom.getBoundingClientRect()\n const { pageX, pageY } = position\n // getBoundingClientRect是不考虑窗口滚动的\n const left = pageX - window.scrollX\n const top = pageY - window.scrollY\n const {\n left: areaLeft,\n top: areaTop,\n width: areaWidth,\n height: areaHeight,\n } = areaPosition\n const inside\n = left > areaLeft\n && left < areaLeft + areaWidth\n && top > areaTop\n && top < areaTop + areaHeight\n return inside\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"editor.utils.cjs.js","sources":["../../../src/config/editor.utils.ts"],"sourcesContent":["import Quill from 'quill'\n\nconst Delta = Quill.import('delta')\n\n// color hex to rgba\nexport function hexToRgbA(hex: string) {\n let color\n if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {\n color = hex.substring(1).split('')\n if (color.length === 3) {\n color = [color[0], color[0], color[1], color[1], color[2], color[2]]\n }\n color = `0x${color.join('')}`\n return (\n `rgba(${\n [(color >> 16) & 255, (color >> 8) & 255, color & 255].join(',')\n },1)`\n )\n }\n}\n\n/**\n * 将File格式的图片转换成Url格式\n * @param imageFile File格式的图片\n */\nexport function imageFileToUrl(imageFile) {\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.readAsDataURL(imageFile)\n reader.onload = function (e) {\n resolve(e.target.result)\n }\n reader.onerror = reject\n }).catch((error) => {\n console.error('Error reading file:', error)\n })\n}\n\n/**\n * 将Url格式的图片转换成File格式\n * @param imageUrl 图片的URL\n */\nexport function imageUrlToFile(imageUrl, isErrorImage?: boolean) {\n return new Promise((resolve, reject) => {\n fetch(imageUrl, {\n method: 'get',\n mode: 'no-cors',\n })\n .then(res => res.blob())\n .then((blob) => {\n if (!blob.type.includes('image') || !blob.type) {\n return reject()\n }\n const fileType = blob.type.replace(/^.*\\//, '')\n const fileName = isErrorImage\n ? 'editorx-error-image.png'\n : `image-${new Date().getTime()}.${fileType}`\n const file = new File([blob], fileName, blob)\n resolve(file)\n })\n .catch(reject)\n })\n}\n\nexport function isNullOrUndefined(param) {\n return param === null || param === undefined\n}\n\n/**\n * omit\n * @param obj target Object\n * @param uselessKeys keys of removed properties\n * @return new Object without useless properties\n */\nexport function omit(obj, uselessKeys) {\n return (\n obj\n && Object.keys(obj).reduce((acc, key) => {\n return uselessKeys.includes(key) ? acc : { ...acc, [key]: obj[key] }\n }, {})\n )\n}\n\n/**\n * 将delta中的图片替换成制定的图片数组,用于图片上传到服务器的场景\n * @param delta 原始delta\n * @param imageUrls 图片数组\n * @param imagePlaceholder 标识是否是占位图的数组,与图片数组一一对应\n * @return 替换之后的delta\n */\nexport function replaceDeltaImage(delta, imageUrls, imagePlaceholder) {\n let imageIndex = 0\n return delta.reduce((newDelta, op) => {\n if (op.insert.image && !op.insert.image.hasExisted) {\n const attributes = imagePlaceholder[imageIndex]\n ? { ...op.attributes, width: 'auto', height: 225 } // 占位图片应该固定大小\n : op.attributes\n newDelta.insert({ image: imageUrls[imageIndex] }, attributes)\n imageIndex++\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nconst WHITE_SPACE_CHAR_RE = /^[\\u3000\\u0020]$/\nconst WHITE_SPACE_OR_NBSP_RE = /^[\\u3000\\u0020\\u00A0]$/\n\n/**\n * 将连续空格转为不间断空格,以便 innerHTML 输出 &nbsp; 并在预览中保留间距。\n * - 行首空白:全部转为 \\u00A0\n * - 正文内连续空白:保留第一个普通空格,其余转为 \\u00A0\n */\nexport function replaceStrWhiteSpace(str: string) {\n let textWithWhiteSpace = ''\n let beginHasChar = false\n let consecutiveSpaces = 0\n for (const char of str) {\n if (WHITE_SPACE_CHAR_RE.test(char)) {\n consecutiveSpaces += 1\n if (!beginHasChar) {\n textWithWhiteSpace += '\\u00A0'\n }\n else if (consecutiveSpaces === 1) {\n textWithWhiteSpace += char\n }\n else {\n textWithWhiteSpace += '\\u00A0'\n }\n }\n else {\n consecutiveSpaces = 0\n textWithWhiteSpace += char\n beginHasChar = true\n }\n }\n return textWithWhiteSpace\n}\n\n/** 空格键是否应插入不间断空格(与 replaceStrWhiteSpace 规则一致) */\nexport function shouldInsertNbspOnSpace(prefix: string) {\n if (!prefix) {\n return true\n }\n const lastChar = prefix.slice(-1)\n return WHITE_SPACE_OR_NBSP_RE.test(lastChar)\n}\n\nexport function splitWithBreak(insertContent: string) {\n const lines = []\n const insertStr = insertContent\n let start = 0\n for (let i = 0; i < insertContent.length; i++) {\n if (insertStr.charAt(i) === '\\n') {\n if (i === 0) {\n lines.push('\\n')\n }\n else {\n lines.push(insertStr.substring(start, i))\n lines.push('\\n')\n }\n start = i + 1\n }\n }\n\n const tailStr = insertStr.substring(start)\n if (tailStr) {\n lines.push(tailStr)\n }\n\n return lines\n}\n\n/**\n * getEventComposedPath\n * compatibility fixed for Event.path/Event.composedPath\n * Event.path is only for chrome/opera\n * Event.composedPath is for Safari, FF\n * Neither for Micro Edge\n * @return an array of event.path\n */\nexport function getEventComposedPath(evt) {\n let path\n // chrome, opera, safari, firefox\n path = evt.path || (evt.composedPath && evt.composedPath())\n\n // other: edge\n if (path === undefined && evt.target) {\n path = []\n let target = evt.target\n path.push(target)\n\n while (target && target.parentNode) {\n target = target.parentNode\n path.push(target)\n }\n }\n\n return path\n}\n\nexport function sanitize(url, protocols) {\n const anchor = document.createElement('a')\n anchor.href = url\n const protocol = anchor.href.slice(0, anchor.href.indexOf(':'))\n return protocols.includes(protocol)\n}\n\nexport function hadProtocol(url: string) {\n if (!url || !/^(?:f|ht)tps?\\:\\/\\//.test(url)) {\n return false\n }\n return true\n}\n\nexport function isInside(position, dom) {\n const areaPosition = dom.getBoundingClientRect()\n const { pageX, pageY } = position\n // getBoundingClientRect是不考虑窗口滚动的\n const left = pageX - window.scrollX\n const top = pageY - window.scrollY\n const {\n left: areaLeft,\n top: areaTop,\n width: areaWidth,\n height: areaHeight,\n } = areaPosition\n const inside\n = left > areaLeft\n && left < areaLeft + areaWidth\n && top > areaTop\n && top < areaTop + areaHeight\n return inside\n}\n"],"names":[],"mappings":"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"}
@@ -48,7 +48,9 @@ exports.isInside = editor_utils.isInside;
48
48
  exports.isNullOrUndefined = editor_utils.isNullOrUndefined;
49
49
  exports.omit = editor_utils.omit;
50
50
  exports.replaceDeltaImage = editor_utils.replaceDeltaImage;
51
+ exports.replaceStrWhiteSpace = editor_utils.replaceStrWhiteSpace;
51
52
  exports.sanitize = editor_utils.sanitize;
53
+ exports.shouldInsertNbspOnSpace = editor_utils.shouldInsertNbspOnSpace;
52
54
  exports.splitWithBreak = editor_utils.splitWithBreak;
53
55
  exports.AUDIO_VIDEO_UPLOADER_MIME_TYPES = editor_config.AUDIO_VIDEO_UPLOADER_MIME_TYPES;
54
56
  exports.BIG_DELTA_LIMIT = editor_config.BIG_DELTA_LIMIT;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../../../src/config/index.ts"],"sourcesContent":["import { isNullOrUndefined } from './editor.utils'\n\nexport * from './editor.config'\nexport * from './editor.utils'\nexport * from './types'\n\n// 触发上传\nexport function inputFile(type: 'image' | 'video' | 'file', accept: string[]) {\n if (accept.length <= 0) return\n let fileInput = this.container.querySelector(`input.ql-${type}[type=file]`)\n if (isNullOrUndefined(fileInput)) {\n fileInput = document.createElement('input')\n fileInput.style.display = 'none'\n fileInput.classList.add(`ql-${type}`)\n fileInput.setAttribute('type', 'file')\n fileInput.setAttribute('accept', accept.map(mime => mime === '*' ? `${type}/*` : mime).join(','))\n const multiple = this.quill.uploader.getMultiple(type)\n if (multiple) {\n fileInput.setAttribute('multiple', '')\n }\n fileInput.addEventListener('change', () => {\n const range = this.quill.getSelection(true)\n this.quill.uploader.upload(range, fileInput.files, type)\n fileInput.value = ''\n })\n this.container.appendChild(fileInput)\n }\n fileInput.click()\n}\n\nexport function getListValue(value, preListValue) {\n let curListValue = value\n if (preListValue && preListValue === value) {\n curListValue = false\n }\n else if (value === 'check') {\n if (preListValue === 'checked' || preListValue === 'unchecked') {\n curListValue = false\n }\n else {\n curListValue = 'unchecked'\n }\n }\n return curListValue\n}\n/** css namespace */\nexport const namespace = 'fe'\n"],"names":["isNullOrUndefined"],"mappings":";;;;;AAOO,SAAS,UAAU,MAAkC,QAAkB;AAC5E,MAAI,OAAO,UAAU,EAAG;AACxB,MAAI,YAAY,KAAK,UAAU,cAAc,YAAY,IAAI,aAAa;AAC1E,MAAIA,aAAAA,kBAAkB,SAAS,GAAG;AAChC,gBAAY,SAAS,cAAc,OAAO;AAC1C,cAAU,MAAM,UAAU;AAC1B,cAAU,UAAU,IAAI,MAAM,IAAI,EAAE;AACpC,cAAU,aAAa,QAAQ,MAAM;AACrC,cAAU,aAAa,UAAU,OAAO,IAAI,UAAQ,SAAS,MAAM,GAAG,IAAI,OAAO,IAAI,EAAE,KAAK,GAAG,CAAC;AAChG,UAAM,WAAW,KAAK,MAAM,SAAS,YAAY,IAAI;AACrD,QAAI,UAAU;AACZ,gBAAU,aAAa,YAAY,EAAE;AAAA,IACvC;AACA,cAAU,iBAAiB,UAAU,MAAM;AACzC,YAAM,QAAQ,KAAK,MAAM,aAAa,IAAI;AAC1C,WAAK,MAAM,SAAS,OAAO,OAAO,UAAU,OAAO,IAAI;AACvD,gBAAU,QAAQ;AAAA,IACpB,CAAC;AACD,SAAK,UAAU,YAAY,SAAS;AAAA,EACtC;AACA,YAAU,MAAA;AACZ;AAEO,SAAS,aAAa,OAAO,cAAc;AAChD,MAAI,eAAe;AACnB,MAAI,gBAAgB,iBAAiB,OAAO;AAC1C,mBAAe;AAAA,EACjB,WACS,UAAU,SAAS;AAC1B,QAAI,iBAAiB,aAAa,iBAAiB,aAAa;AAC9D,qBAAe;AAAA,IACjB,OACK;AACH,qBAAe;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../../../src/config/index.ts"],"sourcesContent":["import { isNullOrUndefined } from './editor.utils'\n\nexport * from './editor.config'\nexport * from './editor.utils'\nexport * from './types'\n\n// 触发上传\nexport function inputFile(type: 'image' | 'video' | 'file', accept: string[]) {\n if (accept.length <= 0) return\n let fileInput = this.container.querySelector(`input.ql-${type}[type=file]`)\n if (isNullOrUndefined(fileInput)) {\n fileInput = document.createElement('input')\n fileInput.style.display = 'none'\n fileInput.classList.add(`ql-${type}`)\n fileInput.setAttribute('type', 'file')\n fileInput.setAttribute('accept', accept.map(mime => mime === '*' ? `${type}/*` : mime).join(','))\n const multiple = this.quill.uploader.getMultiple(type)\n if (multiple) {\n fileInput.setAttribute('multiple', '')\n }\n fileInput.addEventListener('change', () => {\n const range = this.quill.getSelection(true)\n this.quill.uploader.upload(range, fileInput.files, type)\n fileInput.value = ''\n })\n this.container.appendChild(fileInput)\n }\n fileInput.click()\n}\n\nexport function getListValue(value, preListValue) {\n let curListValue = value\n if (preListValue && preListValue === value) {\n curListValue = false\n }\n else if (value === 'check') {\n if (preListValue === 'checked' || preListValue === 'unchecked') {\n curListValue = false\n }\n else {\n curListValue = 'unchecked'\n }\n }\n return curListValue\n}\n/** css namespace */\nexport const namespace = 'fe'\n"],"names":["isNullOrUndefined"],"mappings":";;;;;AAOO,SAAS,UAAU,MAAkC,QAAkB;AAC5E,MAAI,OAAO,UAAU,EAAG;AACxB,MAAI,YAAY,KAAK,UAAU,cAAc,YAAY,IAAI,aAAa;AAC1E,MAAIA,aAAAA,kBAAkB,SAAS,GAAG;AAChC,gBAAY,SAAS,cAAc,OAAO;AAC1C,cAAU,MAAM,UAAU;AAC1B,cAAU,UAAU,IAAI,MAAM,IAAI,EAAE;AACpC,cAAU,aAAa,QAAQ,MAAM;AACrC,cAAU,aAAa,UAAU,OAAO,IAAI,UAAQ,SAAS,MAAM,GAAG,IAAI,OAAO,IAAI,EAAE,KAAK,GAAG,CAAC;AAChG,UAAM,WAAW,KAAK,MAAM,SAAS,YAAY,IAAI;AACrD,QAAI,UAAU;AACZ,gBAAU,aAAa,YAAY,EAAE;AAAA,IACvC;AACA,cAAU,iBAAiB,UAAU,MAAM;AACzC,YAAM,QAAQ,KAAK,MAAM,aAAa,IAAI;AAC1C,WAAK,MAAM,SAAS,OAAO,OAAO,UAAU,OAAO,IAAI;AACvD,gBAAU,QAAQ;AAAA,IACpB,CAAC;AACD,SAAK,UAAU,YAAY,SAAS;AAAA,EACtC;AACA,YAAU,MAAA;AACZ;AAEO,SAAS,aAAa,OAAO,cAAc;AAChD,MAAI,eAAe;AACnB,MAAI,gBAAgB,iBAAiB,OAAO;AAC1C,mBAAe;AAAA,EACjB,WACS,UAAU,SAAS;AAC1B,QAAI,iBAAiB,aAAa,iBAAiB,aAAa;AAC9D,qBAAe;AAAA,IACjB,OACK;AACH,qBAAe;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/lib/index.cjs.js CHANGED
@@ -111,7 +111,9 @@ exports.isInside = editor_utils.isInside;
111
111
  exports.isNullOrUndefined = editor_utils.isNullOrUndefined;
112
112
  exports.omit = editor_utils.omit;
113
113
  exports.replaceDeltaImage = editor_utils.replaceDeltaImage;
114
+ exports.replaceStrWhiteSpace = editor_utils.replaceStrWhiteSpace;
114
115
  exports.sanitize = editor_utils.sanitize;
116
+ exports.shouldInsertNbspOnSpace = editor_utils.shouldInsertNbspOnSpace;
115
117
  exports.splitWithBreak = editor_utils.splitWithBreak;
116
118
  exports.EmojiBlot = emoji.EmojiBlot;
117
119
  exports.SoftBreak = softBreak.SoftBreak;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -349,20 +349,6 @@ function rebuildDelta(delta, cellLine) {
349
349
  }, new Delta());
350
350
  return buildedDelta;
351
351
  }
352
- function replaceStrWhiteSpace(str) {
353
- const isWhiteSpace = (value) => /^(\u3000|\u0020){1}$/.test(value);
354
- let textWithWhiteSpace = "";
355
- let beginHasChar = false;
356
- for (const char of str) {
357
- if (isWhiteSpace(char) && !beginHasChar) {
358
- textWithWhiteSpace += " ";
359
- } else {
360
- textWithWhiteSpace += char;
361
- beginHasChar = true;
362
- }
363
- }
364
- return textWithWhiteSpace;
365
- }
366
352
  function replaceDeltaWhiteSpace(delta, rootBgColor) {
367
353
  return delta.reduce((newDelta, op) => {
368
354
  if (rootBgColor && op.attributes && op.attributes.color && !op.attributes.background) {
@@ -376,7 +362,7 @@ function replaceDeltaWhiteSpace(delta, rootBgColor) {
376
362
  const lines = editor_utils.splitWithBreak(op.insert);
377
363
  let insertWithWhiteSpace = "";
378
364
  lines.forEach((text) => {
379
- insertWithWhiteSpace += replaceStrWhiteSpace(text);
365
+ insertWithWhiteSpace += editor_utils.replaceStrWhiteSpace(text);
380
366
  });
381
367
  newDelta.insert(insertWithWhiteSpace, op.attributes);
382
368
  } else {
@@ -1 +1 @@
1
- {"version":3,"file":"custom-clipboard.cjs.js","sources":["../../../src/modules/custom-clipboard.ts"],"sourcesContent":["import type TypeClipboard from 'quill/modules/clipboard'\nimport type FluentEditor from '../fluent-editor'\nimport Quill from 'quill'\nimport {\n ERROR_IMAGE_PLACEHOLDER_CN,\n ERROR_IMAGE_PLACEHOLDER_EN,\n} from '../config/base64-image'\nimport { BIG_DELTA_LIMIT } from '../config/editor.config'\nimport {\n hexToRgbA,\n imageUrlToFile,\n isNullOrUndefined,\n omit,\n replaceDeltaImage,\n splitWithBreak,\n} from '../config/editor.utils'\nimport { isString } from '../utils/is'\n\nconst Clipboard = Quill.import('modules/clipboard') as typeof TypeClipboard\nconst Delta = Quill.import('delta')\n\nexport class CustomClipboard extends Clipboard {\n declare quill: FluentEditor\n\n constructor(public quill: FluentEditor) {\n super(quill)\n // 解决 quill 组件在中文输入法的情景下开始输入placeholder不消失的问题\n this.quill.root.addEventListener('input', () => {\n if (this.quill.root.innerText !== '\\\\n' && this.quill.root.classList.contains('ql-blank')) {\n this.quill.root.classList.toggle('ql-blank', false)\n }\n else {\n this.quill.options.placeholder = this.quill.getLangText('editor-placeholder')\n }\n })\n }\n\n prepareMatching(container: HTMLElement, nodeMatches) {\n const elementMatchers = []\n const textMatchers = []\n this.matchers.forEach((pair) => {\n const [selector, matcher] = pair\n if (selector === Node.TEXT_NODE) {\n textMatchers.push(matcher)\n }\n else if (selector === Node.ELEMENT_NODE) {\n elementMatchers.push(matcher)\n }\n else if (isString(selector)) {\n // word 的 v:shape 系列标签只能通过 getElementsByTagName 获取\n const vRegex = /v:(.+)/\n const nodeList = Array.from(\n vRegex.test(selector)\n ? container.getElementsByTagName(selector)\n : container.querySelectorAll(selector),\n )\n nodeList.forEach((node) => {\n if (nodeMatches.has(node)) {\n const matches = nodeMatches.get(node)\n matches.push(matcher)\n }\n else {\n nodeMatches.set(node, [matcher])\n }\n })\n }\n })\n return [elementMatchers, textMatchers]\n }\n\n onCaptureCopy(e, isCut = false) {\n if (e.defaultPrevented) {\n return\n }\n e.preventDefault()\n const [range] = this.quill.selection.getRange()\n if (isNullOrUndefined(range)) {\n return\n }\n const { html, text } = this.onCopy(range, isCut)\n\n // 兼容IE11浏览器`\n if (!e.clipboardData) {\n e.clipboardData = {\n types: 'text/plain',\n setData: (_type, value) => {\n // @ts-ignore\n return window.clipboardData.setData('Text', value)\n },\n }\n }\n\n // 复制代码时移除utf8中产生的不间断空格\\u00A0\n let plainText = text\n if (html.startsWith('<pre>')) {\n plainText = text.replace(/\\u00A0/g, ' ')\n }\n\n e.clipboardData.setData('text/html', html)\n e.clipboardData.setData('text/plain', plainText)\n if (isCut) {\n this.quill.deleteText(range, Quill.sources.USER)\n }\n }\n\n onCapturePaste(e: ClipboardEvent) {\n if (e.defaultPrevented || !this.quill.isEnabled()) {\n return\n }\n e.preventDefault()\n const range = this.quill.getSelection(true)\n if (isNullOrUndefined(range)) {\n return\n }\n\n // 兼容IE11浏览器\n if (!e.clipboardData) {\n // @ts-ignore\n e.clipboardData = {\n types: 'text/plain',\n getData: () => {\n // @ts-ignore\n return window.clipboardData.getData('Text')\n },\n }\n }\n\n const html = e.clipboardData.getData('text/html')\n const text = e.clipboardData.getData('text/plain')\n const files = Array.from(e.clipboardData.files || [])\n const msExcelCheck = /<meta.*?Microsoft Excel\\s[\\d].*?>/\n\n if (html.search(msExcelCheck) === -1 && files.length > 0) {\n this.quill.uploader.upload(range, files)\n }\n else {\n const msWordCheck1\n = /<meta\\s*name=\"?generator\"?\\s*content=\"?microsoft\\s*word\\s*\\d+\"?\\/?>/i\n const msWordCheck2 = /xmlns:o=\"urn:schemas-microsoft-com/i\n const result = { html, text, files, rtf: null }\n if (html.search(msExcelCheck) !== -1) {\n result.html = renderStyles(html)\n }\n if (msWordCheck1.test(html) || msWordCheck2.test(html)) {\n // TODO: 当word文档包含heading时text/rtf读取为空,无法获取hex图片,待修复。可参考ckeditor5/issues/2493\n result.rtf = e.clipboardData.getData('text/rtf')\n }\n this.onPaste(range, result)\n }\n }\n\n onPaste(range, { html, text, files: clipboardFiles, rtf }) {\n const hexImages = this.extractImageDataFromRtf(rtf)\n const rootBgColor = getComputedStyle(this.quill.root).backgroundColor\n const formats = this.quill.getFormat(range.index)\n let pastedDelta = this.convert({ text, html }, formats)\n pastedDelta = replaceDeltaWhiteSpace(pastedDelta, rootBgColor)\n const deltaLength = pastedDelta.ops.length\n\n let loadingTipsContainer\n if (deltaLength > BIG_DELTA_LIMIT) {\n loadingTipsContainer = this.quill.addContainer('ql-loading-tips')\n loadingTipsContainer.innerHTML = this.quill.getLangText('pasting')\n }\n\n const linePos = { index: range.index, length: range.length, fix: 0 }\n const [line, offset] = this.quill.getLine(range.index)\n\n const handlePasteContent = (content: any) => {\n const pastedContent = content\n\n const oldDelta = new Delta().retain(linePos.index).delete(linePos.length)\n const delta = oldDelta.concat(pastedContent)\n\n setTimeout(() => {\n this.quill.updateContents(delta, Quill.sources.USER)\n // 光标位置应该在粘贴内容之后:原光标位置 + 粘贴内容长度\n const newSelectionIndex = linePos.index + (pastedContent.length ? pastedContent.length() : 0)\n this.quill.setSelection(\n newSelectionIndex,\n Quill.sources.SILENT,\n )\n this.quill.scrollSelectionIntoView()\n if (loadingTipsContainer) {\n loadingTipsContainer.remove()\n }\n })\n }\n\n ;(async () => {\n try {\n const [files, placeholders, originalUrls, imageIndexs] = this.flipFilesArray(\n await this.extractFilesFromDelta(\n pastedDelta,\n clipboardFiles,\n hexImages,\n ),\n )\n\n if (files.length === 0) {\n handlePasteContent(pastedDelta)\n }\n else {\n if (this.quill.options.editorPaste && this.quill.options.editorPaste.observers.length !== 0) {\n // 设置editorPaste回调的情况\n this.quill.options.editorPaste.emit({\n files,\n callback: ({ code, message, data }) => {\n if (code === 0) {\n const { imageUrls } = data\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n handlePasteContent(pastedDelta)\n }\n else {\n console.error('error message:', message)\n }\n },\n })\n }\n else {\n // 没有originalUrls 也没有文件粘贴\n if (files[0] !== undefined || originalUrls.length === 0) {\n // 没有设置editorPaste回调的情况下,File格式的占位图需要手动转换成url格式,插入到编辑器中\n const imageUrls = await this.files2urls(\n files,\n placeholders,\n originalUrls,\n pastedDelta,\n imageIndexs,\n )\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n }\n handlePasteContent(pastedDelta)\n }\n }\n }\n catch (_e) {\n throw new Error('Paste failed.')\n }\n })()\n }\n\n files2urls(files: File[], placeholders, originalUrls, pastedDelta, imageIndexs) {\n return Promise.all(\n files.map(async (imageFile, index) => {\n const netImgExp = /^((http|https)\\:)?\\/\\/([\\s\\S]+)$/\n if (\n !placeholders[index]\n && originalUrls[index]\n && netImgExp.test(originalUrls[index])\n ) {\n // 不是占位图的普通url图片直接返回url\n return new Promise((resolve) => {\n resolve(originalUrls[index])\n })\n }\n else {\n const range = this.getImgSelection(pastedDelta, imageIndexs[index])\n const urls = await this.quill.uploader.getFileUrls([imageFile], range)\n return urls[0] || undefined\n }\n }),\n )\n }\n\n flipFilesArray(filesArr) {\n const files = []\n const placeholders = []\n const originalUrls = []\n const imageIndexs = []\n filesArr.forEach((item: any) => {\n if (item) {\n const [file, placeholder, originalUrl, imageIndex] = item\n files.push(file)\n placeholders.push(placeholder)\n originalUrls.push(originalUrl)\n if (imageIndex === 0 || imageIndex) {\n imageIndexs.push(imageIndex)\n }\n }\n })\n return [files, placeholders, originalUrls, imageIndexs]\n }\n\n // 将图片从hex转为base64\n convertHexToBase64(hexString) {\n return btoa(\n hexString\n .match(/\\w{2}/g)\n .map((char) => {\n return String.fromCharCode(Number.parseInt(char, 16))\n })\n .join(''),\n )\n }\n\n // 匹配rtf中的图片,存储为{hex, type}对象数组\n extractImageDataFromRtf(rtfData) {\n if (!rtfData) {\n return []\n }\n\n const regexPictureHeader\n = /{\\\\pict[\\s\\S]+?\\\\bliptag-?\\d+(\\\\blipupi-?\\d+)?({\\\\\\*\\\\blipuid\\s?[\\da-fA-F]+)?[\\s}]*?/\n const regexPicture = new RegExp(\n `(?:(${regexPictureHeader.source}))([\\\\da-fA-F\\\\s]+)\\\\}`,\n 'g',\n )\n const images = rtfData.match(regexPicture)\n const result = []\n\n if (images) {\n for (const image of images) {\n let imageType = ''\n\n if (image.includes('\\\\pngblip')) {\n imageType = 'image/png'\n }\n else if (image.includes('\\\\jpegblip')) {\n imageType = 'image/jpeg'\n }\n\n if (imageType) {\n result.push({\n hex: image\n .replace(regexPictureHeader, '')\n .replace(/[^\\da-fA-F]/g, ''),\n type: imageType,\n })\n }\n }\n }\n\n return result\n }\n\n extractFilesFromDelta(delta, clipboardFiles, hexImages?) {\n let index = -1\n return Promise.all(\n delta.map(async (op) => {\n index++\n const image = op.insert.image\n if (!image || image.hasExisted) {\n return\n }\n\n let file\n let isPlaceholderImage = false\n let imageIndex\n try {\n // hex 图片存在则为 file:/// 协议本地图片,使用 hex 图片转为 base64 读取\n const hexImage = hexImages.length && hexImages.shift()\n const newImage\n = hexImage\n && `data:${hexImage.type};base64,${this.convertHexToBase64(\n hexImage.hex,\n )}`\n imageIndex = index\n file = await imageUrlToFile(newImage || image.src || image)\n }\n catch (_err) {\n if (clipboardFiles.length !== 0) {\n // 跨域获取图片失败时从剪切板获取图片\n const clipboardFile = clipboardFiles[0]\n const imageType\n = clipboardFile.type?.indexOf('image') === -1\n ? 'image/png'\n : clipboardFile.type\n const blob = clipboardFile.slice(0, clipboardFile.size, imageType)\n file = new File([blob], `image-CORS-${new Date().getTime()}.png`, {\n type: imageType,\n })\n }\n else if (image.src.startsWith('http')) {\n // 什么都不做\n }\n else {\n // 剪切板中无图片,用失败占位图替换\n const errorImagePlaceholderJpg\n = this.quill.getLangText('img-error') === 'Image Copy Error'\n ? ERROR_IMAGE_PLACEHOLDER_EN\n : ERROR_IMAGE_PLACEHOLDER_CN\n file = await imageUrlToFile(errorImagePlaceholderJpg, true)\n isPlaceholderImage = true\n }\n }\n\n return [file, isPlaceholderImage, image, imageIndex]\n }),\n )\n }\n\n getImgSelection(delta, imageIndex) {\n let length = 0\n delta.ops.every((op, index) => {\n if (index === imageIndex) {\n return false\n }\n if (typeof op.insert === 'string') {\n length += op.insert.length\n }\n else if (typeof op.insert === 'object') {\n // 对于图片、提及等对象类型的 insert,长度为 1\n length += 1\n }\n return true\n })\n const range = {\n index: length,\n length: 0,\n }\n return range\n }\n}\n\nfunction rebuildDelta(delta, cellLine) {\n const { cell: cellId, colspan, row: rowId, rowspan } = cellLine\n const buildedDelta = delta.reduce((newDelta, op) => {\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n\n lines.forEach((text) => {\n if (text === '\\n') {\n // 对换行增加 table-cell-line 格式,以避免表格断开\n newDelta.insert('\\n', {\n ...op.attributes,\n 'table-cell-line': { row: rowId, cell: cellId, rowspan, colspan },\n })\n }\n else {\n text = text.endsWith('\\r') ? text.slice(0, -1) : text\n newDelta.insert(\n text,\n omit(op.attributes, ['table', 'table-cell-line']),\n )\n }\n })\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n\n return newDelta\n }, new Delta())\n\n return buildedDelta\n}\n\nfunction replaceStrWhiteSpace(str) {\n const isWhiteSpace = value => /^(\\u3000|\\u0020){1}$/.test(value) // 空白字符\n let textWithWhiteSpace = ''\n let beginHasChar = false\n for (const char of str) {\n if (isWhiteSpace(char) && !beginHasChar) {\n textWithWhiteSpace += '\\u00A0'\n }\n else {\n textWithWhiteSpace += char\n beginHasChar = true\n }\n }\n return textWithWhiteSpace\n}\n\nfunction replaceDeltaWhiteSpace(delta, rootBgColor?) {\n return delta.reduce((newDelta, op) => {\n // fix: 当粘贴文字颜色和编辑器背景色一致且自身无背景色的情况下移除文字颜色样式,避免误导用户粘贴无效\n if (\n rootBgColor\n && op.attributes\n && op.attributes.color\n && !op.attributes.background\n ) {\n const originColor = op.attributes.color\n const fontColor\n = originColor.indexOf('#') === 0 ? hexToRgbA(originColor) : originColor\n if (\n fontColor === rootBgColor\n || (fontColor === 'rgba(255,255,255,1)'\n && rootBgColor === 'rgba(0, 0, 0, 0)')\n ) {\n delete op.attributes.color\n }\n }\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n let insertWithWhiteSpace = ''\n lines.forEach((text) => {\n insertWithWhiteSpace += replaceStrWhiteSpace(text)\n })\n newDelta.insert(insertWithWhiteSpace, op.attributes)\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nfunction renderStyles(html) {\n let htmlString = html\n // Trim unnecessary parts.\n htmlString = htmlString.substring(\n htmlString.indexOf('<html '),\n htmlString.length,\n )\n htmlString = htmlString.substring(\n 0,\n htmlString.lastIndexOf('</html>') + '</html>'.length,\n )\n\n // Add temporary iframe.\n const iframe = document.createElement('iframe')\n iframe.style.display = 'none'\n document.body.appendChild(iframe)\n\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document\n iframeDoc.open()\n iframeDoc.write(htmlString)\n iframeDoc.close()\n\n let collection\n let pointer\n const rules\n = iframeDoc.styleSheets[iframeDoc.styleSheets.length - 1].cssRules\n\n // Convert internal styles to inline style of respective node.\n for (let idx = 0; idx < rules.length; idx++) {\n if ((rules[idx] as CSSStyleRule).selectorText === '') {\n continue\n }\n collection = iframeDoc.body.querySelectorAll(\n (rules[idx] as CSSStyleRule).selectorText,\n )\n\n for (pointer = 0; pointer < collection.length; pointer++) {\n collection[pointer].style.cssText += (\n rules[idx] as CSSStyleRule\n ).style.cssText\n }\n }\n\n // @ts-ignore\n const convertedString = iframeDoc.firstChild.outerHTML\n // Remove temporary iframe.\n iframe.parentNode.removeChild(iframe)\n\n return convertedString\n}\n"],"names":["isString","isNullOrUndefined","BIG_DELTA_LIMIT","replaceDeltaImage","imageUrlToFile","ERROR_IMAGE_PLACEHOLDER_EN","ERROR_IMAGE_PLACEHOLDER_CN","splitWithBreak","omit","hexToRgbA"],"mappings":"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"}
1
+ {"version":3,"file":"custom-clipboard.cjs.js","sources":["../../../src/modules/custom-clipboard.ts"],"sourcesContent":["import type TypeClipboard from 'quill/modules/clipboard'\nimport type FluentEditor from '../fluent-editor'\nimport Quill from 'quill'\nimport {\n ERROR_IMAGE_PLACEHOLDER_CN,\n ERROR_IMAGE_PLACEHOLDER_EN,\n} from '../config/base64-image'\nimport { BIG_DELTA_LIMIT } from '../config/editor.config'\nimport {\n hexToRgbA,\n imageUrlToFile,\n isNullOrUndefined,\n omit,\n replaceDeltaImage,\n replaceStrWhiteSpace,\n splitWithBreak,\n} from '../config/editor.utils'\nimport { isString } from '../utils/is'\n\nconst Clipboard = Quill.import('modules/clipboard') as typeof TypeClipboard\nconst Delta = Quill.import('delta')\n\nexport class CustomClipboard extends Clipboard {\n declare quill: FluentEditor\n\n constructor(public quill: FluentEditor) {\n super(quill)\n // 解决 quill 组件在中文输入法的情景下开始输入placeholder不消失的问题\n this.quill.root.addEventListener('input', () => {\n if (this.quill.root.innerText !== '\\\\n' && this.quill.root.classList.contains('ql-blank')) {\n this.quill.root.classList.toggle('ql-blank', false)\n }\n else {\n this.quill.options.placeholder = this.quill.getLangText('editor-placeholder')\n }\n })\n }\n\n prepareMatching(container: HTMLElement, nodeMatches) {\n const elementMatchers = []\n const textMatchers = []\n this.matchers.forEach((pair) => {\n const [selector, matcher] = pair\n if (selector === Node.TEXT_NODE) {\n textMatchers.push(matcher)\n }\n else if (selector === Node.ELEMENT_NODE) {\n elementMatchers.push(matcher)\n }\n else if (isString(selector)) {\n // word 的 v:shape 系列标签只能通过 getElementsByTagName 获取\n const vRegex = /v:(.+)/\n const nodeList = Array.from(\n vRegex.test(selector)\n ? container.getElementsByTagName(selector)\n : container.querySelectorAll(selector),\n )\n nodeList.forEach((node) => {\n if (nodeMatches.has(node)) {\n const matches = nodeMatches.get(node)\n matches.push(matcher)\n }\n else {\n nodeMatches.set(node, [matcher])\n }\n })\n }\n })\n return [elementMatchers, textMatchers]\n }\n\n onCaptureCopy(e, isCut = false) {\n if (e.defaultPrevented) {\n return\n }\n e.preventDefault()\n const [range] = this.quill.selection.getRange()\n if (isNullOrUndefined(range)) {\n return\n }\n const { html, text } = this.onCopy(range, isCut)\n\n // 兼容IE11浏览器`\n if (!e.clipboardData) {\n e.clipboardData = {\n types: 'text/plain',\n setData: (_type, value) => {\n // @ts-ignore\n return window.clipboardData.setData('Text', value)\n },\n }\n }\n\n // 复制代码时移除utf8中产生的不间断空格\\u00A0\n let plainText = text\n if (html.startsWith('<pre>')) {\n plainText = text.replace(/\\u00A0/g, ' ')\n }\n\n e.clipboardData.setData('text/html', html)\n e.clipboardData.setData('text/plain', plainText)\n if (isCut) {\n this.quill.deleteText(range, Quill.sources.USER)\n }\n }\n\n onCapturePaste(e: ClipboardEvent) {\n if (e.defaultPrevented || !this.quill.isEnabled()) {\n return\n }\n e.preventDefault()\n const range = this.quill.getSelection(true)\n if (isNullOrUndefined(range)) {\n return\n }\n\n // 兼容IE11浏览器\n if (!e.clipboardData) {\n // @ts-ignore\n e.clipboardData = {\n types: 'text/plain',\n getData: () => {\n // @ts-ignore\n return window.clipboardData.getData('Text')\n },\n }\n }\n\n const html = e.clipboardData.getData('text/html')\n const text = e.clipboardData.getData('text/plain')\n const files = Array.from(e.clipboardData.files || [])\n const msExcelCheck = /<meta.*?Microsoft Excel\\s[\\d].*?>/\n\n if (html.search(msExcelCheck) === -1 && files.length > 0) {\n this.quill.uploader.upload(range, files)\n }\n else {\n const msWordCheck1\n = /<meta\\s*name=\"?generator\"?\\s*content=\"?microsoft\\s*word\\s*\\d+\"?\\/?>/i\n const msWordCheck2 = /xmlns:o=\"urn:schemas-microsoft-com/i\n const result = { html, text, files, rtf: null }\n if (html.search(msExcelCheck) !== -1) {\n result.html = renderStyles(html)\n }\n if (msWordCheck1.test(html) || msWordCheck2.test(html)) {\n // TODO: 当word文档包含heading时text/rtf读取为空,无法获取hex图片,待修复。可参考ckeditor5/issues/2493\n result.rtf = e.clipboardData.getData('text/rtf')\n }\n this.onPaste(range, result)\n }\n }\n\n onPaste(range, { html, text, files: clipboardFiles, rtf }) {\n const hexImages = this.extractImageDataFromRtf(rtf)\n const rootBgColor = getComputedStyle(this.quill.root).backgroundColor\n const formats = this.quill.getFormat(range.index)\n let pastedDelta = this.convert({ text, html }, formats)\n pastedDelta = replaceDeltaWhiteSpace(pastedDelta, rootBgColor)\n const deltaLength = pastedDelta.ops.length\n\n let loadingTipsContainer\n if (deltaLength > BIG_DELTA_LIMIT) {\n loadingTipsContainer = this.quill.addContainer('ql-loading-tips')\n loadingTipsContainer.innerHTML = this.quill.getLangText('pasting')\n }\n\n const linePos = { index: range.index, length: range.length, fix: 0 }\n const [line, offset] = this.quill.getLine(range.index)\n\n const handlePasteContent = (content: any) => {\n const pastedContent = content\n\n const oldDelta = new Delta().retain(linePos.index).delete(linePos.length)\n const delta = oldDelta.concat(pastedContent)\n\n setTimeout(() => {\n this.quill.updateContents(delta, Quill.sources.USER)\n // 光标位置应该在粘贴内容之后:原光标位置 + 粘贴内容长度\n const newSelectionIndex = linePos.index + (pastedContent.length ? pastedContent.length() : 0)\n this.quill.setSelection(\n newSelectionIndex,\n Quill.sources.SILENT,\n )\n this.quill.scrollSelectionIntoView()\n if (loadingTipsContainer) {\n loadingTipsContainer.remove()\n }\n })\n }\n\n ;(async () => {\n try {\n const [files, placeholders, originalUrls, imageIndexs] = this.flipFilesArray(\n await this.extractFilesFromDelta(\n pastedDelta,\n clipboardFiles,\n hexImages,\n ),\n )\n\n if (files.length === 0) {\n handlePasteContent(pastedDelta)\n }\n else {\n if (this.quill.options.editorPaste && this.quill.options.editorPaste.observers.length !== 0) {\n // 设置editorPaste回调的情况\n this.quill.options.editorPaste.emit({\n files,\n callback: ({ code, message, data }) => {\n if (code === 0) {\n const { imageUrls } = data\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n handlePasteContent(pastedDelta)\n }\n else {\n console.error('error message:', message)\n }\n },\n })\n }\n else {\n // 没有originalUrls 也没有文件粘贴\n if (files[0] !== undefined || originalUrls.length === 0) {\n // 没有设置editorPaste回调的情况下,File格式的占位图需要手动转换成url格式,插入到编辑器中\n const imageUrls = await this.files2urls(\n files,\n placeholders,\n originalUrls,\n pastedDelta,\n imageIndexs,\n )\n pastedDelta = replaceDeltaImage(\n pastedDelta,\n imageUrls,\n placeholders,\n )\n }\n handlePasteContent(pastedDelta)\n }\n }\n }\n catch (_e) {\n throw new Error('Paste failed.')\n }\n })()\n }\n\n files2urls(files: File[], placeholders, originalUrls, pastedDelta, imageIndexs) {\n return Promise.all(\n files.map(async (imageFile, index) => {\n const netImgExp = /^((http|https)\\:)?\\/\\/([\\s\\S]+)$/\n if (\n !placeholders[index]\n && originalUrls[index]\n && netImgExp.test(originalUrls[index])\n ) {\n // 不是占位图的普通url图片直接返回url\n return new Promise((resolve) => {\n resolve(originalUrls[index])\n })\n }\n else {\n const range = this.getImgSelection(pastedDelta, imageIndexs[index])\n const urls = await this.quill.uploader.getFileUrls([imageFile], range)\n return urls[0] || undefined\n }\n }),\n )\n }\n\n flipFilesArray(filesArr) {\n const files = []\n const placeholders = []\n const originalUrls = []\n const imageIndexs = []\n filesArr.forEach((item: any) => {\n if (item) {\n const [file, placeholder, originalUrl, imageIndex] = item\n files.push(file)\n placeholders.push(placeholder)\n originalUrls.push(originalUrl)\n if (imageIndex === 0 || imageIndex) {\n imageIndexs.push(imageIndex)\n }\n }\n })\n return [files, placeholders, originalUrls, imageIndexs]\n }\n\n // 将图片从hex转为base64\n convertHexToBase64(hexString) {\n return btoa(\n hexString\n .match(/\\w{2}/g)\n .map((char) => {\n return String.fromCharCode(Number.parseInt(char, 16))\n })\n .join(''),\n )\n }\n\n // 匹配rtf中的图片,存储为{hex, type}对象数组\n extractImageDataFromRtf(rtfData) {\n if (!rtfData) {\n return []\n }\n\n const regexPictureHeader\n = /{\\\\pict[\\s\\S]+?\\\\bliptag-?\\d+(\\\\blipupi-?\\d+)?({\\\\\\*\\\\blipuid\\s?[\\da-fA-F]+)?[\\s}]*?/\n const regexPicture = new RegExp(\n `(?:(${regexPictureHeader.source}))([\\\\da-fA-F\\\\s]+)\\\\}`,\n 'g',\n )\n const images = rtfData.match(regexPicture)\n const result = []\n\n if (images) {\n for (const image of images) {\n let imageType = ''\n\n if (image.includes('\\\\pngblip')) {\n imageType = 'image/png'\n }\n else if (image.includes('\\\\jpegblip')) {\n imageType = 'image/jpeg'\n }\n\n if (imageType) {\n result.push({\n hex: image\n .replace(regexPictureHeader, '')\n .replace(/[^\\da-fA-F]/g, ''),\n type: imageType,\n })\n }\n }\n }\n\n return result\n }\n\n extractFilesFromDelta(delta, clipboardFiles, hexImages?) {\n let index = -1\n return Promise.all(\n delta.map(async (op) => {\n index++\n const image = op.insert.image\n if (!image || image.hasExisted) {\n return\n }\n\n let file\n let isPlaceholderImage = false\n let imageIndex\n try {\n // hex 图片存在则为 file:/// 协议本地图片,使用 hex 图片转为 base64 读取\n const hexImage = hexImages.length && hexImages.shift()\n const newImage\n = hexImage\n && `data:${hexImage.type};base64,${this.convertHexToBase64(\n hexImage.hex,\n )}`\n imageIndex = index\n file = await imageUrlToFile(newImage || image.src || image)\n }\n catch (_err) {\n if (clipboardFiles.length !== 0) {\n // 跨域获取图片失败时从剪切板获取图片\n const clipboardFile = clipboardFiles[0]\n const imageType\n = clipboardFile.type?.indexOf('image') === -1\n ? 'image/png'\n : clipboardFile.type\n const blob = clipboardFile.slice(0, clipboardFile.size, imageType)\n file = new File([blob], `image-CORS-${new Date().getTime()}.png`, {\n type: imageType,\n })\n }\n else if (image.src.startsWith('http')) {\n // 什么都不做\n }\n else {\n // 剪切板中无图片,用失败占位图替换\n const errorImagePlaceholderJpg\n = this.quill.getLangText('img-error') === 'Image Copy Error'\n ? ERROR_IMAGE_PLACEHOLDER_EN\n : ERROR_IMAGE_PLACEHOLDER_CN\n file = await imageUrlToFile(errorImagePlaceholderJpg, true)\n isPlaceholderImage = true\n }\n }\n\n return [file, isPlaceholderImage, image, imageIndex]\n }),\n )\n }\n\n getImgSelection(delta, imageIndex) {\n let length = 0\n delta.ops.every((op, index) => {\n if (index === imageIndex) {\n return false\n }\n if (typeof op.insert === 'string') {\n length += op.insert.length\n }\n else if (typeof op.insert === 'object') {\n // 对于图片、提及等对象类型的 insert,长度为 1\n length += 1\n }\n return true\n })\n const range = {\n index: length,\n length: 0,\n }\n return range\n }\n}\n\nfunction rebuildDelta(delta, cellLine) {\n const { cell: cellId, colspan, row: rowId, rowspan } = cellLine\n const buildedDelta = delta.reduce((newDelta, op) => {\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n\n lines.forEach((text) => {\n if (text === '\\n') {\n // 对换行增加 table-cell-line 格式,以避免表格断开\n newDelta.insert('\\n', {\n ...op.attributes,\n 'table-cell-line': { row: rowId, cell: cellId, rowspan, colspan },\n })\n }\n else {\n text = text.endsWith('\\r') ? text.slice(0, -1) : text\n newDelta.insert(\n text,\n omit(op.attributes, ['table', 'table-cell-line']),\n )\n }\n })\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n\n return newDelta\n }, new Delta())\n\n return buildedDelta\n}\n\nfunction replaceDeltaWhiteSpace(delta, rootBgColor?) {\n return delta.reduce((newDelta, op) => {\n // fix: 当粘贴文字颜色和编辑器背景色一致且自身无背景色的情况下移除文字颜色样式,避免误导用户粘贴无效\n if (\n rootBgColor\n && op.attributes\n && op.attributes.color\n && !op.attributes.background\n ) {\n const originColor = op.attributes.color\n const fontColor\n = originColor.indexOf('#') === 0 ? hexToRgbA(originColor) : originColor\n if (\n fontColor === rootBgColor\n || (fontColor === 'rgba(255,255,255,1)'\n && rootBgColor === 'rgba(0, 0, 0, 0)')\n ) {\n delete op.attributes.color\n }\n }\n if (op.insert && typeof op.insert === 'string') {\n const lines = splitWithBreak(op.insert)\n let insertWithWhiteSpace = ''\n lines.forEach((text) => {\n insertWithWhiteSpace += replaceStrWhiteSpace(text)\n })\n newDelta.insert(insertWithWhiteSpace, op.attributes)\n }\n else {\n newDelta.insert(op.insert, op.attributes)\n }\n return newDelta\n }, new Delta())\n}\n\nfunction renderStyles(html) {\n let htmlString = html\n // Trim unnecessary parts.\n htmlString = htmlString.substring(\n htmlString.indexOf('<html '),\n htmlString.length,\n )\n htmlString = htmlString.substring(\n 0,\n htmlString.lastIndexOf('</html>') + '</html>'.length,\n )\n\n // Add temporary iframe.\n const iframe = document.createElement('iframe')\n iframe.style.display = 'none'\n document.body.appendChild(iframe)\n\n const iframeDoc = iframe.contentDocument || iframe.contentWindow.document\n iframeDoc.open()\n iframeDoc.write(htmlString)\n iframeDoc.close()\n\n let collection\n let pointer\n const rules\n = iframeDoc.styleSheets[iframeDoc.styleSheets.length - 1].cssRules\n\n // Convert internal styles to inline style of respective node.\n for (let idx = 0; idx < rules.length; idx++) {\n if ((rules[idx] as CSSStyleRule).selectorText === '') {\n continue\n }\n collection = iframeDoc.body.querySelectorAll(\n (rules[idx] as CSSStyleRule).selectorText,\n )\n\n for (pointer = 0; pointer < collection.length; pointer++) {\n collection[pointer].style.cssText += (\n rules[idx] as CSSStyleRule\n ).style.cssText\n }\n }\n\n // @ts-ignore\n const convertedString = iframeDoc.firstChild.outerHTML\n // Remove temporary iframe.\n iframe.parentNode.removeChild(iframe)\n\n return convertedString\n}\n"],"names":["isString","isNullOrUndefined","BIG_DELTA_LIMIT","replaceDeltaImage","imageUrlToFile","ERROR_IMAGE_PLACEHOLDER_EN","ERROR_IMAGE_PLACEHOLDER_CN","splitWithBreak","omit","hexToRgbA","replaceStrWhiteSpace"],"mappings":"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"}
@@ -19,7 +19,24 @@ OriginSnowTheme.DEFAULTS = {
19
19
  "i18n": true,
20
20
  "keyboard": {
21
21
  bindings: {
22
- ...index.shortKey
22
+ ...index.shortKey,
23
+ preserveWhiteSpace: {
24
+ key: " ",
25
+ collapsed: true,
26
+ handler(range, context) {
27
+ if (!editor_utils.shouldInsertNbspOnSpace(context.prefix)) {
28
+ return true;
29
+ }
30
+ this.quill.insertText(
31
+ range.index,
32
+ " ",
33
+ context.format,
34
+ fluentEditor.default.sources.USER
35
+ );
36
+ this.quill.setSelection(range.index + 1, fluentEditor.default.sources.SILENT);
37
+ return false;
38
+ }
39
+ }
23
40
  }
24
41
  },
25
42
  "toolbar": {
@@ -1 +1 @@
1
- {"version":3,"file":"snow.cjs.js","sources":["../../../src/themes/snow.ts"],"sourcesContent":["import type { ThemeOptions } from 'quill/core/theme'\nimport type TypeToolbar from 'quill/modules/toolbar'\nimport type TypeIconPicker from 'quill/ui/icon-picker'\nimport { I18N_LOCALE_CHANGE } from 'quill-i18n'\nimport { inputFile, isNullOrUndefined } from '../config'\nimport FluentEditor from '../core/fluent-editor'\nimport { CustomImageSpec } from '../modules/custom-image/specs/custom-image-spec'\nimport { LinkTooltip } from '../modules/link'\nimport { shortKey } from '../modules/shortcut-key'\nimport { ColorPicker, Picker } from '../modules/toolbar/better-picker'\nimport { FormatPainter } from '../tools/format-painter'\nimport { fullscreenHandler } from '../tools/fullscreen'\nimport { Screenshot } from '../tools/screenshot'\n\nconst OriginSnowTheme = FluentEditor.import('themes/snow') as any\nconst IconPicker = FluentEditor.import('ui/icon-picker') as typeof TypeIconPicker\n\nOriginSnowTheme.DEFAULTS = {\n modules: {\n 'i18n': true,\n 'keyboard': {\n bindings: {\n ...shortKey,\n },\n },\n 'toolbar': {\n handlers: {\n ...(OriginSnowTheme.DEFAULTS as Record<string, any>).modules.toolbar.handlers,\n 'formula': function () {\n if (!this.quill.isEnabled()) return\n const mathlive = this.quill.getModule('mathlive')\n if (!mathlive) {\n this.quill.theme.tooltip.edit('formula')\n }\n else {\n mathlive.createDialog()\n }\n },\n 'undo': function () {\n this.quill.history.undo()\n },\n 'redo': function () {\n this.quill.history.redo()\n },\n 'file': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('file')\n inputFile.call(this, 'file', accept)\n },\n 'image': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('image')\n inputFile.call(this, 'image', accept)\n },\n 'video': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('video')\n inputFile.call(this, 'video', accept)\n },\n 'ai': function () {},\n 'emoji': function () {},\n 'fullscreen': fullscreenHandler,\n [FormatPainter.toolName]: FormatPainter,\n [Screenshot.toolName]: Screenshot,\n 'line-height': function (value) {\n this.quill.format('line-height', value)\n },\n 'divider': function () {\n const range = this.quill.getSelection(true)\n this.quill.insertText(range.index, '\\n', FluentEditor.sources.USER)\n this.quill.insertEmbed(range.index + 1, 'divider', true, FluentEditor.sources.USER)\n this.quill.setSelection(range.index + 2, FluentEditor.sources.SILENT)\n },\n },\n },\n 'image': {\n specs: [CustomImageSpec],\n overlay: {\n style: {\n border: '1px dashed rgb(68, 68, 68)',\n },\n },\n },\n 'shortcut-key': false,\n 'file': true,\n },\n}\n\nconst ALIGNS = [false, 'center', 'right']\nconst COLORS = [\n '',\n 'rgb(255, 255, 255)',\n 'rgb(0, 0, 0)',\n 'rgb(72, 83, 104)',\n 'rgb(41, 114, 244)',\n 'rgb(0, 163, 245)',\n 'rgb(49, 155, 98)',\n 'rgb(222, 60, 54)',\n 'rgb(248, 136, 37)',\n 'rgb(245, 196, 0)',\n 'rgb(153, 56, 215)',\n\n 'rgb(242, 242, 242)',\n 'rgb(127, 127, 127)',\n 'rgb(243, 245, 247)',\n 'rgb(229, 239, 255)',\n 'rgb(229, 246, 255)',\n 'rgb(234, 250, 241)',\n 'rgb(254, 233, 232)',\n 'rgb(254, 243, 235)',\n 'rgb(254, 249, 227)',\n 'rgb(253, 235, 255)',\n\n 'rgb(216, 216, 216)',\n 'rgb(89, 89, 89)',\n 'rgb(197, 202, 211)',\n 'rgb(199, 220, 255)',\n 'rgb(199, 236, 255)',\n 'rgb(195, 234, 213)',\n 'rgb(255, 201, 199)',\n 'rgb(255, 220, 196)',\n 'rgb(255, 238, 173)',\n 'rgb(242, 199, 255)',\n\n 'rgb(191, 191, 191)',\n 'rgb(63, 63, 63)',\n 'rgb(128, 139, 158)',\n 'rgb(153, 190, 255)',\n 'rgb(153, 221, 255)',\n 'rgb(152, 215, 182)',\n 'rgb(255, 156, 153)',\n 'rgb(255, 186, 132)',\n 'rgb(255, 226, 112)',\n 'rgb(213, 142, 255)',\n\n 'rgb(165, 165, 165)',\n 'rgb(38, 38, 38)',\n 'rgb(53, 59, 69)',\n 'rgb(20, 80, 184)',\n 'rgb(18, 116, 165)',\n 'rgb(39, 124, 79)',\n 'rgb(158, 30, 26)',\n 'rgb(184, 96, 20)',\n 'rgb(163, 130, 0)',\n 'rgb(94, 34, 129)',\n\n 'rgb(147, 147, 147)',\n 'rgb(13, 13, 13)',\n 'rgb(36, 39, 46)',\n 'rgb(12, 48, 110)',\n 'rgb(10, 65, 92)',\n 'rgb(24, 78, 50)',\n 'rgb(88, 17, 14)',\n 'rgb(92, 48, 10)',\n 'rgb(102, 82, 0)',\n 'rgb(59, 21, 81)',\n\n 'custom',\n]\nconst FONTS = [false, 'serif', 'monospace']\nconst HEADERS = ['1', '2', '3', false]\nconst SIZES = ['small', false, 'large', 'huge']\nconst LINEHEIGHT = [false, '1.2', '1.5', '2']\n\nclass SnowTheme extends OriginSnowTheme {\n constructor(public quill: FluentEditor, options: ThemeOptions) {\n super(quill, options)\n\n this.quill.on(I18N_LOCALE_CHANGE, () => {\n this.i18nTextToolbar()\n })\n }\n\n i18nTextToolbar() {\n const toolbar = this.quill.getModule('toolbar') as TypeToolbar\n ColorPicker.clearText = this.quill.getLangText('clear-color')\n ColorPicker.customText = this.quill.getLangText('custom-color')\n\n if (!toolbar || !this.pickers) return\n\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) {\n // EasyColorPicker have not dts. abd origin quill ColorPicker dts not complete. use any to resovle ts type error\n const colorPicker = picker as any\n colorPicker.options.remove()\n Array.from(colorPicker.select.options).forEach((option: any) => {\n if (option.hasAttribute('custom')) {\n option.remove()\n }\n })\n colorPicker.buildOptions()\n colorPicker.createUsedColor()\n }\n\n if (picker.select && picker.select.classList.contains('ql-header')) {\n const getLabelKey = (v: string | null) => (v ? `header-${v}` : 'header-normal')\n\n // 更新 label 文本\n const labelValue = picker.label.getAttribute('data-value')\n picker.label.setAttribute('data-label', this.quill.getLangText(getLabelKey(labelValue)))\n\n const select = picker.select as HTMLSelectElement\n Array.from(select.options).forEach((option) => {\n const value = option.getAttribute('value')\n option.textContent = this.quill.getLangText(getLabelKey(value))\n })\n\n picker.options.remove()\n picker.buildOptions()\n }\n })\n }\n\n buildPickers(selects: NodeListOf<HTMLSelectElement>, icons: Record<string, string | Record<string, string>>) {\n this.pickers = Array.from(selects).map((select) => {\n if (select.classList.contains('ql-align')) {\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillSelect(select, ALIGNS)\n }\n return new IconPicker(select, icons.align as Record<string, string>)\n }\n\n if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n const format = select.classList.contains('ql-background') ? 'background' : 'color'\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillColorSelect(select, COLORS, format, format === 'background' ? '#ffffff' : '#000000')\n }\n return new ColorPicker(select, icons[format] as string, {\n expandIcon: '<i class=\"icon\" />',\n closeAfterChange: false,\n })\n }\n\n if (isNullOrUndefined(select.querySelector('option'))) {\n if (select.classList.contains('ql-font')) {\n fillSelect(select, FONTS)\n }\n else if (select.classList.contains('ql-header')) {\n fillSelect(select, HEADERS)\n }\n else if (select.classList.contains('ql-size')) {\n fillSelect(select, SIZES)\n }\n else if (select.classList.contains('ql-line-height')) {\n fillSelect(select, LINEHEIGHT)\n }\n }\n return new Picker(select)\n })\n\n const update = () => {\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) return\n picker.update()\n })\n }\n this.quill.on(FluentEditor.events.EDITOR_CHANGE, update)\n }\n\n extendToolbar(toolbar) {\n const icons = FluentEditor.import('ui/icons') as Record<string, any>\n toolbar.container.classList.add('ql-snow')\n this.buildButtons(toolbar.container.querySelectorAll('button'), icons)\n this.buildPickers(toolbar.container.querySelectorAll('select'), icons)\n this.tooltip = new LinkTooltip(this.quill, this.options.bounds)\n }\n}\n\nfunction fillSelect(select, values, defaultValue = false) {\n values.forEach((value) => {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n option.setAttribute('value', value)\n }\n select.appendChild(option)\n })\n}\n\nfunction fillColorSelect(\n select: HTMLSelectElement,\n values: Array<string | boolean>,\n format: string,\n defaultValue: unknown = false,\n) {\n const colorGetter = document.createElement('span') as HTMLElement\n for (const value of values) {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n if (value !== 'custom') {\n colorGetter.style[format] = String(value)\n option.setAttribute('value', colorGetter.style[format])\n }\n else {\n option.setAttribute('value', value)\n }\n }\n select.appendChild(option)\n }\n}\n\nexport default SnowTheme\n"],"names":["FluentEditor","shortKey","inputFile","fullscreenHandler","FormatPainter","Screenshot","CustomImageSpec","I18N_LOCALE_CHANGE","ColorPicker","isNullOrUndefined","Picker","LinkTooltip"],"mappings":"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"}
1
+ {"version":3,"file":"snow.cjs.js","sources":["../../../src/themes/snow.ts"],"sourcesContent":["import type { ThemeOptions } from 'quill/core/theme'\nimport type TypeToolbar from 'quill/modules/toolbar'\nimport type TypeIconPicker from 'quill/ui/icon-picker'\nimport { I18N_LOCALE_CHANGE } from 'quill-i18n'\nimport { inputFile, isNullOrUndefined, shouldInsertNbspOnSpace } from '../config'\nimport FluentEditor from '../core/fluent-editor'\nimport { CustomImageSpec } from '../modules/custom-image/specs/custom-image-spec'\nimport { LinkTooltip } from '../modules/link'\nimport { shortKey } from '../modules/shortcut-key'\nimport { ColorPicker, Picker } from '../modules/toolbar/better-picker'\nimport { FormatPainter } from '../tools/format-painter'\nimport { fullscreenHandler } from '../tools/fullscreen'\nimport { Screenshot } from '../tools/screenshot'\n\nconst OriginSnowTheme = FluentEditor.import('themes/snow') as any\nconst IconPicker = FluentEditor.import('ui/icon-picker') as typeof TypeIconPicker\n\nOriginSnowTheme.DEFAULTS = {\n modules: {\n 'i18n': true,\n 'keyboard': {\n bindings: {\n ...shortKey,\n preserveWhiteSpace: {\n key: ' ',\n collapsed: true,\n handler(this: { quill: FluentEditor }, range, context) {\n if (!shouldInsertNbspOnSpace(context.prefix)) {\n return true\n }\n this.quill.insertText(\n range.index,\n '\\u00A0',\n context.format,\n FluentEditor.sources.USER,\n )\n this.quill.setSelection(range.index + 1, FluentEditor.sources.SILENT)\n return false\n },\n },\n },\n },\n 'toolbar': {\n handlers: {\n ...(OriginSnowTheme.DEFAULTS as Record<string, any>).modules.toolbar.handlers,\n 'formula': function () {\n if (!this.quill.isEnabled()) return\n const mathlive = this.quill.getModule('mathlive')\n if (!mathlive) {\n this.quill.theme.tooltip.edit('formula')\n }\n else {\n mathlive.createDialog()\n }\n },\n 'undo': function () {\n this.quill.history.undo()\n },\n 'redo': function () {\n this.quill.history.redo()\n },\n 'file': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('file')\n inputFile.call(this, 'file', accept)\n },\n 'image': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('image')\n inputFile.call(this, 'image', accept)\n },\n 'video': function () {\n const accept = (this.quill as FluentEditor).uploader.getAccept('video')\n inputFile.call(this, 'video', accept)\n },\n 'ai': function () {},\n 'emoji': function () {},\n 'fullscreen': fullscreenHandler,\n [FormatPainter.toolName]: FormatPainter,\n [Screenshot.toolName]: Screenshot,\n 'line-height': function (value) {\n this.quill.format('line-height', value)\n },\n 'divider': function () {\n const range = this.quill.getSelection(true)\n this.quill.insertText(range.index, '\\n', FluentEditor.sources.USER)\n this.quill.insertEmbed(range.index + 1, 'divider', true, FluentEditor.sources.USER)\n this.quill.setSelection(range.index + 2, FluentEditor.sources.SILENT)\n },\n },\n },\n 'image': {\n specs: [CustomImageSpec],\n overlay: {\n style: {\n border: '1px dashed rgb(68, 68, 68)',\n },\n },\n },\n 'shortcut-key': false,\n 'file': true,\n },\n}\n\nconst ALIGNS = [false, 'center', 'right']\nconst COLORS = [\n '',\n 'rgb(255, 255, 255)',\n 'rgb(0, 0, 0)',\n 'rgb(72, 83, 104)',\n 'rgb(41, 114, 244)',\n 'rgb(0, 163, 245)',\n 'rgb(49, 155, 98)',\n 'rgb(222, 60, 54)',\n 'rgb(248, 136, 37)',\n 'rgb(245, 196, 0)',\n 'rgb(153, 56, 215)',\n\n 'rgb(242, 242, 242)',\n 'rgb(127, 127, 127)',\n 'rgb(243, 245, 247)',\n 'rgb(229, 239, 255)',\n 'rgb(229, 246, 255)',\n 'rgb(234, 250, 241)',\n 'rgb(254, 233, 232)',\n 'rgb(254, 243, 235)',\n 'rgb(254, 249, 227)',\n 'rgb(253, 235, 255)',\n\n 'rgb(216, 216, 216)',\n 'rgb(89, 89, 89)',\n 'rgb(197, 202, 211)',\n 'rgb(199, 220, 255)',\n 'rgb(199, 236, 255)',\n 'rgb(195, 234, 213)',\n 'rgb(255, 201, 199)',\n 'rgb(255, 220, 196)',\n 'rgb(255, 238, 173)',\n 'rgb(242, 199, 255)',\n\n 'rgb(191, 191, 191)',\n 'rgb(63, 63, 63)',\n 'rgb(128, 139, 158)',\n 'rgb(153, 190, 255)',\n 'rgb(153, 221, 255)',\n 'rgb(152, 215, 182)',\n 'rgb(255, 156, 153)',\n 'rgb(255, 186, 132)',\n 'rgb(255, 226, 112)',\n 'rgb(213, 142, 255)',\n\n 'rgb(165, 165, 165)',\n 'rgb(38, 38, 38)',\n 'rgb(53, 59, 69)',\n 'rgb(20, 80, 184)',\n 'rgb(18, 116, 165)',\n 'rgb(39, 124, 79)',\n 'rgb(158, 30, 26)',\n 'rgb(184, 96, 20)',\n 'rgb(163, 130, 0)',\n 'rgb(94, 34, 129)',\n\n 'rgb(147, 147, 147)',\n 'rgb(13, 13, 13)',\n 'rgb(36, 39, 46)',\n 'rgb(12, 48, 110)',\n 'rgb(10, 65, 92)',\n 'rgb(24, 78, 50)',\n 'rgb(88, 17, 14)',\n 'rgb(92, 48, 10)',\n 'rgb(102, 82, 0)',\n 'rgb(59, 21, 81)',\n\n 'custom',\n]\nconst FONTS = [false, 'serif', 'monospace']\nconst HEADERS = ['1', '2', '3', false]\nconst SIZES = ['small', false, 'large', 'huge']\nconst LINEHEIGHT = [false, '1.2', '1.5', '2']\n\nclass SnowTheme extends OriginSnowTheme {\n constructor(public quill: FluentEditor, options: ThemeOptions) {\n super(quill, options)\n\n this.quill.on(I18N_LOCALE_CHANGE, () => {\n this.i18nTextToolbar()\n })\n }\n\n i18nTextToolbar() {\n const toolbar = this.quill.getModule('toolbar') as TypeToolbar\n ColorPicker.clearText = this.quill.getLangText('clear-color')\n ColorPicker.customText = this.quill.getLangText('custom-color')\n\n if (!toolbar || !this.pickers) return\n\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) {\n // EasyColorPicker have not dts. abd origin quill ColorPicker dts not complete. use any to resovle ts type error\n const colorPicker = picker as any\n colorPicker.options.remove()\n Array.from(colorPicker.select.options).forEach((option: any) => {\n if (option.hasAttribute('custom')) {\n option.remove()\n }\n })\n colorPicker.buildOptions()\n colorPicker.createUsedColor()\n }\n\n if (picker.select && picker.select.classList.contains('ql-header')) {\n const getLabelKey = (v: string | null) => (v ? `header-${v}` : 'header-normal')\n\n // 更新 label 文本\n const labelValue = picker.label.getAttribute('data-value')\n picker.label.setAttribute('data-label', this.quill.getLangText(getLabelKey(labelValue)))\n\n const select = picker.select as HTMLSelectElement\n Array.from(select.options).forEach((option) => {\n const value = option.getAttribute('value')\n option.textContent = this.quill.getLangText(getLabelKey(value))\n })\n\n picker.options.remove()\n picker.buildOptions()\n }\n })\n }\n\n buildPickers(selects: NodeListOf<HTMLSelectElement>, icons: Record<string, string | Record<string, string>>) {\n this.pickers = Array.from(selects).map((select) => {\n if (select.classList.contains('ql-align')) {\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillSelect(select, ALIGNS)\n }\n return new IconPicker(select, icons.align as Record<string, string>)\n }\n\n if (select.classList.contains('ql-background') || select.classList.contains('ql-color')) {\n const format = select.classList.contains('ql-background') ? 'background' : 'color'\n if (isNullOrUndefined(select.querySelector('option'))) {\n fillColorSelect(select, COLORS, format, format === 'background' ? '#ffffff' : '#000000')\n }\n return new ColorPicker(select, icons[format] as string, {\n expandIcon: '<i class=\"icon\" />',\n closeAfterChange: false,\n })\n }\n\n if (isNullOrUndefined(select.querySelector('option'))) {\n if (select.classList.contains('ql-font')) {\n fillSelect(select, FONTS)\n }\n else if (select.classList.contains('ql-header')) {\n fillSelect(select, HEADERS)\n }\n else if (select.classList.contains('ql-size')) {\n fillSelect(select, SIZES)\n }\n else if (select.classList.contains('ql-line-height')) {\n fillSelect(select, LINEHEIGHT)\n }\n }\n return new Picker(select)\n })\n\n const update = () => {\n this.pickers.forEach((picker) => {\n if (picker instanceof ColorPicker) return\n picker.update()\n })\n }\n this.quill.on(FluentEditor.events.EDITOR_CHANGE, update)\n }\n\n extendToolbar(toolbar) {\n const icons = FluentEditor.import('ui/icons') as Record<string, any>\n toolbar.container.classList.add('ql-snow')\n this.buildButtons(toolbar.container.querySelectorAll('button'), icons)\n this.buildPickers(toolbar.container.querySelectorAll('select'), icons)\n this.tooltip = new LinkTooltip(this.quill, this.options.bounds)\n }\n}\n\nfunction fillSelect(select, values, defaultValue = false) {\n values.forEach((value) => {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n option.setAttribute('value', value)\n }\n select.appendChild(option)\n })\n}\n\nfunction fillColorSelect(\n select: HTMLSelectElement,\n values: Array<string | boolean>,\n format: string,\n defaultValue: unknown = false,\n) {\n const colorGetter = document.createElement('span') as HTMLElement\n for (const value of values) {\n const option = document.createElement('option')\n if (value === defaultValue) {\n option.setAttribute('selected', 'selected')\n }\n else {\n if (value !== 'custom') {\n colorGetter.style[format] = String(value)\n option.setAttribute('value', colorGetter.style[format])\n }\n else {\n option.setAttribute('value', value)\n }\n }\n select.appendChild(option)\n }\n}\n\nexport default SnowTheme\n"],"names":["FluentEditor","shortKey","shouldInsertNbspOnSpace","inputFile","fullscreenHandler","FormatPainter","Screenshot","CustomImageSpec","I18N_LOCALE_CHANGE","ColorPicker","isNullOrUndefined","Picker","LinkTooltip"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@opentiny/fluent-editor",
3
3
  "type": "module",
4
- "version": "4.1.1",
4
+ "version": "4.1.2",
5
5
  "description": "A rich text editor based on Quill 2.0, which extends rich modules and formats on the basis of Quill. It's powerful and out-of-the-box.",
6
6
  "author": "OpenTiny Team",
7
7
  "license": "MIT",
@@ -30,7 +30,8 @@
30
30
  "quill": "^2.0.0",
31
31
  "quill-easy-color": "^0.0.10",
32
32
  "quill-i18n": "^0.2.0",
33
- "quill-shortcut-key": "^0.2.1"
33
+ "quill-shortcut-key": "^0.2.1",
34
+ "quill-table-up": "^3.5.0"
34
35
  },
35
36
  "exports": {
36
37
  ".": {
package/style.css CHANGED
@@ -125,6 +125,9 @@ html.dark .toolbar-tip__tooltip {
125
125
  .ql-editor li.ordered.ql-indent-1::before {
126
126
  content: counter(list-1, lower-alpha) ". ";
127
127
  }
128
+ .ql-editor li.ql-indent-1 .ql-ui:before {
129
+ content: counter(list-1, decimal) ". " !important;
130
+ }
128
131
  .ql-editor li.ordered.ql-indent-1 {
129
132
  counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
130
133
  }
@@ -134,6 +137,9 @@ html.dark .toolbar-tip__tooltip {
134
137
  .ql-editor li.ordered.ql-indent-2::before {
135
138
  content: counter(list-2, lower-roman) ". ";
136
139
  }
140
+ .ql-editor li.ql-indent-2 .ql-ui:before {
141
+ content: counter(list-2, decimal) ". " !important;
142
+ }
137
143
  .ql-editor li.ordered.ql-indent-2 {
138
144
  counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
139
145
  }
@@ -143,6 +149,9 @@ html.dark .toolbar-tip__tooltip {
143
149
  .ql-editor li.ordered.ql-indent-3::before {
144
150
  content: counter(list-3, decimal) ". ";
145
151
  }
152
+ .ql-editor li.ql-indent-3 .ql-ui:before {
153
+ content: counter(list-3, decimal) ". " !important;
154
+ }
146
155
  .ql-editor li.ordered.ql-indent-3 {
147
156
  counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
148
157
  }
@@ -152,6 +161,9 @@ html.dark .toolbar-tip__tooltip {
152
161
  .ql-editor li.ordered.ql-indent-4::before {
153
162
  content: counter(list-4, lower-alpha) ". ";
154
163
  }
164
+ .ql-editor li.ql-indent-4 .ql-ui:before {
165
+ content: counter(list-4, decimal) ". " !important;
166
+ }
155
167
  .ql-editor li.ordered.ql-indent-4 {
156
168
  counter-reset: list-5 list-6 list-7 list-8 list-9;
157
169
  }
@@ -161,6 +173,9 @@ html.dark .toolbar-tip__tooltip {
161
173
  .ql-editor li.ordered.ql-indent-5::before {
162
174
  content: counter(list-5, lower-roman) ". ";
163
175
  }
176
+ .ql-editor li.ql-indent-5 .ql-ui:before {
177
+ content: counter(list-5, decimal) ". " !important;
178
+ }
164
179
  .ql-editor li.ordered.ql-indent-5 {
165
180
  counter-reset: list-6 list-7 list-8 list-9;
166
181
  }
@@ -170,6 +185,9 @@ html.dark .toolbar-tip__tooltip {
170
185
  .ql-editor li.ordered.ql-indent-6::before {
171
186
  content: counter(list-6, decimal) ". ";
172
187
  }
188
+ .ql-editor li.ql-indent-6 .ql-ui:before {
189
+ content: counter(list-6, decimal) ". " !important;
190
+ }
173
191
  .ql-editor li.ordered.ql-indent-6 {
174
192
  counter-reset: list-7 list-8 list-9;
175
193
  }
@@ -179,6 +197,9 @@ html.dark .toolbar-tip__tooltip {
179
197
  .ql-editor li.ordered.ql-indent-7::before {
180
198
  content: counter(list-7, lower-alpha) ". ";
181
199
  }
200
+ .ql-editor li.ql-indent-7 .ql-ui:before {
201
+ content: counter(list-7, decimal) ". " !important;
202
+ }
182
203
  .ql-editor li.ordered.ql-indent-7 {
183
204
  counter-reset: list-8 list-9;
184
205
  }
@@ -188,6 +209,9 @@ html.dark .toolbar-tip__tooltip {
188
209
  .ql-editor li.ordered.ql-indent-8::before {
189
210
  content: counter(list-8, lower-roman) ". ";
190
211
  }
212
+ .ql-editor li.ql-indent-8 .ql-ui:before {
213
+ content: counter(list-8, decimal) ". " !important;
214
+ }
191
215
  .ql-editor li.ordered.ql-indent-8 {
192
216
  counter-reset: list-9;
193
217
  }
@@ -197,6 +221,9 @@ html.dark .toolbar-tip__tooltip {
197
221
  .ql-editor li.ordered.ql-indent-9::before {
198
222
  content: counter(list-9, decimal) ". ";
199
223
  }
224
+ .ql-editor li.ql-indent-9 .ql-ui:before {
225
+ content: counter(list-9, decimal) ". " !important;
226
+ }
200
227
  .ql-editor li.ql-direction-rtl::before, .ql-editor li.ql-direction-rtl.checked > .ql-ui, .ql-editor li.ql-direction-rtl.unchecked > .ql-ui {
201
228
  margin-left: 0.3em;
202
229
  margin-right: -1.5em;
@@ -25,6 +25,14 @@ export declare function omit(obj: any, uselessKeys: any): {};
25
25
  * @return 替换之后的delta
26
26
  */
27
27
  export declare function replaceDeltaImage(delta: any, imageUrls: any, imagePlaceholder: any): any;
28
+ /**
29
+ * 将连续空格转为不间断空格,以便 innerHTML 输出 &nbsp; 并在预览中保留间距。
30
+ * - 行首空白:全部转为 \u00A0
31
+ * - 正文内连续空白:保留第一个普通空格,其余转为 \u00A0
32
+ */
33
+ export declare function replaceStrWhiteSpace(str: string): string;
34
+ /** 空格键是否应插入不间断空格(与 replaceStrWhiteSpace 规则一致) */
35
+ export declare function shouldInsertNbspOnSpace(prefix: string): boolean;
28
36
  export declare function splitWithBreak(insertContent: string): any[];
29
37
  /**
30
38
  * getEventComposedPath