@open-feishu-card/renderer 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/all.css CHANGED
@@ -443,6 +443,32 @@
443
443
  /* 非跳转按钮:保留外观,但不可点(默认光标、无 hover、不响应点击) */
444
444
  .universal-card-button--static { cursor: default; pointer-events: none; }
445
445
 
446
+ /* ---- overflow 折叠按钮组 ---- */
447
+ /* 真机是一个「…」图标按钮,点击弹出选项菜单。静态预览只画触发器(--static 不可点)。 */
448
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
449
+ .universal-card-overflow--static { display: inline-flex; }
450
+ .universal-card-overflow__summary {
451
+ list-style: none; cursor: pointer; display: inline-flex;
452
+ }
453
+ /* 去掉 <summary> 的浏览器默认三角标记 */
454
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
455
+ .universal-card-overflow__summary::marker { content: ''; }
456
+ /* 菜单:默认隐藏,<details open> 时浮在触发器下方 */
457
+ .universal-card-overflow__menu {
458
+ display: none;
459
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
460
+ min-width: 120px; padding: 4px 0;
461
+ background: #fff; border-radius: 6px;
462
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
463
+ }
464
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
465
+ .universal-card-overflow__item {
466
+ display: block; padding: 6px 12px;
467
+ font-size: 14px; line-height: 22px; color: #1f2329;
468
+ text-decoration: none; white-space: nowrap; cursor: pointer;
469
+ }
470
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
471
+
446
472
  /* ---- 表单(预览:仅透传,无表单语义) ---- */
447
473
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
448
474
  .universal-card-form > * { width: 100%; }
