@pixui-dev/pixui-richtext-helper 0.2.11 → 0.2.12-beta.1

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.
@@ -34,6 +34,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
35
  exports.RichTextCore = exports.LinkNodeType = void 0;
36
36
  var cheerio = __importStar(require("cheerio"));
37
+ var version_1 = require("./version");
37
38
  /*****************************************************
38
39
  * 类型、枚举定义
39
40
  *****************************************************/
@@ -129,15 +130,66 @@ var mergeStyles = function () {
129
130
  return serializeStyleObject(mergedStyleObj);
130
131
  };
131
132
  var CLASS_STYLE_MAP = {
132
- "ql-direction-rtl": function (old) { return mergeStyles("direction: rtl;", old); },
133
- "ql-align-right": function (old) { return mergeStyles("text-align: right;", old); },
133
+ "ql-direction-rtl": function (old) { return mergeStyles(old, "direction: rtl;"); },
134
+ "ql-align-right": function (old) { return mergeStyles(old, "text-align: right;"); },
135
+ };
136
+ // data-list value → 需追加的 class 名
137
+ var DATA_LIST_CLASS_MAP = {
138
+ bullet: "ql-indent-0",
134
139
  };
135
140
  var _loop_1 = function (i) {
136
141
  var cls = "ql-indent-".concat(i);
137
- CLASS_STYLE_MAP[cls] = function (old) { return mergeStyles("padding-left: ".concat(i * 2, "em"), old); };
142
+ //如果之前加了ql-indent-x,取max
143
+ // 注意:此 mapper 可能在同一节点被多次调用(当存在多个 ql-indent-* 类时)
144
+ // 只有当当前 cls 对应的缩进值是该节点所有 ql-indent-* 中的最大值时才真正处理样式。
145
+ CLASS_STYLE_MAP[cls] = function (old, $elem) {
146
+ // 1) 计算该元素所有 ql-indent-* 中的最大值
147
+ var clsArr = ($elem.attr("class") || "").split(/\s+/).filter(Boolean);
148
+ var isRtl = clsArr.includes("ql-direction-rtl");
149
+ var maxIndentNum = 0;
150
+ clsArr.forEach(function (c) {
151
+ var m = c.match(/^ql-indent-(\d+)$/);
152
+ if (m) {
153
+ var n = parseInt(m[1]);
154
+ if (!isNaN(n) && n > maxIndentNum)
155
+ maxIndentNum = n;
156
+ }
157
+ });
158
+ // 当前 cls 对应的缩进级别
159
+ var currentIndentMatch = cls.match(/^ql-indent-(\d+)$/);
160
+ var currentIndentNum = currentIndentMatch ? parseInt(currentIndentMatch[1]) : 0;
161
+ // 当前处理的不是最大缩进级别
162
+ if (currentIndentNum !== maxIndentNum) {
163
+ return old;
164
+ }
165
+ // H5 基准:0级=1.5em,之后每级递增3em → padding = 1.5em + 3em * level
166
+ var desiredPadEm = 1.5 + (maxIndentNum + 1) * 3;
167
+ var styleObj = parseStyleString(old);
168
+ // 依据方向选择缩进属性键
169
+ var paddingKey = isRtl ? "padding-right" : "padding-left";
170
+ // 已有 padding-left/right,需比较取更大者
171
+ var existPadEm = -1;
172
+ var existPad = styleObj[paddingKey];
173
+ if (existPad) {
174
+ var m = existPad.match(/^([0-9.]+)(px|em)?$/i);
175
+ if (m) {
176
+ var num = parseFloat(m[1]);
177
+ var unit = (m[2] || "px").toLowerCase();
178
+ if (!isNaN(num)) {
179
+ existPadEm = unit === "em" ? num : num / 16;
180
+ }
181
+ }
182
+ }
183
+ var finalPadEm = existPadEm > desiredPadEm ? existPadEm : desiredPadEm;
184
+ styleObj[paddingKey] = "".concat(finalPadEm, "em");
185
+ // 设置 list-style-type
186
+ var markersList = ["disc", "circle", "square", "empty square"];
187
+ styleObj["list-style-type"] = markersList[maxIndentNum % markersList.length];
188
+ return mergeStyles(old, serializeStyleObject(styleObj));
189
+ };
138
190
  };
