refactor(icons): migrate workspace to Icones

This commit is contained in:
Maofeng
2026-09-20 15:52:22 +08:00
parent 1fc56b6982
commit ea00a71112
95 changed files with 1409 additions and 725 deletions
+4 -8
View File
@@ -11,16 +11,14 @@
"@base-ui/react": "^1.6.0",
"@base-ui/utils": "^0.3.1",
"@floating-ui/utils": "^0.2.12",
"@icones/react": "^0.0.1",
"@lexical/react": "^0.48.0",
"@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",
"lexical": "^0.48.0",
"lucide-react": "^1.27.0",
"react": "^19.2.6",
"react-dom": "^19.2.6"
},
@@ -46,6 +44,7 @@
"./layout": "./src/blocks/layout/index.ts",
"./layout/locales": "./src/blocks/layout/locales/catalogs.ts",
"./layout/locales/*": "./src/blocks/layout/locales/*.ts",
"./layouts/vega": "./src/blocks/layouts/vega/index.tsx",
"./media": "./src/blocks/media/index.ts",
"./media/locales": "./src/blocks/media/locales/catalogs.ts",
"./media/locales/*": "./src/blocks/media/locales/*.ts",
@@ -54,9 +53,6 @@
"./navigation/locales/*": "./src/blocks/navigation/locales/*.ts",
"./notifications": "./src/blocks/notifications/index.ts",
"./notifications/locales": "./src/blocks/notifications/locales/catalogs.ts",
"./notifications/locales/*": "./src/blocks/notifications/locales/*.ts",
"./search": "./src/blocks/search/index.ts",
"./search/locales": "./src/blocks/search/locales/catalogs.ts",
"./search/locales/*": "./src/blocks/search/locales/*.ts"
"./notifications/locales/*": "./src/blocks/notifications/locales/*.ts"
}
}
}
@@ -1,4 +1,4 @@
import { MonitorIcon, MoonIcon, SunIcon } from "lucide-react"
import { MonitorIcon, MoonIcon, SunIcon } from "@workspace/ui/components/icon"
import { LocalizedText, useMessage } from "@workspace/i18n"
import {
Select,
@@ -1,4 +1,4 @@
import { MoonIcon, SunIcon } from "lucide-react"
import { MoonIcon, SunIcon } from "@workspace/ui/components/icon"
import { LocalizedText } from "@workspace/i18n"
import { useUiState } from "./ui-state"
import { Badge } from "@workspace/ui/components/badge"
@@ -2,7 +2,7 @@ import React from "react"
import { Button } from "@workspace/ui/components/button"
import { useUiState } from "./ui-state"
import { Icon, Moon02Icon, Sun03Icon } from "@workspace/icons"
import { Icon } from "@icones/react"
import { cn } from "@workspace/ui/lib/utils"
type ThemeToggleButtonProps = Omit<
@@ -50,7 +50,7 @@ export function ThemeToggleButton({
>
<Icon
strokeWidth={2.5}
data={resolvedTheme === "dark" ? Sun03Icon : Moon02Icon}
name={resolvedTheme === "dark" ? "huge:sun-03" : "huge:moon-02"}
/>
</Button>
)
@@ -27,7 +27,7 @@ import {
SendHorizontalIcon,
SmileIcon,
XIcon,
} from "lucide-react"
} from "@workspace/ui/components/icon"
import { customerServiceEmojiGroups, customerServiceEmojis } from "./emojis"
import { chatMessages } from "./messages"
@@ -6,7 +6,7 @@ import {
} from "@workspace/ui/components/avatar"
import { Input } from "@workspace/ui/components/input"
import { cn } from "@workspace/ui/lib/utils"
import { SearchIcon } from "lucide-react"
import { SearchIcon } from "@workspace/ui/components/icon"
import { chatMessages } from "./messages"
import type { ChatConversation } from "./types"
@@ -34,7 +34,7 @@ import {
ImageIcon,
LinkIcon,
MessageCircleIcon,
} from "lucide-react"
} from "@workspace/ui/components/icon"
import { chatMessages } from "./messages"
import type {
@@ -10,7 +10,11 @@ import {
} from "@workspace/ui/components/empty"
import { useIsMobile } from "@workspace/ui/hooks/use-breakpoint"
import { cn } from "@workspace/ui/lib/utils"
import { ArrowLeftIcon, InfoIcon, MessageCircleIcon } from "lucide-react"
import {
ArrowLeftIcon,
InfoIcon,
MessageCircleIcon,
} from "@workspace/ui/components/icon"
import { ChatComposer } from "./chat-composer"
import {
@@ -1,6 +1,6 @@
import { Link } from "@tanstack/react-router"
import * as React from "react"
import { Icon } from "@workspace/icons"
import { Icon } from "@icones/react"
import { useMessage } from "@workspace/i18n"
import {
Breadcrumb,
@@ -102,24 +102,24 @@ function useVisibleEntryCount(entries: readonly BreadcrumbEntry[]) {
const breadcrumb = breadcrumbRef.current
const list = listRef.current
const measurement = measurementRef.current
const measurementElement = measurementRef.current
if (!breadcrumb || !list || !measurement) {
if (!breadcrumb || !list || !measurementElement) {
return
}
const updateVisibleCount = () => {
const entryWidths = Array.from(
measurement.querySelectorAll<HTMLElement>(
measurementElement.querySelectorAll<HTMLElement>(
"[data-breadcrumb-measure-entry]"
)
).map((element) => element.getBoundingClientRect().width)
const menuWidth =
measurement
measurementElement
.querySelector<HTMLElement>("[data-breadcrumb-measure-menu]")
?.getBoundingClientRect().width ?? 0
const separatorWidth =
measurement
measurementElement
.querySelector<HTMLElement>("[data-breadcrumb-measure-separator]")
?.getBoundingClientRect().width ?? 0
const listStyle = window.getComputedStyle(list)
@@ -169,7 +169,7 @@ function useVisibleEntryCount(entries: readonly BreadcrumbEntry[]) {
const observer = new ResizeObserver(updateVisibleCount)
observer.observe(breadcrumb)
observer.observe(measurement)
observer.observe(measurementElement)
return () => observer.disconnect()
}, [entries, entriesKey, maximumVisibleCount])
@@ -190,7 +190,7 @@ function useVisibleEntryCount(entries: readonly BreadcrumbEntry[]) {
function BreadcrumbEntryContent({ entry }: { entry: BreadcrumbEntry }) {
return (
<>
{entry.icon && <Icon data={entry.icon} className="size-4 shrink-0" />}
{entry.icon && <Icon name={entry.icon} className="size-4 shrink-0" />}
<NavigationLabel label={entry.label} />
</>
)
@@ -326,7 +326,7 @@ function BreadcrumbMenu({ entries }: { entries: readonly BreadcrumbEntry[] }) {
const content = (
<>
{entry.icon && (
<Icon data={entry.icon} className="size-4 shrink-0" />
<Icon name={entry.icon} className="size-4 shrink-0" />
)}
<NavigationLabel label={entry.label} />
</>
@@ -409,7 +409,7 @@ export function AppBreadcrumb() {
<BreadcrumbPage className="inline-flex min-w-0 items-center gap-1.5 text-primary">
{item.entry.icon && (
<Icon
data={item.entry.icon}
name={item.entry.icon}
className="size-4 shrink-0"
/>
)}
@@ -440,7 +440,7 @@ export function AppBreadcrumb() {
className="inline-flex items-center gap-1.5 font-normal"
>
{entry.icon && (
<Icon data={entry.icon} className="size-4 shrink-0" />
<Icon name={entry.icon} className="size-4 shrink-0" />
)}
<NavigationLabel label={entry.label} />
</span>
@@ -1,11 +1,10 @@
import * as React from "react"
import { ChevronLeftIcon, ChevronRightIcon, SearchIcon } from "lucide-react"
import {
BellIcon,
Icon,
MessageMultiple01Icon,
Search01Icon,
} from "@workspace/icons"
ChevronLeftIcon,
ChevronRightIcon,
SearchIcon,
} from "@workspace/ui/components/icon"
import { Icon } from "@icones/react"
import { Button } from "@workspace/ui/components/button"
import { Kbd } from "@workspace/ui/components/kbd"
import { useIsTablet } from "@workspace/ui/hooks/use-breakpoint"
@@ -153,12 +152,14 @@ function AppHeader({ actions }: { actions?: React.ReactNode }) {
<GlobalSearchTrigger />
{actions}
<NotificationCenterTrigger
render={<HeaderActionButton icon={BellIcon} showIndicator />}
render={
<HeaderActionButton icon="huge:notification-01" showIndicator />
}
/>
<ChatThreadTrigger
render={
<HeaderActionButton
icon={MessageMultiple01Icon}
icon="huge:message-multiple-01"
showIndicator
/>
}
@@ -201,7 +202,7 @@ function GlobalSearchTrigger() {
variant="secondary"
>
<Icon
data={Search01Icon}
name="huge:search-01"
className={cn(
"pointer-event-none size-5 transition-all not-mobile:hidden",
headerActionIconClassName
@@ -1,4 +1,3 @@
import { ReloadIcon } from "@workspace/icons"
import { useMessage } from "@workspace/i18n"
import { useRefresh } from "../../hooks/use-refresh"
import {
@@ -36,7 +35,7 @@ export function AppQueryIndicator({ className }: { className?: string }) {
aria-label={refreshLabel}
className={cn("[&_svg]:size-4.5", className)}
disabled={!canRefresh}
icon={ReloadIcon}
icon="huge:reload"
onClick={refresh}
/>
}
@@ -1,4 +1,4 @@
import { Icon, type IconData } from "@workspace/icons"
import { Icon, type IconName } from "@icones/react"
import { Button, type ButtonProps } from "@workspace/ui/components/button"
import { cn } from "@workspace/ui/lib/utils"
@@ -6,7 +6,7 @@ export const headerActionIconClassName =
"transition-all transform origin-center group-hover/button:scale-105"
type HeaderActionButtonProps = ButtonProps & {
icon?: IconData
icon?: IconName<"huge">
showIndicator?: boolean
}
@@ -20,7 +20,7 @@ export function HeaderActionButton({
return (
<Button variant="ghost" size="icon-lg" className={cn(className)} {...props}>
{icon ? (
<Icon data={icon} className={cn("size-5", headerActionIconClassName)} />
<Icon name={icon} className={cn("size-5", headerActionIconClassName)} />
) : (
children
)}
@@ -1,12 +1,5 @@
import { formatForDisplay } from "@tanstack/react-hotkeys"
import {
Icon,
type IconData,
KeyboardIcon,
LogoutCircle02Icon,
ShieldKeyIcon,
SwatchIcon,
} from "@workspace/icons"
import { Icon, type IconName } from "@icones/react"
import { useMessage } from "@workspace/i18n"
import { Button, type ButtonProps } from "@workspace/ui/components/button"
import {
@@ -49,7 +42,7 @@ interface UserMenuItem {
id: string
action?: NavigationAction
shortcut?: string
icon: IconData
icon: IconName<"huge">
label: string
}
@@ -137,18 +130,18 @@ function UserMenuContent({ onItemSelect }: { onItemSelect: VoidFunction }) {
{
id: "appearance",
action: navigationActions.appearance,
icon: SwatchIcon,
icon: "huge:swatch",
label: appearanceLabel,
shortcut: "Mod+,",
},
{
id: "keybindings",
icon: KeyboardIcon,
icon: "huge:keyboard",
label: keybindingsLabel,
},
{
id: "account-password",
icon: ShieldKeyIcon,
icon: "huge:shield-key",
label: accountPasswordLabel,
shortcut: "Mod+P",
},
@@ -172,14 +165,14 @@ function UserMenuContent({ onItemSelect }: { onItemSelect: VoidFunction }) {
className="w-full justify-start font-normal"
size="lg"
variant="ghost"
nativeButton={item.action ? true : false}
nativeButton={Boolean(item.action)}
render={item.action ? undefined : <a />}
onClick={onItemSelect}
{...(item.action
? getNavigationCommandProps(item.action)
: undefined)}
>
<Icon data={item.icon} size="sm" />
<Icon name={item.icon} size="sm" />
{item.label}
{item.shortcut && <MenuShortcut shortcut={item.shortcut} />}
</Button>
@@ -189,7 +182,7 @@ function UserMenuContent({ onItemSelect }: { onItemSelect: VoidFunction }) {
</div>
<div className="rounded-b-md border-t p-2">
<Button className="w-full font-normal" variant="destructive" size="lg">
<Icon data={LogoutCircle02Icon} size="sm" />
<Icon name="huge:logout-circle-02" size="sm" />
{logoutLabel}
<MenuShortcutSequence shortcuts={["Q", "Q", "Q"]} />
</Button>
@@ -0,0 +1,156 @@
import { Icon } from "@icones/react";
import { Button } from "@workspace/ui/components/button";
import { cn } from "@workspace/ui/lib/utils";
import React from "react";
type Props = {
children: React.ReactNode
className?: string
}
/*
+------------------+
| header |
+-------+-+--------+
| | | |
| aside | | main |
| | | |
+-------+-+--------+
^
|
gap
*/
export function Layout({ children, className }: Props) {
return (
<div
className={cn(
"isolate",
"[--app-header-height:--spacing(18)]",
"[--app-aside-width:--spacing(56)]",
"[--app-split-gap:--spacing(4)]",
className,
)}
data-slot="root"
>
<header className="z-10 sticky top-0 h-0" data-slot="header">
<div className="h-(--app-header-height)">
header
</div>
<div className="border-t h-0 absolute inset-x-0 top-(--app-header-height)" />
</header>
<div className="flex gap-(--app-split-gap)">
<aside
className="w-(--app-aside-width) h-svh pt-(--app-header-height) shrink-0"
data-slot="aside"
>
<div className="flex flex-col items-stretch h-full py-4">
<Navigation items={groups} />
<Navigation className="mt-auto" items={[links[1]]} />
</div>
</aside>
<main className="pt-(--app-header-height)" data-slot="main">
<div className="p-4">
{children}
</div>
</main>
</div>
</div>
)
}
const links = [
{ key: "inbox", label: "收件箱", icon: <Icon name="tabler:inbox" /> },
{ key: "starred", label: "已加星标", icon: <Icon name="tabler:star-filled" /> },
{ key: "sent", label: "已发送", icon: <Icon name="tabler:send" /> },
{ key: "drafts", label: "草稿", icon: <Icon name="tabler:edit" /> },
{ key: "deleted", label: "已删除", icon: <Icon name="tabler:trash" /> },
{ key: "sent-mail", label: "已发邮件", icon: <Icon name="tabler:send" /> },
]
const groups = [
{
key: "group-1",
label: "group 1",
items: [...links],
},
{
key: "group-2",
label: "group 2",
items: [...links],
},
...links.slice(2, 4),
]
type NavItem = { key: string; label: string; icon: React.ReactNode }
type NavGroup = { key: string; label: string; items: Array<NavItem> }
type NavItems = Array<NavGroup | NavItem>
function isGroup(item: NavGroup | NavItem): item is NavGroup {
return (item as NavGroup).items !== undefined
}
function Navigation({ items, className }: { items: NavItems; className?: string }) {
return (
<ul className={className}>
{items.map((item, index) => {
if (isGroup(item)) {
const previousItem = index > 0 ? items[index - 1] : undefined
return (
<li
key={item.key}
className={cn(
"not-last:mb-4",
previousItem && isGroup(previousItem)
? "mt-0"
: "not-first:mt-4",
)}
>
<span className="text-xs px-2.5 text-muted-foreground">{item.label}</span>
<ul>
{item.items.map(subItem => (
<li key={subItem.key}>
<NavigationItem
label={subItem.label}
icon={subItem.icon}
current={item.key === "drafts"}
/>
</li>
))}
</ul>
</li>
);
}
return (
<li key={item.key}>
<NavigationItem
label={item.label}
icon={item.icon}
current={item.key === "drafts"}
/>
</li>
);
})}
</ul>
)
}
function NavigationItem({ label, icon, current }: {
label: string
icon: React.ReactNode
current: boolean
}) {
return (
<Button
variant="ghost"
aria-current={current ? "page" : undefined}
className="flex w-full justify-start rounded-l-none rounded-r-full aria-[current=page]:text-red-600 aria-[current=page]:bg-current/10 [&_svg:not([class*='size-'])]:size-5"
size="lg"
>
{icon}
{label}
</Button>
)
}
@@ -11,7 +11,7 @@ import {
PencilIcon,
Trash2Icon,
UploadIcon,
} from "lucide-react"
} from "@workspace/ui/components/icon"
import { useTranslate } from "@workspace/i18n"
import {
ContextMenu,
@@ -49,6 +49,10 @@ export interface MediaContextMenuProps {
onUpload: (target: MediaStorageTarget) => void
}
function runAfterClose(action: VoidFunction) {
window.setTimeout(action)
}
export function MediaContextMenu({
asset,
children,
@@ -73,8 +77,6 @@ export function MediaContextMenu({
const cloudTargets = storageTargets.filter(
(target) => target.kind === "cloud"
)
const runAfterClose = (action: VoidFunction) => window.setTimeout(action)
const copy = async (text: string, successMessage: string) => {
try {
await navigator.clipboard.writeText(text)
@@ -5,7 +5,7 @@ import {
HeartIcon,
ImageIcon,
Trash2Icon,
} from "lucide-react"
} from "@workspace/ui/components/icon"
import { useTranslate } from "@workspace/i18n"
import { Button } from "@workspace/ui/components/button"
import {
@@ -1,4 +1,4 @@
import { CheckIcon, FilmIcon, HeartIcon } from "lucide-react"
import { CheckIcon, FilmIcon, HeartIcon } from "@workspace/ui/components/icon"
import { useTranslate } from "@workspace/i18n"
import { cn } from "@workspace/ui/lib/utils"
@@ -7,7 +7,7 @@ import {
LoaderCircleIcon,
SearchIcon,
UploadIcon,
} from "lucide-react"
} from "@workspace/ui/components/icon"
import { useTranslate } from "@workspace/i18n"
import {
AlertDialog,
@@ -57,7 +57,6 @@ import type {
MediaStorageTarget,
} from "./types"
import {
defaultMediaReference,
getMediaFolderPath,
isMediaFile,
mediaKindForFile,
@@ -67,7 +66,6 @@ import {
const PAGE_SIZE = 40
const EMPTY_ASSETS: readonly MediaAsset[] = []
const EMPTY_FOLDERS: readonly MediaFolder[] = []
const DEFAULT_STORAGE_TARGET: MediaStorageTarget = {
id: "local",
kind: "local",
@@ -216,15 +214,7 @@ export function MediaLibrary({
} finally {
if (assetRequestId.current === requestId) setIsLoadingAssets(false)
}
}, [
adapter,
allowedKinds,
allowedKindsKey,
deferredKeyword,
filter,
folder,
t,
])
}, [adapter, allowedKinds, deferredKeyword, filter, folder, t])
React.useEffect(() => {
void refreshFolders()
+1 -1
View File
@@ -12,7 +12,7 @@ import {
PlusIcon,
Trash2Icon,
UnlinkIcon,
} from "lucide-react"
} from "@workspace/ui/components/icon"
import { useTranslate } from "@workspace/i18n"
import { Button } from "@workspace/ui/components/button"
import {
@@ -1,4 +1,4 @@
import { Icon } from "@workspace/icons"
import { Icon } from "@icones/react"
import {
DropdownMenuItem,
DropdownMenuSub,
@@ -23,7 +23,7 @@ export function SidebarFlyoutMenuItems({
return items.map((item) => {
const icon = resolveNavigationItemIcon(item)
const iconElement = icon ? (
<Icon data={icon} className="size-5 shrink-0" />
<Icon name={icon} className="size-5 shrink-0" />
) : null
const routeState = getRouteState(item.to)
@@ -1,10 +1,10 @@
import type { IconData } from "@workspace/icons"
import type { IconName } from "@icones/react"
import type { NavigationItem } from "./types"
export function resolveNavigationItemIcon(
item: NavigationItem,
visibleByDefault = false
): IconData | undefined {
): IconName<"huge"> | undefined {
return (item.showIcon ?? visibleByDefault) ? item.icon : undefined
}
@@ -1,5 +1,6 @@
import { useLocation } from "@tanstack/react-router"
import { Icon, Menu02Icon } from "@workspace/icons"
import { Fragment } from "react"
import { Icon } from "@icones/react"
import { useMessage } from "@workspace/i18n"
import type { ButtonProps } from "@workspace/ui/components/button"
import { Separator } from "@workspace/ui/components/separator"
@@ -13,8 +14,8 @@ import {
SheetTrigger,
} from "@workspace/ui/components/sheet"
import { useOnBreakpoint } from "@workspace/ui/hooks/use-breakpoint"
import { cn, separate } from "@workspace/ui/lib/utils"
import { ChevronRightIcon } from "lucide-react"
import { cn } from "@workspace/ui/lib/utils"
import { ChevronRightIcon } from "@workspace/ui/components/icon"
import { HeaderActionButton } from "../layout/header-action-button"
import { navigationMessages } from "./messages"
@@ -50,7 +51,7 @@ export function MobileNavigationSheetTrigger({
}
>
<Icon
data={Menu02Icon}
name="huge:menu-02"
strokeWidth={2.25}
className="size-5 [&_path]:transition-all group-hover/button:[&_path]:not-first:not-last:opacity-40 [&_path]:first:opacity-40 group-hover/button:[&_path]:first:translate-x-1 group-hover/button:[&_path]:first:opacity-100 [&_path]:last:opacity-60 group-hover/button:[&_path]:last:translate-x-2 group-hover/button:[&_path]:last:opacity-100"
/>
@@ -136,14 +137,13 @@ function FlatNavigationGroupList({
<NavigationLabel label={group.label} />
</div>
<div className="rounded-md ring ring-sidebar-border">
{separate({
items: group.items,
iterator: (item) => {
const routeState = getRouteState(item.to)
{group.items.map((item, index) => {
const routeState = getRouteState(item.to)
return (
return (
<Fragment key={item.id}>
{index > 0 && <Separator className="ms-12 me-3 w-auto!" />}
<NavigationButton
key={`item-${item.id}`}
label={item.label}
layout="inline"
icon={resolveNavigationItemIcon(
@@ -167,14 +167,8 @@ function FlatNavigationGroupList({
</>
}
/>
)
},
separator: (index) => (
<Separator
key={`separator-${index}`}
className="ms-12 me-3 w-auto!"
/>
),
</Fragment>
)
})}
</div>
</div>
@@ -2,11 +2,11 @@ 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 { Icon, type IconName } from "@icones/react"
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 { ChevronRightIcon } from "@workspace/ui/components/icon"
import { NavigationLabel, TruncatedNavigationLabel } from "./navigation-label"
import type { NavigationLabelValue } from "./types"
@@ -15,7 +15,7 @@ export interface NavigationButtonProps extends Omit<
useRender.ComponentProps<"button", {}>,
"className" | "style"
> {
icon?: IconData
icon?: IconName<"huge">
isActive: boolean
isCurrent: boolean
label: NavigationLabelValue
@@ -104,7 +104,7 @@ export function NavigationButton({
: undefined
)}
>
<Icon data={icon} />
<Icon name={icon} />
</span>
)}
<span
@@ -1,4 +1,4 @@
import type { IconData } from "@workspace/icons"
import type { IconName } from "@icones/react"
import type { MessageDescriptor } from "@workspace/i18n"
export type NavigationLabelValue = string | MessageDescriptor
@@ -7,7 +7,7 @@ export interface NavigationItem {
id: string
to?: string
label: NavigationLabelValue
icon?: IconData
icon?: IconName<"huge">
showIcon?: boolean
items?: readonly NavigationItem[]
}
@@ -1,6 +1,6 @@
import { useLocation } from "@tanstack/react-router"
import * as React from "react"
import type { IconData } from "@workspace/icons"
import type { IconName } from "@icones/react"
import { resolveNavigationItemIcon } from "./item-icon"
import type {
@@ -13,7 +13,7 @@ export type NavigationKey = NavigationItem["id"]
export interface NavigationInfo {
id: NavigationKey
icon?: IconData
icon?: IconName<"huge">
items?: readonly NavigationInfo[]
label: NavigationLabelValue
to?: string
@@ -40,26 +40,25 @@ function normalizePathname(pathname: string) {
return pathname.replace(/\/+$/, "")
}
function createInfoItems(
items: readonly NavigationItem[],
showIconByDefault: boolean
): readonly NavigationInfo[] {
return items.map((item) => ({
id: item.id,
icon: resolveNavigationItemIcon(item, showIconByDefault),
items: item.items?.length ? createInfoItems(item.items, false) : undefined,
label: item.label,
to: item.to,
}))
}
function createNavigationIndex(
groups: readonly NavigationGroup[]
): NavigationIndex {
const recordsByKey = new Map<NavigationKey, NavigationRecord>()
const recordsByPathname = new Map<string, NavigationRecord>()
const createInfoItems = (
items: readonly NavigationItem[],
showIconByDefault: boolean
): readonly NavigationInfo[] =>
items.map((item) => ({
id: item.id,
icon: resolveNavigationItemIcon(item, showIconByDefault),
items: item.items?.length
? createInfoItems(item.items, false)
: undefined,
label: item.label,
to: item.to,
}))
const indexInfoItems = (
items: readonly NavigationInfo[],
parentStack: readonly NavigationInfo[]
@@ -1,5 +1,5 @@
import * as React from "react"
import { CheckCheckIcon } from "lucide-react"
import { CheckCheckIcon } from "@workspace/ui/components/icon"
import { useMessage } from "@workspace/i18n"
import { Button } from "@workspace/ui/components/button"
import {
@@ -1,6 +1,6 @@
import * as React from "react"
import { Link } from "@tanstack/react-router"
import { Icon } from "@workspace/icons"
import { Icon } from "@icones/react"
import { useFormatters, useMessage } from "@workspace/i18n"
import {
Avatar,
@@ -109,7 +109,7 @@ function NotificationItemMedia({ media }: { media: NotificationMedia }) {
ICON_TONE_CLASSES[media.tone ?? "neutral"]
)}
>
<Icon data={media.icon} size="lg" />
<Icon name={media.icon} size="lg" />
</span>
)
}
@@ -1,4 +1,4 @@
import type { IconData } from "@workspace/icons"
import type { IconName } from "@icones/react"
export type NotificationTone =
| "destructive"
@@ -15,7 +15,7 @@ export interface NotificationAvatarMedia {
}
export interface NotificationIconMedia {
icon: IconData
icon: IconName<"huge">
kind: "icon"
tone?: NotificationTone
}