@open-feishu-card/renderer 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/all.css CHANGED
@@ -288,6 +288,13 @@
288
288
  word-break: break-word;
289
289
  vertical-align: middle;
290
290
  }
291
+ /* 前缀图标(markdown.icon):与首行文字垂直居中,右侧留 4px。
292
+ 容器是 flex row,图标作为首个 flex 子项参与排布。 */
293
+ .universal-card-markdown__icon {
294
+ display: inline-flex; align-items: center; justify-content: center;
295
+ flex-shrink: 0; margin-right: 4px;
296
+ height: 1.6em;
297
+ }
291
298
  .universal-card-markdown__children {
292
299
  flex: 1;
293
300
  min-width: 0;
@@ -443,6 +450,32 @@
443
450
  /* 非跳转按钮:保留外观,但不可点(默认光标、无 hover、不响应点击) */
444
451
  .universal-card-button--static { cursor: default; pointer-events: none; }
445
452
 
453
+ /* ---- overflow 折叠按钮组 ---- */
454
+ /* 真机是一个「…」图标按钮,点击弹出选项菜单。静态预览只画触发器(--static 不可点)。 */
455
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
456
+ .universal-card-overflow--static { display: inline-flex; }
457
+ .universal-card-overflow__summary {
458
+ list-style: none; cursor: pointer; display: inline-flex;
459
+ }
460
+ /* 去掉 <summary> 的浏览器默认三角标记 */
461
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
462
+ .universal-card-overflow__summary::marker { content: ''; }
463
+ /* 菜单:默认隐藏,<details open> 时浮在触发器下方 */
464
+ .universal-card-overflow__menu {
465
+ display: none;
466
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
467
+ min-width: 120px; padding: 4px 0;
468
+ background: #fff; border-radius: 6px;
469
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
470
+ }
471
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
472
+ .universal-card-overflow__item {
473
+ display: block; padding: 6px 12px;
474
+ font-size: 14px; line-height: 22px; color: #1f2329;
475
+ text-decoration: none; white-space: nowrap; cursor: pointer;
476
+ }
477
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
478
+
446
479
  /* ---- 表单(预览:仅透传,无表单语义) ---- */
447
480
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
448
481
  .universal-card-form > * { width: 100%; }
package/dist/index.cjs CHANGED
@@ -1258,6 +1258,13 @@ var BASE_STYLES_CSS = `/**
1258
1258
  word-break: break-word;
1259
1259
  vertical-align: middle;
1260
1260
  }
1261
+ /* \u524D\u7F00\u56FE\u6807\uFF08markdown.icon\uFF09\uFF1A\u4E0E\u9996\u884C\u6587\u5B57\u5782\u76F4\u5C45\u4E2D\uFF0C\u53F3\u4FA7\u7559 4px\u3002
1262
+ \u5BB9\u5668\u662F flex row\uFF0C\u56FE\u6807\u4F5C\u4E3A\u9996\u4E2A flex \u5B50\u9879\u53C2\u4E0E\u6392\u5E03\u3002 */
1263
+ .universal-card-markdown__icon {
1264
+ display: inline-flex; align-items: center; justify-content: center;
1265
+ flex-shrink: 0; margin-right: 4px;
1266
+ height: 1.6em;
1267
+ }
1261
1268
  .universal-card-markdown__children {
1262
1269
  flex: 1;
1263
1270
  min-width: 0;
@@ -1413,6 +1420,32 @@ var BASE_STYLES_CSS = `/**
1413
1420
  /* \u975E\u8DF3\u8F6C\u6309\u94AE\uFF1A\u4FDD\u7559\u5916\u89C2\uFF0C\u4F46\u4E0D\u53EF\u70B9\uFF08\u9ED8\u8BA4\u5149\u6807\u3001\u65E0 hover\u3001\u4E0D\u54CD\u5E94\u70B9\u51FB\uFF09 */
1414
1421
  .universal-card-button--static { cursor: default; pointer-events: none; }
1415
1422
 
1423
+ /* ---- overflow \u6298\u53E0\u6309\u94AE\u7EC4 ---- */
1424
+ /* \u771F\u673A\u662F\u4E00\u4E2A\u300C\u2026\u300D\u56FE\u6807\u6309\u94AE\uFF0C\u70B9\u51FB\u5F39\u51FA\u9009\u9879\u83DC\u5355\u3002\u9759\u6001\u9884\u89C8\u53EA\u753B\u89E6\u53D1\u5668\uFF08--static \u4E0D\u53EF\u70B9\uFF09\u3002 */
1425
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
1426
+ .universal-card-overflow--static { display: inline-flex; }
1427
+ .universal-card-overflow__summary {
1428
+ list-style: none; cursor: pointer; display: inline-flex;
1429
+ }
1430
+ /* \u53BB\u6389 <summary> \u7684\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0 */
1431
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
1432
+ .universal-card-overflow__summary::marker { content: ''; }
1433
+ /* \u83DC\u5355\uFF1A\u9ED8\u8BA4\u9690\u85CF\uFF0C<details open> \u65F6\u6D6E\u5728\u89E6\u53D1\u5668\u4E0B\u65B9 */
1434
+ .universal-card-overflow__menu {
1435
+ display: none;
1436
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
1437
+ min-width: 120px; padding: 4px 0;
1438
+ background: #fff; border-radius: 6px;
1439
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
1440
+ }
1441
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
1442
+ .universal-card-overflow__item {
1443
+ display: block; padding: 6px 12px;
1444
+ font-size: 14px; line-height: 22px; color: #1f2329;
1445
+ text-decoration: none; white-space: nowrap; cursor: pointer;
1446
+ }
1447
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
1448
+
1416
1449
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1417
1450
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1418
1451
  .universal-card-form > * { width: 100%; }
@@ -5699,15 +5732,50 @@ function registerBuiltins() {
5699
5732
  }
5700
5733
  const childCtx = { ...ctx, textMetrics: { fontSize, lineHeight } };
5701
5734
  const linesStyle = lineClampStyle(p.lines ?? p.textLines);
5735
+ const iconVNode = renderPrefixIcon(p.icon, ctx, fontSize);
5702
5736
  return h(
5703
5737
  "div",
5704
5738
  { className: `${CLS}-markdown`, style },
5739
+ iconVNode,
5705
5740
  h("div", {
5706
5741
  className: `${CLS}-markdown__children`,
5707
5742
  style: Object.keys(linesStyle).length ? linesStyle : void 0
5708
5743
  }, renderElements(p.elements, childCtx))
5709
5744
  );
5710
5745
  }