139
191
  // 动态生成段落缩进 ql-indent-1 ~ ql-indent-10
140
- for (var i = 1; i <= 10; i++) {
192
+ for (var i = 0; i <= 10; i++) {
141
193
  _loop_1(i);
142
194
  }
143
195
  /*****************************************************
@@ -167,6 +219,8 @@ var RichTextCore = /** @class */ (function () {
167
219
  if ($1("text").length > 0) {
168
220
  return str;
169
221
  }
222
+ var oriNodeCount = $1("*").length;
223
+ console.log("pixui-richHelper转换---- 版本:" + " " + version_1.LIB_VERSION + " " + " 原始节点数:" + oriNodeCount);
170
224
  // ---------- 第 1 步:段落分割(基础实现:根节点切分) ----------
171
225
  var segments = RichTextCore.splitIntoSegments($1);
172
226
  // ---------- 第 2/3 步:遍历 + 展平 ----------
@@ -175,7 +229,7 @@ var RichTextCore = /** @class */ (function () {
175
229
  var processedSegments = segments.map(function (seg) { return RichTextCore.processSegment(seg, config); });
176
230
  // ---------- 第 4 步:拼装 ----------
177
231
  var Result = processedSegments.join("");
178
- // console.log(Result);
232
+ console.log(Result);
179
233
  return Result;
180
234
  };
181
235
  /**
@@ -383,24 +437,39 @@ var RichTextCore = /** @class */ (function () {
383
437
  // 根据映射表统一处理 class → style
384
438
  $("*").each(function () {
385
439
  var _this = this;
386
- var classStr = $(this).attr("class") || "";
387
- if (!classStr)
440
+ // 1) 先处理 data-list,可能追加 class
441
+ var dataList = $(this).attr("data-list");
442
+ if (dataList) {
443
+ var clsToAdd = DATA_LIST_CLASS_MAP[dataList];
444
+ if (clsToAdd) {
445
+ $(this).addClass(clsToAdd);
446
+ $(this).removeAttr("data-list");
447
+ }
448
+ }
449
+ // 2) 再处理 class → style 映射(包含刚刚追加的 class)
450
+ var classStrNow = $(this).attr("class") || "";
451
+ if (!classStrNow)
388
452
  return;
389
- var classArr = classStr.split(/\s+/).filter(Boolean);
453
+ var classArr = classStrNow.split(/\s+/).filter(Boolean);
390
454
  if (classArr.length === 0)
391
455
  return;
392
456
  var styleStr = $(this).attr("style") || "";
393
457
  var changed = false;
458
+ // 先累计样式,再统一移除命中的类,避免在 mapper 内部读取 class 时出现中途被修改导致的误判
459
+ var removedClasses = [];
394
460
  classArr.forEach(function (cls) {
395
461
  var mapper = CLASS_STYLE_MAP[cls];
396
462
  if (mapper) {
397
463
  styleStr = mapper(styleStr, $(_this));
398
- $(_this).removeClass(cls);
464
+ removedClasses.push(cls);
399
465
  changed = true;
400
466
  }
401
467
  });
402
468
  if (changed) {
403
469
  $(this).attr("style", styleStr);
470
+ if (removedClasses.length > 0) {
471
+ $(this).removeClass(removedClasses.join(" "));
472
+ }
404
473
  }
405
474
  });
406
475
  // 在所有标题标签后插入换行 <br>
@@ -411,7 +480,7 @@ var RichTextCore = /** @class */ (function () {
411
480
  $(this).after("<br>");
412
481
  }
413
482
  });
414
- // 将所有的margin-left转换为padding-left
483
+ // 将所有的margin-left,margin-right转换为padding-left,padding-right
415
484
  $("*").each(function () {
416
485
  var oriStyle = $(this).attr("style") || "";
417
486
  if (!oriStyle)
@@ -419,9 +488,12 @@ var RichTextCore = /** @class */ (function () {
419
488
  var styleObj = parseStyleString(oriStyle);
420
489
  // 1) 处理 margin-left 单独声明
421
490
  var marginLeftVal = styleObj["margin-left"];
422
- if (marginLeftVal !== undefined) {
491
+ var marginRightVal = styleObj["margin-right"];
492
+ if (marginLeftVal !== undefined || marginRightVal !== undefined) {
423
493
  delete styleObj["margin-left"];
494
+ delete styleObj["margin-right"];
424
495
  styleObj["padding-left"] = styleObj["padding-left"] || marginLeftVal;
496
+ styleObj["padding-right"] = styleObj["padding-right"] || marginRightVal;
425
497
  }
426
498
  // 2) 处理 margin shorthand,提取 left
427
499
  if (styleObj["margin"]) {
@@ -446,10 +518,9 @@ var RichTextCore = /** @class */ (function () {
446
518
  bottom = parts[2];
447
519
  left = parts[3];
448
520
  }
449
- // 将 left 移到 padding-left
450
- if (left) {
451
- styleObj["padding-left"] = styleObj["padding-left"] || left;
452
- }
521
+ // 将 left,right 移到 padding-left,padding-right
522
+ left && (styleObj["padding-left"] = styleObj["padding-left"] || left);
523
+ right && (styleObj["padding-right"] = styleObj["padding-right"] || right);
453
524
  // 重构 margin,去掉 left(用 0 占位保持布局)
454
525
  var newMargin = "".concat(top_1, " ").concat(right, " ").concat(bottom, " 0");
455
526
  styleObj["margin"] = newMargin.trim();
@@ -568,17 +639,40 @@ var RichTextCore = /** @class */ (function () {
568
639
  if (isOrdered) {
569
640
  orderIndex = $child.prevAll("li").length + 1;
570
641
  }
571
- var marker = isOrdered ? "".concat(orderIndex, ".") : "•";
572
642
  var paddingLeft = currentLevel * 2; // 每层缩进 2em
573
643
  var $liClone = $child.clone();
574
644
  $liClone.find("ol, ul").remove();
575
- var textContent = $liClone.text().trim() || "";
645
+ var textContent = $liClone.html() || "";
576
646
  var style = $child.attr("style") || "";
647
+ // 解析 style 判断 list-style-type
648
+ var styleObj = parseStyleString(style);
649
+ var listStyle = (styleObj["list-style-type"] || "").trim().toLowerCase();
650
+ // 查找有没有继承下来的属性
651
+ if (!listStyle) {
652
+ var parentStyleObj = parseStyleString($container.attr("style") || "");
653
+ listStyle = (parentStyleObj["list-style-type"] || "").trim().toLowerCase();
654
+ }
655
+ // 根据 list-style-type 与列表类型决定 marker
656
+ var BULLET_MARKER_MAP = {
657
+ disc: "●",
658
+ circle: "○",
659
+ square: "■",
660
+ "empty square": "□",
661
+ none: "",
662
+ };
663
+ var finalMarker = "";
664
+ if (listStyle && Object.prototype.hasOwnProperty.call(BULLET_MARKER_MAP, listStyle)) {
665
+ // 即使映射值为空字符串(none)也应使用
666
+ finalMarker = BULLET_MARKER_MAP[listStyle];
667
+ }
668
+ else {
669
+ finalMarker = isOrdered ? "".concat(orderIndex, ".") : "•";
670
+ }
577
671
  items.push({
578
672
  level: currentLevel,
579
673
  isOrdered: isOrdered,
580
674
  orderIndex: orderIndex,
581
- marker: marker,
675
+ marker: finalMarker,
582
676
  paddingLeft: paddingLeft,
583
677
  textContent: textContent,
584
678
  style: style,
@@ -620,14 +714,38 @@ var RichTextCore = /** @class */ (function () {
620
714
  var marginTopVal = idx === 0 ? 1 * lineHeightScale : 0.5 * lineHeightScale;
621
715
  var marginBottomVal = idx === items.length - 1 ? 1 * lineHeightScale : 0.5 * lineHeightScale;
622
716
  var marginAdjust = ["margin-top: ".concat(marginTopVal, "em"), "margin-bottom: ".concat(marginBottomVal, "em")];
623
- return ("<div style=\"".concat(mergeStyles.apply(void 0, __spreadArray(__spreadArray([it.style], marginAdjust, false), ["padding-left: ".concat(it.paddingLeft, "em"), "flex-shrink: 0",
624
- "width: 100%",
625
- "flex-direction: row",
626
- "display: flex"], false)), "\" data-list-item=\"true\">") +
627
- "<text style=\"".concat(mergeStyles("flex-shrink: 0", "width: 100%", "display: flex", "flex-direction: row"), "\">") +
628
- "<key style=\"".concat(mergeStyles("flex-shrink: 0", "padding-right: 0.5em"), "\">").concat(it.marker, "</key>") +
629
- "<key style=\"".concat(mergeStyles("flex-shrink: 0", "flex: 1"), "\">").concat(it.textContent, "</key>") +
630
- "</text></div>");
717
+ // 解析 direction 与缩进,按 H5 悬挂缩进思路改为三列布局
718
+ var styleObj = parseStyleString(it.style || "");
719
+ var isRtl = (styleObj["direction"] || "").trim().toLowerCase() === "rtl";
720
+ var paddingKey = isRtl ? "padding-right" : "padding-left";
721
+ var padVal = styleObj[paddingKey];
722
+ var markerEm = 1.2;
723
+ var markerPx = markerEm * BASE_FONT_SIZE; // 约 19.2px
724
+ var indentPx = it.level * 40; // 回退:嵌套层级
725
+ if (padVal) {
726
+ var m = padVal.match(/^([0-9.]+)(px|em)?$/i);
727
+ if (m) {
728
+ var num = parseFloat(m[1]);
729
+ var unit = (m[2] || "px").toLowerCase();
730
+ if (!isNaN(num)) {
731
+ var padPx = unit === "em" ? num * BASE_FONT_SIZE : num;
732
+ indentPx = Math.max(padPx - markerPx, 0);
733
+ }
734
+ }
735
+ }
736
+ // 容器样式:移除 padding 与 list-style-type,设置 flex 布局并按方向翻转
737
+ delete styleObj["padding-left"];
738
+ delete styleObj["padding-right"];
739
+ delete styleObj["list-style-type"]; // 由三列布局承担
740
+ var rowDir = isRtl ? "row-reverse" : "row";
741
+ var outerStyle = mergeStyles.apply(void 0, __spreadArray(__spreadArray([serializeStyleObject(styleObj)], marginAdjust, false), ["flex-shrink: 0", "width: 100%", "display: flex", "flex-direction: ".concat(rowDir)], false));
742
+ // 三列:缩进列 + 标号列 + 文本列
743
+ var indentCol = "<key style=\"width:".concat(indentPx, "px;flex-shrink:0\"></key>");
744
+ var markerCol = it.marker
745
+ ? "<key style=\"".concat(mergeStyles("flex-shrink: 0", "width: ".concat(markerEm, "em"), isRtl ? "padding-left: 0.3em" : "padding-right: 0.3em"), "\">").concat(it.marker, "</key>")
746
+ : "";
747
+ var textCol = "<text style=\"".concat(mergeStyles("flex: 1", "min-width: 0", "word-break: break-word"), "\">").concat(it.textContent, "</text>");
748
+ return "<div style=\"".concat(outerStyle, "\" data-list-item=\"true\">").concat(indentCol).concat(markerCol).concat(textCol, "</div>");
631
749
  })
632
750
  .join("");
633
751
  placeholder.after(html);
@@ -698,7 +816,7 @@ var RichTextCore = /** @class */ (function () {
698
816
  if (this.type === "text") {
699
817
  var text = this.data.trim();
700
818
  if (text.length > 0) {
701
- $(this).replaceWith("<text style=\"".concat(mergeStyles("flex-shrink: 0", "flex-direction: row"), "\">").concat(text, "</text>"));
819
+ $(this).replaceWith("<text style=\"".concat(mergeStyles("flex-shrink: 0", "flex-direction: row", "word-break: break-word"), "\">").concat(text, "</text>"));
702
820
  }
703
821
  }
704
822
  });
@@ -721,7 +839,13 @@ var RichTextCore = /** @class */ (function () {
721
839
  $(tag).each(function () {
722
840
  var html = $(this).html();
723
841
  var oriStyle = $(this).attr("style") || "";
724
- $(this).replaceWith("<text style=\"".concat(mergeStyles(oriStyle, style, "flex-shrink: 0"), "\">").concat(html, "</text>"));
842
+ var isInListTextColumn = $(this).closest("[data-list-item='true']").length > 0 && $(this).closest("text").length > 0;
843
+ if (isInListTextColumn) {
844
+ $(this).replaceWith("<key style=\"".concat(mergeStyles(oriStyle, style, "flex-shrink: 0", "word-break: break-word"), "\">").concat(html, "</key>"));
845
+ }
846
+ else {
847
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(oriStyle, style, "flex-shrink: 0", "word-break: break-word"), "\">").concat(html, "</text>"));
848
+ }
725
849
  hasLeftover = true;
726
850
  });
727
851
  });
@@ -729,7 +853,13 @@ var RichTextCore = /** @class */ (function () {
729
853
  $("span").each(function () {
730
854
  var html = $(this).html();
731
855
  var oriStyle = $(this).attr("style") || "";
732
- $(this).replaceWith("<text style=\"".concat(mergeStyles(oriStyle, "flex-shrink: 0"), "\">").concat(html, "</text>"));
856
+ var isInListTextColumn = $(this).closest("[data-list-item='true']").length > 0 && $(this).closest("text").length > 0;
857
+ if (isInListTextColumn) {
858
+ $(this).replaceWith("<key style=\"".concat(mergeStyles(oriStyle, "flex-shrink: 0", "word-break: break-word"), "\">").concat(html, "</key>"));
859
+ }
860
+ else {
861
+ $(this).replaceWith("<text style=\"".concat(mergeStyles(oriStyle, "flex-shrink: 0", "word-break: break-word"), "\">").concat(html, "</text>"));
862
+ }
733
863
  hasLeftover = true;
734
864
  });
735
865
  }
@@ -857,7 +987,7 @@ var RichTextCore = /** @class */ (function () {
857
987
  }
858
988
  }
859
989
  // 保证外层tag的style不合并子div的style
860
- $(node).replaceWith("<".concat(tag, " style=\"").concat(mergeStyles(tagStyle, "flex-direction: row"), "\"").concat(idAttr).concat(customAttrs, "><text style=\"").concat(mergeStyles("flex-shrink: 0", "width: 100%"), "\">").concat(tagHtml, "</text></").concat(tag, ">"));
990
+ $(node).replaceWith("<".concat(tag, " style=\"").concat(mergeStyles(tagStyle, "flex-direction: row"), "\"").concat(idAttr).concat(customAttrs, "><text style=\"").concat(mergeStyles("flex-shrink: 0", "width: 100%", "word-break: break-word"), "\">").concat(tagHtml, "</text></").concat(tag, ">"));
861
991
  }
862
992
  });
863
993
  };
@@ -0,0 +1 @@
1
+ export declare const LIB_VERSION = "0.2.11";
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LIB_VERSION = void 0;
4
+ /* Auto-generated by scripts/generate-version.js */
5
+ exports.LIB_VERSION = "0.2.11";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixui-dev/pixui-richtext-helper",
3
- "version": "0.2.11",
3
+ "version": "0.2.12-beta.1",
4
4
  "description": "pixui richtext helper",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -31,6 +31,8 @@ componentDidMount() { // 在节点渲染后绑定点击事件
31
31
 
32
32
  组件在转换长富文本(节点数量较多,参考节点数量大于100)的时候可能会比较慢阻塞活动显示,如果需要转换很长的富文本,建议询问管理端是否可以提供预转换服务,或者活动在前置虚拟机中预先转换以后保存到本地,活动中直接使用转换以后的结果。
33
33
 
34
+ 组件当前只适配并测试过由floriazhang提供的管理端富文本编辑工具输出的富文本内容,除此之外的富文本内容不保证可以完全正确解析显示。
35
+
34
36
  ### 自定义节点样式
35
37
 
36
38
  `convertRichTextToPixuiStyle` 在转换过程中会为特定节点增加 class 或 data-attribute,方便业务侧按需覆盖样式:
@@ -133,7 +135,8 @@ componentDidMount() { // 在节点渲染后绑定点击事件
133
135
 
134
136
  1. 增加对class ql-align-right -> style="text-align: right;"的转换支持
135
137
 
136
- 0.2.11
138
+ 0.2.11(f1dcaccc3b6c72b82b6b76cea654d50d5ba7ccad)
137
139
 
138
140
  1. 修复图片原始尺寸和样式尺寸不一样,某些pixui版本尺寸不对的问题
139
141
  2. 修复direction属性不生效的问题
142
+ 3. text节点下的文本节点从div标签改为pixui推荐的key标签