@open-feishu-card/renderer 0.2.16 → 0.2.18

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
@@ -306,6 +306,33 @@
306
306
  margin: 8px 0;
307
307
  }
308
308
 
309
+ .universal-card-collapse-divider {
310
+ width: 100%;
311
+ height: 1px;
312
+ background-color: #dee0e3;
313
+ border: none;
314
+ margin: 8px 0;
315
+ }
316
+
317
+ .universal-card-avatar {
318
+ display: inline-flex;
319
+ align-items: center;
320
+ vertical-align: middle;
321
+ }
322
+
323
+ .universal-card-avatar__img {
324
+ border-radius: 50%;
325
+ object-fit: cover;
326
+ display: block;
327
+ background: #eff0f1;
328
+ }
329
+
330
+ .universal-card-fallback-text {
331
+ color: #646a73;
332
+ font-size: 14px;
333
+ line-height: 22px;
334
+ }
335
+
309
336
  /* ---- div / note ---- */
310
337
  .universal-card-div > * + * { margin-top: 8px; }
311
338
  /* div 带前置图标:icon 在左、文本在右排成一行(复刻网页版 .universal-card-div 布局) */
@@ -508,9 +535,10 @@
508
535
 
509
536
 
510
537
  /* ---- interactive_container 可点击交互容器 ---- */
538
+ /* 真机无默认圆角;圆角只由 DSL/RAW corner_radius 内联输出(见 resolveCornerRadius)。 */
511
539
  .universal-card-interactive-container {
512
540
  display: flex; flex-direction: column; box-sizing: border-box;
513
- border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
541
+ padding: 0; text-decoration: none; color: inherit;
514
542
  row-gap: 4px;
515
543
  transition: background-color .1s;
516
544
  }