5746
+ function renderPrefixIcon(raw, ctx, fontSize) {
5747
+ if (!isRecord(raw)) return null;
5748
+ const inner = isRecord(raw.property) ? raw.property : raw;
5749
+ const tag = typeof raw.tag === "string" ? raw.tag : "standard_icon";
5750
+ const sizeProp = typeof inner.size === "string" ? inner.size : typeof raw.size === "string" ? raw.size : void 0;
5751
+ if (tag === "custom_icon") {
5752
+ const imgKey = typeof inner.img_key === "string" ? inner.img_key : typeof raw.img_key === "string" ? raw.img_key : void 0;
5753
+ if (!imgKey) return null;
5754
+ const src = resolveImageSrc({ img_key: imgKey }, ctx) ?? imgKey;
5755
+ const sizePx = sizeProp && /^\d/.test(sizeProp) ? Number(/^(\d+(?:\.\d+)?)/.exec(sizeProp)[1]) : fontSize;
5756
+ return h(
5757
+ "span",
5758
+ { className: `${CLS}-markdown__icon` },
5759
+ h("img", {
5760
+ src,
5761
+ alt: "",
5762
+ style: { width: sizePx + "px", height: sizePx + "px", objectFit: "contain" }
5763
+ })
5764
+ );
5765
+ }
5766
+ const token = typeof inner.token === "string" ? inner.token : "";
5767
+ if (!token) return null;
5768
+ const iconNode = renderIcon({
5769
+ property: {
5770
+ token,
5771
+ color: typeof inner.color === "string" ? inner.color : void 0,
5772
+ // 未显式给 size 时跟随文本字号,避免回落到固定 16px 与文字不搭
5773
+ size: sizeProp ?? fontSize + "px"
5774
+ }
5775
+ }, ctx);
5776
+ if (!iconNode) return null;
5777
+ return h("span", { className: `${CLS}-markdown__icon` }, iconNode);
5778
+ }
5711
5779
  registerComponent("markdown", MarkdownComponent);
