refactor(icons): migrate workspace to Icones
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user