@open-feishu-card/renderer 0.2.11 → 0.2.12

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
@@ -18,13 +18,13 @@
18
18
  line-height: 1.6;
19
19
  color: #1f2329;
20
20
  background-color: #fff;
21
+ position: relative;
21
22
  border-radius: 8px;
22
- border: 1px solid #dee0e3;
23
+ /* 真机 root 默认无边框;showCardBorder 时才 outline(见 --has-border) */
23
24
  overflow: hidden;
24
25
  width: 100%;
25
26
  max-width: 605px;
26
27
  box-sizing: border-box;
27
- box-shadow: 0 4px 14px rgba(31, 35, 41, 0.08);
28
28
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
29
29
  /* 正文 color:var(--O400) 等依赖这些变量;与飞书 :root light 色阶对齐 */
30
30
  --B50: #f0f4ff;
@@ -189,6 +189,7 @@
189
189
  /* 真机 light:--bg-float-overlay: var(--N50)。column_set background_style=grey 走此 token */
190
190
  --bg-float-overlay: var(--N50);
191
191
  }
192
+ .universal-card-root--has-border { outline: 1px solid #dee0e3; }
192
193
  .universal-card-root *,
193
194
  .universal-card-root *::before,
194
195
  .universal-card-root *::after { box-sizing: border-box; }
@@ -271,7 +272,8 @@
271
272
  padding: 12px;
272
273
  display: flex;
273
274
  flex-direction: column;
274
- row-gap: 8px;
275
+ row-gap: 12px;
276
+ column-gap: 12px;
275
277
  align-items: flex-start;
276
278
  }
277
279
  .universal-card-body > * { width: 100%; box-sizing: border-box; }
@@ -509,9 +511,12 @@
509
511
  .universal-card-interactive-container {
510
512
  display: flex; flex-direction: column; box-sizing: border-box;
511
513
  border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
512
- cursor: pointer; transition: background-color .1s;
514
+ row-gap: 4px;
515
+ transition: background-color .1s;
513
516
  }
