@open-feishu-card/renderer 0.2.14 → 0.2.15
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/all.css +11 -2
- package/dist/index.cjs +141 -17
- package/dist/index.js +141 -17
- package/dist/styles.css +11 -2
- package/package.json +2 -2
package/dist/all.css
CHANGED
|
@@ -530,8 +530,9 @@
|
|
|
530
530
|
.universal-card-collapsible-panel {
|
|
531
531
|
box-sizing: border-box; overflow: hidden;
|
|
532
532
|
}
|
|
533
|
-
/* header(<summary
|
|
534
|
-
|
|
533
|
+
/* header(<summary>):可点击整行;去掉浏览器默认三角标记。
|
|
534
|
+
默认 title flex:1 把箭头推到右侧(icon_position=right);
|
|
535
|
+
icon_position=left 时靠 DOM 顺序在左。 */
|
|
535
536
|
.universal-card-collapsible-panel__header {
|
|
536
537
|
display: flex; align-items: center; gap: 8px;
|
|
537
538
|
padding: 8px 12px;
|
|
@@ -544,6 +545,14 @@
|
|
|
544
545
|
flex: 1; min-width: 0; font-size: 14px; line-height: 22px; color: #1f2329;
|
|
545
546
|
word-break: break-word;
|
|
546
547
|
}
|
|
548
|
+
/* follow_text:箭头紧贴文字(真机 __header-content--icon-follow),不拉满整行 */
|
|
549
|
+
.universal-card-collapsible-panel__header--icon-follow {
|
|
550
|
+
gap: 4px;
|
|
551
|
+
}
|
|
552
|
+
.universal-card-collapsible-panel__header--icon-follow .universal-card-collapsible-panel__title {
|
|
553
|
+
flex: 0 1 auto;
|
|
554
|
+
width: auto;
|
|
555
|
+
}
|
|
547
556
|
/* 箭头图标包裹:收起态角度 0;展开态(details[open])旋转到 --panel-icon-angle(内联给出,默认 -180deg)。
|
|
548
557
|
由用户点击 <summary> 切换 [open],箭头方向随之自动更新,无需 JS。 */
|
|
549
558
|
.universal-card-collapsible-panel__icon {
|
package/dist/index.cjs
CHANGED
|
@@ -710,6 +710,91 @@ function marginToStyle(margin) {
|
|
|
710
710
|
if (typeof margin === "string" && margin.trim()) return margin.trim();
|
|
711
711
|
return boxToStyle(margin);
|
|
712
712
|
}
|
|
713
|
+
function horizontalMarginPx(margin) {
|
|
714
|
+
if (typeof margin === "string" && margin.trim()) {
|
|
715
|
+
const parts = margin.trim().split(/\s+/).map((t) => spacingToPx(t));
|
|
716
|
+
if (parts.length === 1) return { left: parts[0], right: parts[0] };
|
|
717
|
+
if (parts.length === 2 || parts.length === 3) return { left: parts[1], right: parts[1] };
|
|
718
|
+
if (parts.length >= 4) return { left: parts[3], right: parts[1] };
|
|
719
|
+
}
|
|
720
|
+
if (margin && typeof margin === "object") {
|
|
721
|
+
const m = margin;
|
|
722
|
+
return { left: Number(m.left?.value ?? 0), right: Number(m.right?.value ?? 0) };
|
|
723
|
+
}
|
|
724
|
+
return { left: 0, right: 0 };
|
|
725
|
+
}
|
|
726
|
+
function applyOuterMargin(style, margin) {
|
|
727
|
+
const mg = marginToStyle(margin);
|
|
728
|
+
if (!mg) return;
|
|
729
|
+
style.margin = mg;
|
|
730
|
+
const { left, right } = horizontalMarginPx(margin);
|
|
731
|
+
const inset = left + right;
|
|
732
|
+
if (inset <= 0) return;
|
|
733
|
+
const w = style.width;
|
|
734
|
+
if (w && /px$/i.test(w) && !w.includes("%")) return;
|
|
735
|
+
if (!w || w === "100%" || w === "fill") {
|
|
736
|
+
style.width = `calc(100% - ${inset}px)`;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
function resolveDimensionPx(direct, valueObj) {
|
|
740
|
+
const fromObj = () => {
|
|
741
|
+
if (!valueObj) return void 0;
|
|
742
|
+
const t = String(valueObj.type || "").toLowerCase();
|
|
743
|
+
if (t === "pixels" || t === "pixel") {
|
|
744
|
+
const n = Number(valueObj.value);
|
|
745
|
+
return Number.isFinite(n) ? n + "px" : void 0;
|
|
746
|
+
}
|
|
747
|
+
const v = valueObj.value;
|
|
748
|
+
if (v === "fill") return "100%";
|
|
749
|
+
if (v === "auto" || v === "default") return void 0;
|
|
750
|
+
if (typeof v === "number" && Number.isFinite(v)) return v + "px";
|
|
751
|
+
if (typeof v === "string") {
|
|
752
|
+
const m = /^(-?\d+(?:\.\d+)?)(?:px)?$/i.exec(v.trim());
|
|
753
|
+
if (m) return m[1] + "px";
|
|
754
|
+
}
|
|
755
|
+
return void 0;
|
|
756
|
+
};
|
|
757
|
+
if (typeof direct === "number" && Number.isFinite(direct)) return direct + "px";
|
|
758
|
+
if (typeof direct === "string") {
|
|
759
|
+
const s = direct.trim();
|
|
760
|
+
if (s === "fill") return "100%";
|
|
761
|
+
if (s === "auto" || s === "default" || !s) return void 0;
|
|
762
|
+
const m = /^(-?\d+(?:\.\d+)?)(?:px)?$/i.exec(s);
|
|
763
|
+
if (m) return m[1] + "px";
|
|
764
|
+
}
|
|
765
|
+
return fromObj();
|
|
766
|
+
}
|
|
767
|
+
function resolveImageSize(property) {
|
|
768
|
+
if (!property) return {};
|
|
769
|
+
const size = property.size ?? property.size_value;
|
|
770
|
+
if (typeof size === "string") {
|
|
771
|
+
const s = size.trim();
|
|
772
|
+
const pxPair = /^(\d+(?:\.\d+)?)px\s+(\d+(?:\.\d+)?)px$/i.exec(s);
|
|
773
|
+
if (pxPair) return { width: pxPair[1] + "px", height: pxPair[2] + "px" };
|
|
774
|
+
const ratio = /^(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)$/.exec(s);
|
|
775
|
+
if (ratio) return { aspectRatio: `${ratio[1]} / ${ratio[2]}` };
|
|
776
|
+
}
|
|
777
|
+
const echo = property.sizeValue ?? property.size_value;
|
|
778
|
+
if (echo && typeof echo === "object") {
|
|
779
|
+
const t = String(echo.type || "").toLowerCase();
|
|
780
|
+
const v = echo.value;
|
|
781
|
+
if (t === "width_height_pixels" && v && typeof v === "object") {
|
|
782
|
+
const w = Number(v.width);
|
|
783
|
+
const h2 = Number(v.height);
|
|
784
|
+
if (Number.isFinite(w) && Number.isFinite(h2)) {
|
|
785
|
+
return { width: w + "px", height: h2 + "px" };
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (t === "aspect_ratio" && v && typeof v === "object") {
|
|
789
|
+
const w = Number(v.width);
|
|
790
|
+
const h2 = Number(v.height);
|
|
791
|
+
if (Number.isFinite(w) && Number.isFinite(h2) && h2 !== 0) {
|
|
792
|
+
return { aspectRatio: `${w} / ${h2}` };
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
return {};
|
|
797
|
+
}
|
|
713
798
|
function resolveCornerRadius(property) {
|
|
714
799
|
if (!property) return void 0;
|
|
715
800
|
const direct = property.corner_radius ?? property.cornerRadius;
|
|
@@ -1367,8 +1452,9 @@ var BASE_STYLES_CSS = `/**
|
|
|
1367
1452
|
.universal-card-collapsible-panel {
|
|
1368
1453
|
box-sizing: border-box; overflow: hidden;
|
|
1369
1454
|
}
|
|
1370
|
-
/* header\uFF08<summary>\uFF09\uFF1A\u53EF\u70B9\u51FB\u6574\u884C\uFF1B\u53BB\u6389\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0\
|
|
1371
|
-
\
|
|
1455
|
+
/* header\uFF08<summary>\uFF09\uFF1A\u53EF\u70B9\u51FB\u6574\u884C\uFF1B\u53BB\u6389\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0\u3002
|
|
1456
|
+
\u9ED8\u8BA4 title flex:1 \u628A\u7BAD\u5934\u63A8\u5230\u53F3\u4FA7\uFF08icon_position=right\uFF09\uFF1B
|
|
1457
|
+
icon_position=left \u65F6\u9760 DOM \u987A\u5E8F\u5728\u5DE6\u3002 */
|
|
1372
1458
|
.universal-card-collapsible-panel__header {
|
|
1373
1459
|
display: flex; align-items: center; gap: 8px;
|
|
1374
1460
|
padding: 8px 12px;
|
|
@@ -1381,6 +1467,14 @@ var BASE_STYLES_CSS = `/**
|
|
|
1381
1467
|
flex: 1; min-width: 0; font-size: 14px; line-height: 22px; color: #1f2329;
|
|
1382
1468
|
word-break: break-word;
|
|
1383
1469
|
}
|
|
1470
|
+
/* follow_text\uFF1A\u7BAD\u5934\u7D27\u8D34\u6587\u5B57\uFF08\u771F\u673A __header-content--icon-follow\uFF09\uFF0C\u4E0D\u62C9\u6EE1\u6574\u884C */
|
|
1471
|
+
.universal-card-collapsible-panel__header--icon-follow {
|
|
1472
|
+
gap: 4px;
|
|
1473
|
+
}
|
|
1474
|
+
.universal-card-collapsible-panel__header--icon-follow .universal-card-collapsible-panel__title {
|
|
1475
|
+
flex: 0 1 auto;
|
|
1476
|
+
width: auto;
|
|
1477
|
+
}
|
|
1384
1478
|
/* \u7BAD\u5934\u56FE\u6807\u5305\u88F9\uFF1A\u6536\u8D77\u6001\u89D2\u5EA6 0\uFF1B\u5C55\u5F00\u6001\uFF08details[open]\uFF09\u65CB\u8F6C\u5230 --panel-icon-angle\uFF08\u5185\u8054\u7ED9\u51FA\uFF0C\u9ED8\u8BA4 -180deg\uFF09\u3002
|
|
1385
1479
|
\u7531\u7528\u6237\u70B9\u51FB <summary> \u5207\u6362 [open]\uFF0C\u7BAD\u5934\u65B9\u5411\u968F\u4E4B\u81EA\u52A8\u66F4\u65B0\uFF0C\u65E0\u9700 JS\u3002 */
|
|
1386
1480
|
.universal-card-collapsible-panel__icon {
|
|
@@ -5435,8 +5529,7 @@ function registerBuiltins() {
|
|
|
5435
5529
|
registerComponent("markdown", (el, ctx) => {
|
|
5436
5530
|
const p = el.property || {};
|
|
5437
5531
|
const style = { ...alignToStyle(p.textAlign), ...textStyleToStyle(p.textStyle) };
|
|
5438
|
-
|
|
5439
|
-
if (mg) style.margin = mg;
|
|
5532
|
+
applyOuterMargin(style, p.margin);
|
|
5440
5533
|
const sizeKey = resolveTextSizeKey(p);
|
|
5441
5534
|
let fontSize = BASE_FONT_SIZE;
|
|
5442
5535
|
let lineHeight = BASE_FONT_SIZE + 8;
|
|
@@ -5502,8 +5595,7 @@ function registerBuiltins() {
|
|
|
5502
5595
|
if (hAlign) {
|
|
5503
5596
|
style.justifyContent = { left: "flex-start", center: "center", right: "flex-end" }[hAlign] || "flex-start";
|
|
5504
5597
|
}
|
|
5505
|
-
|
|
5506
|
-
if (mg) style.margin = mg;
|
|
5598
|
+
applyOuterMargin(style, p.margin);
|
|
5507
5599
|
const bg = propertyBackgroundColor(p);
|
|
5508
5600
|
if (bg) style.background = bg;
|
|
5509
5601
|
const pad = boxToStyle(p.padding);
|
|
@@ -5557,7 +5649,21 @@ function registerBuiltins() {
|
|
|
5557
5649
|
const wrapStyle = {};
|
|
5558
5650
|
const imgStyle = {};
|
|
5559
5651
|
if (p.corner_radius != null) imgStyle.borderRadius = sizeToPx(p.corner_radius);
|
|
5560
|
-
|
|
5652
|
+
const sized = resolveImageSize(p);
|
|
5653
|
+
if (sized.width && sized.height) {
|
|
5654
|
+
wrapStyle.width = sized.width;
|
|
5655
|
+
wrapStyle.height = sized.height;
|
|
5656
|
+
wrapStyle.flexShrink = "0";
|
|
5657
|
+
imgStyle.width = "100%";
|
|
5658
|
+
imgStyle.height = "100%";
|
|
5659
|
+
imgStyle.objectFit = scale === "fit_horizontal" || scale === "stretch" ? "fill" : "cover";
|
|
5660
|
+
} else if (sized.aspectRatio) {
|
|
5661
|
+
wrapStyle.width = "100%";
|
|
5662
|
+
imgStyle.width = "100%";
|
|
5663
|
+
imgStyle.aspectRatio = sized.aspectRatio;
|
|
5664
|
+
imgStyle.height = "auto";
|
|
5665
|
+
imgStyle.objectFit = "cover";
|
|
5666
|
+
} else if (scale === "fit_horizontal" || scale === "stretch") {
|
|
5561
5667
|
wrapStyle.width = "100%";
|
|
5562
5668
|
imgStyle.width = "100%";
|
|
5563
5669
|
imgStyle.height = "auto";
|
|
@@ -5565,7 +5671,7 @@ function registerBuiltins() {
|
|
|
5565
5671
|
return h(
|
|
5566
5672
|
"div",
|
|
5567
5673
|
{ className: `${CLS}-image__wrapper`, style: wrapStyle },
|
|
5568
|
-
src ? h("img", { className: `${CLS}-image__host`, src, alt: altText, style: imgStyle }) : h("div", { className: `${CLS}-image__placeholder
|
|
5674
|
+
src ? h("img", { className: `${CLS}-image__host`, src, alt: altText, style: imgStyle }) : h("div", { className: `${CLS}-image__placeholder`, style: imgStyle }, altText || "[\u56FE\u7247]")
|
|
5569
5675
|
);
|
|
5570
5676
|
});
|
|
5571
5677
|
registerComponent("video", (el, ctx) => {
|
|
@@ -5713,7 +5819,7 @@ function registerBuiltins() {
|
|
|
5713
5819
|
const tip = extractText(p.hoverTips);
|
|
5714
5820
|
const borderNamed = p.border_color ?? p.borderColor;
|
|
5715
5821
|
const borderCustom = adapter.resolveCustomBorderLight(p);
|
|
5716
|
-
const borderColor =
|
|
5822
|
+
const borderColor = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
|
|
5717
5823
|
const hasBorder = borderColor != null || p.hasBorder === true || p.has_border === true;
|
|
5718
5824
|
const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
|
|
5719
5825
|
const style = {};
|
|
@@ -5727,7 +5833,16 @@ function registerBuiltins() {
|
|
|
5727
5833
|
const vGap = p.verticalSpacingValue?.value ?? p.vertical_spacing;
|
|
5728
5834
|
if (vGap != null) style.rowGap = spacingToPx(vGap) + "px";
|
|
5729
5835
|
const icWv = p.widthValue;
|
|
5730
|
-
|
|
5836
|
+
const icHv = p.heightValue;
|
|
5837
|
+
const w = resolveDimensionPx(p.width, icWv);
|
|
5838
|
+
const hPx = resolveDimensionPx(p.height, icHv);
|
|
5839
|
+
if (w) style.width = w;
|
|
5840
|
+
if (hPx) {
|
|
5841
|
+
style.height = hPx;
|
|
5842
|
+
style.flexShrink = "0";
|
|
5843
|
+
style.boxSizing = "border-box";
|
|
5844
|
+
}
|
|
5845
|
+
applyOuterMargin(style, p.margin);
|
|
5731
5846
|
const props = { className: cls };
|
|
5732
5847
|
if (Object.keys(style).length) props.style = style;
|
|
5733
5848
|
if (url && !disabled) props["data-url"] = url;
|
|
@@ -5747,10 +5862,16 @@ function registerBuiltins() {
|
|
|
5747
5862
|
const bg = bgStyleToColor(bgRaw);
|
|
5748
5863
|
if (bg) style.background = bg;
|
|
5749
5864
|
}
|
|
5750
|
-
const
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5865
|
+
const borderRaw = p.border;
|
|
5866
|
+
if (borderRaw && typeof borderRaw === "object") {
|
|
5867
|
+
const border = borderRaw;
|
|
5868
|
+
const borderColor = resolveColor(border.color) || "#dee0e3";
|
|
5869
|
+
style.border = `1px solid ${borderColor}`;
|
|
5870
|
+
style.borderRadius = border.corner_radius != null ? sizeToPx(border.corner_radius) : "8px";
|
|
5871
|
+
} else {
|
|
5872
|
+
style.borderRadius = "8px";
|
|
5873
|
+
}
|
|
5874
|
+
applyOuterMargin(style, p.margin);
|
|
5754
5875
|
const headerStyle = {};
|
|
5755
5876
|
const hpad = typeof hdr.padding === "string" ? hdr.padding : boxToStyle(hdr.padding);
|
|
5756
5877
|
if (hpad) headerStyle.padding = hpad;
|
|
@@ -5758,7 +5879,7 @@ function registerBuiltins() {
|
|
|
5758
5879
|
const titleVNode = h("div", { className: `${CLS}-collapsible-panel__title` }, title);
|
|
5759
5880
|
let iconVNode = null;
|
|
5760
5881
|
const iconRaw = hdr.icon;
|
|
5761
|
-
const openAngle = Number(hdr.icon_expanded_angle ?? -180);
|
|
5882
|
+
const openAngle = Number(hdr.icon_expanded_angle ?? hdr.iconExpandedAngle ?? -180);
|
|
5762
5883
|
if (iconRaw && iconRaw.tag) {
|
|
5763
5884
|
const iconEl = iconRaw.property ? iconRaw : { tag: iconRaw.tag, property: iconRaw };
|
|
5764
5885
|
const inner = renderIcon(iconEl, ctx);
|
|
@@ -5766,10 +5887,13 @@ function registerBuiltins() {
|
|
|
5766
5887
|
iconVNode = h("div", { className: `${CLS}-collapsible-panel__icon` }, inner);
|
|
5767
5888
|
}
|
|
5768
5889
|
}
|
|
5769
|
-
const
|
|
5890
|
+
const iconPos = String(hdr.icon_position ?? hdr.iconPosition ?? "right");
|
|
5891
|
+
const iconLeft = iconPos === "left";
|
|
5892
|
+
const iconFollow = iconPos === "follow_text" || iconPos === "followText";
|
|
5770
5893
|
const headerChildren = iconLeft ? [iconVNode, titleVNode] : [titleVNode, iconVNode];
|
|
5771
5894
|
const summaryStyle = { ...headerStyle, "--panel-icon-angle": `${openAngle}deg` };
|
|
5772
|
-
const
|
|
5895
|
+
const headerCls = `${CLS}-collapsible-panel__header` + (iconFollow ? ` ${CLS}-collapsible-panel__header--icon-follow` : "");
|
|
5896
|
+
const summary = h("summary", { className: headerCls, style: summaryStyle }, headerChildren);
|
|
5773
5897
|
const contentStyle = {};
|
|
5774
5898
|
const cpad = typeof p.padding === "string" ? p.padding : boxToStyle(p.padding);
|
|
5775
5899
|
if (cpad) contentStyle.padding = cpad;
|
package/dist/index.js
CHANGED
|
@@ -709,6 +709,91 @@ function marginToStyle(margin) {
|
|
|
709
709
|
if (typeof margin === "string" && margin.trim()) return margin.trim();
|
|
710
710
|
return boxToStyle(margin);
|
|
711
711
|
}
|
|
712
|
+
function horizontalMarginPx(margin) {
|
|
713
|
+
if (typeof margin === "string" && margin.trim()) {
|
|
714
|
+
const parts = margin.trim().split(/\s+/).map((t) => spacingToPx(t));
|
|
715
|
+
if (parts.length === 1) return { left: parts[0], right: parts[0] };
|
|
716
|
+
if (parts.length === 2 || parts.length === 3) return { left: parts[1], right: parts[1] };
|
|
717
|
+
if (parts.length >= 4) return { left: parts[3], right: parts[1] };
|
|
718
|
+
}
|
|
719
|
+
if (margin && typeof margin === "object") {
|
|
720
|
+
const m = margin;
|
|
721
|
+
return { left: Number(m.left?.value ?? 0), right: Number(m.right?.value ?? 0) };
|
|
722
|
+
}
|
|
723
|
+
return { left: 0, right: 0 };
|
|
724
|
+
}
|
|
725
|
+
function applyOuterMargin(style, margin) {
|
|
726
|
+
const mg = marginToStyle(margin);
|
|
727
|
+
if (!mg) return;
|
|
728
|
+
style.margin = mg;
|
|
729
|
+
const { left, right } = horizontalMarginPx(margin);
|
|
730
|
+
const inset = left + right;
|
|
731
|
+
if (inset <= 0) return;
|
|
732
|
+
const w = style.width;
|
|
733
|
+
if (w && /px$/i.test(w) && !w.includes("%")) return;
|
|
734
|
+
if (!w || w === "100%" || w === "fill") {
|
|
735
|
+
style.width = `calc(100% - ${inset}px)`;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
function resolveDimensionPx(direct, valueObj) {
|
|
739
|
+
const fromObj = () => {
|
|
740
|
+
if (!valueObj) return void 0;
|
|
741
|
+
const t = String(valueObj.type || "").toLowerCase();
|
|
742
|
+
if (t === "pixels" || t === "pixel") {
|
|
743
|
+
const n = Number(valueObj.value);
|
|
744
|
+
return Number.isFinite(n) ? n + "px" : void 0;
|
|
745
|
+
}
|
|
746
|
+
const v = valueObj.value;
|
|
747
|
+
if (v === "fill") return "100%";
|
|
748
|
+
if (v === "auto" || v === "default") return void 0;
|
|
749
|
+
if (typeof v === "number" && Number.isFinite(v)) return v + "px";
|
|
750
|
+
if (typeof v === "string") {
|
|
751
|
+
const m = /^(-?\d+(?:\.\d+)?)(?:px)?$/i.exec(v.trim());
|
|
752
|
+
if (m) return m[1] + "px";
|
|
753
|
+
}
|
|
754
|
+
return void 0;
|
|
755
|
+
};
|
|
756
|
+
if (typeof direct === "number" && Number.isFinite(direct)) return direct + "px";
|
|
757
|
+
if (typeof direct === "string") {
|
|
758
|
+
const s = direct.trim();
|
|
759
|
+
if (s === "fill") return "100%";
|
|
760
|
+
if (s === "auto" || s === "default" || !s) return void 0;
|
|
761
|
+
const m = /^(-?\d+(?:\.\d+)?)(?:px)?$/i.exec(s);
|
|
762
|
+
if (m) return m[1] + "px";
|
|
763
|
+
}
|
|
764
|
+
return fromObj();
|
|
765
|
+
}
|
|
766
|
+
function resolveImageSize(property) {
|
|
767
|
+
if (!property) return {};
|
|
768
|
+
const size = property.size ?? property.size_value;
|
|
769
|
+
if (typeof size === "string") {
|
|
770
|
+
const s = size.trim();
|
|
771
|
+
const pxPair = /^(\d+(?:\.\d+)?)px\s+(\d+(?:\.\d+)?)px$/i.exec(s);
|
|
772
|
+
if (pxPair) return { width: pxPair[1] + "px", height: pxPair[2] + "px" };
|
|
773
|
+
const ratio = /^(\d+(?:\.\d+)?)\s*:\s*(\d+(?:\.\d+)?)$/.exec(s);
|
|
774
|
+
if (ratio) return { aspectRatio: `${ratio[1]} / ${ratio[2]}` };
|
|
775
|
+
}
|
|
776
|
+
const echo = property.sizeValue ?? property.size_value;
|
|
777
|
+
if (echo && typeof echo === "object") {
|
|
778
|
+
const t = String(echo.type || "").toLowerCase();
|
|
779
|
+
const v = echo.value;
|
|
780
|
+
if (t === "width_height_pixels" && v && typeof v === "object") {
|
|
781
|
+
const w = Number(v.width);
|
|
782
|
+
const h2 = Number(v.height);
|
|
783
|
+
if (Number.isFinite(w) && Number.isFinite(h2)) {
|
|
784
|
+
return { width: w + "px", height: h2 + "px" };
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (t === "aspect_ratio" && v && typeof v === "object") {
|
|
788
|
+
const w = Number(v.width);
|
|
789
|
+
const h2 = Number(v.height);
|
|
790
|
+
if (Number.isFinite(w) && Number.isFinite(h2) && h2 !== 0) {
|
|
791
|
+
return { aspectRatio: `${w} / ${h2}` };
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return {};
|
|
796
|
+
}
|
|
712
797
|
function resolveCornerRadius(property) {
|
|
713
798
|
if (!property) return void 0;
|
|
714
799
|
const direct = property.corner_radius ?? property.cornerRadius;
|
|
@@ -1366,8 +1451,9 @@ var BASE_STYLES_CSS = `/**
|
|
|
1366
1451
|
.universal-card-collapsible-panel {
|
|
1367
1452
|
box-sizing: border-box; overflow: hidden;
|
|
1368
1453
|
}
|
|
1369
|
-
/* header\uFF08<summary>\uFF09\uFF1A\u53EF\u70B9\u51FB\u6574\u884C\uFF1B\u53BB\u6389\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0\
|
|
1370
|
-
\
|
|
1454
|
+
/* header\uFF08<summary>\uFF09\uFF1A\u53EF\u70B9\u51FB\u6574\u884C\uFF1B\u53BB\u6389\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0\u3002
|
|
1455
|
+
\u9ED8\u8BA4 title flex:1 \u628A\u7BAD\u5934\u63A8\u5230\u53F3\u4FA7\uFF08icon_position=right\uFF09\uFF1B
|
|
1456
|
+
icon_position=left \u65F6\u9760 DOM \u987A\u5E8F\u5728\u5DE6\u3002 */
|
|
1371
1457
|
.universal-card-collapsible-panel__header {
|
|
1372
1458
|
display: flex; align-items: center; gap: 8px;
|
|
1373
1459
|
padding: 8px 12px;
|
|
@@ -1380,6 +1466,14 @@ var BASE_STYLES_CSS = `/**
|
|
|
1380
1466
|
flex: 1; min-width: 0; font-size: 14px; line-height: 22px; color: #1f2329;
|
|
1381
1467
|
word-break: break-word;
|
|
1382
1468
|
}
|
|
1469
|
+
/* follow_text\uFF1A\u7BAD\u5934\u7D27\u8D34\u6587\u5B57\uFF08\u771F\u673A __header-content--icon-follow\uFF09\uFF0C\u4E0D\u62C9\u6EE1\u6574\u884C */
|
|
1470
|
+
.universal-card-collapsible-panel__header--icon-follow {
|
|
1471
|
+
gap: 4px;
|
|
1472
|
+
}
|
|
1473
|
+
.universal-card-collapsible-panel__header--icon-follow .universal-card-collapsible-panel__title {
|
|
1474
|
+
flex: 0 1 auto;
|
|
1475
|
+
width: auto;
|
|
1476
|
+
}
|
|
1383
1477
|
/* \u7BAD\u5934\u56FE\u6807\u5305\u88F9\uFF1A\u6536\u8D77\u6001\u89D2\u5EA6 0\uFF1B\u5C55\u5F00\u6001\uFF08details[open]\uFF09\u65CB\u8F6C\u5230 --panel-icon-angle\uFF08\u5185\u8054\u7ED9\u51FA\uFF0C\u9ED8\u8BA4 -180deg\uFF09\u3002
|
|
1384
1478
|
\u7531\u7528\u6237\u70B9\u51FB <summary> \u5207\u6362 [open]\uFF0C\u7BAD\u5934\u65B9\u5411\u968F\u4E4B\u81EA\u52A8\u66F4\u65B0\uFF0C\u65E0\u9700 JS\u3002 */
|
|
1385
1479
|
.universal-card-collapsible-panel__icon {
|
|
@@ -5434,8 +5528,7 @@ function registerBuiltins() {
|
|
|
5434
5528
|
registerComponent("markdown", (el, ctx) => {
|
|
5435
5529
|
const p = el.property || {};
|
|
5436
5530
|
const style = { ...alignToStyle(p.textAlign), ...textStyleToStyle(p.textStyle) };
|
|
5437
|
-
|
|
5438
|
-
if (mg) style.margin = mg;
|
|
5531
|
+
applyOuterMargin(style, p.margin);
|
|
5439
5532
|
const sizeKey = resolveTextSizeKey(p);
|
|
5440
5533
|
let fontSize = BASE_FONT_SIZE;
|
|
5441
5534
|
let lineHeight = BASE_FONT_SIZE + 8;
|
|
@@ -5501,8 +5594,7 @@ function registerBuiltins() {
|
|
|
5501
5594
|
if (hAlign) {
|
|
5502
5595
|
style.justifyContent = { left: "flex-start", center: "center", right: "flex-end" }[hAlign] || "flex-start";
|
|
5503
5596
|
}
|
|
5504
|
-
|
|
5505
|
-
if (mg) style.margin = mg;
|
|
5597
|
+
applyOuterMargin(style, p.margin);
|
|
5506
5598
|
const bg = propertyBackgroundColor(p);
|
|
5507
5599
|
if (bg) style.background = bg;
|
|
5508
5600
|
const pad = boxToStyle(p.padding);
|
|
@@ -5556,7 +5648,21 @@ function registerBuiltins() {
|
|
|
5556
5648
|
const wrapStyle = {};
|
|
5557
5649
|
const imgStyle = {};
|
|
5558
5650
|
if (p.corner_radius != null) imgStyle.borderRadius = sizeToPx(p.corner_radius);
|
|
5559
|
-
|
|
5651
|
+
const sized = resolveImageSize(p);
|
|
5652
|
+
if (sized.width && sized.height) {
|
|
5653
|
+
wrapStyle.width = sized.width;
|
|
5654
|
+
wrapStyle.height = sized.height;
|
|
5655
|
+
wrapStyle.flexShrink = "0";
|
|
5656
|
+
imgStyle.width = "100%";
|
|
5657
|
+
imgStyle.height = "100%";
|
|
5658
|
+
imgStyle.objectFit = scale === "fit_horizontal" || scale === "stretch" ? "fill" : "cover";
|
|
5659
|
+
} else if (sized.aspectRatio) {
|
|
5660
|
+
wrapStyle.width = "100%";
|
|
5661
|
+
imgStyle.width = "100%";
|
|
5662
|
+
imgStyle.aspectRatio = sized.aspectRatio;
|
|
5663
|
+
imgStyle.height = "auto";
|
|
5664
|
+
imgStyle.objectFit = "cover";
|
|
5665
|
+
} else if (scale === "fit_horizontal" || scale === "stretch") {
|
|
5560
5666
|
wrapStyle.width = "100%";
|
|
5561
5667
|
imgStyle.width = "100%";
|
|
5562
5668
|
imgStyle.height = "auto";
|
|
@@ -5564,7 +5670,7 @@ function registerBuiltins() {
|
|
|
5564
5670
|
return h(
|
|
5565
5671
|
"div",
|
|
5566
5672
|
{ className: `${CLS}-image__wrapper`, style: wrapStyle },
|
|
5567
|
-
src ? h("img", { className: `${CLS}-image__host`, src, alt: altText, style: imgStyle }) : h("div", { className: `${CLS}-image__placeholder
|
|
5673
|
+
src ? h("img", { className: `${CLS}-image__host`, src, alt: altText, style: imgStyle }) : h("div", { className: `${CLS}-image__placeholder`, style: imgStyle }, altText || "[\u56FE\u7247]")
|
|
5568
5674
|
);
|
|
5569
5675
|
});
|
|
5570
5676
|
registerComponent("video", (el, ctx) => {
|
|
@@ -5712,7 +5818,7 @@ function registerBuiltins() {
|
|
|
5712
5818
|
const tip = extractText(p.hoverTips);
|
|
5713
5819
|
const borderNamed = p.border_color ?? p.borderColor;
|
|
5714
5820
|
const borderCustom = resolveCustomBorderLight(p);
|
|
5715
|
-
const borderColor =
|
|
5821
|
+
const borderColor = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
|
|
5716
5822
|
const hasBorder = borderColor != null || p.hasBorder === true || p.has_border === true;
|
|
5717
5823
|
const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
|
|
5718
5824
|
const style = {};
|
|
@@ -5726,7 +5832,16 @@ function registerBuiltins() {
|
|
|
5726
5832
|
const vGap = p.verticalSpacingValue?.value ?? p.vertical_spacing;
|
|
5727
5833
|
if (vGap != null) style.rowGap = spacingToPx(vGap) + "px";
|
|
5728
5834
|
const icWv = p.widthValue;
|
|
5729
|
-
|
|
5835
|
+
const icHv = p.heightValue;
|
|
5836
|
+
const w = resolveDimensionPx(p.width, icWv);
|
|
5837
|
+
const hPx = resolveDimensionPx(p.height, icHv);
|
|
5838
|
+
if (w) style.width = w;
|
|
5839
|
+
if (hPx) {
|
|
5840
|
+
style.height = hPx;
|
|
5841
|
+
style.flexShrink = "0";
|
|
5842
|
+
style.boxSizing = "border-box";
|
|
5843
|
+
}
|
|
5844
|
+
applyOuterMargin(style, p.margin);
|
|
5730
5845
|
const props = { className: cls };
|
|
5731
5846
|
if (Object.keys(style).length) props.style = style;
|
|
5732
5847
|
if (url && !disabled) props["data-url"] = url;
|
|
@@ -5746,10 +5861,16 @@ function registerBuiltins() {
|
|
|
5746
5861
|
const bg = bgStyleToColor(bgRaw);
|
|
5747
5862
|
if (bg) style.background = bg;
|
|
5748
5863
|
}
|
|
5749
|
-
const
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5864
|
+
const borderRaw = p.border;
|
|
5865
|
+
if (borderRaw && typeof borderRaw === "object") {
|
|
5866
|
+
const border = borderRaw;
|
|
5867
|
+
const borderColor = resolveColor(border.color) || "#dee0e3";
|
|
5868
|
+
style.border = `1px solid ${borderColor}`;
|
|
5869
|
+
style.borderRadius = border.corner_radius != null ? sizeToPx(border.corner_radius) : "8px";
|
|
5870
|
+
} else {
|
|
5871
|
+
style.borderRadius = "8px";
|
|
5872
|
+
}
|
|
5873
|
+
applyOuterMargin(style, p.margin);
|
|
5753
5874
|
const headerStyle = {};
|
|
5754
5875
|
const hpad = typeof hdr.padding === "string" ? hdr.padding : boxToStyle(hdr.padding);
|
|
5755
5876
|
if (hpad) headerStyle.padding = hpad;
|
|
@@ -5757,7 +5878,7 @@ function registerBuiltins() {
|
|
|
5757
5878
|
const titleVNode = h("div", { className: `${CLS}-collapsible-panel__title` }, title);
|
|
5758
5879
|
let iconVNode = null;
|
|
5759
5880
|
const iconRaw = hdr.icon;
|
|
5760
|
-
const openAngle = Number(hdr.icon_expanded_angle ?? -180);
|
|
5881
|
+
const openAngle = Number(hdr.icon_expanded_angle ?? hdr.iconExpandedAngle ?? -180);
|
|
5761
5882
|
if (iconRaw && iconRaw.tag) {
|
|
5762
5883
|
const iconEl = iconRaw.property ? iconRaw : { tag: iconRaw.tag, property: iconRaw };
|
|
5763
5884
|
const inner = renderIcon(iconEl, ctx);
|
|
@@ -5765,10 +5886,13 @@ function registerBuiltins() {
|
|
|
5765
5886
|
iconVNode = h("div", { className: `${CLS}-collapsible-panel__icon` }, inner);
|
|
5766
5887
|
}
|
|
5767
5888
|
}
|
|
5768
|
-
const
|
|
5889
|
+
const iconPos = String(hdr.icon_position ?? hdr.iconPosition ?? "right");
|
|
5890
|
+
const iconLeft = iconPos === "left";
|
|
5891
|
+
const iconFollow = iconPos === "follow_text" || iconPos === "followText";
|
|
5769
5892
|
const headerChildren = iconLeft ? [iconVNode, titleVNode] : [titleVNode, iconVNode];
|
|
5770
5893
|
const summaryStyle = { ...headerStyle, "--panel-icon-angle": `${openAngle}deg` };
|
|
5771
|
-
const
|
|
5894
|
+
const headerCls = `${CLS}-collapsible-panel__header` + (iconFollow ? ` ${CLS}-collapsible-panel__header--icon-follow` : "");
|
|
5895
|
+
const summary = h("summary", { className: headerCls, style: summaryStyle }, headerChildren);
|
|
5772
5896
|
const contentStyle = {};
|
|
5773
5897
|
const cpad = typeof p.padding === "string" ? p.padding : boxToStyle(p.padding);
|
|
5774
5898
|
if (cpad) contentStyle.padding = cpad;
|
package/dist/styles.css
CHANGED
|
@@ -530,8 +530,9 @@
|
|
|
530
530
|
.universal-card-collapsible-panel {
|
|
531
531
|
box-sizing: border-box; overflow: hidden;
|
|
532
532
|
}
|
|
533
|
-
/* header(<summary
|
|
534
|
-
|
|
533
|
+
/* header(<summary>):可点击整行;去掉浏览器默认三角标记。
|
|
534
|
+
默认 title flex:1 把箭头推到右侧(icon_position=right);
|
|
535
|
+
icon_position=left 时靠 DOM 顺序在左。 */
|
|
535
536
|
.universal-card-collapsible-panel__header {
|
|
536
537
|
display: flex; align-items: center; gap: 8px;
|
|
537
538
|
padding: 8px 12px;
|
|
@@ -544,6 +545,14 @@
|
|
|
544
545
|
flex: 1; min-width: 0; font-size: 14px; line-height: 22px; color: #1f2329;
|
|
545
546
|
word-break: break-word;
|
|
546
547
|
}
|
|
548
|
+
/* follow_text:箭头紧贴文字(真机 __header-content--icon-follow),不拉满整行 */
|
|
549
|
+
.universal-card-collapsible-panel__header--icon-follow {
|
|
550
|
+
gap: 4px;
|
|
551
|
+
}
|
|
552
|
+
.universal-card-collapsible-panel__header--icon-follow .universal-card-collapsible-panel__title {
|
|
553
|
+
flex: 0 1 auto;
|
|
554
|
+
width: auto;
|
|
555
|
+
}
|
|
547
556
|
/* 箭头图标包裹:收起态角度 0;展开态(details[open])旋转到 --panel-icon-angle(内联给出,默认 -180deg)。
|
|
548
557
|
由用户点击 <summary> 切换 [open],箭头方向随之自动更新,无需 JS。 */
|
|
549
558
|
.universal-card-collapsible-panel__icon {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-feishu-card/renderer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "零框架依赖的飞书卡片渲染引擎:Web / DOM / SSR,输入 RAW 或公开 DSL,输出真实 DOM 或 HTML 字符串",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"./dist/*.cjs"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@open-feishu-card/adapter": "0.3.
|
|
31
|
+
"@open-feishu-card/adapter": "0.3.11"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"tsup": "^8.3.5",
|