package/dist/index.cjs CHANGED
@@ -798,6 +798,11 @@ function resolveImageSize(property) {
798
798
  function resolveCornerRadius(property) {
799
799
  if (!property) return void 0;
800
800
  const direct = property.corner_radius ?? property.cornerRadius;
801
+ if (typeof direct === "string") {
802
+ const t = direct.trim();
803
+ if (!t) return void 0;
804
+ return sizeToPx(t);
805
+ }
801
806
  if (direct != null) return sizeToPx(direct);
802
807
  const echo = property.cornerRadiusValue;
803
808
  if (echo && typeof echo.value === "number") return echo.value + "px";
@@ -1228,6 +1233,33 @@ var BASE_STYLES_CSS = `/**
1228
1233
  margin: 8px 0;
1229
1234
  }
1230
1235
 
1236
+ .universal-card-collapse-divider {
1237
+ width: 100%;
1238
+ height: 1px;
1239
+ background-color: #dee0e3;
1240
+ border: none;
1241
+ margin: 8px 0;
1242
+ }
1243
+
1244
+ .universal-card-avatar {
1245
+ display: inline-flex;
1246
+ align-items: center;
1247
+ vertical-align: middle;
1248
+ }
1249
+
1250
+ .universal-card-avatar__img {
1251
+ border-radius: 50%;
1252
+ object-fit: cover;
1253
+ display: block;
1254
+ background: #eff0f1;
1255
+ }
1256
+
1257
+ .universal-card-fallback-text {
1258
+ color: #646a73;
1259
+ font-size: 14px;
1260
+ line-height: 22px;
1261
+ }
1262
+
1231
1263
  /* ---- div / note ---- */
1232
1264
  .universal-card-div > * + * { margin-top: 8px; }
1233
1265
  /* div \u5E26\u524D\u7F6E\u56FE\u6807\uFF1Aicon \u5728\u5DE6\u3001\u6587\u672C\u5728\u53F3\u6392\u6210\u4E00\u884C\uFF08\u590D\u523B\u7F51\u9875\u7248 .universal-card-div \u5E03\u5C40\uFF09 */
@@ -1430,9 +1462,10 @@ var BASE_STYLES_CSS = `/**
1430
1462
 
1431
1463
 
1432
1464
  /* ---- interactive_container \u53EF\u70B9\u51FB\u4EA4\u4E92\u5BB9\u5668 ---- */
1465
+ /* \u771F\u673A\u65E0\u9ED8\u8BA4\u5706\u89D2\uFF1B\u5706\u89D2\u53EA\u7531 DSL/RAW corner_radius \u5185\u8054\u8F93\u51FA\uFF08\u89C1 resolveCornerRadius\uFF09\u3002 */
1433
1466
  .universal-card-interactive-container {
1434
1467
  display: flex; flex-direction: column; box-sizing: border-box;
1435
- border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
1468
+ padding: 0; text-decoration: none; color: inherit;
1436
1469
  row-gap: 4px;
1437
1470
  transition: background-color .1s;
1438
1471
  }
@@ -5958,6 +5991,11 @@ function registerBuiltins() {
5958
5991
  registerComponent("input", (el) => renderStaticField(el));
5959
5992
  registerComponent("multi_select_static", (el) => renderStaticField(el));
5960
5993
  registerComponent("select_static", (el) => renderStaticField(el));
5994
+ registerComponent("date_picker", (el) => renderStaticField(el));
5995
+ registerComponent("picker_time", (el) => renderStaticField(el));
5996
+ registerComponent("picker_datetime", (el) => renderStaticField(el));
5997
+ registerComponent("select_person", (el) => renderStaticField(el));
5998
+ registerComponent("multi_select_person", (el) => renderStaticField(el));
5961
5999
  registerComponent("checker", (el, ctx) => {
5962
6000
  const p = el.property || {};
5963
6001
  const checked = p.checked === true;
@@ -6105,6 +6143,40 @@ function registerBuiltins() {
6105
6143
  const p = el.property || {};
6106
6144
  return renderPersonChip(p.userID, p, ctx);
6107
6145
  });
6146
+ const AVATAR_SIZE_PX = {
6147
+ tiny: 16,
6148
+ small: 24,
6149
+ medium: 32,
6150
+ large: 40,
6151
+ huge: 48
6152
+ };
6153
+ registerComponent("avatar", (el, ctx) => {
6154
+ const p = el.property || {};
6155
+ const userID = p.userID || p.user_id;
6156
+ const sizeKey = String(p.size ?? "medium");
6157
+ const custom = typeof p.customSize === "number" ? p.customSize : typeof p.custom_size === "number" ? p.custom_size : void 0;
6158
+ const px = sizeKey === "custom" && typeof custom === "number" && custom > 0 ? custom : AVATAR_SIZE_PX[sizeKey] || AVATAR_SIZE_PX.medium;
6159
+ const { name, src } = resolvePersonDisplay(userID, ctx);
6160
+ return h(
6161
+ "div",
6162
+ { className: `${CLS}-avatar`, title: name },
6163
+ renderAvatarNode(`${CLS}-avatar__img`, px, src, name)
6164
+ );
6165
+ });
6166
+ registerComponent(
6167
+ "collapse_divider",
6168
+ () => h("div", { className: `${CLS}-collapse-divider` })
6169
+ );
6170
+ registerComponent("fallback_text", (el, ctx) => {
6171
+ const p = el.property || {};
6172
+ const textEl = p.text;
6173
+ if (textEl && typeof textEl === "object" && textEl.tag) {
6174
+ return h("div", { className: `${CLS}-fallback-text` }, renderElement(textEl, ctx));
6175
+ }
6176
+ const content = extractPlainText(p.text);
6177
+ if (!content) return null;
6178
+ return h("div", { className: `${CLS}-fallback-text` }, content);
6179
+ });
6108
6180
  registerComponent("person_list", (el, ctx) => {
6109
6181
  const p = el.property || {};
6110
6182
  const list = Array.isArray(p.persons) ? p.persons : [];
package/dist/index.js CHANGED
@@ -797,6 +797,11 @@ function resolveImageSize(property) {
797
797
  function resolveCornerRadius(property) {
798
798
  if (!property) return void 0;
799
799
  const direct = property.corner_radius ?? property.cornerRadius;
800
+ if (typeof direct === "string") {
801
+ const t = direct.trim();
802
+ if (!t) return void 0;
803
+ return sizeToPx(t);
804
+ }
800
805
  if (direct != null) return sizeToPx(direct);
801
806
  const echo = property.cornerRadiusValue;
802
807
  if (echo && typeof echo.value === "number") return echo.value + "px";
@@ -1227,6 +1232,33 @@ var BASE_STYLES_CSS = `/**
1227
1232
  margin: 8px 0;
1228
1233
  }
1229
1234
 
1235
+ .universal-card-collapse-divider {
1236
+ width: 100%;
1237
+ height: 1px;
1238
+ background-color: #dee0e3;
1239
+ border: none;
1240
+ margin: 8px 0;
1241
+ }
1242
+
1243
+ .universal-card-avatar {
1244
+ display: inline-flex;
1245
+ align-items: center;
1246
+ vertical-align: middle;
1247
+ }
1248
+
1249
+ .universal-card-avatar__img {
1250
+ border-radius: 50%;
1251
+ object-fit: cover;
1252
+ display: block;
1253
+ background: #eff0f1;
1254
+ }
1255
+
1256
+ .universal-card-fallback-text {
1257
+ color: #646a73;
1258
+ font-size: 14px;
1259
+ line-height: 22px;
1260
+ }
1261
+
1230
1262
  /* ---- div / note ---- */
1231
1263
  .universal-card-div > * + * { margin-top: 8px; }
1232
1264
  /* div \u5E26\u524D\u7F6E\u56FE\u6807\uFF1Aicon \u5728\u5DE6\u3001\u6587\u672C\u5728\u53F3\u6392\u6210\u4E00\u884C\uFF08\u590D\u523B\u7F51\u9875\u7248 .universal-card-div \u5E03\u5C40\uFF09 */
@@ -1429,9 +1461,10 @@ var BASE_STYLES_CSS = `/**
1429
1461
 
1430
1462
 
1431
1463
  /* ---- interactive_container \u53EF\u70B9\u51FB\u4EA4\u4E92\u5BB9\u5668 ---- */
1464
+ /* \u771F\u673A\u65E0\u9ED8\u8BA4\u5706\u89D2\uFF1B\u5706\u89D2\u53EA\u7531 DSL/RAW corner_radius \u5185\u8054\u8F93\u51FA\uFF08\u89C1 resolveCornerRadius\uFF09\u3002 */
1432
1465
  .universal-card-interactive-container {
1433
1466
  display: flex; flex-direction: column; box-sizing: border-box;
1434
- border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
1467
+ padding: 0; text-decoration: none; color: inherit;
1435
1468
  row-gap: 4px;
1436
1469
  transition: background-color .1s;
1437
1470
  }
@@ -5957,6 +5990,11 @@ function registerBuiltins() {
5957
5990
  registerComponent("input", (el) => renderStaticField(el));
5958
5991
  registerComponent("multi_select_static", (el) => renderStaticField(el));
5959
5992
  registerComponent("select_static", (el) => renderStaticField(el));
5993
+ registerComponent("date_picker", (el) => renderStaticField(el));
5994
+ registerComponent("picker_time", (el) => renderStaticField(el));
5995
+ registerComponent("picker_datetime", (el) => renderStaticField(el));
5996
+ registerComponent("select_person", (el) => renderStaticField(el));
5997
+ registerComponent("multi_select_person", (el) => renderStaticField(el));
5960
5998
  registerComponent("checker", (el, ctx) => {
5961
5999
  const p = el.property || {};
5962
6000
  const checked = p.checked === true;
@@ -6104,6 +6142,40 @@ function registerBuiltins() {
6104
6142
  const p = el.property || {};
6105
6143
  return renderPersonChip(p.userID, p, ctx);
6106
6144
  });
6145
+ const AVATAR_SIZE_PX = {
6146
+ tiny: 16,
6147
+ small: 24,
6148
+ medium: 32,
6149
+ large: 40,
6150
+ huge: 48
6151
+ };
6152
+ registerComponent("avatar", (el, ctx) => {
6153
+ const p = el.property || {};
6154
+ const userID = p.userID || p.user_id;
6155
+ const sizeKey = String(p.size ?? "medium");
6156
+ const custom = typeof p.customSize === "number" ? p.customSize : typeof p.custom_size === "number" ? p.custom_size : void 0;
6157
+ const px = sizeKey === "custom" && typeof custom === "number" && custom > 0 ? custom : AVATAR_SIZE_PX[sizeKey] || AVATAR_SIZE_PX.medium;
6158
+ const { name, src } = resolvePersonDisplay(userID, ctx);
6159
+ return h(
6160
+ "div",
6161
+ { className: `${CLS}-avatar`, title: name },
6162
+ renderAvatarNode(`${CLS}-avatar__img`, px, src, name)
6163
+ );
6164
+ });
6165
+ registerComponent(
6166
+ "collapse_divider",
6167
+ () => h("div", { className: `${CLS}-collapse-divider` })
6168
+ );
6169
+ registerComponent("fallback_text", (el, ctx) => {
6170
+ const p = el.property || {};
6171
+ const textEl = p.text;
6172
+ if (textEl && typeof textEl === "object" && textEl.tag) {
6173
+ return h("div", { className: `${CLS}-fallback-text` }, renderElement(textEl, ctx));
6174
+ }
6175
+ const content = extractPlainText(p.text);
6176
+ if (!content) return null;
6177
+ return h("div", { className: `${CLS}-fallback-text` }, content);
6178
+ });
6107
6179
  registerComponent("person_list", (el, ctx) => {
6108
6180
  const p = el.property || {};
6109
6181
  const list = Array.isArray(p.persons) ? p.persons : [];
package/dist/styles.css CHANGED
@@ -306,6 +306,33 @@
306
306
  margin: 8px 0;
307
307
  }
308
308
 
309
+ .universal-card-collapse-divider {
310
+ width: 100%;
311
+ height: 1px;
312
+ background-color: #dee0e3;
313
+ border: none;
314
+ margin: 8px 0;
315
+ }
316
+
317
+ .universal-card-avatar {
318
+ display: inline-flex;
319
+ align-items: center;
320
+ vertical-align: middle;
321
+ }
322
+
323
+ .universal-card-avatar__img {
324
+ border-radius: 50%;
325
+ object-fit: cover;
326
+ display: block;
327
+ background: #eff0f1;
328
+ }
329
+
330
+ .universal-card-fallback-text {
331
+ color: #646a73;
332
+ font-size: 14px;
333
+ line-height: 22px;
334
+ }
335
+
309
336
  /* ---- div / note ---- */
310
337
  .universal-card-div > * + * { margin-top: 8px; }
311
338
  /* div 带前置图标:icon 在左、文本在右排成一行(复刻网页版 .universal-card-div 布局) */
@@ -508,9 +535,10 @@
508
535
 
509
536
 
510
537
  /* ---- interactive_container 可点击交互容器 ---- */
538
+ /* 真机无默认圆角;圆角只由 DSL/RAW corner_radius 内联输出(见 resolveCornerRadius)。 */
511
539
  .universal-card-interactive-container {
512
540
  display: flex; flex-direction: column; box-sizing: border-box;
513
- border-radius: 6px; padding: 0; text-decoration: none; color: inherit;
541
+ padding: 0; text-decoration: none; color: inherit;
514
542
  row-gap: 4px;
515
543
  transition: background-color .1s;
516
544
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-feishu-card/renderer",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
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.11"
31
+ "@open-feishu-card/adapter": "0.3.12"
32
32
  },
33
33
  "devDependencies": {
34
34
  "tsup": "^8.3.5",