514
- .universal-card-interactive-container:not(.universal-card-interactive-container--disabled):hover {
517
+ /* 真机:cursor:pointer 与 hover 底只在 __action 层;纯展示容器(无 actions/url)保持默认箭头 */
518
+ .universal-card-interactive-container--action { cursor: pointer; }
519
+ .universal-card-interactive-container--action:not(.universal-card-interactive-container--disabled):hover {
515
520
  background-color: rgba(31, 35, 41, 0.05);
516
521
  }
517
522
  .universal-card-interactive-container--bordered { border: 1px solid #dee0e3; }
package/dist/index.cjs CHANGED
@@ -706,13 +706,25 @@ function resolveURL(url) {
706
706
  function sizeToPx(v) {
707
707
  return typeof v === "number" ? v + "px" : String(v);
708
708
  }
709
+ function marginToStyle(margin) {
710
+ if (typeof margin === "string" && margin.trim()) return margin.trim();
711
+ return boxToStyle(margin);
712
+ }
713
+ function resolveCornerRadius(property) {
714
+ if (!property) return void 0;
715
+ const direct = property.corner_radius ?? property.cornerRadius;
716
+ if (direct != null) return sizeToPx(direct);
717
+ const echo = property.cornerRadiusValue;
718
+ if (echo && typeof echo.value === "number") return echo.value + "px";
719
+ if (echo && typeof echo.value === "string" && echo.value.trim()) return sizeToPx(echo.value);
720
+ return void 0;
721
+ }
709
722
  function boxToStyle(box) {
710
723
  if (!box || typeof box !== "object") return null;
711
724
  const t = box.top?.value ?? 0;
712
725
  const r = box.right?.value ?? 0;
713
726
  const b = box.bottom?.value ?? 0;
714
727
  const l = box.left?.value ?? 0;
715
- if (!t && !r && !b && !l) return null;
716
728
  return `${t}px ${r}px ${b}px ${l}px`;
717
729
  }
718
730
  function bgStyleToColor(style) {
@@ -843,13 +855,13 @@ var BASE_STYLES_CSS = `/**
843
855
  line-height: 1.6;
844
856
  color: #1f2329;
845
857
  background-color: #fff;
858
+ position: relative;
846
859
  border-radius: 8px;
847
- border: 1px solid #dee0e3;
860
+ /* \u771F\u673A root \u9ED8\u8BA4\u65E0\u8FB9\u6846\uFF1BshowCardBorder \u65F6\u624D outline\uFF08\u89C1 --has-border\uFF09 */
848
861
  overflow: hidden;
849
862
  width: 100%;
850
863
  max-width: 605px;
851
864
  box-sizing: border-box;
852
- box-shadow: 0 4px 14px rgba(31, 35, 41, 0.08);
853
865
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
854
866
  /* \u6B63\u6587 color:var(--O400) \u7B49\u4F9D\u8D56\u8FD9\u4E9B\u53D8\u91CF\uFF1B\u4E0E\u98DE\u4E66 :root light \u8272\u9636\u5BF9\u9F50 */
855
867
  --B50: #f0f4ff;
@@ -1014,6 +1026,7 @@ var BASE_STYLES_CSS = `/**
1014
1026
  /* \u771F\u673A light\uFF1A--bg-float-overlay: var(--N50)\u3002column_set background_style=grey \u8D70\u6B64 token */
1015
1027
  --bg-float-overlay: var(--N50);
1016
1028
  }
1029
+ .universal-card-root--has-border { outline: 1px solid #dee0e3; }
1017
1030
  .universal-card-root *,
1018
1031
  .universal-card-root *::before,
1019
1032
  .universal-card-root *::after { box-sizing: border-box; }
@@ -1096,7 +1109,8 @@ var BASE_STYLES_CSS = `/**
1096
1109
  padding: 12px;
1097
1110
  display: flex;
1098
1111
  flex-direction: column;
1099
- row-gap: 8px;
1112
+ row-gap: 12px;
1113
+ column-gap: 12px;
1100
1114
  align-items: flex-start;
1101
1115
  }
1102
1116
  .universal-card-body > * { width: 100%; box-sizing: border-box; }
@@ -1334,9 +1348,12 @@ var BASE_STYLES_CSS = `/**
1334
1348
  .universal-card-interactive-container {
1335
1349
  display: flex; flex-direction: column; box-sizing: border-box;
1336
1350
  border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
1337
- cursor: pointer; transition: background-color .1s;
1351
+ row-gap: 4px;
1352
+ transition: background-color .1s;
1338
1353
  }
1339
- .universal-card-interactive-container:not(.universal-card-interactive-container--disabled):hover {
1354
+ /* \u771F\u673A\uFF1Acursor:pointer \u4E0E hover \u5E95\u53EA\u5728 __action \u5C42\uFF1B\u7EAF\u5C55\u793A\u5BB9\u5668\uFF08\u65E0 actions/url\uFF09\u4FDD\u6301\u9ED8\u8BA4\u7BAD\u5934 */
1355
+ .universal-card-interactive-container--action { cursor: pointer; }
1356
+ .universal-card-interactive-container--action:not(.universal-card-interactive-container--disabled):hover {
1340
1357
  background-color: rgba(31, 35, 41, 0.05);
1341
1358
  }
1342
1359
  .universal-card-interactive-container--bordered { border: 1px solid #dee0e3; }
@@ -5418,6 +5435,8 @@ function registerBuiltins() {
5418
5435
  registerComponent("markdown", (el, ctx) => {
5419
5436
  const p = el.property || {};
5420
5437
  const style = { ...alignToStyle(p.textAlign), ...textStyleToStyle(p.textStyle) };
5438
+ const mg = marginToStyle(p.margin);
5439
+ if (mg) style.margin = mg;
5421
5440
  const sizeKey = resolveTextSizeKey(p);
5422
5441
  let fontSize = BASE_FONT_SIZE;
5423
5442
  let lineHeight = BASE_FONT_SIZE + 8;
@@ -5689,17 +5708,24 @@ function registerBuiltins() {
5689
5708
  const disabled = p.disabled === true;
5690
5709
  const act = Array.isArray(p.actions) ? p.actions[0] : void 0;
5691
5710
  const url = resolveURL(act?.action?.url);
5711
+ const hasActions = Array.isArray(p.actions) && p.actions.length > 0;
5712
+ const clickable = !disabled && (Boolean(url) || hasActions);
5692
5713
  const tip = extractText(p.hoverTips);
5693
- const borderColor = resolveColor(p.border_color ?? p.borderColor);
5694
- const hasBorder = borderColor != null || p.hasBorder === true;
5695
- const cls = `${CLS}-interactive-container` + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
5714
+ const borderNamed = p.border_color ?? p.borderColor;
5715
+ const borderCustom = adapter.resolveCustomBorderLight(p);
5716
+ const borderColor = resolveColor(borderNamed) ?? (borderCustom ? resolveColor(borderCustom) : void 0);
5717
+ const hasBorder = borderColor != null || p.hasBorder === true || p.has_border === true;
5718
+ const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
5696
5719
  const style = {};
5697
5720
  const bg = propertyBackgroundColor(p);
5698
5721
  if (bg) style.background = bg;
5699
5722
  const pad = boxToStyle(p.padding);
5700
5723
  if (pad) style.padding = pad;
5701
5724
  if (borderColor) style.border = `1px solid ${borderColor}`;
5702
- if (p.corner_radius != null) style.borderRadius = sizeToPx(p.corner_radius);
5725
+ const radius = resolveCornerRadius(p);
5726
+ if (radius != null) style.borderRadius = radius;
5727
+ const vGap = p.verticalSpacingValue?.value ?? p.vertical_spacing;
5728
+ if (vGap != null) style.rowGap = spacingToPx(vGap) + "px";
5703
5729
  const icWv = p.widthValue;
5704
5730
  if ((p.width ?? icWv?.value) === "fill") style.width = "100%";
5705
5731
  const props = { className: cls };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { dslToRaw, resolveTextStyleColor, resolveCustomBackgroundLight } from '@open-feishu-card/adapter';
1
+ import { resolveCustomBorderLight, dslToRaw, resolveTextStyleColor, resolveCustomBackgroundLight } from '@open-feishu-card/adapter';
2
2
  export { dslToRaw } from '@open-feishu-card/adapter';
3
3
 
4
4
  // src/constants.ts
@@ -705,13 +705,25 @@ function resolveURL(url) {
705
705
  function sizeToPx(v) {
706
706
  return typeof v === "number" ? v + "px" : String(v);
707
707
  }
708
+ function marginToStyle(margin) {
709
+ if (typeof margin === "string" && margin.trim()) return margin.trim();
710
+ return boxToStyle(margin);
711
+ }
712
+ function resolveCornerRadius(property) {
713
+ if (!property) return void 0;
714
+ const direct = property.corner_radius ?? property.cornerRadius;
715
+ if (direct != null) return sizeToPx(direct);
716
+ const echo = property.cornerRadiusValue;
717
+ if (echo && typeof echo.value === "number") return echo.value + "px";
718
+ if (echo && typeof echo.value === "string" && echo.value.trim()) return sizeToPx(echo.value);
719
+ return void 0;
720
+ }
708
721
  function boxToStyle(box) {
709
722
  if (!box || typeof box !== "object") return null;
710
723
  const t = box.top?.value ?? 0;
711
724
  const r = box.right?.value ?? 0;
712
725
  const b = box.bottom?.value ?? 0;
713
726
  const l = box.left?.value ?? 0;
714
- if (!t && !r && !b && !l) return null;
715
727
  return `${t}px ${r}px ${b}px ${l}px`;
716
728
  }
717
729
  function bgStyleToColor(style) {
@@ -842,13 +854,13 @@ var BASE_STYLES_CSS = `/**
842
854
  line-height: 1.6;
843
855
  color: #1f2329;
844
856
  background-color: #fff;
857
+ position: relative;
845
858
  border-radius: 8px;
846
- border: 1px solid #dee0e3;
859
+ /* \u771F\u673A root \u9ED8\u8BA4\u65E0\u8FB9\u6846\uFF1BshowCardBorder \u65F6\u624D outline\uFF08\u89C1 --has-border\uFF09 */
847
860
  overflow: hidden;
848
861
  width: 100%;
849
862
  max-width: 605px;
850
863
  box-sizing: border-box;
851
- box-shadow: 0 4px 14px rgba(31, 35, 41, 0.08);
852
864
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
853
865
  /* \u6B63\u6587 color:var(--O400) \u7B49\u4F9D\u8D56\u8FD9\u4E9B\u53D8\u91CF\uFF1B\u4E0E\u98DE\u4E66 :root light \u8272\u9636\u5BF9\u9F50 */
854
866
  --B50: #f0f4ff;
@@ -1013,6 +1025,7 @@ var BASE_STYLES_CSS = `/**
1013
1025
  /* \u771F\u673A light\uFF1A--bg-float-overlay: var(--N50)\u3002column_set background_style=grey \u8D70\u6B64 token */
1014
1026
  --bg-float-overlay: var(--N50);
1015
1027
  }
1028
+ .universal-card-root--has-border { outline: 1px solid #dee0e3; }
1016
1029
  .universal-card-root *,
1017
1030
  .universal-card-root *::before,
1018
1031
  .universal-card-root *::after { box-sizing: border-box; }
@@ -1095,7 +1108,8 @@ var BASE_STYLES_CSS = `/**
1095
1108
  padding: 12px;
1096
1109
  display: flex;
1097
1110
  flex-direction: column;
1098
- row-gap: 8px;
1111
+ row-gap: 12px;
1112
+ column-gap: 12px;
1099
1113
  align-items: flex-start;
1100
1114
  }
1101
1115
  .universal-card-body > * { width: 100%; box-sizing: border-box; }
@@ -1333,9 +1347,12 @@ var BASE_STYLES_CSS = `/**
1333
1347
  .universal-card-interactive-container {
1334
1348
  display: flex; flex-direction: column; box-sizing: border-box;
1335
1349
  border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
1336
- cursor: pointer; transition: background-color .1s;
1350
+ row-gap: 4px;
1351
+ transition: background-color .1s;
1337
1352
  }
1338
- .universal-card-interactive-container:not(.universal-card-interactive-container--disabled):hover {
1353
+ /* \u771F\u673A\uFF1Acursor:pointer \u4E0E hover \u5E95\u53EA\u5728 __action \u5C42\uFF1B\u7EAF\u5C55\u793A\u5BB9\u5668\uFF08\u65E0 actions/url\uFF09\u4FDD\u6301\u9ED8\u8BA4\u7BAD\u5934 */
1354
+ .universal-card-interactive-container--action { cursor: pointer; }
1355
+ .universal-card-interactive-container--action:not(.universal-card-interactive-container--disabled):hover {
1339
1356
  background-color: rgba(31, 35, 41, 0.05);
1340
1357
  }
1341
1358
  .universal-card-interactive-container--bordered { border: 1px solid #dee0e3; }
@@ -5417,6 +5434,8 @@ function registerBuiltins() {
5417
5434
  registerComponent("markdown", (el, ctx) => {
5418
5435
  const p = el.property || {};
5419
5436
  const style = { ...alignToStyle(p.textAlign), ...textStyleToStyle(p.textStyle) };
5437
+ const mg = marginToStyle(p.margin);
5438
+ if (mg) style.margin = mg;
5420
5439
  const sizeKey = resolveTextSizeKey(p);
5421
5440
  let fontSize = BASE_FONT_SIZE;
5422
5441
  let lineHeight = BASE_FONT_SIZE + 8;
@@ -5688,17 +5707,24 @@ function registerBuiltins() {
5688
5707
  const disabled = p.disabled === true;
5689
5708
  const act = Array.isArray(p.actions) ? p.actions[0] : void 0;
5690
5709
  const url = resolveURL(act?.action?.url);
5710
+ const hasActions = Array.isArray(p.actions) && p.actions.length > 0;
5711
+ const clickable = !disabled && (Boolean(url) || hasActions);
5691
5712
  const tip = extractText(p.hoverTips);
5692
- const borderColor = resolveColor(p.border_color ?? p.borderColor);
5693
- const hasBorder = borderColor != null || p.hasBorder === true;
5694
- const cls = `${CLS}-interactive-container` + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
5713
+ const borderNamed = p.border_color ?? p.borderColor;
5714
+ const borderCustom = resolveCustomBorderLight(p);
5715
+ const borderColor = resolveColor(borderNamed) ?? (borderCustom ? resolveColor(borderCustom) : void 0);
5716
+ const hasBorder = borderColor != null || p.hasBorder === true || p.has_border === true;
5717
+ const cls = `${CLS}-interactive-container` + (clickable ? ` ${CLS}-interactive-container--action` : "") + (hasBorder && !borderColor ? ` ${CLS}-interactive-container--bordered` : "") + (disabled ? ` ${CLS}-interactive-container--disabled` : "");
5695
5718
  const style = {};
5696
5719
  const bg = propertyBackgroundColor(p);
5697
5720
  if (bg) style.background = bg;
5698
5721
  const pad = boxToStyle(p.padding);
5699
5722
  if (pad) style.padding = pad;
5700
5723
  if (borderColor) style.border = `1px solid ${borderColor}`;
5701
- if (p.corner_radius != null) style.borderRadius = sizeToPx(p.corner_radius);
5724
+ const radius = resolveCornerRadius(p);
5725
+ if (radius != null) style.borderRadius = radius;
5726
+ const vGap = p.verticalSpacingValue?.value ?? p.vertical_spacing;
5727
+ if (vGap != null) style.rowGap = spacingToPx(vGap) + "px";
5702
5728
  const icWv = p.widthValue;
5703
5729
  if ((p.width ?? icWv?.value) === "fill") style.width = "100%";
5704
5730
  const props = { className: cls };
package/dist/styles.css CHANGED
@@ -18,13 +18,13 @@
18
18
  line-height: 1.6;
19
19
  color: #1f2329;
20
20
  background-color: #fff;
21
+ position: relative;
21
22
  border-radius: 8px;
22
- border: 1px solid #dee0e3;
23
+ /* 真机 root 默认无边框;showCardBorder 时才 outline(见 --has-border) */
23
24
  overflow: hidden;
24
25
  width: 100%;
25
26
  max-width: 605px;
26
27
  box-sizing: border-box;
27
- box-shadow: 0 4px 14px rgba(31, 35, 41, 0.08);
28
28
  /* BEGIN PALETTE VARS (auto-generated from constants.ts PALETTE) */
29
29
  /* 正文 color:var(--O400) 等依赖这些变量;与飞书 :root light 色阶对齐 */
30
30
  --B50: #f0f4ff;
@@ -189,6 +189,7 @@
189
189
  /* 真机 light:--bg-float-overlay: var(--N50)。column_set background_style=grey 走此 token */
190
190
  --bg-float-overlay: var(--N50);
191
191
  }
192
+ .universal-card-root--has-border { outline: 1px solid #dee0e3; }
192
193
  .universal-card-root *,
193
194
  .universal-card-root *::before,
194
195
  .universal-card-root *::after { box-sizing: border-box; }
@@ -271,7 +272,8 @@
271
272
  padding: 12px;
272
273
  display: flex;
273
274
  flex-direction: column;
274
- row-gap: 8px;
275
+ row-gap: 12px;
276
+ column-gap: 12px;
275
277
  align-items: flex-start;
276
278
  }
277
279
  .universal-card-body > * { width: 100%; box-sizing: border-box; }
@@ -509,9 +511,12 @@
509
511
  .universal-card-interactive-container {
510
512
  display: flex; flex-direction: column; box-sizing: border-box;
511
513
  border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
512
- cursor: pointer; transition: background-color .1s;
514
+ row-gap: 4px;
515
+ transition: background-color .1s;
513
516
  }
514
- .universal-card-interactive-container:not(.universal-card-interactive-container--disabled):hover {
517
+ /* 真机:cursor:pointer 与 hover 底只在 __action 层;纯展示容器(无 actions/url)保持默认箭头 */
518
+ .universal-card-interactive-container--action { cursor: pointer; }
519
+ .universal-card-interactive-container--action:not(.universal-card-interactive-container--disabled):hover {
515
520
  background-color: rgba(31, 35, 41, 0.05);
516
521
  }
517
522
  .universal-card-interactive-container--bordered { border: 1px solid #dee0e3; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-feishu-card/renderer",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
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.6"
31
+ "@open-feishu-card/adapter": "0.3.8"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.3.5",