feat(icons): extract shared workspace icon package

Create @workspace/icons as the single Hugeicons-backed Icon primitive and icon-data export surface, with size presets and inherited current color.

Migrate blocks and the web app to the shared package, remove duplicated direct Hugeicons dependencies and the blocks-local Icon component, and update the lockfile.
This commit is contained in:
Maofeng
2026-07-31 16:06:39 +08:00
parent 38d2defeb1
commit d364e5e39f
22 changed files with 120 additions and 49 deletions
+1 -2
View File
@@ -11,11 +11,10 @@
"@base-ui/react": "^1.6.0",
"@base-ui/utils": "^0.3.1",
"@floating-ui/utils": "^0.2.12",
"@hugeicons/core-free-icons": "^4.2.3",
"@hugeicons/react": "^1.1.9",
"@tanstack/react-hotkeys": "^0.10.0",
"@tanstack/react-query": "^5.101.4",
"@tanstack/react-router": "^1.170.18",
"@workspace/icons": "workspace:*",
"@workspace/i18n": "workspace:*",
"@workspace/ui": "workspace:*",
"cmdk": "^1.1.1",
@@ -1,5 +1,6 @@
import { Link } from "@tanstack/react-router"
import * as React from "react"
import { Icon } from "@workspace/icons"
import { useMessage } from "@workspace/i18n"
import {
Breadcrumb,
@@ -27,7 +28,6 @@ import {
type NavigationInfo,
useNavigationStack,
} from "../navigation"
import { Icon } from "../../components/icon"
import { layoutMessages } from "./messages"
const MAX_VISIBLE_ENTRIES = 3
@@ -1,4 +1,10 @@
import * as React from "react"
import {
BellIcon,
Icon,
MessageMultiple01Icon,
Search01Icon,
} from "@workspace/icons"
import { Button } from "@workspace/ui/components/button"
import { Kbd } from "@workspace/ui/components/kbd"
import { useIsTablet } from "@workspace/ui/hooks/use-breakpoint"
@@ -6,7 +12,6 @@ import { cn } from "@workspace/ui/lib/utils"
import { ChevronLeftIcon, ChevronRightIcon, SearchIcon } from "lucide-react"
import { ChatPopover, ChatPopoverTrigger, type ChatThread } from "../chats"
import { Icon } from "../../components/icon"
import {
MobileNavigationSheet,
MobileNavigationSheetTrigger,
@@ -18,12 +23,6 @@ import {
NotificationCenterTrigger,
type UseNotificationsOptions,
} from "../notifications"
import {
BellIcon,
MessageMultiple01Icon,
Search01Icon,
} from "@hugeicons/core-free-icons"
import { AppSidebar } from "./app-sidebar"
import {
headerActionIconClassName,
@@ -1,3 +1,4 @@
import { ReloadIcon } from "@workspace/icons"
import { useMessage } from "@workspace/i18n"
import { useRefresh } from "../../hooks/use-refresh"
import {
@@ -6,7 +7,6 @@ import {
TooltipTrigger,
} from "@workspace/ui/components/tooltip"
import { HeaderActionButton } from "./header-action-button"
import { ReloadIcon } from "@hugeicons/core-free-icons"
import { cn } from "@workspace/ui/lib/utils"
import { layoutMessages } from "./messages"
@@ -1,4 +1,4 @@
import { Icon, type IconData } from "../../components/icon"
import { Icon, type IconData } from "@workspace/icons"
import { Button, type ButtonProps } from "@workspace/ui/components/button"
import { cn } from "@workspace/ui/lib/utils"
@@ -1,4 +1,12 @@
import { formatForDisplay } from "@tanstack/react-hotkeys"
import {
Icon,
type IconData,
KeyboardIcon,
LogoutCircle02Icon,
ShieldKeyIcon,
SwatchIcon,
} from "@workspace/icons"
import { useMessage } from "@workspace/i18n"
import { Button, type ButtonProps } from "@workspace/ui/components/button"
import {
@@ -35,13 +43,6 @@ import {
HeaderActionButton,
} from "./header-action-button"
import { useOnScroll } from "../../hooks/use-on-scroll"
import { Icon, type IconData } from "../../components/icon"
import {
KeyboardIcon,
LogoutCircle02Icon,
ShieldKeyIcon,
SwatchIcon,
} from "@hugeicons/core-free-icons"
import { layoutMessages } from "./messages"
interface UserMenuItem {
@@ -1,12 +1,10 @@
import { Icon } from "@workspace/icons"
import {
DropdownMenuItem,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
} from "@workspace/ui/components/dropdown-menu"
import { Icon } from "../../components/icon"
import { resolveNavigationItemIcon } from "./item-icon"
import { NavigationBadge, NavigationLink } from "./navigation-button"
import { NavigationLabel } from "./navigation-label"
@@ -1,4 +1,4 @@
import type { IconData } from "../../components/icon"
import type { IconData } from "@workspace/icons"
import type { NavigationItem } from "./types"
@@ -1,4 +1,5 @@
import { useLocation } from "@tanstack/react-router"
import { Icon, Menu02Icon } from "@workspace/icons"
import { useMessage } from "@workspace/i18n"
import type { ButtonProps } from "@workspace/ui/components/button"
import { Separator } from "@workspace/ui/components/separator"
@@ -31,8 +32,6 @@ import {
type GetNavigationRouteState,
} from "./route-state"
import type { NavigationGroup } from "./types"
import { Icon } from "../../components/icon"
import { Menu02Icon } from "@hugeicons/core-free-icons"
const mobileNavigationSheetHandle = createSheetHandle<void>()
@@ -2,12 +2,12 @@ import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { Link } from "@tanstack/react-router"
import * as React from "react"
import { Icon, type IconData } from "@workspace/icons"
import { Badge } from "@workspace/ui/components/badge"
import { useRippleRef } from "@workspace/ui/hooks/use-ripple"
import { cn } from "@workspace/ui/lib/utils"
import { ChevronRightIcon } from "lucide-react"
import { Icon, type IconData } from "../../components/icon"
import { NavigationLabel, TruncatedNavigationLabel } from "./navigation-label"
import type { NavigationLabelValue } from "./types"
@@ -1,7 +1,6 @@
import type { IconData } from "@workspace/icons"
import type { MessageDescriptor } from "@workspace/i18n"
import type { IconData } from "../../components/icon"
export type NavigationLabelValue = string | MessageDescriptor
export interface NavigationItem {
@@ -1,7 +1,7 @@
import { useLocation } from "@tanstack/react-router"
import * as React from "react"
import type { IconData } from "@workspace/icons"
import type { IconData } from "../../components/icon"
import { resolveNavigationItemIcon } from "./item-icon"
import type {
NavigationGroup,
@@ -1,5 +1,6 @@
import * as React from "react"
import { Link } from "@tanstack/react-router"
import { Icon } from "@workspace/icons"
import { useFormatters, useMessage } from "@workspace/i18n"
import {
Avatar,
@@ -17,7 +18,6 @@ import {
} from "@workspace/ui/components/item"
import { cn } from "@workspace/ui/lib/utils"
import { Icon } from "../../components/icon"
import { notificationMessages } from "./messages"
import type {
@@ -1,4 +1,4 @@
import type { IconData } from "../../components/icon"
import type { IconData } from "@workspace/icons"
export type NotificationTone =
| "destructive"
-30
View File
@@ -1,30 +0,0 @@
import { type HugeiconsIconProps, HugeiconsIcon } from "@hugeicons/react"
import { cn } from "@workspace/ui/lib/utils"
export type IconData = HugeiconsIconProps["icon"]
export type IconProps = Omit<HugeiconsIconProps, "icon"> & {
data: IconData
size?: "xs" | "sm" | "md" | "lg" | "xl"
}
const sizeClasses = {
xs: "size-3",
sm: "size-4",
md: "size-5",
lg: "size-6",
xl: "size-7",
}
export function Icon({ data, className, size = "md", ...props }: IconProps) {
return (
<HugeiconsIcon
icon={data}
color="currentColor"
aria-hidden="true"
strokeWidth={1.75}
className={cn(sizeClasses[size], className)}
{...props}
/>
)
}