@open-feishu-card/renderer 0.3.6 → 0.5.0

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 CHANGED
@@ -380,6 +380,90 @@
380
380
  font-size: 12px;
381
381
  }
382
382
 
383
+ /* ---- img_combination 多图混排(比例逆向自网页版 .universal-card-img-combination) ---- */
384
+ .universal-card-img-combination { display: flex; width: 100%; justify-content: space-between; }
385
+ .universal-card-img-combination__item { min-width: 0; overflow: hidden; }
386
+ .universal-card-img-combination__item > .universal-card-image__host { display: block; width: 100%; height: 100%; object-fit: cover; }
387
+ .universal-card-img-combination__item > .universal-card-image__placeholder { width: 100%; height: 100%; }
388
+ /* double:左窄(32.4%, 24:33) + 右宽(67.6%, 49.33:33) */
389
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(odd) {
390
+ width: 32.4%; aspect-ratio: 24 / 33;
391
+ }
392
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(2n) {
393
+ width: calc(67.6% - 4px); aspect-ratio: 49.33 / 33;
394
+ }
395
+ /* triple:左大图(66.5%, 1:1) + 右列两张小图 */
396
+ .universal-card-img-combination--triple .universal-card-img-combination__left {
397
+ width: 66.5%; aspect-ratio: 1; margin-right: 1%;
398
+ }
399
+ .universal-card-img-combination--triple .universal-card-img-combination__right {
400
+ width: 33%; display: flex; flex-direction: column; justify-content: space-between;
401
+ }
402
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item {
403
+ width: 100%; aspect-ratio: 1;
404
+ }
405
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item:first-child {
406
+ margin-bottom: 1.5%;
407
+ }
408
+ /* bisect / trisect:等宽方格换行 */
409
+ .universal-card-img-combination--bisect { flex-wrap: wrap; gap: 4px; }
410
+ .universal-card-img-combination--bisect .universal-card-img-combination__item {
411
+ width: calc(50% - 2px); aspect-ratio: 1;
412
+ }
413
+ .universal-card-img-combination--trisect { flex-wrap: wrap; justify-content: flex-start; gap: 4px; }
414
+ .universal-card-img-combination--trisect .universal-card-img-combination__item {
415
+ width: calc(33.33% - 2.67px); aspect-ratio: 1;
416
+ }
417
+
418
+ /* ---- select_img 多图选择(对齐网页版 .universal-card-select-img) ---- */
419
+ .universal-card-select-img {
420
+ width: 100%; max-width: 100%; display: grid; grid-gap: 4px; box-sizing: border-box;
421
+ }
422
+ .universal-card-select-img--stretch { grid-template-columns: 1fr; }
423
+ .universal-card-select-img--bisect { grid-template-columns: 1fr 1fr; }
424
+ .universal-card-select-img--trisect { grid-template-columns: 1fr 1fr 1fr; }
425
+ .universal-card-select-img__item-wrapper { position: relative; border-radius: 6px; }
426
+ .universal-card-select-img__item-wrapper::after {
427
+ content: " "; position: absolute; inset: 0; box-sizing: border-box;
428
+ border: 1px solid var(--N300); border-radius: 6px; pointer-events: none; z-index: 1;
429
+ }
430
+ .universal-card-select-img__item-wrapper--selected::after { border: 2px solid var(--B500); }
431
+ .universal-card-select-img__item {
432
+ position: relative; overflow: hidden; box-sizing: border-box; border-radius: 6px;
433
+ cursor: pointer; width: 100%;
434
+ }
435
+ .universal-card-select-img__item--disabled { cursor: not-allowed; }
436
+ .universal-card-select-img__item--disabled::after {
437
+ content: " "; position: absolute; inset: 0; z-index: 1;
438
+ background-color: rgba(255, 255, 255, .5);
439
+ }
440
+ .universal-card-select-img__item > .universal-card-image__host {
441
+ display: block; width: 100%; height: 100%; object-fit: cover;
442
+ }
443
+ .universal-card-select-img__item > .universal-card-image__placeholder {
444
+ width: 100%; height: 100%;
445
+ }
446
+ /* 表单内复选/单选标记(真机仅 in-form 显示;表单外用选中描边表达状态) */
447
+ .universal-card-select-img__checkbox {
448
+ position: absolute; top: 0; right: 0; z-index: 2; padding: 5px 8px; cursor: pointer;
449
+ }
450
+ .universal-card-select-img__checkbox--disabled { cursor: not-allowed; }
451
+ .universal-card-select-img__checkbox-box {
452
+ display: inline-flex; align-items: center; justify-content: center;
453
+ width: 16px; height: 16px; box-sizing: border-box; border-radius: 50%;
454
+ background-color: rgba(0, 0, 0, .15); border: 1px solid #eff0f1;
455
+ box-shadow: 0 0 2px rgba(0, 0, 0, .1);
456
+ }
457
+ .universal-card-select-img__checkbox-box--square { border-radius: 4px; }
458
+ .universal-card-select-img__checkbox-box--checked {
459
+ background-color: var(--B500); border-color: var(--B500);
460
+ }
461
+ .universal-card-select-img__checkbox-box--checked::after {
462
+ content: ""; width: 3.5px; height: 7px;
463
+ border: solid #fff; border-width: 0 2px 2px 0;
464
+ transform: rotate(45deg) translateY(-1px);
465
+ }
466
+
383
467
  /* ---- 按钮 ---- */
384
468
  /* 飞书按钮文本是纯文本、单行:min-height 容纳偶发换行,文字超长省略号收尾。
385
469
  样式全集逆向自网页版 ud__button:三大族——outlined(描边)/ text(纯文字)/ filled(实心),
@@ -623,10 +707,32 @@
623
707
  }
624
708
  .universal-card-collapsible-panel__header::-webkit-details-marker { display: none; }
625
709
  .universal-card-collapsible-panel__header::marker { content: ''; }
710
+ /* header.vertical_align:center(默认)/ top / bottom */
711
+ .universal-card-collapsible-panel__header--valign-top { align-items: flex-start; }
712
+ .universal-card-collapsible-panel__header--valign-bottom { align-items: flex-end; }
713
+ /* header.width:fill(默认,撑满整行)/ auto(自适应内容宽)/
714
+ auto_when_fold(折叠自适应、展开撑满——真机语义) */
715
+ .universal-card-collapsible-panel__header--w-auto,
716
+ .universal-card-collapsible-panel__header--w-auto-when-fold {
717
+ width: max-content; max-width: 100%;
718
+ }
719
+ .universal-card-collapsible-panel[open] > .universal-card-collapsible-panel__header--w-auto-when-fold {
720
+ width: 100%;
721
+ }
626
722
  .universal-card-collapsible-panel__title {
627
723
  flex: 1; min-width: 0; font-size: 14px; line-height: 22px;
628
724
  word-break: break-word;
629
725
  }
726
+ /* title ↔ expanded_title:有 expanded_title 时,折叠展示 title、展开展示 expanded_title
727
+ (真机语义)。纯 CSS 由 details[open] 驱动,不学要 JS 同步。 */
728
+ .universal-card-collapsible-panel__title-text { display: block; }
729
+ .universal-card-collapsible-panel__title--has-expanded
730
+ .universal-card-collapsible-panel__title-text--expanded { display: none; }
731
+ .universal-card-collapsible-panel[open]
732
+ .universal-card-collapsible-panel__title--has-expanded .universal-card-collapsible-panel__title-text { display: none; }
733
+ .universal-card-collapsible-panel[open]
734
+ .universal-card-collapsible-panel__title--has-expanded
735
+ .universal-card-collapsible-panel__title-text--expanded { display: block; }
630
736
  /* follow_text:箭头紧贴文字(真机 __header-content--icon-follow),不拉满整行 */
631
737
  .universal-card-collapsible-panel__header--icon-follow {
632
738
  gap: 4px;
package/dist/index.cjs CHANGED
@@ -850,8 +850,13 @@ function resolveCornerRadius(property) {
850
850
  }
851
851
  if (direct != null) return sizeToPx(direct);
852
852
  const echo = property.cornerRadiusValue;
853
- if (echo && typeof echo.value === "number") return echo.value + "px";
854
- if (echo && typeof echo.value === "string" && echo.value.trim()) return sizeToPx(echo.value);
853
+ if (typeof echo === "number") return echo + "px";
854
+ if (typeof echo === "string" && echo.trim()) return sizeToPx(echo);
855
+ if (echo && typeof echo === "object") {
856
+ const value = echo.value;
857
+ if (typeof value === "number") return value + "px";
858
+ if (typeof value === "string" && value.trim()) return sizeToPx(value);
859
+ }
855
860
  return void 0;
856
861
  }
