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:
@@ -19,8 +19,6 @@
|
||||
"@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",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@lingui/core": "^6",
|
||||
"@tanstack/react-form": "^1.33.2",
|
||||
@@ -29,6 +27,7 @@
|
||||
"@tanstack/react-router": "^1.170.18",
|
||||
"@tanstack/react-start": "^1.168.32",
|
||||
"@workspace/blocks": "workspace:*",
|
||||
"@workspace/icons": "workspace:*",
|
||||
"@workspace/i18n": "workspace:*",
|
||||
"@workspace/lexical": "workspace:*",
|
||||
"@workspace/ui": "workspace:*",
|
||||
|
||||
@@ -56,7 +56,7 @@ import {
|
||||
UserShield01Icon,
|
||||
Wallet01Icon,
|
||||
WalletCardsIcon,
|
||||
} from "@hugeicons/core-free-icons"
|
||||
} from "@workspace/icons"
|
||||
|
||||
import type { NavigationGroup } from "@workspace/blocks/navigation"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
Mail01Icon,
|
||||
Message01Icon,
|
||||
PackageIcon,
|
||||
} from "@hugeicons/core-free-icons"
|
||||
} from "@workspace/icons"
|
||||
import type { QueryFunctionContext } from "@tanstack/react-query"
|
||||
|
||||
import type { Notification } from "@workspace/blocks/notifications"
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
"@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",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@lingui/core": "^6",
|
||||
"@tanstack/react-form": "^1.33.2",
|
||||
@@ -28,6 +26,7 @@
|
||||
"@tanstack/react-start": "^1.168.32",
|
||||
"@workspace/blocks": "workspace:*",
|
||||
"@workspace/i18n": "workspace:*",
|
||||
"@workspace/icons": "workspace:*",
|
||||
"@workspace/lexical": "workspace:*",
|
||||
"@workspace/ui": "workspace:*",
|
||||
"country-flag-icons": "^1.6.20",
|
||||
@@ -58,12 +57,11 @@
|
||||
"@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/i18n": "workspace:*",
|
||||
"@workspace/icons": "workspace:*",
|
||||
"@workspace/ui": "workspace:*",
|
||||
"cmdk": "^1.1.1",
|
||||
"lucide-react": "^1.27.0",
|
||||
@@ -109,6 +107,19 @@
|
||||
"vitest": "^4",
|
||||
},
|
||||
},
|
||||
"packages/icons": {
|
||||
"name": "@workspace/icons",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@hugeicons/core-free-icons": "^4.2.3",
|
||||
"@hugeicons/react": "^1.1.9",
|
||||
"react": "^19.2.6",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19",
|
||||
"typescript": "~6",
|
||||
},
|
||||
},
|
||||
"packages/lexical": {
|
||||
"name": "@workspace/lexical",
|
||||
"version": "0.0.0",
|
||||
@@ -793,6 +804,8 @@
|
||||
|
||||
"@workspace/i18n": ["@workspace/i18n@workspace:packages/i18n"],
|
||||
|
||||
"@workspace/icons": ["@workspace/icons@workspace:packages/icons"],
|
||||
|
||||
"@workspace/lexical": ["@workspace/lexical@workspace:packages/lexical"],
|
||||
|
||||
"@workspace/ui": ["@workspace/ui@workspace:packages/ui"],
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# @workspace/icons
|
||||
|
||||
Workspace icon primitives backed by Hugeicons.
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { Icon, Search01Icon } from "@workspace/icons"
|
||||
|
||||
export function SearchButton() {
|
||||
return <Icon data={Search01Icon} size="lg" />
|
||||
}
|
||||
```
|
||||
|
||||
`Icon` defaults to the `md` size and inherits the current text color. A
|
||||
Tailwind size class supplied through `className` overrides the size preset:
|
||||
|
||||
```tsx
|
||||
<Icon data={Search01Icon} className="size-8 text-primary" />
|
||||
```
|
||||
|
||||
Available presets are `xs`, `sm`, `md`, `lg`, and `xl`.
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@workspace/icons",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hugeicons/core-free-icons": "^4.2.3",
|
||||
"@hugeicons/react": "^1.1.9",
|
||||
"react": "^19.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19",
|
||||
"typescript": "~6"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.tsx"
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,32 @@
|
||||
import { type HugeiconsIconProps, HugeiconsIcon } from "@hugeicons/react"
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
|
||||
export * from "@hugeicons/core-free-icons"
|
||||
|
||||
export type IconData = HugeiconsIconProps["icon"]
|
||||
export type IconSize = keyof typeof sizeValues
|
||||
|
||||
export type IconProps = Omit<HugeiconsIconProps, "icon"> & {
|
||||
export type IconProps = Omit<HugeiconsIconProps, "icon" | "size"> & {
|
||||
data: IconData
|
||||
size?: "xs" | "sm" | "md" | "lg" | "xl"
|
||||
size?: IconSize
|
||||
}
|
||||
|
||||
const sizeClasses = {
|
||||
xs: "size-3",
|
||||
sm: "size-4",
|
||||
md: "size-5",
|
||||
lg: "size-6",
|
||||
xl: "size-7",
|
||||
const sizeValues = {
|
||||
xs: 12,
|
||||
sm: 16,
|
||||
md: 20,
|
||||
lg: 24,
|
||||
xl: 28,
|
||||
}
|
||||
|
||||
export function Icon({ data, className, size = "md", ...props }: IconProps) {
|
||||
return (
|
||||
<HugeiconsIcon
|
||||
icon={data}
|
||||
size={sizeValues[size]}
|
||||
color="currentColor"
|
||||
aria-hidden="true"
|
||||
strokeWidth={1.75}
|
||||
className={cn(sizeClasses[size], className)}
|
||||
className={className}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"paths": {
|
||||
"@workspace/icons": ["./src/index.tsx"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user