@pixui-dev/pixui-richtext-helper 0.2.12-beta.1 → 0.2.12-dev.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.
- package/dist/RichTextCore.d.ts +7 -2
- package/dist/RichTextCore.js +116 -20
- package/package.json +1 -1
package/dist/RichTextCore.d.ts
CHANGED
|
@@ -65,12 +65,17 @@ export declare class RichTextCore {
|
|
|
65
65
|
private static handleLists;
|
|
66
66
|
/** 将连续 <br> 替换为透明占位文字,确保 PixUI 换行 */
|
|
67
67
|
private static replaceBrWithPlaceholder;
|
|
68
|
-
/** 处理 <a>
|
|
68
|
+
/** 处理 <a> 标签:文本链接直接转为 key,图片链接直接下放到 img 上 */
|
|
69
69
|
private static handleAnchorTag;
|
|
70
70
|
/** 用 text 标签包裹纯文本节点 */
|
|
71
71
|
private static wrapTextNodes;
|
|
72
72
|
/** strong / em / u / s / span 等标签替换成 text */
|
|
73
73
|
private static replaceStyleTags;
|
|
74
|
+
/**
|
|
75
|
+
* 规范化:将列表项文本列中的裸文本片段包裹为 <key>
|
|
76
|
+
* 作用域限定在三列布局生成后的列表行:div[data-list-item="true"] > text
|
|
77
|
+
*/
|
|
78
|
+
private static normalizeListItemPlainTextToKey;
|
|
74
79
|
/** p → div */
|
|
75
80
|
private static replacePTag;
|
|
76
81
|
/** img 的 href 转移处理 & 记录链接 */
|
|
@@ -87,7 +92,7 @@ export declare class RichTextCore {
|
|
|
87
92
|
private static adjustLineHeightAndLetterSpacing;
|
|
88
93
|
/** 把 div 的 text-align 转移到子 text 上 */
|
|
89
94
|
private static transferTextAlign;
|
|
90
|
-
/** 删除空 text
|
|
95
|
+
/** 删除空 text 节点;并在安全场景下清理空 key 节点(不影响占位/换行) */
|
|
91
96
|
private static removeEmptyText;
|
|
92
97
|
/** 补默认 heading 字体大小 */
|
|
93
98
|
private static fixHeadingFontSize;
|
package/dist/RichTextCore.js
CHANGED
|
@@ -383,24 +383,43 @@ var RichTextCore = /** @class */ (function () {
|
|
|
383
383
|
*/
|
|
384
384
|
RichTextCore.processSegment = function (segmentHtml, config) {
|
|
385
385
|
var $ = cheerio.load(segmentHtml, null, false);
|
|
386
|
-
//
|
|
386
|
+
// 预处理:data-list → class、class → style、标题后补 <br>、margin→padding 合并
|
|
387
387
|
this.preprocess($);
|
|
388
|
+
// 规范图片尺寸单位:img 的 width/height 数字补 px
|
|
388
389
|
this.fixImgSizeUnit($);
|
|
390
|
+
// COS 图片缩略参数:按最小可用宽度收敛/追加 imageMogr2/thumbnail/<w>x
|
|
389
391
|
this.handleCosImageMogr2($, config);
|
|
392
|
+
// 合并img的width,height属性到 style,属性存在以 style 优先,移除 width/height 属性
|
|
390
393
|
this.consolidateImgSizeStyle($);
|
|
394
|
+
// 列表规范化:ol/ul/li 转三列布局(缩进/标号/文本),保留缩进与方向。节点结构:段落div(缩进key节点、标号key节点、text节点区域)
|
|
391
395
|
this.handleLists($, (config === null || config === void 0 ? void 0 : config.lineHeightScale) || 1);
|
|
396
|
+
// 换行处理:<br> → 透明占位 div,确保 PixUI 内正确换行
|
|
392
397
|
this.replaceBrWithPlaceholder($);
|
|
398
|
+
// 超链接:<a> → 可点击的 div,汇聚子样式并记录 data-link-*
|
|
393
399
|
this.handleAnchorTag($);
|
|
400
|
+
// 文本包装:将普通文本包入 <text>(跳过列表/链接/换行占位)
|
|
394
401
|
this.wrapTextNodes($);
|
|
402
|
+
// 样式标签:strong/em/u/s/span → 文本节点(列表文本列下改为 <key>)
|
|
395
403
|
this.replaceStyleTags($);
|
|
404
|
+
// 列表文本列规范化:裸文本统一包裹为 <key>,清理空白
|
|
405
|
+
this.normalizeListItemPlainTextToKey($);
|
|
406
|
+
// 段落:<p> → <div>,补 flex-shrink 与 width:100%
|
|
396
407
|
this.replacePTag($);
|
|
408
|
+
// 图片链接:img 的 href 转移至 data-link-href,并登记点击节点
|
|
397
409
|
this.handleImgHref($);
|
|
410
|
+
// 防收缩:常见块级元素补 flex-shrink:0(不作用于列表项)
|
|
398
411
|
this.addFlexShrink($);
|
|
412
|
+
// 扁平化:合并 text 样式并去掉嵌套的父 <text>
|
|
399
413
|
this.flattenNestedText($);
|
|
414
|
+
// 同级文本:拆分为 <key> 片段并统一包入父 <text>
|
|
400
415
|
this.convertSiblingTextToDiv($);
|
|
416
|
+
// 首行缩进:text-indent 转透明占位
|
|
401
417
|
this.handleTextIndent($);
|
|
418
|
+
// 行高与字间距:聚合到父 <text> 并按配置缩放 line-height
|
|
402
419
|
this.adjustLineHeightAndLetterSpacing($, config);
|
|
420
|
+
// 文本对齐与方向:从 div 下沉至子 <text>
|
|
403
421
|
this.transferTextAlign($);
|
|
422
|
+
// 清理空 <text>
|
|
404
423
|
this.removeEmptyText($);
|
|
405
424
|
// this.fixHeadingFontSize($);
|
|
406
425
|
// 最后:修正 img 结束标签、nbsp
|
|
@@ -758,7 +777,7 @@ var RichTextCore = /** @class */ (function () {
|
|
|
758
777
|
$(this).replaceWith("<div style=\"".concat(mergeStyles("color: transparent", "flex-shrink: 0", "font-size: 20px"), "\" class='pixui-richtext-br-placeholder'>1</div>"));
|
|
759
778
|
});
|
|
760
779
|
};
|
|
761
|
-
/** 处理 <a>
|
|
780
|
+
/** 处理 <a> 标签:文本链接直接转为 key,图片链接直接下放到 img 上 */
|
|
762
781
|
RichTextCore.handleAnchorTag = function ($) {
|
|
763
782
|
var self = this;
|
|
764
783
|
$("a").each(function () {
|
|
@@ -785,10 +804,33 @@ var RichTextCore = /** @class */ (function () {
|
|
|
785
804
|
});
|
|
786
805
|
var finalStyle = mergeStyles.apply(void 0, __spreadArray([originalStyle], mergedStyles, false));
|
|
787
806
|
var encodedHref = href || ""; // 已在预处理阶段编码,避免二次编码
|
|
788
|
-
|
|
789
|
-
|
|
807
|
+
var hasImg = $(this).find("img").length > 0;
|
|
808
|
+
if (!hasImg) {
|
|
809
|
+
// 文本超链接 → 直接输出可点击的 <key>
|
|
810
|
+
if (href) {
|
|
811
|
+
self.linkNodes.push({ type: LinkNodeType.DIV, href: href, id: id });
|
|
812
|
+
}
|
|
813
|
+
$(this).replaceWith("<key style=\"".concat(finalStyle, "\" id=\"").concat(id, "\" class=\"").concat(RichTextCore.LINK_NODE_CLASS, " PA_RichTextHref_ATag\" data-link-type=\"div\" data-link-href=\"").concat(encodedHref, "\">").concat(textContent, "</key>"));
|
|
814
|
+
}
|
|
815
|
+
else {
|
|
816
|
+
// 图片超链接 → 将链接属性下放到 <img>,去掉 <a>
|
|
817
|
+
var $a = $(this);
|
|
818
|
+
$a.find("img").each(function () {
|
|
819
|
+
// 合并 a 的布局相关样式到 img(img 自身优先)
|
|
820
|
+
var aStyleObj = parseStyleString(finalStyle);
|
|
821
|
+
var imgStyleObj = parseStyleString($(this).attr("style") || "");
|
|
822
|
+
Object.keys(aStyleObj).forEach(function (k) {
|
|
823
|
+
if (!imgStyleObj[k])
|
|
824
|
+
imgStyleObj[k] = aStyleObj[k];
|
|
825
|
+
});
|
|
826
|
+
$(this).attr("style", serializeStyleObject(imgStyleObj));
|
|
827
|
+
// 设置链接标记(id 将在 handleImgHref 统一设置)
|
|
828
|
+
$(this).attr("data-link-type", "img");
|
|
829
|
+
$(this).attr("data-link-href", encodedHref);
|
|
830
|
+
});
|
|
831
|
+
// 用 <a> 的内部内容替换掉 <a>
|
|
832
|
+
$a.replaceWith($a.html() || "");
|
|
790
833
|
}
|
|
791
|
-
$(this).replaceWith("<div style=\"".concat(finalStyle, "\" id=\"").concat(id, "\" class=\"").concat(RichTextCore.LINK_NODE_CLASS, " PA_RichTextHref_ATag\" data-link-type=\"div\" data-link-href=\"").concat(encodedHref, "\">").concat(textContent, "</div>"));
|
|
792
834
|
});
|
|
793
835
|
};
|
|
794
836
|
/** 用 text 标签包裹纯文本节点 */
|
|
@@ -864,6 +906,30 @@ var RichTextCore = /** @class */ (function () {
|
|
|
864
906
|
});
|
|
865
907
|
}
|
|
866
908
|
};
|
|
909
|
+
/**
|
|
910
|
+
* 规范化:将列表项文本列中的裸文本片段包裹为 <key>
|
|
911
|
+
* 作用域限定在三列布局生成后的列表行:div[data-list-item="true"] > text
|
|
912
|
+
*/
|
|
913
|
+
RichTextCore.normalizeListItemPlainTextToKey = function ($) {
|
|
914
|
+
$("div[data-list-item='true'] > text").each(function () {
|
|
915
|
+
// 遍历直接子内容,遇到纯文本且非空则包裹为 <key>
|
|
916
|
+
$(this)
|
|
917
|
+
.contents()
|
|
918
|
+
.each(function () {
|
|
919
|
+
if (this.type === "text") {
|
|
920
|
+
var raw = this.data;
|
|
921
|
+
if (raw && raw.trim().length > 0) {
|
|
922
|
+
var wrapped = "<key style=\"".concat(mergeStyles("flex-shrink: 0", "word-break: break-word"), "\">").concat(raw, "</key>");
|
|
923
|
+
$(this).replaceWith(wrapped);
|
|
924
|
+
}
|
|
925
|
+
else {
|
|
926
|
+
// 空白文本直接移除,避免无意义节点
|
|
927
|
+
$(this).remove();
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
};
|
|
867
933
|
/** p → div */
|
|
868
934
|
RichTextCore.replacePTag = function ($) {
|
|
869
935
|
$("p").each(function () {
|
|
@@ -910,21 +976,29 @@ var RichTextCore = /** @class */ (function () {
|
|
|
910
976
|
};
|
|
911
977
|
/** 把嵌套的 text 展平 */
|
|
912
978
|
RichTextCore.flattenNestedText = function ($) {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
var
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
979
|
+
var flattenSameTagNested = function (tagName) {
|
|
980
|
+
var selector = "".concat(tagName, " > ").concat(tagName);
|
|
981
|
+
while ($(selector).length > 0) {
|
|
982
|
+
$(tagName).each(function () {
|
|
983
|
+
var $parent = $(this);
|
|
984
|
+
var parentStyle = $parent.attr("style") || "";
|
|
985
|
+
// 合并父样式到直接子同名标签
|
|
986
|
+
$parent.children(tagName).each(function () {
|
|
987
|
+
var $child = $(this);
|
|
988
|
+
var childStyle = $child.attr("style") || "";
|
|
989
|
+
var mergedStyle = mergeStyles(parentStyle, childStyle);
|
|
990
|
+
$child.attr("style", mergedStyle);
|
|
991
|
+
});
|
|
992
|
+
// 若仍存在子同名标签,则用内部内容替换父节点
|
|
993
|
+
if ($parent.children(tagName).length > 0) {
|
|
994
|
+
$parent.replaceWith($parent.html() || "");
|
|
995
|
+
}
|
|
922
996
|
});
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
// 先处理 text > text,再处理 key > key
|
|
1000
|
+
flattenSameTagNested("text");
|
|
1001
|
+
flattenSameTagNested("key");
|
|
928
1002
|
};
|
|
929
1003
|
/** 将同一行的 text 节点转为 div 并外包一层 text */
|
|
930
1004
|
RichTextCore.convertSiblingTextToDiv = function ($) {
|
|
@@ -1092,13 +1166,35 @@ var RichTextCore = /** @class */ (function () {
|
|
|
1092
1166
|
});
|
|
1093
1167
|
});
|
|
1094
1168
|
};
|
|
1095
|
-
/** 删除空 text
|
|
1169
|
+
/** 删除空 text 节点;并在安全场景下清理空 key 节点(不影响占位/换行) */
|
|
1096
1170
|
RichTextCore.removeEmptyText = function ($) {
|
|
1097
1171
|
$("text").each(function () {
|
|
1098
1172
|
var t = $(this).html() || "";
|
|
1099
1173
|
if (t.trim() === "")
|
|
1100
1174
|
$(this).remove();
|
|
1101
1175
|
});
|
|
1176
|
+
// 仅清理列表“文本列”中的空 key:div[data-list-item=true] > text 后代
|
|
1177
|
+
// 排除:
|
|
1178
|
+
// 1) 列表三列布局中用于缩进/标号的 <key>(这些是 div[data-list-item=true] 的直接子元素,不在 text 内)
|
|
1179
|
+
// 2) 任何带有非空 style 且含有可见内容的节点
|
|
1180
|
+
$("div[data-list-item='true'] > text").each(function () {
|
|
1181
|
+
$(this)
|
|
1182
|
+
.find("key")
|
|
1183
|
+
.each(function () {
|
|
1184
|
+
var $key = $(this);
|
|
1185
|
+
var html = $key.html() || "";
|
|
1186
|
+
var textOnly = html.replace(/<[^>]*>/g, "");
|
|
1187
|
+
var hasVisibleText = textOnly.trim().length > 0;
|
|
1188
|
+
if (hasVisibleText)
|
|
1189
|
+
return;
|
|
1190
|
+
// 保留显式占位用途的 key:如果样式包含 width/height/font-size 等,且为非空数值,则视作占位
|
|
1191
|
+
var styleObj = parseStyleString($key.attr("style") || "");
|
|
1192
|
+
var isPlaceholderByStyle = ["width", "height", "font-size", "padding-left", "padding-right", "margin-left", "margin-right"].some(function (k) { return !!styleObj[k]; });
|
|
1193
|
+
if (!isPlaceholderByStyle) {
|
|
1194
|
+
$key.remove();
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
});
|
|
1102
1198
|
};
|
|
1103
1199
|
/** 补默认 heading 字体大小 */
|
|
1104
1200
|
RichTextCore.fixHeadingFontSize = function ($) {
|