package/dist/index.cjs CHANGED
@@ -1413,6 +1413,32 @@ var BASE_STYLES_CSS = `/**
1413
1413
  /* \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
1414
  .universal-card-button--static { cursor: default; pointer-events: none; }
1415
1415
 
1416
+ /* ---- overflow \u6298\u53E0\u6309\u94AE\u7EC4 ---- */
1417
+ /* \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 */
1418
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
1419
+ .universal-card-overflow--static { display: inline-flex; }
1420
+ .universal-card-overflow__summary {
1421
+ list-style: none; cursor: pointer; display: inline-flex;
1422
+ }
1423
+ /* \u53BB\u6389 <summary> \u7684\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0 */
1424
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
1425
+ .universal-card-overflow__summary::marker { content: ''; }
1426
+ /* \u83DC\u5355\uFF1A\u9ED8\u8BA4\u9690\u85CF\uFF0C<details open> \u65F6\u6D6E\u5728\u89E6\u53D1\u5668\u4E0B\u65B9 */
1427
+ .universal-card-overflow__menu {
1428
+ display: none;
1429
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
1430
+ min-width: 120px; padding: 4px 0;
1431
+ background: #fff; border-radius: 6px;
1432
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
1433
+ }
1434
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
1435
+ .universal-card-overflow__item {
1436
+ display: block; padding: 6px 12px;
1437
+ font-size: 14px; line-height: 22px; color: #1f2329;
1438
+ text-decoration: none; white-space: nowrap; cursor: pointer;
1439
+ }
1440
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
1441
+
1416
1442
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1417
1443
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1418
1444
  .universal-card-form > * { width: 100%; }
@@ -6035,8 +6061,9 @@ function registerBuiltins() {
6035
6061
  const tip = extractText(p.hoverTips);
6036
6062
  const borderNamed = p.border_color ?? p.borderColor;
6037
6063
  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;
6064
+ const borderColorResolved = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
6065
+ const hasBorder = p.hasBorder === true || p.has_border === true;
6066
+ const borderColor = hasBorder ? borderColorResolved : void 0;
6040
6067
  const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
6041
6068
  const style = {};
6042
6069
  const bg = propertyBackgroundColor(p);
@@ -6204,6 +6231,43 @@ function registerBuiltins() {
6204
6231
  };
6205
6232
  return fromList(p.actions) ?? fromList(p.behaviors) ?? (isRecord(p.value) ? p.value : null);
6206
6233
  }
6234
+ registerComponent("overflow", (el, ctx) => {
6235
+ const p = el.property || {};
6236
+ const disabled = p.disabled === true;
6237
+ const options = Array.isArray(p.options) ? p.options : [];
6238
+ const widthTok = p.width ?? p.widthValue?.value;
6239
+ const block = widthTok === "fill";
6240
+ const triggerIcon = renderIcon(
6241
+ { property: { token: "more_outlined", size: "14px" } },
6242
+ ctx
6243
+ );
6244
+ const triggerCls = `${CLS}-button ${CLS}-button--default ${CLS}-button--static ${CLS}-overflow__trigger` + (disabled ? ` ${CLS}-button--disabled` : "") + (block ? ` ${CLS}-button--block` : "");
6245
+ const trigger = triggerIcon ? h("span", { className: triggerCls }, h("span", { className: `${CLS}-button__icon` }, triggerIcon)) : h("span", { className: triggerCls }, "\xB7\xB7\xB7");
6246
+ const items = options.map((raw) => {
6247
+ const opt = isRecord(raw) ? raw : {};
6248
+ const textObj = opt.text;
6249
+ const label = (isRecord(textObj) && typeof textObj.content === "string" ? textObj.content : extractPlainText(textObj)) || extractPlainText(opt);
6250
+ const multi = isRecord(opt.multi_url) ? opt.multi_url : void 0;
6251
+ const href = resolveURL(multi?.url ?? opt.url);
6252
+ const itemCls = `${CLS}-overflow__item`;
6253
+ if (href && !disabled) {
6254
+ return h("a", { className: itemCls, href, target: "_blank", rel: "noopener" }, label);
6255
+ }
6256
+ return h("span", { className: itemCls }, label);
6257
+ });
6258
+ const style = {};
6259
+ if (p.margin) applyOuterMargin(style, p.margin);
6260
+ const styleProp = Object.keys(style).length ? { style } : {};
6261
+ if (!ctx.options.interactive) {
6262
+ return h("span", { className: `${CLS}-overflow ${CLS}-overflow--static`, ...styleProp }, trigger);
6263
+ }
6264
+ return h(
6265
+ "details",
6266
+ { className: `${CLS}-overflow`, ...styleProp },
6267
+ h("summary", { className: `${CLS}-overflow__summary` }, trigger),
6268
+ h("div", { className: `${CLS}-overflow__menu` }, items)
6269
+ );
6270
+ });
6207
6271
  registerComponent("form", (el, ctx) => {
6208
6272
  const children = renderElements(el.property?.elements, ctx);
6209
6273
  const className = `${CLS}-form`;
package/dist/index.js CHANGED
@@ -1412,6 +1412,32 @@ var BASE_STYLES_CSS = `/**
1412
1412
  /* \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
1413
  .universal-card-button--static { cursor: default; pointer-events: none; }
1414
1414
 
1415
+ /* ---- overflow \u6298\u53E0\u6309\u94AE\u7EC4 ---- */
1416
+ /* \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 */
1417
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
1418
+ .universal-card-overflow--static { display: inline-flex; }
1419
+ .universal-card-overflow__summary {
1420
+ list-style: none; cursor: pointer; display: inline-flex;
1421
+ }
1422
+ /* \u53BB\u6389 <summary> \u7684\u6D4F\u89C8\u5668\u9ED8\u8BA4\u4E09\u89D2\u6807\u8BB0 */
1423
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
1424
+ .universal-card-overflow__summary::marker { content: ''; }
1425
+ /* \u83DC\u5355\uFF1A\u9ED8\u8BA4\u9690\u85CF\uFF0C<details open> \u65F6\u6D6E\u5728\u89E6\u53D1\u5668\u4E0B\u65B9 */
1426
+ .universal-card-overflow__menu {
1427
+ display: none;
1428
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
1429
+ min-width: 120px; padding: 4px 0;
1430
+ background: #fff; border-radius: 6px;
1431
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
1432
+ }
1433
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
1434
+ .universal-card-overflow__item {
1435
+ display: block; padding: 6px 12px;
1436
+ font-size: 14px; line-height: 22px; color: #1f2329;
1437
+ text-decoration: none; white-space: nowrap; cursor: pointer;
1438
+ }
1439
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
1440
+
1415
1441
  /* ---- \u8868\u5355\uFF08\u9884\u89C8\uFF1A\u4EC5\u900F\u4F20\uFF0C\u65E0\u8868\u5355\u8BED\u4E49\uFF09 ---- */
1416
1442
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
1417
1443
  .universal-card-form > * { width: 100%; }
@@ -6034,8 +6060,9 @@ function registerBuiltins() {
6034
6060
  const tip = extractText(p.hoverTips);
6035
6061
  const borderNamed = p.border_color ?? p.borderColor;
6036
6062
  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;
6063
+ const borderColorResolved = (borderCustom ? resolveColor(borderCustom) : void 0) ?? resolveColor(borderNamed);
6064
+ const hasBorder = p.hasBorder === true || p.has_border === true;
6065
+ const borderColor = hasBorder ? borderColorResolved : void 0;
6039
6066
  const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
6040
6067
  const style = {};
6041
6068
  const bg = propertyBackgroundColor(p);
@@ -6203,6 +6230,43 @@ function registerBuiltins() {
6203
6230
  };
6204
6231
  return fromList(p.actions) ?? fromList(p.behaviors) ?? (isRecord(p.value) ? p.value : null);
6205
6232
  }