857
862
  function boxToStyle(box) {
@@ -1352,6 +1357,90 @@ var BASE_STYLES_CSS = `/**
1352
1357
  font-size: 12px;
1353
1358
  }
1354
1359
 
1360
+ /* ---- img_combination \u591A\u56FE\u6DF7\u6392\uFF08\u6BD4\u4F8B\u9006\u5411\u81EA\u7F51\u9875\u7248 .universal-card-img-combination\uFF09 ---- */
1361
+ .universal-card-img-combination { display: flex; width: 100%; justify-content: space-between; }
1362
+ .universal-card-img-combination__item { min-width: 0; overflow: hidden; }
1363
+ .universal-card-img-combination__item > .universal-card-image__host { display: block; width: 100%; height: 100%; object-fit: cover; }
1364
+ .universal-card-img-combination__item > .universal-card-image__placeholder { width: 100%; height: 100%; }
1365
+ /* double\uFF1A\u5DE6\u7A84(32.4%, 24:33) + \u53F3\u5BBD(67.6%, 49.33:33) */
1366
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(odd) {
1367
+ width: 32.4%; aspect-ratio: 24 / 33;
1368
+ }
1369
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(2n) {
1370
+ width: calc(67.6% - 4px); aspect-ratio: 49.33 / 33;
1371
+ }
1372
+ /* triple\uFF1A\u5DE6\u5927\u56FE(66.5%, 1:1) + \u53F3\u5217\u4E24\u5F20\u5C0F\u56FE */
1373
+ .universal-card-img-combination--triple .universal-card-img-combination__left {
1374
+ width: 66.5%; aspect-ratio: 1; margin-right: 1%;
1375
+ }
1376
+ .universal-card-img-combination--triple .universal-card-img-combination__right {
1377
+ width: 33%; display: flex; flex-direction: column; justify-content: space-between;
1378
+ }
1379
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item {
1380
+ width: 100%; aspect-ratio: 1;
1381
+ }
1382
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item:first-child {
1383
+ margin-bottom: 1.5%;
1384
+ }
1385
+ /* bisect / trisect\uFF1A\u7B49\u5BBD\u65B9\u683C\u6362\u884C */
1386
+ .universal-card-img-combination--bisect { flex-wrap: wrap; gap: 4px; }
1387
+ .universal-card-img-combination--bisect .universal-card-img-combination__item {
1388
+ width: calc(50% - 2px); aspect-ratio: 1;
1389
+ }
1390
+ .universal-card-img-combination--trisect { flex-wrap: wrap; justify-content: flex-start; gap: 4px; }
1391
+ .universal-card-img-combination--trisect .universal-card-img-combination__item {
1392
+ width: calc(33.33% - 2.67px); aspect-ratio: 1;
1393
+ }
1394
+
1395
+ /* ---- select_img \u591A\u56FE\u9009\u62E9\uFF08\u5BF9\u9F50\u7F51\u9875\u7248 .universal-card-select-img\uFF09 ---- */
1396
+ .universal-card-select-img {
1397
+ width: 100%; max-width: 100%; display: grid; grid-gap: 4px; box-sizing: border-box;
1398
+ }
1399
+ .universal-card-select-img--stretch { grid-template-columns: 1fr; }
1400
+ .universal-card-select-img--bisect { grid-template-columns: 1fr 1fr; }
1401
+ .universal-card-select-img--trisect { grid-template-columns: 1fr 1fr 1fr; }
1402
+ .universal-card-select-img__item-wrapper { position: relative; border-radius: 6px; }
1403
+ .universal-card-select-img__item-wrapper::after {
1404
+ content: " "; position: absolute; inset: 0; box-sizing: border-box;
1405
+ border: 1px solid var(--N300); border-radius: 6px; pointer-events: none; z-index: 1;
1406
+ }
1407
+ .universal-card-select-img__item-wrapper--selected::after { border: 2px solid var(--B500); }
1408
+ .universal-card-select-img__item {
1409
+ position: relative; overflow: hidden; box-sizing: border-box; border-radius: 6px;
1410
+ cursor: pointer; width: 100%;
1411
+ }
1412
+ .universal-card-select-img__item--disabled { cursor: not-allowed; }
1413
+ .universal-card-select-img__item--disabled::after {
1414
+ content: " "; position: absolute; inset: 0; z-index: 1;
1415
+ background-color: rgba(255, 255, 255, .5);
1416
+ }
1417
+ .universal-card-select-img__item > .universal-card-image__host {
1418
+ display: block; width: 100%; height: 100%; object-fit: cover;
1419
+ }
1420
+ .universal-card-select-img__item > .universal-card-image__placeholder {
1421
+ width: 100%; height: 100%;
1422
+ }
1423
+ /* \u8868\u5355\u5185\u590D\u9009/\u5355\u9009\u6807\u8BB0\uFF08\u771F\u673A\u4EC5 in-form \u663E\u793A\uFF1B\u8868\u5355\u5916\u7528\u9009\u4E2D\u63CF\u8FB9\u8868\u8FBE\u72B6\u6001\uFF09 */
1424
+ .universal-card-select-img__checkbox {
1425
+ position: absolute; top: 0; right: 0; z-index: 2; padding: 5px 8px; cursor: pointer;
1426
+ }
1427
+ .universal-card-select-img__checkbox--disabled { cursor: not-allowed; }
1428
+ .universal-card-select-img__checkbox-box {
1429
+ display: inline-flex; align-items: center; justify-content: center;
1430
+ width: 16px; height: 16px; box-sizing: border-box; border-radius: 50%;
1431
+ background-color: rgba(0, 0, 0, .15); border: 1px solid #eff0f1;
1432
+ box-shadow: 0 0 2px rgba(0, 0, 0, .1);
1433
+ }
1434
+ .universal-card-select-img__checkbox-box--square { border-radius: 4px; }
1435
+ .universal-card-select-img__checkbox-box--checked {
1436
+ background-color: var(--B500); border-color: var(--B500);
1437
+ }
1438
+ .universal-card-select-img__checkbox-box--checked::after {
1439
+ content: ""; width: 3.5px; height: 7px;
1440
+ border: solid #fff; border-width: 0 2px 2px 0;
1441
+ transform: rotate(45deg) translateY(-1px);
1442
+ }
1443
+
1355
1444
  /* ---- \u6309\u94AE ---- */
1356
1445
  /* \u98DE\u4E66\u6309\u94AE\u6587\u672C\u662F\u7EAF\u6587\u672C\u3001\u5355\u884C\uFF1Amin-height \u5BB9\u7EB3\u5076\u53D1\u6362\u884C\uFF0C\u6587\u5B57\u8D85\u957F\u7701\u7565\u53F7\u6536\u5C3E\u3002
1357
1446
  \u6837\u5F0F\u5168\u96C6\u9006\u5411\u81EA\u7F51\u9875\u7248 ud__button\uFF1A\u4E09\u5927\u65CF\u2014\u2014outlined\uFF08\u63CF\u8FB9\uFF09/ text\uFF08\u7EAF\u6587\u5B57\uFF09/ filled\uFF08\u5B9E\u5FC3\uFF09\uFF0C
@@ -1595,10 +1684,32 @@ var BASE_STYLES_CSS = `/**
1595
1684
  }
1596
1685
  .universal-card-collapsible-panel__header::-webkit-details-marker { display: none; }
1597
1686
  .universal-card-collapsible-panel__header::marker { content: ''; }
1687
+ /* header.vertical_align\uFF1Acenter\uFF08\u9ED8\u8BA4\uFF09/ top / bottom */
1688
+ .universal-card-collapsible-panel__header--valign-top { align-items: flex-start; }
1689
+ .universal-card-collapsible-panel__header--valign-bottom { align-items: flex-end; }
1690
+ /* header.width\uFF1Afill\uFF08\u9ED8\u8BA4\uFF0C\u6491\u6EE1\u6574\u884C\uFF09/ auto\uFF08\u81EA\u9002\u5E94\u5185\u5BB9\u5BBD\uFF09/
1691
+ auto_when_fold\uFF08\u6298\u53E0\u81EA\u9002\u5E94\u3001\u5C55\u5F00\u6491\u6EE1\u2014\u2014\u771F\u673A\u8BED\u4E49\uFF09 */
1692
+ .universal-card-collapsible-panel__header--w-auto,
1693
+ .universal-card-collapsible-panel__header--w-auto-when-fold {
1694
+ width: max-content; max-width: 100%;
1695
+ }
1696
+ .universal-card-collapsible-panel[open] > .universal-card-collapsible-panel__header--w-auto-when-fold {
1697
+ width: 100%;
1698
+ }
1598
1699
  .universal-card-collapsible-panel__title {
1599
1700
  flex: 1; min-width: 0; font-size: 14px; line-height: 22px;
1600
1701
  word-break: break-word;
1601
1702
  }
1703
+ /* title \u2194 expanded_title\uFF1A\u6709 expanded_title \u65F6\uFF0C\u6298\u53E0\u5C55\u793A title\u3001\u5C55\u5F00\u5C55\u793A expanded_title
1704
+ \uFF08\u771F\u673A\u8BED\u4E49\uFF09\u3002\u7EAF CSS \u7531 details[open] \u9A71\u52A8\uFF0C\u4E0D\u5B66\u8981 JS \u540C\u6B65\u3002 */
1705
+ .universal-card-collapsible-panel__title-text { display: block; }
1706
+ .universal-card-collapsible-panel__title--has-expanded
1707
+ .universal-card-collapsible-panel__title-text--expanded { display: none; }
1708
+ .universal-card-collapsible-panel[open]
1709
+ .universal-card-collapsible-panel__title--has-expanded .universal-card-collapsible-panel__title-text { display: none; }
1710
+ .universal-card-collapsible-panel[open]
1711
+ .universal-card-collapsible-panel__title--has-expanded
1712
+ .universal-card-collapsible-panel__title-text--expanded { display: block; }
1602
1713
  /* follow_text\uFF1A\u7BAD\u5934\u7D27\u8D34\u6587\u5B57\uFF08\u771F\u673A __header-content--icon-follow\uFF09\uFF0C\u4E0D\u62C9\u6EE1\u6574\u884C */
1603
1714
  .universal-card-collapsible-panel__header--icon-follow {
1604
1715
  gap: 4px;
@@ -5855,6 +5966,8 @@ function registerBuiltins() {
5855
5966
  }
5856
5967
  return h("div", { className: `${CLS}-div` }, content);
5857
5968
  });