5712
5780
  registerComponent("markdown_v1", MarkdownComponent);
5713
5781
  function lineClampStyle(lines) {
@@ -6035,8 +6103,9 @@ function registerBuiltins() {
6035
6103
  const tip = extractText(p.hoverTips);
6036
6104
  const borderNamed = p.border_color ?? p.borderColor;
6037
6105
  const borderCustom = adapter.resolveCustomBorderLight(p);
6038
- const borderColor = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
6039
- const hasBorder = borderColor != null || p.hasBorder === true || p.has_border === true;
6106
+ const borderColorResolved = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
6107
+ const hasBorder = p.hasBorder === true || p.has_border === true;
6108
+ const borderColor = hasBorder ? borderColorResolved : void 0;
6040
6109
  const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
6041
6110
  const style = {};
6042
6111
  const bg = propertyBackgroundColor(p);
@@ -6204,6 +6273,43 @@ function registerBuiltins() {
6204
6273
  };
6205
6274
  return fromList(p.actions) ?? fromList(p.behaviors) ?? (isRecord(p.value) ? p.value : null);
6206
6275
  }
6276
+ registerComponent("overflow", (el, ctx) => {
6277
+ const p = el.property || {};
6278
+ const disabled = p.disabled === true;
6279
+ const options = Array.isArray(p.options) ? p.options : [];
6280
+ const widthTok = p.width ?? p.widthValue?.value;
6281
+ const block = widthTok === "fill";
6282
+ const triggerIcon = renderIcon(
6283
+ { property: { token: "more_outlined", size: "14px" } },
6284
+ ctx
6285
+ );
6286
+ const triggerCls = `${CLS}-button ${CLS}-button--default ${CLS}-button--static ${CLS}-overflow__trigger` + (disabled ? ` ${CLS}-button--disabled` : "") + (block ? ` ${CLS}-button--block` : "");
6287
+ const trigger = triggerIcon ? h("span", { className: triggerCls }, h("span", { className: `${CLS}-button__icon` }, triggerIcon)) : h("span", { className: triggerCls }, "\xB7\xB7\xB7");
6288
+ const items = options.map((raw) => {
6289
+ const opt = isRecord(raw) ? raw : {};
6290
+ const textObj = opt.text;
6291
+ const label = (isRecord(textObj) && typeof textObj.content === "string" ? textObj.content : extractPlainText(textObj)) || extractPlainText(opt);
6292
+ const multi = isRecord(opt.multi_url) ? opt.multi_url : void 0;
6293
+ const href = resolveURL(multi?.url ?? opt.url);
6294
+ const itemCls = `${CLS}-overflow__item`;
6295
+ if (href && !disabled) {
6296
+ return h("a", { className: itemCls, href, target: "_blank", rel: "noopener" }, label);
6297
+ }
6298
+ return h("span", { className: itemCls }, label);
6299
+ });
6300
+ const style = {};
6301
+ if (p.margin) applyOuterMargin(style, p.margin);
6302
+ const styleProp = Object.keys(style).length ? { style } : {};
6303
+ if (!ctx.options.interactive) {
6304
+ return h("span", { className: `${CLS}-overflow ${CLS}-overflow--static`, ...styleProp }, trigger);
6305
+ }
6306
+ return h(
6307
+ "details",
6308
+ { className: `${CLS}-overflow`, ...styleProp },
6309
+ h("summary", { className: `${CLS}-overflow__summary` }, trigger),
6310
+ h("div", { className: `${CLS}-overflow__menu` }, items)
6311
+ );
6312
+ });
6207
6313
  registerComponent("form", (el, ctx) => {
6208
6314
  const children = renderElements(el.property?.elements, ctx);
6209
6315
  const className = `${CLS}-form`;
package/dist/index.js CHANGED
@@ -1257,6 +1257,13 @@ var BASE_STYLES_CSS = `/**
1257
1257
  word-break: break-word;
1258
1258
  vertical-align: middle;
1259
1259
  }
1260
+ /* \u524D\u7F00\u56FE\u6807\uFF08markdown.icon\uFF09\uFF1A\u4E0E\u9996\u884C\u6587\u5B57\u5782\u76F4\u5C45\u4E2D\uFF0C\u53F3\u4FA7\u7559 4px\u3002
1261
+ \u5BB9\u5668\u662F flex row\uFF0C\u56FE\u6807\u4F5C\u4E3A\u9996\u4E2A flex \u5B50\u9879\u53C2\u4E0E\u6392\u5E03\u3002 */
1262
+ .universal-card-markdown__icon {
1263
+ display: inline-flex; align-items: center; justify-content: center;
1264
+ flex-shrink: 0; margin-right: 4px;
1265
+ height: 1.6em;
1266
+ }
1260
1267
  .universal-card-markdown__children {
1261
1268
  flex: 1;
1262
1269
  min-width: 0;
@@ -1412,6 +1419,32 @@ var BASE_STYLES_CSS = `/**
1412
1419
  /* \u975E\u8DF3\u8F6C\u6309\u94AE\uFF1A\u4FDD\u7559\u5916\u89C2\uFF0C\u4F46\u4E0D\u53EF\u70B9\uFF08\u9ED8\u8BA4\u5149\u6807\u3001\u65E0 hover\u3001\u4E0D\u54CD\u5E94\u70B9\u51FB\uFF09 */
1413
1420
  .universal-card-button--static { cursor: default; pointer-events: none; }
1414
1421
 
1422
+ /* ---- overflow \u6298\u53E0\u6309\u94AE\u7EC4 ---- */
1423
+ /* \u771F\u673A\u662F\u4E00\u4E2A\u300C\u2026\u300D\u56FE\u6807\u6309\u94AE\uFF0C\u70B9\u51FB\u5F39\u51FA\u9009\u9879\u83DC\u5355\u3002\u9759\u6001\u9884\u89C8\u53EA\u753B\u89E6\u53D1\u5668\uFF08--static \u4E0D\u53EF\u70B9\uFF09\u3002 */
1424
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
1425
+ .universal-card-overflow--static { display: inline-flex; }
1426
+ .universal-card-overflow__summary {
1427
+ list-style: none; cursor: pointer; display: inline-flex;
1428
+ }
1429
+ /* \u53BB\u6389 <summary> \u7684\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0 */
1430
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
1431
+ .universal-card-overflow__summary::marker { content: ''; }
1432
+ /* \u83DC\u5355\uFF1A\u9ED8\u8BA4\u9690\u85CF\uFF0C<details open> \u65F6\u6D6E\u5728\u89E6\u53D1\u5668\u4E0B\u65B9 */
1433
+ .universal-card-overflow__menu {
1434
+ display: none;
1435
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
1436
+ min-width: 120px; padding: 4px 0;
1437
+ background: #fff; border-radius: 6px;
1438
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
1439
+ }
1440
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
1441
+ .universal-card-overflow__item {
1442
+ display: block; padding: 6px 12px;
1443
+ font-size: 14px; line-height: 22px; color: #1f2329;
1444
+ text-decoration: none; white-space: nowrap; cursor: pointer;
1445
+ }
1446
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
1447
+
1415
1448
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1416
1449
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1417
1450
  .universal-card-form > * { width: 100%; }
@@ -5698,15 +5731,50 @@ function registerBuiltins() {
5698
5731
  }
5699
5732
  const childCtx = { ...ctx, textMetrics: { fontSize, lineHeight } };
5700
5733
  const linesStyle = lineClampStyle(p.lines ?? p.textLines);
5734
+ const iconVNode = renderPrefixIcon(p.icon, ctx, fontSize);
5701
5735
  return h(
5702
5736
  "div",
5703
5737
  { className: `${CLS}-markdown`, style },
5738
+ iconVNode,
5704
5739
  h("div", {
5705
5740
  className: `${CLS}-markdown__children`,
5706
5741
  style: Object.keys(linesStyle).length ? linesStyle : void 0
5707
5742
  }, renderElements(p.elements, childCtx))
5708
5743
  );
5709
5744
  }
5745
+ function renderPrefixIcon(raw, ctx, fontSize) {
5746
+ if (!isRecord(raw)) return null;
5747
+ const inner = isRecord(raw.property) ? raw.property : raw;
5748
+ const tag = typeof raw.tag === "string" ? raw.tag : "standard_icon";
5749
+ const sizeProp = typeof inner.size === "string" ? inner.size : typeof raw.size === "string" ? raw.size : void 0;
5750
+ if (tag === "custom_icon") {
5751
+ const imgKey = typeof inner.img_key === "string" ? inner.img_key : typeof raw.img_key === "string" ? raw.img_key : void 0;
5752
+ if (!imgKey) return null;
5753
+ const src = resolveImageSrc({ img_key: imgKey }, ctx) ?? imgKey;
5754
+ const sizePx = sizeProp && /^\d/.test(sizeProp) ? Number(/^(\d+(?:\.\d+)?)/.exec(sizeProp)[1]) : fontSize;
5755
+ return h(
5756
+ "span",
5757
+ { className: `${CLS}-markdown__icon` },
5758
+ h("img", {
5759
+ src,
5760
+ alt: "",
5761
+ style: { width: sizePx + "px", height: sizePx + "px", objectFit: "contain" }
5762
+ })
5763
+ );
5764
+ }
5765
+ const token = typeof inner.token === "string" ? inner.token : "";
5766
+ if (!token) return null;
5767
+ const iconNode = renderIcon({
5768
+ property: {
5769
+ token,
5770
+ color: typeof inner.color === "string" ? inner.color : void 0,
5771
+ // 未显式给 size 时跟随文本字号,避免回落到固定 16px 与文字不搭
5772
+ size: sizeProp ?? fontSize + "px"
5773
+ }
5774
+ }, ctx);
5775
+ if (!iconNode) return null;
5776
+ return h("span", { className: `${CLS}-markdown__icon` }, iconNode);
5777
+ }
5710
5778
  registerComponent("markdown", MarkdownComponent);
5711
5779
  registerComponent("markdown_v1", MarkdownComponent);
5712
5780
  function lineClampStyle(lines) {
@@ -6034,8 +6102,9 @@ function registerBuiltins() {
6034
6102
  const tip = extractText(p.hoverTips);
6035
6103
  const borderNamed = p.border_color ?? p.borderColor;
6036
6104
  const borderCustom = resolveCustomBorderLight(p);
6037
- const borderColor = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
6038
- const hasBorder = borderColor != null || p.hasBorder === true || p.has_border === true;
6105
+ const borderColorResolved = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
6106
+ const hasBorder = p.hasBorder === true || p.has_border === true;
6107
+ const borderColor = hasBorder ? borderColorResolved : void 0;
6039
6108
  const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
6040
6109
  const style = {};
6041
6110
  const bg = propertyBackgroundColor(p);
@@ -6203,6 +6272,43 @@ function registerBuiltins() {
6203
6272
  };
6204
6273
  return fromList(p.actions) ?? fromList(p.behaviors) ?? (isRecord(p.value) ? p.value : null);
6205
6274
  }
6275
+ registerComponent("overflow", (el, ctx) => {
6276
+ const p = el.property || {};
6277
+ const disabled = p.disabled === true;
6278
+ const options = Array.isArray(p.options) ? p.options : [];
6279
+ const widthTok = p.width ?? p.widthValue?.value;
6280
+ const block = widthTok === "fill";
6281
+ const triggerIcon = renderIcon(
6282
+ { property: { token: "more_outlined", size: "14px" } },
6283
+ ctx
6284
+ );
6285
+ const triggerCls = `${CLS}-button ${CLS}-button--default ${CLS}-button--static ${CLS}-overflow__trigger` + (disabled ? ` ${CLS}-button--disabled` : "") + (block ? ` ${CLS}-button--block` : "");
6286
+ const trigger = triggerIcon ? h("span", { className: triggerCls }, h("span", { className: `${CLS}-button__icon` }, triggerIcon)) : h("span", { className: triggerCls }, "\xB7\xB7\xB7");
6287
+ const items = options.map((raw) => {
6288
+ const opt = isRecord(raw) ? raw : {};
6289
+ const textObj = opt.text;
6290
+ const label = (isRecord(textObj) && typeof textObj.content === "string" ? textObj.content : extractPlainText(textObj)) || extractPlainText(opt);
6291
+ const multi = isRecord(opt.multi_url) ? opt.multi_url : void 0;
6292
+ const href = resolveURL(multi?.url ?? opt.url);
6293
+ const itemCls = `${CLS}-overflow__item`;
6294
+ if (href && !disabled) {
6295
+ return h("a", { className: itemCls, href, target: "_blank", rel: "noopener" }, label);
6296
+ }
6297
+ return h("span", { className: itemCls }, label);
6298
+ });
6299
+ const style = {};
6300
+ if (p.margin) applyOuterMargin(style, p.margin);
6301
+ const styleProp = Object.keys(style).length ? { style } : {};
6302
+ if (!ctx.options.interactive) {
6303
+ return h("span", { className: `${CLS}-overflow ${CLS}-overflow--static`, ...styleProp }, trigger);
6304
+ }
6305
+ return h(
6306
+ "details",
6307
+ { className: `${CLS}-overflow`, ...styleProp },
6308
+ h("summary", { className: `${CLS}-overflow__summary` }, trigger),
6309
+ h("div", { className: `${CLS}-overflow__menu` }, items)
6310
+ );
6311
+ });
6206
6312
  registerComponent("form", (el, ctx) => {
6207
6313
  const children = renderElements(el.property?.elements, ctx);
6208
6314
  const className = `${CLS}-form`;
package/dist/styles.css CHANGED
@@ -288,6 +288,13 @@
288
288
  word-break: break-word;
289
289
  vertical-align: middle;
290
290
  }
291
+ /* 前缀图标(markdown.icon):与首行文字垂直居中,右侧留 4px。
292
+ 容器是 flex row,图标作为首个 flex 子项参与排布。 */
293
+ .universal-card-markdown__icon {
294
+ display: inline-flex; align-items: center; justify-content: center;
295
+ flex-shrink: 0; margin-right: 4px;
296
+ height: 1.6em;
297
+ }
291
298
  .universal-card-markdown__children {
292
299
  flex: 1;
293
300
  min-width: 0;
@@ -443,6 +450,32 @@
443
450
  /* 非跳转按钮:保留外观,但不可点(默认光标、无 hover、不响应点击) */
444
451
  .universal-card-button--static { cursor: default; pointer-events: none; }
445
452
 
453
+ /* ---- overflow 折叠按钮组 ---- */
454
+ /* 真机是一个「…」图标按钮,点击弹出选项菜单。静态预览只画触发器(--static 不可点)。 */
455
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
456
+ .universal-card-overflow--static { display: inline-flex; }
457
+ .universal-card-overflow__summary {
458
+ list-style: none; cursor: pointer; display: inline-flex;
459
+ }
460
+ /* 去掉 <summary> 的浏览器默认三角标记 */
461
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
462
+ .universal-card-overflow__summary::marker { content: ''; }
463
+ /* 菜单:默认隐藏,<details open> 时浮在触发器下方 */
464
+ .universal-card-overflow__menu {
465
+ display: none;
466
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
467
+ min-width: 120px; padding: 4px 0;
468
+ background: #fff; border-radius: 6px;
469
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
470
+ }
471
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
472
+ .universal-card-overflow__item {
473
+ display: block; padding: 6px 12px;
474
+ font-size: 14px; line-height: 22px; color: #1f2329;
475
+ text-decoration: none; white-space: nowrap; cursor: pointer;
476
+ }
477
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
478
+
446
479
  /* ---- 表单(预览:仅透传,无表单语义) ---- */
447
480
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
448
481
  .universal-card-form > * { width: 100%; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-feishu-card/renderer",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "零框架依赖的飞书卡片渲染引擎:Web / DOM / SSR,输入 RAW 或公开 DSL,输出真实 DOM 或 HTML 字符串",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",