6233
+ registerComponent("overflow", (el, ctx) => {
6234
+ const p = el.property || {};
6235
+ const disabled = p.disabled === true;
6236
+ const options = Array.isArray(p.options) ? p.options : [];
6237
+ const widthTok = p.width ?? p.widthValue?.value;
6238
+ const block = widthTok === "fill";
6239
+ const triggerIcon = renderIcon(
6240
+ { property: { token: "more_outlined", size: "14px" } },
6241
+ ctx
6242
+ );
6243
+ const triggerCls = `${CLS}-button ${CLS}-button--default ${CLS}-button--static ${CLS}-overflow__trigger` + (disabled ? ` ${CLS}-button--disabled` : "") + (block ? ` ${CLS}-button--block` : "");
6244
+ const trigger = triggerIcon ? h("span", { className: triggerCls }, h("span", { className: `${CLS}-button__icon` }, triggerIcon)) : h("span", { className: triggerCls }, "\xB7\xB7\xB7");
6245
+ const items = options.map((raw) => {
6246
+ const opt = isRecord(raw) ? raw : {};
6247
+ const textObj = opt.text;
6248
+ const label = (isRecord(textObj) && typeof textObj.content === "string" ? textObj.content : extractPlainText(textObj)) || extractPlainText(opt);
6249
+ const multi = isRecord(opt.multi_url) ? opt.multi_url : void 0;
6250
+ const href = resolveURL(multi?.url ?? opt.url);
6251
+ const itemCls = `${CLS}-overflow__item`;
6252
+ if (href && !disabled) {
6253
+ return h("a", { className: itemCls, href, target: "_blank", rel: "noopener" }, label);
6254
+ }
6255
+ return h("span", { className: itemCls }, label);
6256
+ });
6257
+ const style = {};
6258
+ if (p.margin) applyOuterMargin(style, p.margin);
6259
+ const styleProp = Object.keys(style).length ? { style } : {};
6260
+ if (!ctx.options.interactive) {
6261
+ return h("span", { className: `${CLS}-overflow ${CLS}-overflow--static`, ...styleProp }, trigger);
6262
+ }
6263
+ return h(
6264
+ "details",
6265
+ { className: `${CLS}-overflow`, ...styleProp },
6266
+ h("summary", { className: `${CLS}-overflow__summary` }, trigger),
6267
+ h("div", { className: `${CLS}-overflow__menu` }, items)
6268
+ );
6269
+ });
6206
6270
  registerComponent("form", (el, ctx) => {
6207
6271
  const children = renderElements(el.property?.elements, ctx);
6208
6272
  const className = `${CLS}-form`;
package/dist/styles.css CHANGED
@@ -443,6 +443,32 @@
443
443
  /* 非跳转按钮:保留外观,但不可点(默认光标、无 hover、不响应点击) */
444
444
  .universal-card-button--static { cursor: default; pointer-events: none; }
445
445
 
446
+ /* ---- overflow 折叠按钮组 ---- */
447
+ /* 真机是一个「…」图标按钮,点击弹出选项菜单。静态预览只画触发器(--static 不可点)。 */
448
+ .universal-card-overflow { display: inline-flex; position: relative; vertical-align: middle; }
449
+ .universal-card-overflow--static { display: inline-flex; }
450
+ .universal-card-overflow__summary {
451
+ list-style: none; cursor: pointer; display: inline-flex;
452
+ }
453
+ /* 去掉 <summary> 的浏览器默认三角标记 */
454
+ .universal-card-overflow__summary::-webkit-details-marker { display: none; }
455
+ .universal-card-overflow__summary::marker { content: ''; }
456
+ /* 菜单:默认隐藏,<details open> 时浮在触发器下方 */
457
+ .universal-card-overflow__menu {
458
+ display: none;
459
+ position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
460
+ min-width: 120px; padding: 4px 0;
461
+ background: #fff; border-radius: 6px;
462
+ box-shadow: 0 4px 12px rgba(31, 35, 41, 0.15), 0 0 1px rgba(31, 35, 41, 0.2);
463
+ }
464
+ .universal-card-overflow[open] > .universal-card-overflow__menu { display: block; }
465
+ .universal-card-overflow__item {
466
+ display: block; padding: 6px 12px;
467
+ font-size: 14px; line-height: 22px; color: #1f2329;
468
+ text-decoration: none; white-space: nowrap; cursor: pointer;
469
+ }
470
+ .universal-card-overflow__item:hover { background: #f5f6f7; }
471
+
446
472
  /* ---- 表单(预览:仅透传,无表单语义) ---- */
447
473
  .universal-card-form { display: flex; flex-direction: column; row-gap: 8px; }
448
474
  .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.1",
4
4
  "description": "零框架依赖的飞书卡片渲染引擎:Web / DOM / SSR,输入 RAW 或公开 DSL,输出真实 DOM 或 HTML 字符串",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",