5969
+ const H_ALIGN_MAP = { left: "flex-start", center: "center", right: "flex-end" };
5970
+ const V_ALIGN_MAP = { top: "flex-start", center: "center", bottom: "flex-end" };
5858
5971
  registerComponent("column_set", (el, ctx) => {
5859
5972
  const p = el.property || {};
5860
5973
  const gapRaw = p.horizontalSpacingValue?.value ?? p.horizontal_spacing;
@@ -5869,7 +5982,7 @@ function registerBuiltins() {
5869
5982
  columnGap: gap + "px"
5870
5983
  };
5871
5984
  if (hAlign) {
5872
- style.justifyContent = { left: "flex-start", center: "center", right: "flex-end" }[hAlign] || "flex-start";
5985
+ style.justifyContent = H_ALIGN_MAP[hAlign] || "flex-start";
5873
5986
  }
5874
5987
  applyOuterMargin(style, p.margin);
5875
5988
  const bg = propertyBackgroundColor(p);
@@ -5904,6 +6017,7 @@ function registerBuiltins() {
5904
6017
  flexShrink: 1,
5905
6018
  overflow: "hidden",
5906
6019
  display: "flex",
6020
+ // 缺省纵向排列;direction=horizontal 时改行,两个 align 落到哪个 flex 属性也随之交换
5907
6021
  flexDirection: "column"
5908
6022
  };
5909
6023
  const wv = p.widthValue;
@@ -5939,10 +6053,16 @@ function registerBuiltins() {
5939
6053
  style.flexBasis = "auto";
5940
6054
  }
5941
6055
  }
5942
- const vAlign = p.verticalAlign || p.vertical_align;
5943
- if (vAlign) {
5944
- style.justifyContent = { top: "flex-start", center: "center", bottom: "flex-end" }[vAlign] || "flex-start";
5945
- }
6056
+ const horizontalDir = p.direction === "horizontal";
6057
+ if (horizontalDir) style.flexDirection = "row";
6058
+ const hAlign = p.horizontal_align ?? p.horizontalAlign;
6059
+ const vAlign = p.vertical_align ?? p.verticalAlign;
6060
+ const justify = horizontalDir ? H_ALIGN_MAP[hAlign] : V_ALIGN_MAP[vAlign];
6061
+ const align = horizontalDir ? V_ALIGN_MAP[vAlign] : H_ALIGN_MAP[hAlign];
6062
+ if (justify) style.justifyContent = justify;
6063
+ if (align) style.alignItems = align;
6064
+ const hGap = p.horizontalSpacingValue?.value ?? p.horizontal_spacing ?? p.horizontalSpacing;
6065
+ if (hGap != null) style.columnGap = horizontalSpacingToPx(hGap) + "px";
5946
6066
  const vGap = p.verticalSpacingValue?.value ?? p.vertical_spacing;
5947
6067
  style.rowGap = (vGap != null ? verticalSpacingToPx(vGap) : 8) + "px";
5948
6068
  const bg = propertyBackgroundColor(p);
@@ -5988,6 +6108,123 @@ function registerBuiltins() {
5988
6108
  src ? h("img", { className: `${CLS}-image__host`, src, alt: altText, style: imgStyle }) : h("div", { className: `${CLS}-image__placeholder`, style: imgStyle }, altText || "[\u56FE\u7247]")
5989
6109
  );
5990
6110
  });
