@tfdesign/b-end 1.0.14 → 1.0.15

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.
@@ -12,12 +12,22 @@
12
12
  * @prop {number} [activeIndex=0] — 受控:当前选中索引
13
13
  * @prop {number} [defaultIndex=0] — 非受控初始索引
14
14
  * @prop {function} [onChange=null] — `(index) => void` 切换回调
15
+ * @prop {'auto'|'visible'} [overflow='auto'] — 宽度不足时默认横向滚动,禁止撑破卡片/页面容器
15
16
  * @prop {string} [className=''] — 根节点类名
16
17
  */
17
18
  import { useState } from 'react';
18
19
 
19
20
  /* ── 容器样式 ── */
20
- const CONTAINER_BASE = 'inline-flex items-center relative w-fit';
21
+ const VIEWPORT_BASE = 'tfds-tabs block max-w-full min-w-0';
22
+ const VIEWPORT_OVERFLOW = {
23
+ auto: [
24
+ 'overflow-x-auto overflow-y-hidden overscroll-x-contain',
25
+ '[scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden',
26
+ ].join(' '),
27
+ visible: 'overflow-visible',
28
+ };
29
+
30
+ const CONTAINER_BASE = 'inline-flex w-max max-w-none items-center relative';
21
31
 
22
32
  const CONTAINER_VARIANT = {
23
33
  line: 'gap-2 bg-transparent border-b border-b-border',
@@ -98,27 +108,59 @@ export default function Tabs({
98
108
  activeIndex,
99
109
  defaultIndex = 0,
100
110
  onChange,
111
+ overflow = 'auto',
101
112
  className = '',
102
113
  ...rest
103
114
  }) {
104
115
  const [internalIndex, setInternalIndex] = useState(defaultIndex);
105
116
  const isControlled = activeIndex !== undefined;
106
117
  const currentIndex = isControlled ? (activeIndex ?? 0) : internalIndex;
118
+ const tabs = items ?? [];
107
119
 
108
120
  const handleClick = (index) => {
109
121
  if (!isControlled) setInternalIndex(index);
110
122
  onChange?.(index);
111
123
  };
112
124
 
125
+ const handleKeyDown = (event) => {
126
+ const lastIndex = tabs.length - 1;
127
+ if (lastIndex < 0) return;
128
+
129
+ const nextIndexMap = {
130
+ ArrowRight: Math.min(currentIndex + 1, lastIndex),
131
+ ArrowLeft: Math.max(currentIndex - 1, 0),
132
+ Home: 0,
133
+ End: lastIndex,
134
+ };
135
+ const nextIndex = nextIndexMap[event.key];
136
+ if (nextIndex === undefined || nextIndex === currentIndex) return;
137
+
138
+ event.preventDefault();
139
+ handleClick(nextIndex);
140
+ event.currentTarget.querySelectorAll('[role="tab"]')[nextIndex]?.focus();
141
+ };
142
+
113
143
  const containerCls = [
114
144
  CONTAINER_BASE,
115
145
  CONTAINER_VARIANT[variant],
146
+ ].filter(Boolean).join(' ');
147
+
148
+ const viewportCls = [
149
+ VIEWPORT_BASE,
150
+ VIEWPORT_OVERFLOW[overflow] || VIEWPORT_OVERFLOW.auto,
116
151
  className,
117
152
  ].filter(Boolean).join(' ');
118
153
 
119
154
  return (
120
- <div className={[`tfds-tabs`, containerCls].filter(Boolean).join(' ')} role="tablist" {...rest} data-tfds-component="Tabs">
121
- {(items ?? []).map((item, index) => {
155
+ <div
156
+ className={viewportCls}
157
+ role="tablist"
158
+ onKeyDown={handleKeyDown}
159
+ {...rest}
160
+ data-tfds-component="Tabs"
161
+ >
162
+ <div className={containerCls}>
163
+ {tabs.map((item, index) => {
122
164
  const isActive = index === currentIndex;
123
165
  const itemCls = [
124
166
  ITEM_BASE,
@@ -144,6 +186,7 @@ export default function Tabs({
144
186
  </button>
145
187
  );
146
188
  })}
189
+ </div>
147
190
  </div>
148
191
  );
149
192
  }
@@ -5,6 +5,9 @@
5
5
  export const TABS_TOKEN_MAP = {
6
6
  base: [
7
7
  { label: '默认尺寸', cssProp: 'size', value: 'SM(内容区 / 卡片内 / 表单分段 / 筛选维度默认)' },
8
+ { label: '宽度适配', cssProp: 'overflow-x', value: 'auto(默认启用横向滚动;Tabs 不允许撑破卡片 / 页面容器)' },
9
+ { label: '容器上限', cssProp: 'max-width', value: '100%(根节点必须 max-w-full + min-w-0)' },
10
+ { label: '内容宽度', cssProp: 'width', value: 'w-max(内层按 tab 内容自然宽度排列,外层负责裁切与滚动)' },
8
11
  { label: '字号', cssProp: 'font-size', token: '--text-sm', value: '14px' },
9
12
  { label: '行高', cssProp: 'line-height', value: '20px' },
10
13
  { label: '图标尺寸', cssProp: 'width/height', value: '16px' },
@@ -1114,7 +1114,7 @@ export const COMPONENTS = [
1114
1114
  '【类型】type=data 为数据卡片;type=product 为商品卡片 / 业务对象卡;type=info 为信息卡片1,用于展示带图标、主副标题和底部辅助信息的业务信息对象;type=info2 为信息卡片2,用于 AI 能力、推荐工具、重点入口等静态信息入口场景。type=animated 仅作为旧版兼容别名,生成新页面时不要使用。',
1115
1115
  '【分类决策表】选择 Card 类型时按业务对象优先级判断:1. 如果内容是指标摘要、趋势解读、工具入口、模板入口、脚本/话术/SOP/案例入口,使用 `type="data"`;2. 如果内容是具体商品、订单、门店、素材、工单附件、查询结果等可识别业务对象,使用 `type="product"`;3. 如果内容是智能体、知识库、应用、服务、官方能力等需要表达“身份 + 说明 + 辅助指标”的对象,使用 `type="info"`(信息卡片1);4. 如果内容是 AI 推荐、重点能力、首页推荐、配置向导、单步启动入口,且需要更强入口感,使用 `type="info2"`。当同一内容同时满足多个条件时,优先级为 product(具体对象)> info(身份对象)> info2(推荐入口)> data(摘要入口)。',
1116
1116
  '【分类 Bad Case】禁止用 `type="data"` 承载具体商品/订单对象;禁止用 `type="product"` 展示纯指标趋势或工具入口;禁止用 `type="info"` 只做强推荐按钮或首页营销入口;禁止用 `type="info2"` 承载商品/订单/复杂详情或列表型内容。AI 生成页面时,必须先判断内容语义,再选择卡片分类,不要仅根据是否有图标、是否有标签或视觉喜好选择类型。',
1117
- '【数据卡片适用场景】数据卡片不仅适用于指标摘要、趋势分析、服务能力入口和工具入口,也适用于模板、脚本、案例类内容入口,例如优秀知识模版、优秀案例模版、脚本模版、话术模版、内容生成模版、服务 SOP 模版等。此类内容仍使用数据卡片的摘要入口结构:标题 + 描述 + 可选指标/元信息 + 标签 + 右侧进入箭头;如果需要突出缩略图/封面图或具体商品、订单、附件对象,应改用商品卡片;如果需要强调 AI 能力、智能体、知识库或应用服务身份,应优先使用信息卡片1。',
1117
+ '【数据卡片适用场景】数据卡片不仅适用于指标摘要、趋势分析、服务能力入口和工具入口,也适用于模板、脚本、案例类内容入口,例如优秀知识模版、优秀案例模版、脚本模版、话术模版、内容生成模版、服务 SOP 模版等。此类内容仍使用数据卡片的摘要入口结构:标题 + 描述 + 可选指标/元信息 + 标签 + 右侧进入提示箭头;如果需要突出缩略图/封面图或具体商品、订单、附件对象,应改用商品卡片;如果需要强调 AI 能力、智能体、知识库或应用服务身份,应优先使用信息卡片1。',
1118
1118
  '【数据卡片结构】上半区固定为标题 + 描述 + 1-3 项指标,下半区为左下角标签 + 右侧单步进入按钮。数据卡片支持可选右侧图标:默认 `dataIconVisible="hidden"` 不显示图标;需要图标时设置 `dataIconVisible="visible"` 并传入 `dataIconName`,标题/描述/指标区域居左,图标容器 48×48px、圆角必须使用 rounded-xl token(16px),仅显示在数据卡片右上角并与文字区域顶部对齐,图标与左侧标题内容区左右间距必须等于数据卡片四周 padding,即 p-5 / 20px。图标样式复用信息卡片1同名容器逻辑:`dataIconStyle="inverse"` 为黑底白 icon(默认),`dataIconStyle="tone"` 为彩色浅底;色系通过 `dataIconTone` 选取。',
1119
1119
  '【数据卡片数据项】数据卡片的指标数据通过 `stats` 动态配置,用于按场景展示摘要维度,例如触达用户、会话量、收藏量、转化率、完成率、运行次数、更新时间、异常数量等。数据结构固定为 `{ iconName, value, tooltip? }`,其中 iconName 必须复用 Icon,value 为短数字或短文字,tooltip 用于 hover/focus 展示该数据项的详细说明。最多展示 3 组,超出运行时自动截断;每组样式固定为 14px 图标 + 12px / 400 文案,项间距 20px。Tooltip 只做补充解释,不承载核心操作路径。',
1120
1120
  '【信息卡片1结构】信息卡片1支持两种布局:`infoLayout="icon-right"` 为默认布局,即“图标在右”;左侧承载标题/副标题/底部辅助信息,图标整体放在最右侧,状态徽标紧跟主标题右侧。`infoLayout="default"` 即“图标在左”;左侧固定 48px 图标容器,右侧上半区为主标题 + 副标题 + 右上角状态徽标,底部为辅助显示区。信息卡片1图标与标题内容区左右间距必须等于信息卡片1四周 padding,即 p-6 / 24px。信息卡片1适合内容对象、智能体、知识库、应用、服务入口等需要“图标 + 长说明 + 关键辅助信息”的场景。',
@@ -1131,9 +1131,12 @@ export const COMPONENTS = [
1131
1131
  '【商品文案】标题最多 1 行,字号统一为 text-base / 16px / 600,优先放用户识别对象所需的名称 / 订单号 / 关键标识;副标题最多 1 行,用于展示数量、价格、规格、订单金额、下单时间、售后进度等辅助信息;不要把长说明、操作指引或多段备注塞入副标题',
1132
1132
  '【信息卡片1底部数据项】右下角数据项通过 `infoStats` 动态配置,用于按场景展示该信息对象的关键辅助数据,例如启用量、评分、调用量、响应率、转化率、更新时间、风险数量、操作入口等。数据结构固定为 `{ iconName, value, tooltip? }`,其中 iconName 必须复用 Icon,value 为短数字或短文字,tooltip 用于 hover/focus 展示该数据项的详细说明。最多展示 3 组,超出运行时自动截断;每组样式固定为 14px 图标 + 12px / 400 文案,项间距 20px,必须与底部身份区保持 16px 行高内垂直居中。',
1133
1133
  '【信息卡片1文案】默认文案按抖音官方 AI 能力模拟,标题为“抖音 AI 创作助手”,副标题说明短视频脚本、标题推荐、封面文案和热点灵感生成能力。标题建议 1 行,字号统一为 text-base / 16px / 600;副标题建议 2 行内,字号 12px / 400,不要堆叠多段正文。默认底部数据项使用“启用量 / 评分 / 成功率”,并通过 Tooltip 解释每组数据含义;底部整栏所有头像、图标、文字必须保持 16px 行高内垂直居中并对齐到同一行。信息卡片1右下角数据项应优先展示真实数据维度,不默认展示“立即体验”这类操作文案;单步进入操作应使用信息卡片2或页面内独立按钮。',
1134
- '【信息卡片2】info2 不是通用卡片替代品,只用于需要突出推荐、智能能力、配置向导、重点工具入口的静态信息入口。结构为左上图标 + 标题/描述 + 左下角徽标 + 右下角圆形箭头操作入口;默认标题“智能策略生成”、默认图标色系 grey、徽标“AI 推荐”、操作文案“立即体验”。',
1134
+ '【整卡点击(强约束)】所有 Card 分类只要传入 `onAction`(表示存在详情页 / 二级页 / 深入工作页),默认整张卡片区域都必须可点击进入,并支持鼠标点击与 `Enter / Space` 键盘触发;不要把查看交互只缩成右下角一个小箭头热区。数据卡片和信息卡片2可保留右下角圆形箭头作为“进入提示”,但箭头只做辅助热区,点击后与整卡复用同一回调。若卡片内未来存在独立按钮、更多菜单或 Tag 操作,子交互需阻止冒泡,避免误触整卡跳转。',
1135
+ '【onAction 语义】`onAction` 不是“箭头按钮专属回调”,而是 Card 的统一详情跳转能力。data / product / info / info2 四类卡片都可以传 `onAction`;传入后整卡即视为可进入对象,`actionAriaLabel` 同时作为整卡和辅助箭头按钮的无障碍文案。',
1136
+ '【数据卡片适用场景】数据卡片不仅适用于指标摘要、趋势分析、服务能力入口和工具入口,也适用于模板、脚本、案例类内容入口,例如优秀知识模版、优秀案例模版、脚本模版、话术模版、内容生成模版、服务 SOP 模版等。此类内容仍使用数据卡片的摘要入口结构:标题 + 描述 + 可选指标/元信息 + 标签 + 右侧进入提示箭头;如果需要突出缩略图/封面图或具体商品、订单、附件对象,应改用商品卡片;如果需要强调 AI 能力、智能体、知识库或应用服务身份,应优先使用信息卡片1。',
1137
+ '【信息卡片2】info2 不是通用卡片替代品,只用于需要突出推荐、智能能力、配置向导、重点工具入口的静态信息入口。结构为左上图标 + 标题/描述 + 左下角徽标 + 右下角圆形箭头提示入口;默认标题“智能策略生成”、默认图标色系 grey、徽标“AI 推荐”、操作文案“立即体验”。',
1135
1138
  '【信息卡片2标签】info2 的 animatedBadge 固定展示在左下角,必须与标题左边界对齐,并与右下角圆形箭头操作按钮保持同一行垂直居中。标签默认一律使用 Tag variant="white" + fontWeight="regular",不跟随 animatedTone,禁止把标签放到右上角。',
1136
- '【信息卡片2操作】右下角操作入口必须复用数据卡片同款圆形箭头按钮样式和交互:28×28px、圆形、默认透明描边 + text-tertiary 图标,卡片 hover 或按钮 hover 时变为黑色描边与黑色箭头,按下 active:scale-[0.96]。animatedActionText 只作为操作语义文案,不直接渲染为文字按钮。',
1139
+ '【信息卡片2操作】右下角操作入口必须复用数据卡片同款圆形箭头按钮样式和交互:28×28px、圆形、默认透明描边 + text-tertiary 图标,卡片 hover 或按钮 hover 时变为黑色描边与黑色箭头,按下 active:scale-[0.96]。animatedActionText 只作为操作语义文案,不直接渲染为文字按钮。只要传了 `onAction`,默认整张 info2 卡片和右下角箭头都能进入同一个详情目标。',
1137
1140
  '【信息卡片2视觉】info2 不包含柔光、流动光带或特殊 hover 位移;背景颜色、边框颜色和 hover 投影完全复用普通 Card 的 color=white / grey 规则。animatedTone 只影响左上图标容器色系,不影响卡片背景、边框和底部标签。',
1138
1141
  '【信息卡片2图标 Hover】卡片 hover 时,左上图标容器必须自动切换为当前 animatedTone 对应的 hover 背景、纯白 icon、透明描边:brand→brand-500、blue→blue-500、purple→purple-500、green→green-500、orange→orange-500、grey→black。禁止 hover 后继续显示浅底、深色 icon 或描边色。',
1139
1142
  '【颜色】color=white 使用 65% 白底 + 白色描边;color=grey 使用 Blue Grey 100 背景 + Blue Grey 300 描边',
@@ -1143,7 +1146,7 @@ export const COMPONENTS = [
1143
1146
  '【状态】白底卡默认态使用 65% 白底 + 轻白描边;hover 后补满白底并出现业务卡片专用投影;灰底卡保持灰底与灰描边并保留投影反馈',
1144
1147
  '【指标】指标项推荐控制在 3 项以内,图标 14px、文字 12px,项间距 20px',
1145
1148
  '【标签】通用 tags 只用于数据卡片,展示在左下角并与右侧操作按钮水平对齐;商品卡片和信息卡片1禁止渲染通用 tags,主标题左侧不得出现“标签”等前缀标签,运行时会隐藏误注入的 white Tag。Card 内所有标签默认必须使用圆角矩形样式 `radius="md"`,不使用全圆胶囊 `radius="full"`;数据卡片、商品状态标签、信息卡片1徽标都遵守该规则。数据卡片不论是否展示图标、也不论 dataIconStyle 为 tone 或 inverse,默认标签都必须统一使用 grey 标签样式。商品状态标签和信息卡片1徽标仅可展示在右上角并与标题水平对齐。信息卡片1徽标与 infoIconStyle 联动:`tone` 时一律 grey,`inverse` 时默认彩色 purple;卡片彩色标签优先使用 purple(紫色)/ teal(青绿色)/ blue(蓝色)/ cyan(青色)/ orange(橙色),避免优先使用 pink、red、yellow 等过强警示或装饰色。商品状态标签复用 green + l + md;不显示图标和关闭按钮,建议使用 2-4 字短标签。',
1146
- '【操作】数据卡片右侧箭头按钮只承载进入 / 查看详情等单步操作,aria-label 需说明目标;商品卡片不使用箭头按钮',
1149
+ '【操作】当 `onAction` 存在时,整张 Card 是主进入热区;数据卡片 / 信息卡片2 的右侧圆形箭头只承载同一个进入动作的视觉提示,aria-label 需说明目标;商品卡片和信息卡片1不额外渲染箭头按钮,但仍应支持整卡点击查看。',
1147
1150
  '【信息卡片1语义映射】AI / 智能能力推荐使用 magic-wand-01-stroked + pink 或 brand;数据洞察可用 bar-chart / line-chart 类图标 + blue;任务自动化可用 zap / settings 类图标 + orange;安全、审核、风控可用 shield / alert 类图标 + red 或 orange;知识库、文档、客服可用 book / message 类图标 + green 或 blue。色系只表达类别和状态,不用于装饰。',
1148
1151
  '【文案长度】数据卡片标题优先控制在 1 行内,描述建议 2 行内;商品卡片标题和副标题均为 1 行截断,避免状态标签被挤压或换行;信息卡片1标题 1 行、副标题 2 行,底部辅助区允许换行但不得撑破卡片宽度。',
1149
1152
  ],
@@ -1159,6 +1162,8 @@ export const COMPONENTS = [
1159
1162
  { label: '信息卡片1(风控能力)', code: '<Card type="info" infoIconName="shield-01-stroked" infoIconTone="orange" title="内容安全巡检" description="对视频、评论和私信内容进行风险识别,辅助运营及时处理异常内容。" infoMetaBadge="内测中" infoMetaBadgeVariant="orange" infoMetaLabel="郭泽智" infoStats={[{ iconName: "alert-circle-stroked", value: "23 条风险", tooltip: "当前待处理的高风险内容数量" }, { iconName: "check-circle-stroked", value: "98.6%", tooltip: "系统自动识别准确率" }]} />' },
1160
1163
  { label: '信息卡片2', code: '<Card type="info2" title="智能策略生成" description="基于业务目标、历史数据和运营规则,自动生成可执行策略建议。" animatedIconName="magic-wand-01-stroked" animatedBadge="AI 推荐" animatedActionText="立即体验" onAction={() => {}} />' },
1161
1164
  { label: '信息卡片2(报告入口)', code: '<Card type="info2" animatedTone="purple" title="自动生成报告" description="汇总核心指标、异常归因和建议动作,生成可直接复用的业务报告。" animatedBadge="Beta" animatedActionText="开始生成" onAction={() => {}} />' },
1165
+ { label: '商品卡片整卡点击', code: '<Card type="product" title="订单 202604300018" description="共 3 件 · 实付 ¥268.00 · 退款中" productStatus="退款中" actionAriaLabel="查看订单详情" onAction={() => {}} />' },
1166
+ { label: '信息卡片1整卡点击', code: '<Card type="info" infoIconName="magic-wand-01-stroked" title="抖音 AI 创作助手" description="抖音官方 AI 创作能力,支持短视频脚本、标题推荐、封面文案和热点灵感生成。" infoMetaBadge="官方能力" actionAriaLabel="查看创作助手详情" onAction={() => {}} />' },
1162
1167
  { label: '自定义标题', code: '<Card title="重点客户满意度追踪" description="汇总近 7 天满意度变化、问题归因和渠道分布,并给出跟进建议。" onAction={() => {}} />' },
1163
1168
  { label: '自定义指标', code: '<Card stats={[{ iconName: "users-01-stroked", value: "860", tooltip: "近 7 天触达用户数" }, { iconName: "message-chat-square-stroked", value: "3,421", tooltip: "近 7 天累计会话量" }, { iconName: "hearts-stroked", value: "95", tooltip: "用户收藏或点赞次数" }]} onAction={() => {}} />' },
1164
1169
  { label: '自定义标签', code: '<Card tags={["客服", "高优先"]} actionAriaLabel="查看客服趋势分析" onAction={() => {}} />' },
@@ -1172,6 +1177,7 @@ export const COMPONENTS = [
1172
1177
  { label: '✅ Good(趋势摘要用数据卡片)', code: '<Card title="近 7 天转化趋势" description="成交转化率 +12%,互动提升 8%" stats={[{ iconName: "trend-up-01-stroked", value: "+12%" }]} tags={["趋势", "经营"]} onAction={() => {}} />' },
1173
1178
  { label: '❌ Bad(用信息卡片1做单步推荐入口)', code: '/* 禁止!推荐入口更强调行动,不需要身份区、头像和多项辅助指标 */\n<Card type="info" title="智能策略生成" description="一键生成可执行策略建议" infoMetaBadge="AI 推荐" />' },
1174
1179
  { label: '✅ Good(推荐入口用信息卡片2)', code: '<Card type="info2" title="智能策略生成" description="基于业务目标、历史数据和运营规则,自动生成可执行策略建议。" animatedBadge="AI 推荐" animatedActionText="立即体验" onAction={() => {}} />' },
1180
+ { label: '❌ Bad(有详情页却只让小箭头可点)', code: '/* 禁止!传了 onAction 的 Card 默认整卡都应可点,不要在外层再手动禁用主热区或只保留右下角小按钮 */\n<Card type="info2" onAction={() => {}} className="pointer-events-auto" />' },
1175
1181
  { label: '❌ Bad(用信息卡片2承载商品对象)', code: '/* 禁止!商品对象需要图片/状态/关键识别信息,不应用推荐入口卡承载 */\n<Card type="info2" title="海底捞门店通用双人套餐" description="数量 1 · ¥128.00 · 月售 2,361" animatedBadge="已使用" />' },
1176
1182
  { label: '✅ Good(商品对象用商品卡片)', code: '<Card type="product" title="海底捞门店通用双人套餐" description="数量 1 · ¥128.00 · 月售 2,361" productStatus="已使用" />' },
1177
1183
  ],
@@ -1829,6 +1835,7 @@ export const COMPONENTS = [
1829
1835
  { name: 'activeIndex', type: 'number', default: 0 },
1830
1836
  { name: 'defaultIndex', type: 'number', default: 0 },
1831
1837
  { name: 'onChange', type: 'function', default: null },
1838
+ { name: 'overflow', type: 'enum', options: ['auto', 'visible'], default: 'auto' },
1832
1839
  ],
1833
1840
  labels: {
1834
1841
  variant: {
@@ -1844,6 +1851,8 @@ export const COMPONENTS = [
1844
1851
  '【尺寸默认铁律·最新】所有 4 种 Tabs 变体(line / button / pill / segment)在内容展示区、白卡内、卡片内、表单分段、筛选维度、Playground 面板内切换时,**默认尺寸统一为 `size="sm"`**。AI 生成页面时可以省略 size(组件默认即 SM),或显式写 `size="sm"`;⛔ 禁止在内容区默认写 `size="md"` / `size="lg"`。',
1845
1852
  '【尺寸例外】只有 Tabs 位于**整个平台顶部 header / 页面级顶导 / 产品级一级导航**时,才允许根据场景使用 `size="md"` 或 `size="lg"`:例如页面级 Pill Tabs、平台顶部模块切换、顶部导航胶囊。只要 Tabs 在白卡、面板、内容区、表单区、筛选区内,就回到 SM。',
1846
1853
  '【分段器 = segment 变体】设计稿「分段器」对应代码 **`variant="segment"`**(灰底轨道 + 白底选中块),与 `Tag` 无关。① **卡片内**小模块、指标维度、白/灰卡内并列区块切换 → **默认优先** `variant="segment" size="sm"`。② **页面主内容区顶部**、整块**布局级**主视图切换(并排等大内容区,如「列表/看板/日历」并列)→ **默认优先** `variant="segment" size="sm"`,除非它是页面级顶部 header 导航。',
1854
+ '【防溢出铁律】Tabs 默认 `overflow="auto"`,根节点内置 `max-w-full min-w-0 overflow-x-auto`,内层按 `w-max` 保持单行内容宽度;当 tab 总宽度超过卡片 / 侧栏 / 面板宽度时,必须在组件内部横向滚动,禁止把父容器、白卡或页面撑宽。AI 生成页面时不要给 Tabs 或父级写 `w-fit`、`min-w-max`、固定大宽度、`overflow-visible` 来强行展示全部 tab。',
1855
+ '【父容器适配】Tabs 所在的横向 flex 区、白卡 header、侧栏卡片、抽屉面板内必须允许收缩:父级建议 `min-w-0 max-w-full overflow-hidden`,标题区与操作区并排时,Tabs 容器放在 `min-w-0 flex-1` 或 `max-w-full` 区域内。禁止让 Tabs 与标题互相挤压导致标题竖排、页面横向滚动或卡片越界。',
1847
1856
  '【其它 variant 自判】详情子页签、面板内横向主导航感 → `line`;内容区内强调「块状」分类 → `button`;顶导/浮层/工具条胶囊规格 → `pill`;表单分段、筛选维度、与上两类「分段器优先」场景之外的 2~6 项切换 → 可用 `segment` 或按视觉在 `line`/`button` 间选择。',
1848
1857
  '【选型·与 Button】Tabs 的每一项是「切换可见内容」;Button 是「执行操作」。同一组互斥展示若无需下划线/胶囊视觉,且只有 2 个选项,可用 Radio horizontal,但「多面板」仍优先 Tabs',
1849
1858
  '【选型·与 Select】2~6 个面板级分区用 Tabs;单个字段在表单里从 10+ 枚举中选一个值 → Select,不要把每个枚举值做成 Tab',
@@ -1853,9 +1862,9 @@ export const COMPONENTS = [
1853
1862
  '【内描边】pill 容器的白色描边为内描边(ring-inset / box-shadow inset),不占布局空间,所以容器实际高度 = padding × 2 + 选项高度。若手写胶囊,描边必须用 ring-inset 或 box-shadow inset,不能用 border,否则会撑高 2px',
1854
1863
  '【表单/筛选器】segment(分段器)用于表单切换、筛选条件等场景,灰色容器 + 白色选中块',
1855
1864
  '【悬浮反馈】button、pill、segment 三种变体的非选中项悬浮均使用语义填充色(fill-default)',
1856
- '【标签数量】建议 2-6 个标签,超过时考虑可折叠或下拉',
1857
- '【文字长度】标签文字推荐 2-4 个汉字,保持简洁',
1858
- '【无障碍】每个标签使用 role="tab",容器使用 role="tablist",选中态用 aria-selected',
1865
+ '【标签数量】建议 2-6 个标签;2-6 个但容器较窄时保持单行横向滚动,不允许溢出;超过 6 个或标签会动态增长时,优先改用 Select / Filter / 更多菜单,不要继续横排堆满 Tabs。',
1866
+ '【文字长度】标签文字推荐 2-4 个汉字,保持简洁;业务必须展示长标签时保持 `whitespace-nowrap` 并依赖横向滚动,不要逐字换行或压缩字间距。',
1867
+ '【无障碍】每个标签使用 role="tab",容器使用 role="tablist",选中态用 aria-selected;组件支持 ArrowLeft / ArrowRight / Home / End 键盘切换。',
1859
1868
  ],
1860
1869
  examples: [
1861
1870
  { label: '线条型(默认 SM)', code: '<Tabs variant="line" items={[{label:"标签一"},{label:"标签二"},{label:"标签三"}]} />' },
@@ -1865,6 +1874,7 @@ export const COMPONENTS = [
1865
1874
  { label: '分段器', code: '<Tabs variant="segment" items={[{label:"SM"},{label:"MD"},{label:"LG"}]} defaultIndex={1} />' },
1866
1875
  { label: '卡片内分段器(小模块切换,默认 SM)', code: '<Tabs variant="segment" items={[{label:"概览"},{label:"明细"},{label:"日志"}]} />' },
1867
1876
  { label: '主区顶部分段器(布局切换,默认 SM)', code: '<Tabs variant="segment" items={[{label:"列表"},{label:"看板"},{label:"日历"}]} />' },
1877
+ { label: '窄卡片内自动滚动', code: '<div className="min-w-0 max-w-full overflow-hidden"><Tabs variant="segment" items={[{label:"全部"},{label:"通用"},{label:"社区"},{label:"电商"},{label:"生服"},{label:"直播"}]} /></div>' },
1868
1878
  { label: '内容区显式 SM', code: '<Tabs variant="line" size="sm" items={[{label:"Tab1"},{label:"Tab2"}]} />' },
1869
1879
  { label: '顶部 Header 例外(可用 MD/LG)', code: '<Tabs variant="pill" size="md" items={[{label:"服务策略"},{label:"运行数据"}]} />' },
1870
1880
  { label: '❌ Bad(内容区默认使用 MD)', code: '/* 禁止!Tabs 在白卡/内容区/表单分段/筛选维度内默认必须 SM */\n<Tabs variant="segment" size="md" items={[{label:"概览"},{label:"明细"}]} />' },
@@ -1873,6 +1883,7 @@ export const COMPONENTS = [
1873
1883
  { label: '❌ Bad(手写 div role=tablist)', code: '/* 禁止!手写 Tab 缺 token、缺指示器、键盘交互不全 */\n<div role="tablist"><span className="px-3 border-b-2">列表</span><span className="px-3">看板</span></div>' },
1874
1884
  { label: '✅ Good(user/assistant 切换用 segment)', code: '<Tabs variant="segment" items={[{label:"User"},{label:"Assistant"}]} />' },
1875
1885
  { label: '❌ Bad(用 Tag 当面板切换)', code: '/* 禁止!Tag 是状态/分类标签,不承担面板切换;切换面板必须 Tabs */\n<div className="flex gap-2"><Tag variant="brand">Input</Tag><Tag variant="grey">Output</Tag></div>' },
1886
+ { label: '❌ Bad(Tabs 撑破窄容器)', code: '/* 禁止!w-fit/min-w-max 会把卡片撑破;保持默认 overflow="auto" 即可 */\n<Tabs className="w-fit min-w-max" variant="segment" items={categoryTabs} />' },
1876
1887
  ],
1877
1888
  keywords: [
1878
1889
  'Tabs', 'tabs', '标签页', '页签', '选项卡', '切换器',