@tfdesign/b-end 1.0.15 → 1.0.16

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tfdesign/b-end",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "TFDS B-end React components + Tailwind v4 theme.css; self-contained npm install (no monorepo clone required).",
@@ -30,7 +30,7 @@
30
30
  "access": "public"
31
31
  },
32
32
  "bin": {
33
- "tfds-setup": "./scripts/setup.mjs"
33
+ "tfds-setup": "scripts/setup.mjs"
34
34
  },
35
35
  "scripts": {
36
36
  "prepack": "node ../../scripts/generate-tfds-b-end-bundle.mjs && node ../../scripts/generate-tfds-b-end-types.mjs",
@@ -46,7 +46,7 @@ TFDS 是**桌面优先**的 B 端工具产品,响应式策略和 C 端不同
46
46
  | `1280 × 768` | 筛选栏、Tabs、按钮组、表格工具条不溢出;底部操作 / 输入区可达 |
47
47
  | `1024 × 768` | 次要侧栏允许折叠 / 收窄 / 内滚;主内容仍可完成核心任务 |
48
48
 
49
- ⛔ **禁止**通过浏览器缩放、隐藏关键控件或让 body 横向滚动来掩盖布局问题。
49
+ ⛔ **禁止**通过浏览器缩放、隐藏关键控件或让 body 横向滚动来掩盖布局问题。
50
50
  ✅ 横向超宽内容只能在对应组件 / 面板内部滚动,例如 Table、代码区、Tabs viewport、InfoDisplayPanel 栏内内容。
51
51
 
52
52
  ---
@@ -335,7 +335,7 @@ prompt editor / prompt workspace / runtime / run result /
335
335
 
336
336
  ### 1.5.5 AI 入口页(ChatHomePagePattern)背景与白卡边界(强约束)
337
337
 
338
- AI 入口页的核心是「**从 0 发起任务**」:用户第一步是输入一句需求 / 搜索助手 / 从模板开始。它的视觉应该是**浅灰大背景承载整页氛围**,并在灰底上直接组织 Hero、筛选行与卡片网格。
338
+ AI 入口页的核心是「**从 0 发起任务**」:用户第一步是输入一句需求 / 搜索助手 / 从模板开始。它的视觉应该是**浅灰大背景承载整页氛围**,并在灰底上直接组织 Hero、筛选行与卡片网格。
339
339
  ⚠️ 这里最常见的误用是:为了“看起来更像后台”,在右侧内容区外又包了一整张白色大卡片容器,导致页面层级变成“灰底 → 大白卡 → 卡片”,整体像嵌套面板,入口页气质被破坏。
340
340
 
341
341
  - ✅ **正确**:灰底为主,白卡只出现在**推荐卡片本身**(`<Card color="white" />`)
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "system": "b-end",
3
3
  "skill": "tfds",
4
- "generatedAt": "2026-05-13T11:12:08.290Z",
4
+ "generatedAt": "2026-05-13T13:51:39.617Z",
5
5
  "summary": "B 端 COMPONENTS 37 条 + 列表页模板 4 + PATTERNS 6;import 一律来自 @tfdesign/b-end。",
6
6
  "components": [
7
7
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "system": "b-end",
3
3
  "skill": "tfds",
4
- "generatedAt": "2026-05-13T11:12:08.290Z",
4
+ "generatedAt": "2026-05-13T13:51:39.617Z",
5
5
  "purpose": "轻量组件与页面模板目录。AI 先读本文件做选型;确定命中组件后,再到 components.index.json 按 id 读取 props / rules / examples。",
6
6
  "counts": {
7
7
  "total": 47,
@@ -135,16 +135,16 @@ const TABLE_SHELL = [
135
135
  ].join(' ');
136
136
 
137
137
  const TABLE = 'flex w-full min-w-0 flex-1 min-h-0 flex-col';
138
- const TABLE_VIEWPORT = 'flex flex-col flex-1 min-h-0 w-full overflow-x-auto overflow-y-hidden';
138
+ const TABLE_VIEWPORT = 'flex flex-col flex-1 min-h-0 w-full overflow-auto';
139
139
  const TABLE_CONTENT = 'flex w-full flex-1 min-h-0 flex-col';
140
- const TABLE_BODY = 'flex-1 min-h-0 w-full overflow-y-auto';
140
+ const TABLE_BODY = 'flex-1 min-h-0 w-full';
141
141
 
142
142
  /* ── 表头 / 单元格 ──
143
143
  * Table 自身不带任何背景色,行/单元格全部透明,由外部容器决定底色。
144
144
  * sticky 表头与固定列因此在滚动时会与内容色一致;如业务需要遮挡,
145
145
  * 请在外层容器设置背景色(白卡 / 灰底 page 等)。
146
146
  */
147
- const HEADER_ROW = 'grid w-full min-w-0';
147
+ const HEADER_ROW = 'sticky top-0 z-40 grid w-full min-w-0 bg-surface';
148
148
  const BODY_ROW = 'grid w-full min-w-0';
149
149
 
150
150
  const HEADER_CELL = [
@@ -226,6 +226,7 @@ const PAGE_ELLIPSIS = 'inline-flex size-8 items-center justify-center text-sm fo
226
226
 
227
227
  /* ── 空态 ── */
228
228
  const EMPTY_CELL = 'px-4 py-10 text-center text-sm font-normal leading-5 text-foreground-muted';
229
+ const PINNED_SURFACE_BACKGROUND = 'var(--color-surface, #FFFFFF)';
229
230
 
230
231
  /* ── 卡片型表单 ── */
231
232
  const CARD_FORM_BODY = 'flex min-h-0 flex-1 flex-col gap-3 overflow-y-auto';
@@ -318,6 +319,34 @@ function getColumnTemplateMinWidth(columns) {
318
319
  return columns.reduce((sum, column) => sum + resolveColumnMetrics(column).minWidth, 0);
319
320
  }
320
321
 
322
+ function getPinnedColumnStates(columns, fixedColumnsMode) {
323
+ const states = columns.map(() => null);
324
+
325
+ if (!columns.length || fixedColumnsMode === 'none') {
326
+ return states;
327
+ }
328
+
329
+ const lastColumnIndex = columns.length - 1;
330
+
331
+ if (isFirstColumnFixed(fixedColumnsMode)) {
332
+ states[0] = {
333
+ side: 'left',
334
+ offset: 0,
335
+ edge: 'right',
336
+ };
337
+ }
338
+
339
+ if (isLastColumnFixed(fixedColumnsMode) && lastColumnIndex > 0) {
340
+ states[lastColumnIndex] = {
341
+ side: 'right',
342
+ offset: 0,
343
+ edge: 'left',
344
+ };
345
+ }
346
+
347
+ return states;
348
+ }
349
+
321
350
  function buildCellClass(baseClass, sizeKey, extraClass = '') {
322
351
  return [
323
352
  baseClass,
@@ -327,11 +356,9 @@ function buildCellClass(baseClass, sizeKey, extraClass = '') {
327
356
  }
328
357
 
329
358
  function getPinnedCellStyle({
330
- columnIndex,
331
- columnsLength,
332
359
  align,
333
360
  minHeight,
334
- fixedColumnsMode,
361
+ pinnedState,
335
362
  isHeader = false,
336
363
  }) {
337
364
  const style = {
@@ -339,24 +366,28 @@ function getPinnedCellStyle({
339
366
  textAlign: align || 'left',
340
367
  };
341
368
 
342
- const fixFirst = isFirstColumnFixed(fixedColumnsMode) && columnIndex === 0;
343
- const fixLast = isLastColumnFixed(fixedColumnsMode) && columnIndex === columnsLength - 1;
344
-
345
- if (!fixFirst && !fixLast) {
369
+ if (!pinnedState) {
346
370
  return style;
347
371
  }
348
372
 
349
373
  style.position = 'sticky';
350
- style.background = 'inherit';
351
- style.zIndex = isHeader ? 30 : 20;
374
+ style.background = PINNED_SURFACE_BACKGROUND;
375
+ style.backgroundClip = 'padding-box';
376
+ style.zIndex = isHeader ? 50 : 30;
377
+
378
+ if (pinnedState.side === 'left') {
379
+ style.left = `${pinnedState.offset}px`;
380
+ }
381
+
382
+ if (pinnedState.side === 'right') {
383
+ style.right = `${pinnedState.offset}px`;
384
+ }
352
385
 
353
- if (fixFirst) {
354
- style.left = 0;
386
+ if (pinnedState.edge === 'right') {
355
387
  style.boxShadow = '2px 0 0 rgba(45, 66, 107, 0.06)';
356
388
  }
357
389
 
358
- if (fixLast) {
359
- style.right = 0;
390
+ if (pinnedState.edge === 'left') {
360
391
  style.boxShadow = '-2px 0 0 rgba(45, 66, 107, 0.06)';
361
392
  }
362
393
 
@@ -1107,6 +1138,10 @@ export default function Table({
1107
1138
  : [pageSize];
1108
1139
  const columnTemplate = useMemo(() => buildColumnGridTemplate(columns), [columns]);
1109
1140
  const columnTemplateMinWidth = useMemo(() => getColumnTemplateMinWidth(columns), [columns]);
1141
+ const pinnedColumnStates = useMemo(
1142
+ () => getPinnedColumnStates(columns, fixedColumnsMode),
1143
+ [columns, fixedColumnsMode],
1144
+ );
1110
1145
  const selectValue = Number.isNaN(Number(pageSize)) ? pageSize : Number(pageSize);
1111
1146
  const selectOptions = useMemo(() => {
1112
1147
  const normalized = Array.from(new Set([...pageSizeOptions, pageSize]));
@@ -1269,7 +1304,7 @@ export default function Table({
1269
1304
  <div className={TABLE} role="grid" aria-rowcount={visibleDataSource.length + 1} aria-colcount={columns.length}>
1270
1305
  <div className={HEADER_ROW} role="row" style={{ gridTemplateColumns: columnTemplate }}>
1271
1306
  {columns.map((column, columnIndex) => (
1272
- <div
1307
+ <div
1273
1308
  key={column.key || column.dataIndex || column.title}
1274
1309
  className={[
1275
1310
  HEADER_CELL,
@@ -1277,11 +1312,9 @@ export default function Table({
1277
1312
  ].join(' ')}
1278
1313
  role="columnheader"
1279
1314
  style={getPinnedCellStyle({
1280
- columnIndex,
1281
- columnsLength: columns.length,
1282
1315
  align: column.align,
1283
1316
  minHeight: HEADER_MIN_HEIGHT[normalizeCellSize(column.cellSize)] || HEADER_MIN_HEIGHT.default,
1284
- fixedColumnsMode,
1317
+ pinnedState: pinnedColumnStates[columnIndex],
1285
1318
  isHeader: true,
1286
1319
  })}
1287
1320
  >
@@ -1317,11 +1350,9 @@ export default function Table({
1317
1350
  )}
1318
1351
  role="gridcell"
1319
1352
  style={getPinnedCellStyle({
1320
- columnIndex,
1321
- columnsLength: columns.length,
1322
1353
  align: column.align,
1323
1354
  minHeight: getTypeMinHeight(normalizeColumnType(column.type), normalizeCellSize(column.cellSize)),
1324
- fixedColumnsMode,
1355
+ pinnedState: pinnedColumnStates[columnIndex],
1325
1356
  })}
1326
1357
  >
1327
1358
  <div className="w-full min-w-0">
@@ -4453,6 +4453,7 @@ export const PREVIEW_REGISTRY = {
4453
4453
  hideBaseVariantControl: true,
4454
4454
 
4455
4455
  getPreviewAreaStyle: () => ({
4456
+ background: '#FFFFFF',
4456
4457
  alignItems: 'stretch',
4457
4458
  justifyContent: 'flex-start',
4458
4459
  padding: '24px',