6111
+ const IMG_COMBINATION_LIMIT = {
6112
+ double: 2,
6113
+ triple: 3
6114
+ };
6115
+ function renderCombinationImage(item, cornerRadius, ctx) {
6116
+ const src = resolveImageSrc(item, ctx);
6117
+ const style = {
6118
+ width: "100%",
6119
+ height: "100%",
6120
+ objectFit: "cover",
6121
+ display: "block"
6122
+ };
6123
+ if (cornerRadius) style.borderRadius = cornerRadius;
6124
+ return src ? h("img", { className: `${CLS}-image__host`, src, alt: "", style }) : h("div", { className: `${CLS}-image__placeholder`, style }, "[\u56FE\u7247]");
6125
+ }
6126
+ registerComponent("img_combination", (el, ctx) => {
6127
+ const p = el.property || {};
6128
+ const rawList = Array.isArray(p.imgList) ? p.imgList : Array.isArray(p.img_list) ? p.img_list : [];
6129
+ if (rawList.length === 0) return null;
6130
+ const mode = String(p.combinationMode ?? p.combination_mode ?? "double");
6131
+ const limit = IMG_COMBINATION_LIMIT[mode];
6132
+ const list = (limit ? rawList.slice(0, limit) : rawList).map((item) => typeof item === "string" ? { img_key: item } : item).filter(isRecord);
6133
+ if (list.length === 0) return null;
6134
+ const cornerRadius = resolveCornerRadius(p);
6135
+ const containerStyle = {};
6136
+ applyOuterMargin(containerStyle, p.margin);
6137
+ const cls = `${CLS}-img-combination ${CLS}-img-combination--${mode}`;
6138
+ const itemNode = (item) => h(
6139
+ "div",
6140
+ { className: `${CLS}-img-combination__item` },
6141
+ renderCombinationImage(item, cornerRadius, ctx)
6142
+ );
6143
+ if (mode === "triple") {
6144
+ const [first, ...rest] = list;
6145
+ return h(
6146
+ "div",
6147
+ { className: cls, style: containerStyle },
6148
+ h(
6149
+ "div",
6150
+ { className: `${CLS}-img-combination__item ${CLS}-img-combination__left` },
6151
+ renderCombinationImage(first, cornerRadius, ctx)
6152
+ ),
6153
+ h("div", { className: `${CLS}-img-combination__right` }, rest.map(itemNode))
6154
+ );
6155
+ }
6156
+ return h("div", { className: cls, style: containerStyle }, list.map(itemNode));
6157
+ });
6158
+ function readSelectImgValues(p) {
6159
+ const raw = p.selectedValues ?? p.selected_values ?? p.value;
6160
+ let value = raw;
6161
+ if (typeof value === "string") {
6162
+ try {
6163
+ value = JSON.parse(value);
6164
+ } catch {
6165
+ return typeof value === "string" && value ? [value] : [];
6166
+ }
6167
+ }
6168
+ if (isRecord(value) && Array.isArray(value.value)) value = value.value;
6169
+ if (!Array.isArray(value)) return [];
6170
+ return value.filter((item) => typeof item === "string");
6171
+ }
6172
+ function renderSelectImgCheckbox(checked, disabled, multiple) {
6173
+ const boxClass = `${CLS}-select-img__checkbox-box` + (multiple ? ` ${CLS}-select-img__checkbox-box--square` : "") + (checked ? ` ${CLS}-select-img__checkbox-box--checked` : "");
6174
+ return h(
6175
+ "div",
6176
+ {
6177
+ className: `${CLS}-select-img__checkbox` + (disabled ? ` ${CLS}-select-img__checkbox--disabled` : "")
6178
+ },
6179
+ h("span", { className: boxClass })
6180
+ );
6181
+ }
6182
+ registerComponent("select_img", (el, ctx) => {
6183
+ const p = el.property || {};
6184
+ const options = Array.isArray(p.options) ? p.options : [];
6185
+ if (options.length === 0) return null;
6186
+ const layout = String(p.layout ?? "bisect");
6187
+ const aspectRatio = String(p.aspectRatio ?? p.aspect_ratio ?? "16:9").replace(":", "/");
6188
+ const multiple = p.multiSelect === true || p.multi_select === true;
6189
+ const disabledAll = p.disabled === true;
6190
+ const selected = readSelectImgValues(p);
6191
+ const containerStyle = {};
6192
+ applyOuterMargin(containerStyle, p.margin);
6193
+ const items = options.map((rawOption) => {
6194
+ const option = isRecord(rawOption) ? rawOption : { img_key: rawOption };
6195
+ const value = typeof option.value === "string" ? option.value : void 0;
6196
+ const isSelected = value !== void 0 && selected.includes(value);
6197
+ const disabled = disabledAll || option.disabled === true;
6198
+ const tip = disabled ? extractText(option.disabled_tips ?? option.disabledTips) : extractText(option.hover_tips ?? option.hoverTips);
6199
+ const src = resolveImageSrc(option, ctx);
6200
+ const wrapperClass = `${CLS}-select-img__item-wrapper` + (isSelected ? ` ${CLS}-select-img__item-wrapper--selected` : "") + (disabled ? ` ${CLS}-select-img__item-wrapper--disabled` : "");
6201
+ const itemClass = `${CLS}-select-img__item` + (disabled ? ` ${CLS}-select-img__item--disabled` : "");
6202
+ return h(
6203
+ "div",
6204
+ { className: wrapperClass },
6205
+ h(
6206
+ "div",
6207
+ { className: itemClass, style: { aspectRatio }, title: tip || void 0 },
6208
+ ctx.inForm ? renderSelectImgCheckbox(isSelected, disabled, multiple) : null,
6209
+ src ? h("img", {
6210
+ className: `${CLS}-image__host`,
6211
+ src,
6212
+ alt: "",
6213
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
6214
+ }) : h(
6215
+ "div",
6216
+ { className: `${CLS}-image__placeholder`, style: { width: "100%", height: "100%" } },
6217
+ "[\u56FE\u7247]"
6218
+ )
6219
+ )
6220
+ );
6221
+ });
6222
+ return h(
6223
+ "div",
6224
+ { className: `${CLS}-select-img ${CLS}-select-img--${layout}`, style: containerStyle },
6225
+ items
6226
+ );
6227
+ });
5991
6228
  registerComponent("video", (el, ctx) => {
5992
6229
  const p = el.property || {};
5993
6230
  const src = resolveMediaSrc(p, "video", ctx);
@@ -6156,10 +6393,8 @@ function registerBuiltins() {
6156
6393
  if (horizontalDir) style.flexDirection = "row";
6157
6394
  const hAlign = p.horizontal_align ?? p.horizontalAlign;
6158
6395
  const vAlign = p.vertical_align ?? p.verticalAlign;
6159
- const H_ALIGN = { left: "flex-start", center: "center", right: "flex-end" };
6160
- const V_ALIGN = { top: "flex-start", center: "center", bottom: "flex-end" };
6161
- const justify = horizontalDir ? H_ALIGN[hAlign] : V_ALIGN[vAlign];
6162
- const align = horizontalDir ? V_ALIGN[vAlign] : H_ALIGN[hAlign];
6396
+ const justify = horizontalDir ? H_ALIGN_MAP[hAlign] : V_ALIGN_MAP[vAlign];
6397
+ const align = horizontalDir ? V_ALIGN_MAP[vAlign] : H_ALIGN_MAP[hAlign];
6163
6398
  if (justify) style.justifyContent = justify;
6164
6399
  if (align) style.alignItems = align;
6165
6400
  const hGap = p.horizontalSpacingValue?.value ?? p.horizontal_spacing ?? p.horizontalSpacing;
@@ -6186,7 +6421,7 @@ function registerBuiltins() {
6186
6421
  registerComponent("collapsible_panel", (el, ctx) => {
6187
6422
  const p = el.property || {};
6188
6423
  const hdr = p.header && typeof p.header === "object" ? p.header : {};
6189
- const expanded = p.expanded !== false;
6424
+ const expanded = p.expanded === true;
6190
6425
  const style = { width: "100%" };
6191
6426
  const bgRaw = p.background_color ?? p.backgroundColor;
6192
6427
  const bgCustom = propertyBackgroundColor(p);
@@ -6205,12 +6440,36 @@ function registerBuiltins() {
6205
6440
  } else {
6206
6441
  style.borderRadius = "8px";
6207
6442
  }
6443
+ const headerBottom = String(hdr.position ?? "top") === "bottom";
6444
+ if (headerBottom) {
6445
+ style.display = "flex";
6446
+ style.flexDirection = "column";
6447
+ }
6208
6448
  applyOuterMargin(style, p.margin);
6209
6449
  const headerStyle = {};
6210
6450
  const hpad = typeof hdr.padding === "string" ? hdr.padding : boxToStyle(hdr.padding);
6211
6451
  if (hpad) headerStyle.padding = hpad;
6212
- const title = extractPlainText(hdr.title) || extractText(hdr.title);
6213
- const titleVNode = h("div", { className: `${CLS}-collapsible-panel__title` }, title);
6452
+ const hbg = resolveColor(hdr.background_color ?? hdr.backgroundColor);
6453
+ if (hbg) headerStyle.background = hbg;
6454
+ if (headerBottom) headerStyle.order = "2";
6455
+ const titleLeaf = (raw, className) => {
6456
+ if (!raw || typeof raw !== "object") return null;
6457
+ const t = raw;
6458
+ if (typeof t.tag !== "string") return null;
6459
+ const property = t.property && typeof t.property === "object" ? t.property : t;
6460
+ const node = renderHeaderTextLeaf({ tag: t.tag, property }, ctx, className);
6461
+ if (node) return node;
6462
+ const text = extractPlainText(raw) || extractText(raw);
6463
+ return text ? h("div", { className }, text) : null;
6464
+ };
6465
+ const primaryTitle = titleLeaf(hdr.title, `${CLS}-collapsible-panel__title-text`);
6466
+ const expandedTitleRaw = hdr.expanded_title ?? hdr.expandedTitle;
6467
+ const expandedTitle = titleLeaf(
6468
+ expandedTitleRaw,
6469
+ `${CLS}-collapsible-panel__title-text ${CLS}-collapsible-panel__title-text--expanded`
6470
+ );
6471
+ const titleCls = `${CLS}-collapsible-panel__title` + (expandedTitle ? ` ${CLS}-collapsible-panel__title--has-expanded` : "");
6472
+ const titleVNode = h("div", { className: titleCls }, [primaryTitle, expandedTitle].filter(Boolean));
6214
6473
  let iconVNode = null;
6215
6474
  const iconRaw = hdr.icon;
6216
6475
  const openAngle = Number(hdr.icon_expanded_angle ?? hdr.iconExpandedAngle ?? -180);
@@ -6226,11 +6485,38 @@ function registerBuiltins() {
6226
6485
  const iconFollow = iconPos === "follow_text" || iconPos === "followText";
6227
6486
  const headerChildren = iconLeft ? [iconVNode, titleVNode] : [titleVNode, iconVNode];
6228
6487
  const summaryStyle = { ...headerStyle, "--panel-icon-angle": `${openAngle}deg` };
6229
- const headerCls = `${CLS}-collapsible-panel__header` + (iconFollow ? ` ${CLS}-collapsible-panel__header--icon-follow` : "");
6488
+ const headerWidth = String(hdr.width ?? hdr.headerWidth ?? "fill");
6489
+ const headerVAlign = String(hdr.vertical_align ?? hdr.verticalAlign ?? "center");
6490
+ const headerCls = `${CLS}-collapsible-panel__header` + (iconFollow ? ` ${CLS}-collapsible-panel__header--icon-follow` : "") + (headerWidth === "auto" ? ` ${CLS}-collapsible-panel__header--w-auto` : "") + (headerWidth === "auto_when_fold" ? ` ${CLS}-collapsible-panel__header--w-auto-when-fold` : "") + (headerVAlign === "top" ? ` ${CLS}-collapsible-panel__header--valign-top` : "") + (headerVAlign === "bottom" ? ` ${CLS}-collapsible-panel__header--valign-bottom` : "");
6230
6491
  const summary = h("summary", { className: headerCls, style: summaryStyle }, headerChildren);
6231
6492
  const contentStyle = {};
6232
6493
  const cpad = typeof p.padding === "string" ? p.padding : boxToStyle(p.padding);
6233
6494
  if (cpad) contentStyle.padding = cpad;
6495
+ if (headerBottom) contentStyle.order = "1";
6496
+ const direction = String(p.direction ?? "vertical");
6497
+ const isRow = direction === "horizontal";
6498
+ const hSpacing = p.horizontal_spacing ?? p.horizontalSpacing;
6499
+ const vSpacing = p.vertical_spacing ?? p.verticalSpacing;
6500
+ const hAlign = p.horizontal_align ?? p.horizontalAlign;
6501
+ const vAlign = p.vertical_align ?? p.verticalAlign;
6502
+ if (isRow || hSpacing != null || vSpacing != null || hAlign != null || vAlign != null) {
6503
+ contentStyle.display = "flex";
6504
+ contentStyle.flexDirection = isRow ? "row" : "column";
6505
+ const gapX = hSpacing != null ? horizontalSpacingToPx(hSpacing) : isRow ? 8 : 0;
6506
+ const gapY = vSpacing != null ? verticalSpacingToPx(vSpacing) : 0;
6507
+ if (gapX) contentStyle.columnGap = gapX + "px";
6508
+ if (gapY) contentStyle.rowGap = gapY + "px";
6509
+ const hVal = hAlign ? H_ALIGN_MAP[hAlign] : void 0;
6510
+ const vVal = vAlign ? V_ALIGN_MAP[vAlign] : void 0;
6511
+ if (hVal) {
6512
+ if (isRow) contentStyle.justifyContent = hVal;
6513
+ else contentStyle.alignItems = hVal;
6514
+ }
6515
+ if (vVal) {
6516
+ if (isRow) contentStyle.alignItems = vVal;
6517
+ else contentStyle.justifyContent = vVal;
6518
+ }
6519
+ }
6234
6520
  const content = h(
6235
6521
  "div",
6236
6522
  { className: `${CLS}-collapsible-panel__content`, style: contentStyle },
@@ -6363,7 +6649,7 @@ function registerBuiltins() {
6363
6649
  );
6364
6650
  });
6365
6651
  registerComponent("form", (el, ctx) => {
6366
- const children = renderElements(el.property?.elements, ctx);
6652
+ const children = renderElements(el.property?.elements, { ...ctx, inForm: true });
6367
6653
  const className = `${CLS}-form`;
6368
6654
  if (!ctx.options.interactive) return h("div", { className }, children);
6369
6655
  const name = typeof el.property?.name === "string" ? el.property.name : "";
package/dist/index.d.cts CHANGED
@@ -464,6 +464,11 @@ interface RenderContext {
464
464
  * 客户端把 body paddingTop 从 12 改为 8。由 index.ts 在渲染 body 前注入。
465
465
  */
466
466
  bodyPaddingTop?: number;
467
+ /**
468
+ * 是否处于 form 容器内。真机 select_img 仅在表单内显示选中态 checkbox/radio,
469
+ * 表单外只是可点击的图片选项。form 渲染子元素时注入,不改变外部 ctx。
470
+ */
471
+ inForm?: boolean;
467
472
  }
468
473
  /** 组件渲染函数 */
469
474
  type ComponentFn = (el: CardElement, ctx: RenderContext) => VNode | null;
package/dist/index.d.ts CHANGED
@@ -464,6 +464,11 @@ interface RenderContext {
464
464
  * 客户端把 body paddingTop 从 12 改为 8。由 index.ts 在渲染 body 前注入。
465
465
  */
466
466
  bodyPaddingTop?: number;
467
+ /**
468
+ * 是否处于 form 容器内。真机 select_img 仅在表单内显示选中态 checkbox/radio,
469
+ * 表单外只是可点击的图片选项。form 渲染子元素时注入,不改变外部 ctx。
470
+ */
471
+ inForm?: boolean;
467
472
  }
468
473
  /** 组件渲染函数 */
469
474
  type ComponentFn = (el: CardElement, ctx: RenderContext) => VNode | null;
package/dist/index.js CHANGED
@@ -849,8 +849,13 @@ function resolveCornerRadius(property) {
849
849
  }
850
850
  if (direct != null) return sizeToPx(direct);
851
851
  const echo = property.cornerRadiusValue;
852
- if (echo && typeof echo.value === "number") return echo.value + "px";
853
- if (echo && typeof echo.value === "string" && echo.value.trim()) return sizeToPx(echo.value);
852
+ if (typeof echo === "number") return echo + "px";
853
+ if (typeof echo === "string" && echo.trim()) return sizeToPx(echo);
854
+ if (echo && typeof echo === "object") {
855
+ const value = echo.value;
856
+ if (typeof value === "number") return value + "px";
857
+ if (typeof value === "string" && value.trim()) return sizeToPx(value);
858
+ }
854
859
  return void 0;
855
860
  }
856
861
  function boxToStyle(box) {
@@ -1351,6 +1356,90 @@ var BASE_STYLES_CSS = `/**
1351
1356
  font-size: 12px;
1352
1357
  }
1353
1358
 
1359
+ /* ---- img_combination \u591A\u56FE\u6DF7\u6392\uFF08\u6BD4\u4F8B\u9006\u5411\u81EA\u7F51\u9875\u7248 .universal-card-img-combination\uFF09 ---- */
1360
+ .universal-card-img-combination { display: flex; width: 100%; justify-content: space-between; }
1361
+ .universal-card-img-combination__item { min-width: 0; overflow: hidden; }
1362
+ .universal-card-img-combination__item > .universal-card-image__host { display: block; width: 100%; height: 100%; object-fit: cover; }
1363
+ .universal-card-img-combination__item > .universal-card-image__placeholder { width: 100%; height: 100%; }
1364
+ /* double\uFF1A\u5DE6\u7A84(32.4%, 24:33) + \u53F3\u5BBD(67.6%, 49.33:33) */
1365
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(odd) {
1366
+ width: 32.4%; aspect-ratio: 24 / 33;
1367
+ }
1368
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(2n) {
1369
+ width: calc(67.6% - 4px); aspect-ratio: 49.33 / 33;
1370
+ }
1371
+ /* triple\uFF1A\u5DE6\u5927\u56FE(66.5%, 1:1) + \u53F3\u5217\u4E24\u5F20\u5C0F\u56FE */
1372
+ .universal-card-img-combination--triple .universal-card-img-combination__left {
1373
+ width: 66.5%; aspect-ratio: 1; margin-right: 1%;
1374
+ }
1375
+ .universal-card-img-combination--triple .universal-card-img-combination__right {
1376
+ width: 33%; display: flex; flex-direction: column; justify-content: space-between;
1377
+ }
1378
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item {
1379
+ width: 100%; aspect-ratio: 1;
1380
+ }
1381
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item:first-child {
1382
+ margin-bottom: 1.5%;
1383
+ }
1384
+ /* bisect / trisect\uFF1A\u7B49\u5BBD\u65B9\u683C\u6362\u884C */
1385
+ .universal-card-img-combination--bisect { flex-wrap: wrap; gap: 4px; }
1386
+ .universal-card-img-combination--bisect .universal-card-img-combination__item {
1387
+ width: calc(50% - 2px); aspect-ratio: 1;
1388
+ }
1389
+ .universal-card-img-combination--trisect { flex-wrap: wrap; justify-content: flex-start; gap: 4px; }
1390
+ .universal-card-img-combination--trisect .universal-card-img-combination__item {
1391
+ width: calc(33.33% - 2.67px); aspect-ratio: 1;
1392
+ }
1393
+
1394
+ /* ---- select_img \u591A\u56FE\u9009\u62E9\uFF08\u5BF9\u9F50\u7F51\u9875\u7248 .universal-card-select-img\uFF09 ---- */
1395
+ .universal-card-select-img {
1396
+ width: 100%; max-width: 100%; display: grid; grid-gap: 4px; box-sizing: border-box;
1397
+ }
1398
+ .universal-card-select-img--stretch { grid-template-columns: 1fr; }
1399
+ .universal-card-select-img--bisect { grid-template-columns: 1fr 1fr; }
1400
+ .universal-card-select-img--trisect { grid-template-columns: 1fr 1fr 1fr; }
1401
+ .universal-card-select-img__item-wrapper { position: relative; border-radius: 6px; }
1402
+ .universal-card-select-img__item-wrapper::after {
1403
+ content: " "; position: absolute; inset: 0; box-sizing: border-box;
1404
+ border: 1px solid var(--N300); border-radius: 6px; pointer-events: none; z-index: 1;
1405
+ }
1406
+ .universal-card-select-img__item-wrapper--selected::after { border: 2px solid var(--B500); }
1407
+ .universal-card-select-img__item {
1408
+ position: relative; overflow: hidden; box-sizing: border-box; border-radius: 6px;
1409
+ cursor: pointer; width: 100%;
1410
+ }
1411
+ .universal-card-select-img__item--disabled { cursor: not-allowed; }
1412
+ .universal-card-select-img__item--disabled::after {
1413
+ content: " "; position: absolute; inset: 0; z-index: 1;
1414
+ background-color: rgba(255, 255, 255, .5);
1415
+ }
1416
+ .universal-card-select-img__item > .universal-card-image__host {
1417
+ display: block; width: 100%; height: 100%; object-fit: cover;
1418
+ }
1419
+ .universal-card-select-img__item > .universal-card-image__placeholder {
1420
+ width: 100%; height: 100%;
1421
+ }
1422
+ /* \u8868\u5355\u5185\u590D\u9009/\u5355\u9009\u6807\u8BB0\uFF08\u771F\u673A\u4EC5 in-form \u663E\u793A\uFF1B\u8868\u5355\u5916\u7528\u9009\u4E2D\u63CF\u8FB9\u8868\u8FBE\u72B6\u6001\uFF09 */
1423
+ .universal-card-select-img__checkbox {
1424
+ position: absolute; top: 0; right: 0; z-index: 2; padding: 5px 8px; cursor: pointer;
1425
+ }
1426
+ .universal-card-select-img__checkbox--disabled { cursor: not-allowed; }
1427
+ .universal-card-select-img__checkbox-box {
1428
+ display: inline-flex; align-items: center; justify-content: center;
1429
+ width: 16px; height: 16px; box-sizing: border-box; border-radius: 50%;
1430
+ background-color: rgba(0, 0, 0, .15); border: 1px solid #eff0f1;
1431
+ box-shadow: 0 0 2px rgba(0, 0, 0, .1);
1432
+ }
1433
+ .universal-card-select-img__checkbox-box--square { border-radius: 4px; }
1434
+ .universal-card-select-img__checkbox-box--checked {
1435
+ background-color: var(--B500); border-color: var(--B500);
1436
+ }
1437
+ .universal-card-select-img__checkbox-box--checked::after {
1438
+ content: ""; width: 3.5px; height: 7px;
1439
+ border: solid #fff; border-width: 0 2px 2px 0;
1440
+ transform: rotate(45deg) translateY(-1px);
1441
+ }
1442
+
1354
1443
  /* ---- \u6309\u94AE ---- */
1355
1444
  /* \u98DE\u4E66\u6309\u94AE\u6587\u672C\u662F\u7EAF\u6587\u672C\u3001\u5355\u884C\uFF1Amin-height \u5BB9\u7EB3\u5076\u53D1\u6362\u884C\uFF0C\u6587\u5B57\u8D85\u957F\u7701\u7565\u53F7\u6536\u5C3E\u3002
1356
1445
  \u6837\u5F0F\u5168\u96C6\u9006\u5411\u81EA\u7F51\u9875\u7248 ud__button\uFF1A\u4E09\u5927\u65CF\u2014\u2014outlined\uFF08\u63CF\u8FB9\uFF09/ text\uFF08\u7EAF\u6587\u5B57\uFF09/ filled\uFF08\u5B9E\u5FC3\uFF09\uFF0C
@@ -1594,10 +1683,32 @@ var BASE_STYLES_CSS = `/**
1594
1683
  }
1595
1684
  .universal-card-collapsible-panel__header::-webkit-details-marker { display: none; }
1596
1685
  .universal-card-collapsible-panel__header::marker { content: ''; }
1686
+ /* header.vertical_align\uFF1Acenter\uFF08\u9ED8\u8BA4\uFF09/ top / bottom */
1687
+ .universal-card-collapsible-panel__header--valign-top { align-items: flex-start; }
1688
+ .universal-card-collapsible-panel__header--valign-bottom { align-items: flex-end; }
1689
+ /* header.width\uFF1Afill\uFF08\u9ED8\u8BA4\uFF0C\u6491\u6EE1\u6574\u884C\uFF09/ auto\uFF08\u81EA\u9002\u5E94\u5185\u5BB9\u5BBD\uFF09/
1690
+ auto_when_fold\uFF08\u6298\u53E0\u81EA\u9002\u5E94\u3001\u5C55\u5F00\u6491\u6EE1\u2014\u2014\u771F\u673A\u8BED\u4E49\uFF09 */
1691
+ .universal-card-collapsible-panel__header--w-auto,
1692
+ .universal-card-collapsible-panel__header--w-auto-when-fold {
1693
+ width: max-content; max-width: 100%;
1694
+ }
1695
+ .universal-card-collapsible-panel[open] > .universal-card-collapsible-panel__header--w-auto-when-fold {
1696
+ width: 100%;
1697
+ }
1597
1698
  .universal-card-collapsible-panel__title {
1598
1699
  flex: 1; min-width: 0; font-size: 14px; line-height: 22px;
1599
1700
  word-break: break-word;
1600
1701
  }
1702
+ /* title \u2194 expanded_title\uFF1A\u6709 expanded_title \u65F6\uFF0C\u6298\u53E0\u5C55\u793A title\u3001\u5C55\u5F00\u5C55\u793A expanded_title
1703
+ \uFF08\u771F\u673A\u8BED\u4E49\uFF09\u3002\u7EAF CSS \u7531 details[open] \u9A71\u52A8\uFF0C\u4E0D\u5B66\u8981 JS \u540C\u6B65\u3002 */
1704
+ .universal-card-collapsible-panel__title-text { display: block; }
1705
+ .universal-card-collapsible-panel__title--has-expanded
1706
+ .universal-card-collapsible-panel__title-text--expanded { display: none; }
1707
+ .universal-card-collapsible-panel[open]
1708
+ .universal-card-collapsible-panel__title--has-expanded .universal-card-collapsible-panel__title-text { display: none; }
1709
+ .universal-card-collapsible-panel[open]
1710
+ .universal-card-collapsible-panel__title--has-expanded
1711
+ .universal-card-collapsible-panel__title-text--expanded { display: block; }
1601
1712
  /* follow_text\uFF1A\u7BAD\u5934\u7D27\u8D34\u6587\u5B57\uFF08\u771F\u673A __header-content--icon-follow\uFF09\uFF0C\u4E0D\u62C9\u6EE1\u6574\u884C */
1602
1713
  .universal-card-collapsible-panel__header--icon-follow {
1603
1714
  gap: 4px;
@@ -5854,6 +5965,8 @@ function registerBuiltins() {
5854
5965
  }
5855
5966
  return h("div", { className: `${CLS}-div` }, content);
5856
5967
  });
5968
+ const H_ALIGN_MAP = { left: "flex-start", center: "center", right: "flex-end" };
5969
+ const V_ALIGN_MAP = { top: "flex-start", center: "center", bottom: "flex-end" };
5857
5970
  registerComponent("column_set", (el, ctx) => {
5858
5971
  const p = el.property || {};
5859
5972
  const gapRaw = p.horizontalSpacingValue?.value ?? p.horizontal_spacing;
@@ -5868,7 +5981,7 @@ function registerBuiltins() {
5868
5981
  columnGap: gap + "px"
5869
5982
  };
5870
5983
  if (hAlign) {
5871
- style.justifyContent = { left: "flex-start", center: "center", right: "flex-end" }[hAlign] || "flex-start";
5984
+ style.justifyContent = H_ALIGN_MAP[hAlign] || "flex-start";
5872
5985
  }
5873
5986
  applyOuterMargin(style, p.margin);
5874
5987
  const bg = propertyBackgroundColor(p);
@@ -5903,6 +6016,7 @@ function registerBuiltins() {
5903
6016
  flexShrink: 1,
5904
6017
  overflow: "hidden",
5905
6018
  display: "flex",
6019
+ // 缺省纵向排列;direction=horizontal 时改行,两个 align 落到哪个 flex 属性也随之交换
5906
6020
  flexDirection: "column"
5907
6021
  };
5908
6022
  const wv = p.widthValue;
@@ -5938,10 +6052,16 @@ function registerBuiltins() {
5938
6052
  style.flexBasis = "auto";
5939
6053
  }
5940
6054
  }
5941
- const vAlign = p.verticalAlign || p.vertical_align;
5942
- if (vAlign) {
5943
- style.justifyContent = { top: "flex-start", center: "center", bottom: "flex-end" }[vAlign] || "flex-start";
5944
- }
6055
+ const horizontalDir = p.direction === "horizontal";
6056
+ if (horizontalDir) style.flexDirection = "row";
6057
+ const hAlign = p.horizontal_align ?? p.horizontalAlign;
6058
+ const vAlign = p.vertical_align ?? p.verticalAlign;
6059
+ const justify = horizontalDir ? H_ALIGN_MAP[hAlign] : V_ALIGN_MAP[vAlign];
6060
+ const align = horizontalDir ? V_ALIGN_MAP[vAlign] : H_ALIGN_MAP[hAlign];
6061
+ if (justify) style.justifyContent = justify;
6062
+ if (align) style.alignItems = align;
6063
+ const hGap = p.horizontalSpacingValue?.value ?? p.horizontal_spacing ?? p.horizontalSpacing;
6064
+ if (hGap != null) style.columnGap = horizontalSpacingToPx(hGap) + "px";
5945
6065
  const vGap = p.verticalSpacingValue?.value ?? p.vertical_spacing;
5946
6066
  style.rowGap = (vGap != null ? verticalSpacingToPx(vGap) : 8) + "px";
5947
6067
  const bg = propertyBackgroundColor(p);
@@ -5987,6 +6107,123 @@ function registerBuiltins() {
5987
6107
  src ? h("img", { className: `${CLS}-image__host`, src, alt: altText, style: imgStyle }) : h("div", { className: `${CLS}-image__placeholder`, style: imgStyle }, altText || "[\u56FE\u7247]")
5988
6108
  );
5989
6109
  });
6110
+ const IMG_COMBINATION_LIMIT = {
6111
+ double: 2,
6112
+ triple: 3
6113
+ };
6114
+ function renderCombinationImage(item, cornerRadius, ctx) {
6115
+ const src = resolveImageSrc(item, ctx);
6116
+ const style = {
6117
+ width: "100%",
6118
+ height: "100%",
6119
+ objectFit: "cover",
6120
+ display: "block"
6121
+ };
6122
+ if (cornerRadius) style.borderRadius = cornerRadius;
6123
+ return src ? h("img", { className: `${CLS}-image__host`, src, alt: "", style }) : h("div", { className: `${CLS}-image__placeholder`, style }, "[\u56FE\u7247]");
6124
+ }
6125
+ registerComponent("img_combination", (el, ctx) => {
6126
+ const p = el.property || {};
6127
+ const rawList = Array.isArray(p.imgList) ? p.imgList : Array.isArray(p.img_list) ? p.img_list : [];
6128
+ if (rawList.length === 0) return null;
6129
+ const mode = String(p.combinationMode ?? p.combination_mode ?? "double");
6130
+ const limit = IMG_COMBINATION_LIMIT[mode];
6131
+ const list = (limit ? rawList.slice(0, limit) : rawList).map((item) => typeof item === "string" ? { img_key: item } : item).filter(isRecord);
6132
+ if (list.length === 0) return null;
6133
+ const cornerRadius = resolveCornerRadius(p);
6134
+ const containerStyle = {};
6135
+ applyOuterMargin(containerStyle, p.margin);
6136
+ const cls = `${CLS}-img-combination ${CLS}-img-combination--${mode}`;
6137
+ const itemNode = (item) => h(
6138
+ "div",
6139
+ { className: `${CLS}-img-combination__item` },
6140
+ renderCombinationImage(item, cornerRadius, ctx)
6141
+ );
6142
+ if (mode === "triple") {
6143
+ const [first, ...rest] = list;
6144
+ return h(
6145
+ "div",
6146
+ { className: cls, style: containerStyle },
6147
+ h(
6148
+ "div",
6149
+ { className: `${CLS}-img-combination__item ${CLS}-img-combination__left` },
6150
+ renderCombinationImage(first, cornerRadius, ctx)
6151
+ ),
6152
+ h("div", { className: `${CLS}-img-combination__right` }, rest.map(itemNode))
6153
+ );
6154
+ }
6155
+ return h("div", { className: cls, style: containerStyle }, list.map(itemNode));
6156
+ });
6157
+ function readSelectImgValues(p) {
6158
+ const raw = p.selectedValues ?? p.selected_values ?? p.value;
6159
+ let value = raw;
6160
+ if (typeof value === "string") {
6161
+ try {
6162
+ value = JSON.parse(value);
6163
+ } catch {
6164
+ return typeof value === "string" && value ? [value] : [];
6165
+ }
6166
+ }
6167
+ if (isRecord(value) && Array.isArray(value.value)) value = value.value;
6168
+ if (!Array.isArray(value)) return [];
6169
+ return value.filter((item) => typeof item === "string");
6170
+ }
6171
+ function renderSelectImgCheckbox(checked, disabled, multiple) {
6172
+ const boxClass = `${CLS}-select-img__checkbox-box` + (multiple ? ` ${CLS}-select-img__checkbox-box--square` : "") + (checked ? ` ${CLS}-select-img__checkbox-box--checked` : "");
6173
+ return h(
6174
+ "div",
6175
+ {
6176
+ className: `${CLS}-select-img__checkbox` + (disabled ? ` ${CLS}-select-img__checkbox--disabled` : "")
6177
+ },
6178
+ h("span", { className: boxClass })
6179
+ );
6180
+ }
6181
+ registerComponent("select_img", (el, ctx) => {
6182
+ const p = el.property || {};
6183
+ const options = Array.isArray(p.options) ? p.options : [];
6184
+ if (options.length === 0) return null;
6185
+ const layout = String(p.layout ?? "bisect");
6186
+ const aspectRatio = String(p.aspectRatio ?? p.aspect_ratio ?? "16:9").replace(":", "/");
6187
+ const multiple = p.multiSelect === true || p.multi_select === true;
6188
+ const disabledAll = p.disabled === true;
6189
+ const selected = readSelectImgValues(p);
6190
+ const containerStyle = {};
6191
+ applyOuterMargin(containerStyle, p.margin);
6192
+ const items = options.map((rawOption) => {
6193
+ const option = isRecord(rawOption) ? rawOption : { img_key: rawOption };
6194
+ const value = typeof option.value === "string" ? option.value : void 0;
6195
+ const isSelected = value !== void 0 && selected.includes(value);
6196
+ const disabled = disabledAll || option.disabled === true;
6197
+ const tip = disabled ? extractText(option.disabled_tips ?? option.disabledTips) : extractText(option.hover_tips ?? option.hoverTips);
6198
+ const src = resolveImageSrc(option, ctx);
6199
+ const wrapperClass = `${CLS}-select-img__item-wrapper` + (isSelected ? ` ${CLS}-select-img__item-wrapper--selected` : "") + (disabled ? ` ${CLS}-select-img__item-wrapper--disabled` : "");
6200
+ const itemClass = `${CLS}-select-img__item` + (disabled ? ` ${CLS}-select-img__item--disabled` : "");
6201
+ return h(
6202
+ "div",
6203
+ { className: wrapperClass },
6204
+ h(
6205
+ "div",
6206
+ { className: itemClass, style: { aspectRatio }, title: tip || void 0 },
6207
+ ctx.inForm ? renderSelectImgCheckbox(isSelected, disabled, multiple) : null,
6208
+ src ? h("img", {
6209
+ className: `${CLS}-image__host`,
6210
+ src,
6211
+ alt: "",
6212
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
6213
+ }) : h(
6214
+ "div",
6215
+ { className: `${CLS}-image__placeholder`, style: { width: "100%", height: "100%" } },
6216
+ "[\u56FE\u7247]"
6217
+ )
6218
+ )
6219
+ );
6220
+ });
6221
+ return h(
6222
+ "div",
6223
+ { className: `${CLS}-select-img ${CLS}-select-img--${layout}`, style: containerStyle },
6224
+ items
6225
+ );
6226
+ });
5990
6227
  registerComponent("video", (el, ctx) => {
5991
6228
  const p = el.property || {};
5992
6229
  const src = resolveMediaSrc(p, "video", ctx);
@@ -6155,10 +6392,8 @@ function registerBuiltins() {
6155
6392
  if (horizontalDir) style.flexDirection = "row";
6156
6393
  const hAlign = p.horizontal_align ?? p.horizontalAlign;
6157
6394
  const vAlign = p.vertical_align ?? p.verticalAlign;
6158
- const H_ALIGN = { left: "flex-start", center: "center", right: "flex-end" };
6159
- const V_ALIGN = { top: "flex-start", center: "center", bottom: "flex-end" };
6160
- const justify = horizontalDir ? H_ALIGN[hAlign] : V_ALIGN[vAlign];
6161
- const align = horizontalDir ? V_ALIGN[vAlign] : H_ALIGN[hAlign];
6395
+ const justify = horizontalDir ? H_ALIGN_MAP[hAlign] : V_ALIGN_MAP[vAlign];
6396
+ const align = horizontalDir ? V_ALIGN_MAP[vAlign] : H_ALIGN_MAP[hAlign];
6162
6397
  if (justify) style.justifyContent = justify;
6163
6398
  if (align) style.alignItems = align;
6164
6399
  const hGap = p.horizontalSpacingValue?.value ?? p.horizontal_spacing ?? p.horizontalSpacing;
@@ -6185,7 +6420,7 @@ function registerBuiltins() {
6185
6420
  registerComponent("collapsible_panel", (el, ctx) => {
6186
6421
  const p = el.property || {};
6187
6422
  const hdr = p.header && typeof p.header === "object" ? p.header : {};
6188
- const expanded = p.expanded !== false;
6423
+ const expanded = p.expanded === true;
6189
6424
  const style = { width: "100%" };
6190
6425
  const bgRaw = p.background_color ?? p.backgroundColor;
6191
6426
  const bgCustom = propertyBackgroundColor(p);
@@ -6204,12 +6439,36 @@ function registerBuiltins() {
6204
6439
  } else {
6205
6440
  style.borderRadius = "8px";
6206
6441
  }
6442
+ const headerBottom = String(hdr.position ?? "top") === "bottom";
6443
+ if (headerBottom) {
6444
+ style.display = "flex";
6445
+ style.flexDirection = "column";
6446
+ }
6207
6447
  applyOuterMargin(style, p.margin);
6208
6448
  const headerStyle = {};
6209
6449
  const hpad = typeof hdr.padding === "string" ? hdr.padding : boxToStyle(hdr.padding);
6210
6450
  if (hpad) headerStyle.padding = hpad;
6211
- const title = extractPlainText(hdr.title) || extractText(hdr.title);
6212
- const titleVNode = h("div", { className: `${CLS}-collapsible-panel__title` }, title);
6451
+ const hbg = resolveColor(hdr.background_color ?? hdr.backgroundColor);
6452
+ if (hbg) headerStyle.background = hbg;
6453
+ if (headerBottom) headerStyle.order = "2";
6454
+ const titleLeaf = (raw, className) => {
6455
+ if (!raw || typeof raw !== "object") return null;
6456
+ const t = raw;
6457
+ if (typeof t.tag !== "string") return null;
6458
+ const property = t.property && typeof t.property === "object" ? t.property : t;
6459
+ const node = renderHeaderTextLeaf({ tag: t.tag, property }, ctx, className);
6460
+ if (node) return node;
6461
+ const text = extractPlainText(raw) || extractText(raw);
6462
+ return text ? h("div", { className }, text) : null;
6463
+ };
6464
+ const primaryTitle = titleLeaf(hdr.title, `${CLS}-collapsible-panel__title-text`);
6465
+ const expandedTitleRaw = hdr.expanded_title ?? hdr.expandedTitle;
6466
+ const expandedTitle = titleLeaf(
6467
+ expandedTitleRaw,
6468
+ `${CLS}-collapsible-panel__title-text ${CLS}-collapsible-panel__title-text--expanded`
6469
+ );
6470
+ const titleCls = `${CLS}-collapsible-panel__title` + (expandedTitle ? ` ${CLS}-collapsible-panel__title--has-expanded` : "");
6471
+ const titleVNode = h("div", { className: titleCls }, [primaryTitle, expandedTitle].filter(Boolean));
6213
6472
  let iconVNode = null;
6214
6473
  const iconRaw = hdr.icon;
6215
6474
  const openAngle = Number(hdr.icon_expanded_angle ?? hdr.iconExpandedAngle ?? -180);
@@ -6225,11 +6484,38 @@ function registerBuiltins() {
6225
6484
  const iconFollow = iconPos === "follow_text" || iconPos === "followText";
6226
6485
  const headerChildren = iconLeft ? [iconVNode, titleVNode] : [titleVNode, iconVNode];
6227
6486
  const summaryStyle = { ...headerStyle, "--panel-icon-angle": `${openAngle}deg` };
6228
- const headerCls = `${CLS}-collapsible-panel__header` + (iconFollow ? ` ${CLS}-collapsible-panel__header--icon-follow` : "");
6487
+ const headerWidth = String(hdr.width ?? hdr.headerWidth ?? "fill");
6488
+ const headerVAlign = String(hdr.vertical_align ?? hdr.verticalAlign ?? "center");
6489
+ const headerCls = `${CLS}-collapsible-panel__header` + (iconFollow ? ` ${CLS}-collapsible-panel__header--icon-follow` : "") + (headerWidth === "auto" ? ` ${CLS}-collapsible-panel__header--w-auto` : "") + (headerWidth === "auto_when_fold" ? ` ${CLS}-collapsible-panel__header--w-auto-when-fold` : "") + (headerVAlign === "top" ? ` ${CLS}-collapsible-panel__header--valign-top` : "") + (headerVAlign === "bottom" ? ` ${CLS}-collapsible-panel__header--valign-bottom` : "");
6229
6490
  const summary = h("summary", { className: headerCls, style: summaryStyle }, headerChildren);
6230
6491
  const contentStyle = {};
6231
6492
  const cpad = typeof p.padding === "string" ? p.padding : boxToStyle(p.padding);
6232
6493
  if (cpad) contentStyle.padding = cpad;
6494
+ if (headerBottom) contentStyle.order = "1";
6495
+ const direction = String(p.direction ?? "vertical");
6496
+ const isRow = direction === "horizontal";
6497
+ const hSpacing = p.horizontal_spacing ?? p.horizontalSpacing;
6498
+ const vSpacing = p.vertical_spacing ?? p.verticalSpacing;
6499
+ const hAlign = p.horizontal_align ?? p.horizontalAlign;
6500
+ const vAlign = p.vertical_align ?? p.verticalAlign;
6501
+ if (isRow || hSpacing != null || vSpacing != null || hAlign != null || vAlign != null) {
6502
+ contentStyle.display = "flex";
6503
+ contentStyle.flexDirection = isRow ? "row" : "column";
6504
+ const gapX = hSpacing != null ? horizontalSpacingToPx(hSpacing) : isRow ? 8 : 0;
6505
+ const gapY = vSpacing != null ? verticalSpacingToPx(vSpacing) : 0;
6506
+ if (gapX) contentStyle.columnGap = gapX + "px";
6507
+ if (gapY) contentStyle.rowGap = gapY + "px";
6508
+ const hVal = hAlign ? H_ALIGN_MAP[hAlign] : void 0;
6509
+ const vVal = vAlign ? V_ALIGN_MAP[vAlign] : void 0;
6510
+ if (hVal) {
6511
+ if (isRow) contentStyle.justifyContent = hVal;
6512
+ else contentStyle.alignItems = hVal;
6513
+ }
6514
+ if (vVal) {
6515
+ if (isRow) contentStyle.alignItems = vVal;
6516
+ else contentStyle.justifyContent = vVal;
6517
+ }
6518
+ }
6233
6519
  const content = h(
6234
6520
  "div",
6235
6521
  { className: `${CLS}-collapsible-panel__content`, style: contentStyle },
@@ -6362,7 +6648,7 @@ function registerBuiltins() {
6362
6648
  );
6363
6649
  });
6364
6650
  registerComponent("form", (el, ctx) => {
6365
- const children = renderElements(el.property?.elements, ctx);
6651
+ const children = renderElements(el.property?.elements, { ...ctx, inForm: true });
6366
6652
  const className = `${CLS}-form`;
6367
6653
  if (!ctx.options.interactive) return h("div", { className }, children);
6368
6654
  const name = typeof el.property?.name === "string" ? el.property.name : "";
package/dist/styles.css CHANGED
@@ -380,6 +380,90 @@
380
380
  font-size: 12px;
381
381
  }
382
382
 
383
+ /* ---- img_combination 多图混排(比例逆向自网页版 .universal-card-img-combination) ---- */
384
+ .universal-card-img-combination { display: flex; width: 100%; justify-content: space-between; }
385
+ .universal-card-img-combination__item { min-width: 0; overflow: hidden; }
386
+ .universal-card-img-combination__item > .universal-card-image__host { display: block; width: 100%; height: 100%; object-fit: cover; }
387
+ .universal-card-img-combination__item > .universal-card-image__placeholder { width: 100%; height: 100%; }
388
+ /* double:左窄(32.4%, 24:33) + 右宽(67.6%, 49.33:33) */
389
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(odd) {
390
+ width: 32.4%; aspect-ratio: 24 / 33;
391
+ }
392
+ .universal-card-img-combination--double .universal-card-img-combination__item:nth-child(2n) {
393
+ width: calc(67.6% - 4px); aspect-ratio: 49.33 / 33;
394
+ }
395
+ /* triple:左大图(66.5%, 1:1) + 右列两张小图 */
396
+ .universal-card-img-combination--triple .universal-card-img-combination__left {
397
+ width: 66.5%; aspect-ratio: 1; margin-right: 1%;
398
+ }
399
+ .universal-card-img-combination--triple .universal-card-img-combination__right {
400
+ width: 33%; display: flex; flex-direction: column; justify-content: space-between;
401
+ }
402
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item {
403
+ width: 100%; aspect-ratio: 1;
404
+ }
405
+ .universal-card-img-combination--triple .universal-card-img-combination__right .universal-card-img-combination__item:first-child {
406
+ margin-bottom: 1.5%;
407
+ }
408
+ /* bisect / trisect:等宽方格换行 */
409
+ .universal-card-img-combination--bisect { flex-wrap: wrap; gap: 4px; }
410
+ .universal-card-img-combination--bisect .universal-card-img-combination__item {
411
+ width: calc(50% - 2px); aspect-ratio: 1;
412
+ }
413
+ .universal-card-img-combination--trisect { flex-wrap: wrap; justify-content: flex-start; gap: 4px; }
414
+ .universal-card-img-combination--trisect .universal-card-img-combination__item {
415
+ width: calc(33.33% - 2.67px); aspect-ratio: 1;
416
+ }
417
+
418
+ /* ---- select_img 多图选择(对齐网页版 .universal-card-select-img) ---- */
419
+ .universal-card-select-img {
420
+ width: 100%; max-width: 100%; display: grid; grid-gap: 4px; box-sizing: border-box;
421
+ }
422
+ .universal-card-select-img--stretch { grid-template-columns: 1fr; }
423
+ .universal-card-select-img--bisect { grid-template-columns: 1fr 1fr; }
424
+ .universal-card-select-img--trisect { grid-template-columns: 1fr 1fr 1fr; }
425
+ .universal-card-select-img__item-wrapper { position: relative; border-radius: 6px; }
426
+ .universal-card-select-img__item-wrapper::after {
427
+ content: " "; position: absolute; inset: 0; box-sizing: border-box;
428
+ border: 1px solid var(--N300); border-radius: 6px; pointer-events: none; z-index: 1;
429
+ }
430
+ .universal-card-select-img__item-wrapper--selected::after { border: 2px solid var(--B500); }
431
+ .universal-card-select-img__item {
432
+ position: relative; overflow: hidden; box-sizing: border-box; border-radius: 6px;
433
+ cursor: pointer; width: 100%;
434
+ }
435
+ .universal-card-select-img__item--disabled { cursor: not-allowed; }
436
+ .universal-card-select-img__item--disabled::after {
437
+ content: " "; position: absolute; inset: 0; z-index: 1;
438
+ background-color: rgba(255, 255, 255, .5);
439
+ }
440
+ .universal-card-select-img__item > .universal-card-image__host {
441
+ display: block; width: 100%; height: 100%; object-fit: cover;
442
+ }
443
+ .universal-card-select-img__item > .universal-card-image__placeholder {
444
+ width: 100%; height: 100%;
445
+ }
446
+ /* 表单内复选/单选标记(真机仅 in-form 显示;表单外用选中描边表达状态) */
447
+ .universal-card-select-img__checkbox {
448
+ position: absolute; top: 0; right: 0; z-index: 2; padding: 5px 8px; cursor: pointer;
449
+ }
450
+ .universal-card-select-img__checkbox--disabled { cursor: not-allowed; }
451
+ .universal-card-select-img__checkbox-box {
452
+ display: inline-flex; align-items: center; justify-content: center;
453
+ width: 16px; height: 16px; box-sizing: border-box; border-radius: 50%;
454
+ background-color: rgba(0, 0, 0, .15); border: 1px solid #eff0f1;
455
+ box-shadow: 0 0 2px rgba(0, 0, 0, .1);
456
+ }
457
+ .universal-card-select-img__checkbox-box--square { border-radius: 4px; }
458
+ .universal-card-select-img__checkbox-box--checked {
459
+ background-color: var(--B500); border-color: var(--B500);
460
+ }
461
+ .universal-card-select-img__checkbox-box--checked::after {
462
+ content: ""; width: 3.5px; height: 7px;
463
+ border: solid #fff; border-width: 0 2px 2px 0;
464
+ transform: rotate(45deg) translateY(-1px);
465
+ }
466
+
383
467
  /* ---- 按钮 ---- */
384
468
  /* 飞书按钮文本是纯文本、单行:min-height 容纳偶发换行,文字超长省略号收尾。
385
469
  样式全集逆向自网页版 ud__button:三大族——outlined(描边)/ text(纯文字)/ filled(实心),
@@ -623,10 +707,32 @@
623
707
  }
624
708
  .universal-card-collapsible-panel__header::-webkit-details-marker { display: none; }
625
709
  .universal-card-collapsible-panel__header::marker { content: ''; }
710
+ /* header.vertical_align:center(默认)/ top / bottom */
711
+ .universal-card-collapsible-panel__header--valign-top { align-items: flex-start; }
712
+ .universal-card-collapsible-panel__header--valign-bottom { align-items: flex-end; }
713
+ /* header.width:fill(默认,撑满整行)/ auto(自适应内容宽)/
714
+ auto_when_fold(折叠自适应、展开撑满——真机语义) */
715
+ .universal-card-collapsible-panel__header--w-auto,
716
+ .universal-card-collapsible-panel__header--w-auto-when-fold {
717
+ width: max-content; max-width: 100%;
718
+ }
719
+ .universal-card-collapsible-panel[open] > .universal-card-collapsible-panel__header--w-auto-when-fold {
720
+ width: 100%;
721
+ }
626
722
  .universal-card-collapsible-panel__title {
627
723
  flex: 1; min-width: 0; font-size: 14px; line-height: 22px;
628
724
  word-break: break-word;
629
725
  }
726
+ /* title ↔ expanded_title:有 expanded_title 时,折叠展示 title、展开展示 expanded_title
727
+ (真机语义)。纯 CSS 由 details[open] 驱动,不学要 JS 同步。 */
728
+ .universal-card-collapsible-panel__title-text { display: block; }
729
+ .universal-card-collapsible-panel__title--has-expanded
730
+ .universal-card-collapsible-panel__title-text--expanded { display: none; }
731
+ .universal-card-collapsible-panel[open]
732
+ .universal-card-collapsible-panel__title--has-expanded .universal-card-collapsible-panel__title-text { display: none; }
733
+ .universal-card-collapsible-panel[open]
734
+ .universal-card-collapsible-panel__title--has-expanded
735
+ .universal-card-collapsible-panel__title-text--expanded { display: block; }
630
736
  /* follow_text:箭头紧贴文字(真机 __header-content--icon-follow),不拉满整行 */
631
737
  .universal-card-collapsible-panel__header--icon-follow {
632
738
  gap: 4px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-feishu-card/renderer",
3
- "version": "0.3.6",
3
+ "version": "0.5.0",
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.20"
31
+ "@open-feishu-card/adapter": "0.4.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.3.5",