@pixui-dev/pixui-richtext-helper 0.2.5 → 0.2.6

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.
@@ -27,6 +27,7 @@ export declare class RichTextCore {
27
27
  */
28
28
  static convertRichTextToPixuiStyle(str: string, config?: {
29
29
  lineHeightScale?: number;
30
+ convertPxToEm?: boolean;
30
31
  }): string;
31
32
  /**
32
33
  * 绑定链接点击事件
@@ -44,6 +45,12 @@ export declare class RichTextCore {
44
45
  * 处理单个段落片段,返回转换后的片段 HTML
45
46
  */
46
47
  private static processSegment;
48
+ /**
49
+ * 统一转换HTML中的所有px单位为em单位
50
+ * @param html 原始HTML字符串
51
+ * @returns 转换后的HTML字符串
52
+ */
53
+ private static convertAllPxToEm;
47
54
  private static preprocess;
48
55
  private static fixImgSizeUnit;
49
56
  /** 列表处理(将 ol/ul/li 替换为 div 结构) */
@@ -56,6 +56,33 @@ var LinkNodeType;
56
56
  /*****************************************************
57
57
  * 工具函数
58
58
  *****************************************************/
59
+ /** 基准字体大小,用于px到em转换 */
60
+ var BASE_FONT_SIZE = 16;
61
+ /**
62
+ * 将px单位转换为em单位
63
+ * @param pxValue px数值
64
+ * @returns em值字符串
65
+ */
66
+ var convertPxToEm = function (pxValue) {
67
+ var emValue = pxValue / BASE_FONT_SIZE;
68
+ return "".concat(emValue.toFixed(3), "em");
69
+ };
70
+ /**
71
+ * 转换样式字符串中的px单位为em单位
72
+ * @param styleStr 原始样式字符串
73
+ * @returns 转换后的样式字符串
74
+ */
75
+ var convertStylePxToEm = function (styleStr) {
76
+ if (!styleStr || !styleStr.trim())
77
+ return styleStr;
78
+ return styleStr.replace(/(\d+(?:\.\d+)?)px/g, function (match, pxValue) {
79
+ var px = parseFloat(pxValue);
80
+ if (!isNaN(px)) {
81
+ return convertPxToEm(px);
82
+ }
83
+ return match;
84
+ });
85
+ };
59
86
  /**
60
87
  * 解析 style 字符串为对象
61
88
  */
@@ -121,7 +148,10 @@ var RichTextCore = /** @class */ (function () {
121
148
  // 重置全局状态
122
149
  RichTextCore.linkNodes = [];
123
150
  RichTextCore.hrefIdCnt = 0;
124
- var $1 = cheerio.load(str, null, false);
151
+ var preEncodedStr = str.replace(/href="([^"]*?)"/g, function (_match, hrefVal) {
152
+ return "href=\"".concat(encodeURIComponent(hrefVal), "\"");
153
+ });
154
+ var $1 = cheerio.load(preEncodedStr, null, false);
125
155
  //检查html,如果有text节点,说明是转换过的,直接返回
