diff --git a/design-system/apps/design-lab/src/i18n/componentMetadata.ts b/design-system/apps/design-lab/src/i18n/componentMetadata.ts index 834840cb1a..999b9a822e 100644 --- a/design-system/apps/design-lab/src/i18n/componentMetadata.ts +++ b/design-system/apps/design-lab/src/i18n/componentMetadata.ts @@ -58,6 +58,7 @@ const descriptionKeys: Readonly> = { TerminalControlToolCard: "component.TerminalControlToolCard.description", TodoToolCard: "component.TodoToolCard.description", Toolbar: "component.Toolbar.description", + Tooltip: "component.Tooltip.description", ViewImageToolCard: "component.ViewImageToolCard.description", WebFetchToolCard: "component.WebFetchToolCard.description", WebSearchToolCard: "component.WebSearchToolCard.description", diff --git a/design-system/apps/design-lab/src/i18n/messages.ts b/design-system/apps/design-lab/src/i18n/messages.ts index ec18d6eda6..09046d6a76 100644 --- a/design-system/apps/design-lab/src/i18n/messages.ts +++ b/design-system/apps/design-lab/src/i18n/messages.ts @@ -99,6 +99,7 @@ export const enUSMessages = { "component.TerminalControlToolCard.description": "An ambient trace for interrupting or terminating a terminal session.", "component.TodoToolCard.description": "An ambient task-progress card with compact and expandable list presentations.", "component.Toolbar.description": "A responsive bar with independent leading, centered, trailing, and horizontally scrollable composition regions.", + "component.Tooltip.description": "A portaled, delay-managed hover label that flips placement at viewport edges and supports cursor-following and interactive content.", "component.ViewImageToolCard.description": "An ambient image-result card with an expandable preview and accessible lightbox.", "component.WebFetchToolCard.description": "An ambient web-fetch card with source metadata, result actions, and content preview.", "component.WebSearchToolCard.description": "An ambient web-search card with link, snippet, URL, and summary result presentations.", @@ -244,6 +245,8 @@ export const enUSMessages = { "components.preview.scrollAreaLabel": "Scrollable activity", "components.preview.scrollAreaItem": "Activity item {index}", "components.preview.menuLabel": "Session menu", + "components.preview.tooltipTrigger": "Rename session", + "components.preview.tooltipContent": "Rename the current session", "components.preview.menuSectionTitle": "Sessions", "components.preview.menuItemOne": "Welcome session", "components.preview.menuItemTwo": "Design review", @@ -402,6 +405,8 @@ export const enUSMessages = { "detail.option.left": "Left", "detail.option.right": "Right", "detail.option.confirmation": "Confirmation", + "detail.option.top": "Top", + "detail.option.bottom": "Bottom", "detail.option.off": "Off", "detail.option.on": "On", "detail.option.selected": "Selected", @@ -694,6 +699,7 @@ export const zhCNMessages = { "component.TerminalControlToolCard.description": "低关注终端控制轨迹,用于呈现中断或终止终端会话的结果。", "component.TodoToolCard.description": "低关注任务进度卡片,支持紧凑摘要与可展开列表两种呈现。", "component.Toolbar.description": "具备独立前置、居中、尾部与横向滚动区域的响应式工具栏。", + "component.Tooltip.description": "具备 Portal、延迟管理、视口边缘翻转,并支持跟随光标与可交互内容的悬停提示。", "component.ViewImageToolCard.description": "低关注图片结果卡片,包含可展开预览和无障碍灯箱。", "component.WebFetchToolCard.description": "低关注网页读取卡片,包含来源元数据、结果操作与内容预览。", "component.WebSearchToolCard.description": "低关注网页搜索卡片,统一呈现链接、摘要、网址与结果列表。", @@ -839,6 +845,8 @@ export const zhCNMessages = { "components.preview.scrollAreaLabel": "可滚动动态", "components.preview.scrollAreaItem": "动态项目 {index}", "components.preview.menuLabel": "会话菜单", + "components.preview.tooltipTrigger": "重命名会话", + "components.preview.tooltipContent": "重命名当前会话", "components.preview.menuSectionTitle": "会话", "components.preview.menuItemOne": "欢迎会话", "components.preview.menuItemTwo": "设计审查", @@ -997,6 +1005,8 @@ export const zhCNMessages = { "detail.option.left": "左侧", "detail.option.right": "右侧", "detail.option.confirmation": "待确认", + "detail.option.top": "顶部", + "detail.option.bottom": "底部", "detail.option.off": "关闭", "detail.option.on": "开启", "detail.option.selected": "选中", @@ -1283,6 +1293,7 @@ export const zhTWMessages = { "component.TerminalControlToolCard.description": "低關注終端控制軌跡,用於呈現中斷或終止終端工作階段的結果。", "component.TodoToolCard.description": "低關注任務進度卡片,支援緊湊摘要與可展開列表兩種呈現。", "component.Toolbar.description": "具備獨立前置、置中、尾端與橫向捲動區域的響應式工具列。", + "component.Tooltip.description": "具備 Portal、延遲管理、視口邊緣翻轉,並支援跟隨游標與可互動內容的懸停提示。", "component.ViewImageToolCard.description": "低關注圖片結果卡片,包含可展開預覽和無障礙燈箱。", "component.WebFetchToolCard.description": "低關注網頁讀取卡片,包含來源中繼資料、結果操作與內容預覽。", "component.WebSearchToolCard.description": "低關注網頁搜尋卡片,統一呈現連結、摘要、網址與結果列表。", @@ -1423,6 +1434,8 @@ export const zhTWMessages = { "components.preview.scrollAreaLabel": "可捲動動態", "components.preview.scrollAreaItem": "動態項目 {index}", "components.preview.menuLabel": "工作階段選單", + "components.preview.tooltipTrigger": "重新命名工作階段", + "components.preview.tooltipContent": "重新命名目前的工作階段", "components.preview.menuSectionTitle": "工作階段", "components.preview.menuItemOne": "歡迎工作階段", "components.preview.menuItemTwo": "設計審查", @@ -1575,6 +1588,8 @@ export const zhTWMessages = { "detail.option.left": "左側", "detail.option.right": "右側", "detail.option.confirmation": "待確認", + "detail.option.top": "頂部", + "detail.option.bottom": "底部", "detail.option.off": "關閉", "detail.option.on": "開啟", "detail.option.selected": "已選取", diff --git a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx index 1c9c88d3e6..42f104a4a2 100644 --- a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx +++ b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx @@ -69,6 +69,7 @@ import { ToolbarBadge, ToolbarGroup, ToolbarSeparator, + Tooltip, type ColorScheme, type ConfirmDialogType, type ContrastMode, @@ -282,6 +283,8 @@ export function ComponentDetailPage({ ? "selected" : component.name === "ScrollArea" ? "auto" + : component.name === "Tooltip" + ? "top" : "default", ); const [inspectorDisabled, setInspectorDisabled] = useState(false); @@ -345,6 +348,8 @@ export function ComponentDetailPage({ return ["selected", "unselected", "hover", "disabled"] as const; case "Toolbar": return ["default", "with-center", "overflow"] as const; + case "Tooltip": + return ["top", "bottom", "left", "right"] as const; default: return ["off", "on", "focus-visible", "disabled"] as const; } @@ -436,6 +441,9 @@ export function ComponentDetailPage({ if (component.name === "FieldGroup") { return `import { Field, FieldGroup, FieldRow, FormSection, Input } from "@bitfun/ui";\nimport { Settings } from "lucide-react";\n\n}\n title="${t("components.preview.modalSectionTitle")}"\n>\n \n \n \n \n \n \n \n \n \n \n \n \n`; } + if (component.name === "Tooltip") { + return `import { Tooltip } from "@bitfun/ui";\n\n\n \n`; + } if (component.name === "Menu") { return `import { Menu, MenuItem, MenuSection, MenuSeparator } from "@bitfun/ui";\nimport { MessageCircle } from "lucide-react";\n\n\n \n }>${t("components.preview.menuItemOne")}\n }>${t("components.preview.menuItemTwo")}\n \n \n \n ${t("components.preview.menuDisabledItem")}\n \n`; } @@ -1040,6 +1048,20 @@ export function ComponentDetailPage({ ); } + if (component.name === "Tooltip") { + return ( + + + + ); + } + if (component.name === "Menu") { const itemCount = state === "scrolling" ? 12 : 3; return ( @@ -1619,7 +1641,7 @@ export function ComponentDetailPage({ ))} - ) : component.name === "ActionCard" || component.name === "ActionItem" || component.name === "Field" || component.name === "FieldGroup" || component.name === "Input" || component.name === "KeyHint" || component.name === "Menu" || component.name === "NavigationPanel" || component.name === "PageHeader" || component.name === "ScrollArea" || component.name === "SearchField" || component.name === "SegmentedControl" || component.name === "Select" || component.name === "StatusPill" ? ( + ) : component.name === "ActionCard" || component.name === "ActionItem" || component.name === "Field" || component.name === "FieldGroup" || component.name === "Input" || component.name === "KeyHint" || component.name === "Menu" || component.name === "NavigationPanel" || component.name === "PageHeader" || component.name === "ScrollArea" || component.name === "SearchField" || component.name === "SegmentedControl" || component.name === "Select" || component.name === "StatusPill" || component.name === "Tooltip" ? (
diff --git a/design-system/apps/design-lab/src/pages/ComponentsPage.tsx b/design-system/apps/design-lab/src/pages/ComponentsPage.tsx index 96e0ff5a8e..4a6ff35152 100644 --- a/design-system/apps/design-lab/src/pages/ComponentsPage.tsx +++ b/design-system/apps/design-lab/src/pages/ComponentsPage.tsx @@ -57,6 +57,7 @@ import { ToolbarBadge, ToolbarGroup, ToolbarSeparator, + Tooltip, type ColorScheme, type ContrastMode, type DensityMode, @@ -109,6 +110,7 @@ const componentIcons = { Switch: ToggleLeft, TabGroup: PanelTop, Toolbar: PanelTop, + Tooltip: MessageCircle, } as const; function ComponentCardPreview({ component }: { component: ComponentMeta }) { @@ -452,6 +454,14 @@ function ComponentCardPreview({ component }: { component: ComponentMeta }) { )} /> ); + case "Tooltip": + return ( + + + + ); default: return null; } diff --git a/design-system/packages/design-tokens/src/system.tokens.json b/design-system/packages/design-tokens/src/system.tokens.json index ea7a32b1eb..e48b5612c3 100644 --- a/design-system/packages/design-tokens/src/system.tokens.json +++ b/design-system/packages/design-tokens/src/system.tokens.json @@ -352,6 +352,20 @@ "itemIconSize": { "$value": "14px" }, "scrollbarGap": { "$value": "2px" } }, + "tooltip": { + "$type": "dimension", + "maxInlineSize": { "$value": "280px" }, + "maxBlockSize": { "$value": "320px" }, + "paddingBlock": { "$value": "6px" }, + "paddingInline": { "$value": "10px" }, + "surfaceRadius": { "$value": "{radius.sm}" }, + "arrowSize": { "$value": "8px" }, + "gap": { + "$description": "Distance between the trigger (or cursor) and the tooltip surface.", + "$value": "{space.2}" + }, + "fontSize": { "$value": "{font.size.xs}" } + }, "modal": { "$type": "dimension", "viewportGutter": { diff --git a/design-system/packages/ui/src/components/ActionItem/ActionItem.meta.ts b/design-system/packages/ui/src/components/ActionItem/ActionItem.meta.ts index 8c9c90cf13..06b9fe7043 100644 --- a/design-system/packages/ui/src/components/ActionItem/ActionItem.meta.ts +++ b/design-system/packages/ui/src/components/ActionItem/ActionItem.meta.ts @@ -13,8 +13,9 @@ export const actionItemMeta = { { name: "shortcut", type: "ReactNode" }, { defaultValue: "[]", name: "actions", type: "readonly ActionItemAction[]" }, { defaultValue: "false", name: "disabled", type: "boolean" }, + { defaultValue: "neutral", name: "tone", type: "neutral | danger" }, ], - states: ["default", "hover", "active", "focus-visible", "disabled"], + states: ["default", "hover", "active", "focus-visible", "disabled", "danger"], tokens: [ "color.action.neutral.content", "color.action.neutral.contentDisabled", @@ -22,6 +23,8 @@ export const actionItemMeta = { "color.action.neutral.surfacePressed", "color.content.muted", "color.focus.ring", + "color.status.danger.content", + "color.status.danger.surface", "control.height.sm", "font.family.control", "font.size.small", diff --git a/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css b/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css index 71f05faf6c..334adba05d 100644 --- a/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css +++ b/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css @@ -38,6 +38,20 @@ cursor: not-allowed; } + .root[data-bf-tone="danger"]:not([data-disabled="true"]) { + color: var(--bf-color-status-danger-content); + } + + .root[data-bf-tone="danger"]:not([data-disabled="true"]):hover, + .root[data-bf-tone="danger"]:not([data-disabled="true"]):has(.trigger[data-bf-preview-state="hover"]) { + background: var(--bf-color-status-danger-surface); + } + + .root[data-bf-tone="danger"]:not([data-disabled="true"]):has(.trigger:active), + .root[data-bf-tone="danger"]:not([data-disabled="true"]):has(.trigger[data-bf-preview-state="active"]) { + background: var(--bf-color-status-danger-surface); + } + .trigger { display: inline-flex; flex: 1 1 auto; diff --git a/design-system/packages/ui/src/components/ActionItem/ActionItem.tsx b/design-system/packages/ui/src/components/ActionItem/ActionItem.tsx index dbcea44a8b..4489219504 100644 --- a/design-system/packages/ui/src/components/ActionItem/ActionItem.tsx +++ b/design-system/packages/ui/src/components/ActionItem/ActionItem.tsx @@ -17,6 +17,8 @@ export interface ActionItemAction { tone?: IconButtonProps["tone"]; } +export type ActionItemTone = "neutral" | "danger"; + export interface ActionItemProps extends Omit, "children" | "className"> { actions?: readonly ActionItemAction[]; @@ -26,6 +28,7 @@ export interface ActionItemProps metadata?: ReactNode; reserveLeadingSpace?: boolean; shortcut?: ReactNode; + tone?: ActionItemTone; triggerClassName?: string; } @@ -38,6 +41,7 @@ export const ActionItem = forwardRef(functio metadata, reserveLeadingSpace = false, shortcut, + tone = "neutral", triggerClassName, type = "button", ...props @@ -48,6 +52,7 @@ export const ActionItem = forwardRef(functio
- + -
= ({ onClick={() => void copyToClipboard(version.gitCommit ?? '', 'commit')} aria-label={t('about.copyCommit')} > - {copiedItem === 'commit' ? : } + {copiedItem === 'commit' ? : } ) : null} @@ -424,7 +418,7 @@ export const AboutDialog: React.FC = ({
-
= ({ {licenseName}
-
+
= ({ className="bitfun-about-dialog__star-rule bitfun-about-dialog__star-rule--leading" aria-hidden="true" /> -
- + ); const renderViewHeader = () => { @@ -1680,7 +1668,7 @@ export const RemoteConnectDialog: React.FC = ({ > ), }); @@ -172,7 +172,7 @@ const SceneBar: React.FC = ({ data-bf-component="scene-bar" data-bf-part="scrollNext" > -