126
156
  if ($1("text").length > 0) {
127
157
  return str;
@@ -144,9 +174,38 @@ var RichTextCore = /** @class */ (function () {
144
174
  // 直接查询统一的 class
145
175
  setTimeout(function () {
146
176
  var elements = Array.from(document.querySelectorAll(".".concat(RichTextCore.LINK_NODE_CLASS)));
177
+ // HTML 实体解码工具函数(纯算法实现,避免依赖 textarea 节点)
178
+ var decodeHtmlEntities = function (str) {
179
+ if (!str)
180
+ return str;
181
+ var named = {
182
+ quot: '"',
183
+ amp: "&",
184
+ lt: "<",
185
+ gt: ">",
186
+ apos: "'",
187
+ };
188
+ return str.replace(/&(#(?:x[0-9a-fA-F]+|\d+)|[a-zA-Z]+);/g, function (_, ent) {
189
+ if (ent[0] === "#") {
190
+ // 数字实体
191
+ var isHex = ent[1].toLowerCase() === "x";
192
+ var numStr = isHex ? ent.slice(2) : ent.slice(1);
193
+ var codePoint = parseInt(numStr, isHex ? 16 : 10);
194
+ if (!isNaN(codePoint)) {
195
+ return String.fromCodePoint(codePoint);
196
+ }
197
+ return _; // 解析失败,原样返回
198
+ }
199
+ // 命名实体
200
+ if (named.hasOwnProperty(ent))
201
+ return named[ent];
202
+ return _; // 未知实体,原样返回
203
+ });
204
+ };
147
205
  elements.forEach(function (el) {
148
206
  var hrefEncoded = el.getAttribute("data-link-href") || "";
149
- var href = decodeURIComponent(hrefEncoded);
207
+ // URI 解码,再做 HTML 实体解码
208
+ var href = decodeHtmlEntities(decodeURIComponent(hrefEncoded));
150
209
  var typeStr = el.getAttribute("data-link-type");
151
210
  var type = typeStr === LinkNodeType.IMG ? LinkNodeType.IMG : LinkNodeType.DIV;
152
211
  // 避免重复绑定
@@ -230,8 +289,12 @@ var RichTextCore = /** @class */ (function () {
230
289
  if (tagName === "div" || tagName === "text") {
231
290
  return []; // 丢弃空 div / text
232
291
  }
292
+ else if (tagName === "img") {
293
+ // img标签直接返回,不包装在div中
294
+ return [$.html(node)];
295
+ }
233
296
  else {
234
- // 其它空标签保留(img...)
297
+ // 其它空标签保留
235
298
  return [wrapNode($elem, "")];
236
299
  }
237
300
  }
@@ -281,8 +344,25 @@ var RichTextCore = /** @class */ (function () {
281
344
  .replaceAll(/&amp;/g, "&")
282
345
  .replaceAll(/&quot;/g, '"')
283
346
  .replaceAll("\"", "\"");
347
+ // 统一进行px到em转换
348
+ if (config === null || config === void 0 ? void 0 : config.convertPxToEm) {
349
+ res = this.convertAllPxToEm(res);
350
+ }
284
351
  return res;
285
352
  };
353
+ /**
354
+ * 统一转换HTML中的所有px单位为em单位
355
+ * @param html 原始HTML字符串
356
+ * @returns 转换后的HTML字符串
357
+ */
358
+ RichTextCore.convertAllPxToEm = function (html) {
359
+ // 转换style属性中的px
360
+ html = html.replace(/style="([^"]*?)"/g, function (match, styleContent) {
361
+ var convertedStyle = convertStylePxToEm(styleContent);
362
+ return "style=\"".concat(convertedStyle, "\"");
363
+ });
364
+ return html;
365
+ };
286
366
  // ------------------------------
287
367
  // 各独立处理步骤实现
288
368
  // ------------------------------
@@ -479,7 +559,7 @@ var RichTextCore = /** @class */ (function () {
479
559
  mergedStyles.push(innerStyle);
480
560
  });
481
561
  var finalStyle = mergeStyles.apply(void 0, __spreadArray([originalStyle], mergedStyles, false));
482
- var encodedHref = href ? encodeURIComponent(href) : "";
562
+ var encodedHref = href || ""; // 已在预处理阶段编码,避免二次编码
483
563
  if (href) {
484
564
  self.linkNodes.push({ type: LinkNodeType.DIV, href: href, id: id });
485
565
  }
@@ -566,7 +646,7 @@ var RichTextCore = /** @class */ (function () {
566
646
  self.linkNodes.push({ type: LinkNodeType.IMG, href: href, id: id });
567
647
  }
568
648
  // 将链接信息以转义形式保存在 data- 属性中,避免默认跳转
569
- var encodedHref = href ? encodeURIComponent(href) : "";
649
+ var encodedHref = href || ""; // 已在预处理阶段编码,避免二次编码
570
650
  $(this).attr("data-link-type", "img");
571
651
  $(this).attr("data-link-href", encodedHref);
572
652
  $(this).attr("href", "");
@@ -654,7 +734,8 @@ var RichTextCore = /** @class */ (function () {
654
734
  var attribs = ($(node).get(0) || {}).attribs || {};
655
735
  for (var _i = 0, _a = Object.entries(attribs); _i < _a.length; _i++) {
656
736
  var _b = _a[_i], k = _b[0], v = _b[1];
657
- if (k !== "style" && k !== "id") {
737
+ // 跳过styleid和href属性,避免重复处理或错误处理
738
+ if (k !== "style" && k !== "id" && k !== "href") {
658
739
  customAttrs += " ".concat(k, "=\"").concat(v, "\"");
659
740
  }
660
741
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixui-dev/pixui-richtext-helper",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "pixui richtext helper",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -92,4 +92,7 @@ componentDidMount() { // 在节点渲染后绑定点击事件
92
92
 
93
93
  0.2.4
94
94
  1. 拆分margin中的margin-left转换为padding-left
95
- 2. 修复在嵌套节点中加br不会被拆成不同段落的问题
95
+ 2. 修复在嵌套节点中加br不会被拆成不同段落的问题
96
+
97
+ 0.2.5
98
+ 1. 修复img节点丢失的问题