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
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
"@lingui/core": "^6",
|
||||
"@lingui/react": "^6",
|
||||
"@vitejs/plugin-react": "^6",
|
||||
"@workspace/ui": "workspace:*",
|
||||
"ansi-to-react": "^6.2.6",
|
||||
"cac": "^7.0.0",
|
||||
"lucide-react": "^1.27.0",
|
||||
"open": "^11.0.0",
|
||||
"react": "^19.2.6",
|
||||
"react-dom": "^19.2.6",
|
||||
@@ -43,4 +43,4 @@
|
||||
"typescript": "~6",
|
||||
"vitest": "^4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ChevronUpIcon,
|
||||
TerminalIcon,
|
||||
XIcon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
import { AnsiText } from "./ansi-text"
|
||||
import { useDevtoolLocalization } from "./devtool-localization"
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
AlignLeftIcon,
|
||||
AlignRightIcon,
|
||||
SettingsIcon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
import type {
|
||||
I18nDevtoolPanelPlacement,
|
||||
@@ -35,14 +35,8 @@ const panelPlacements = [
|
||||
] as const
|
||||
|
||||
const interfaceLocaleLabels: Record<I18nDevtoolLocale, string> = {
|
||||
de: "Deutsch",
|
||||
en: "English",
|
||||
es: "Español",
|
||||
fr: "Français",
|
||||
ja: "日本語",
|
||||
ko: "한국어",
|
||||
"en-US": "English",
|
||||
"zh-Hans": "简体中文",
|
||||
"zh-Hant": "繁體中文",
|
||||
}
|
||||
|
||||
export function DevtoolSettings({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MonitorIcon, MoonIcon, SunIcon } from "lucide-react"
|
||||
import { MonitorIcon, MoonIcon, SunIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
import type { I18nDevtoolTheme } from "./devtool-preferences"
|
||||
import { useDevtoolLocalization } from "./devtool-localization"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { LanguagesIcon } from "lucide-react"
|
||||
import { LanguagesIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
import { useDevtoolLocalization } from "./devtool-localization"
|
||||
import { useFloatingTrigger } from "./use-floating-trigger"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import * as React from "react"
|
||||
import { Maximize2Icon, Minimize2Icon, XIcon } from "lucide-react"
|
||||
import {
|
||||
Maximize2Icon,
|
||||
Minimize2Icon,
|
||||
XIcon,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { createPortal } from "react-dom"
|
||||
|
||||
import { useLocale, useLocales } from "../runtime"
|
||||
@@ -25,6 +29,7 @@ import { DevtoolSettings } from "./devtool-settings"
|
||||
import { I18nDevtoolTrigger } from "./i18n-devtool-trigger"
|
||||
import { MessagePanel } from "./message-panel"
|
||||
import { MessageRepositoryProvider } from "./message-repository"
|
||||
// oxlint-disable-next-line import/no-unassigned-import -- Loads the Devtool stylesheet.
|
||||
import "./i18n-devtool.css"
|
||||
|
||||
export interface I18nDevtoolProps {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
ChevronDownIcon,
|
||||
CopyIcon,
|
||||
TriangleAlertIcon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
import { useDevtoolLocalization } from "./devtool-localization"
|
||||
import type { DevtoolMessage, MessageOrigin } from "./types"
|
||||
@@ -53,6 +53,35 @@ async function writeClipboard(value: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleMenuKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
if (!["ArrowDown", "ArrowUp", "End", "Home"].includes(event.key)) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
const items = Array.from(
|
||||
event.currentTarget.querySelectorAll<HTMLButtonElement>(
|
||||
'[role="menuitem"]:not(:disabled)'
|
||||
)
|
||||
)
|
||||
|
||||
if (items.length === 0) return
|
||||
|
||||
const currentIndex = items.indexOf(
|
||||
document.activeElement as HTMLButtonElement
|
||||
)
|
||||
const nextIndex =
|
||||
event.key === "Home"
|
||||
? 0
|
||||
: event.key === "End"
|
||||
? items.length - 1
|
||||
: event.key === "ArrowUp"
|
||||
? (currentIndex - 1 + items.length) % items.length
|
||||
: (currentIndex + 1) % items.length
|
||||
|
||||
items[nextIndex]?.focus()
|
||||
}
|
||||
|
||||
export function MessageCopyMenu({
|
||||
message,
|
||||
translation,
|
||||
@@ -132,37 +161,6 @@ export function MessageCopyMenu({
|
||||
}
|
||||
}
|
||||
|
||||
const handleMenuKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (!["ArrowDown", "ArrowUp", "End", "Home"].includes(event.key)) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
const items = Array.from(
|
||||
event.currentTarget.querySelectorAll<HTMLButtonElement>(
|
||||
'[role="menuitem"]:not(:disabled)'
|
||||
)
|
||||
)
|
||||
|
||||
if (items.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentIndex = items.indexOf(
|
||||
document.activeElement as HTMLButtonElement
|
||||
)
|
||||
const nextIndex =
|
||||
event.key === "Home"
|
||||
? 0
|
||||
: event.key === "End"
|
||||
? items.length - 1
|
||||
: event.key === "ArrowUp"
|
||||
? (currentIndex - 1 + items.length) % items.length
|
||||
: (currentIndex + 1) % items.length
|
||||
|
||||
items[nextIndex]?.focus()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="i18n-message-copy" ref={rootRef}>
|
||||
<button
|
||||
@@ -236,10 +234,10 @@ export function MessageCopyMenu({
|
||||
<span className="i18n-message-copy__label">
|
||||
{messages.messagePanel.sourceLocations}
|
||||
</span>
|
||||
{message.origins.map((origin, index) => (
|
||||
{message.origins.map((origin) => (
|
||||
<button
|
||||
type="button"
|
||||
key={`${getOriginLocation(origin)}:${index}`}
|
||||
key={getOriginLocation(origin)}
|
||||
role="menuitem"
|
||||
title={getOriginLocation(origin)}
|
||||
onClick={() => void copy(getOriginLocation(origin))}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import * as React from "react"
|
||||
import { ChevronDownIcon, FolderIcon, PackageIcon } from "lucide-react"
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
FolderIcon,
|
||||
PackageIcon,
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
import { useDevtoolLocalization } from "./devtool-localization"
|
||||
import type { DevtoolMessage, DevtoolMessagePackage } from "./types"
|
||||
@@ -137,19 +141,20 @@ function NamespaceBranch({
|
||||
packageName: string
|
||||
selectedKey?: string
|
||||
}) {
|
||||
const sortedNodes = Array.from(nodes.values())
|
||||
sortedNodes.sort((left, right) => left.segment.localeCompare(right.segment))
|
||||
|
||||
return (
|
||||
<ul className="i18n-message-tree__branch">
|
||||
{Array.from(nodes.values())
|
||||
.sort((left, right) => left.segment.localeCompare(right.segment))
|
||||
.map((node) => (
|
||||
<NamespaceNode
|
||||
key={node.path}
|
||||
node={node}
|
||||
packageName={packageName}
|
||||
selectedKey={selectedKey}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
{sortedNodes.map((node) => (
|
||||
<NamespaceNode
|
||||
key={node.path}
|
||||
node={node}
|
||||
packageName={packageName}
|
||||
selectedKey={selectedKey}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
RotateCcwIcon,
|
||||
SaveIcon,
|
||||
SearchIcon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
import { useDevtoolLocalization } from "./devtool-localization"
|
||||
import { formatMessageOrigin, MessageCopyMenu } from "./message-copy-menu"
|
||||
@@ -20,6 +20,7 @@ import type {
|
||||
DevtoolMessagePackage,
|
||||
MessageRepository,
|
||||
} from "./types"
|
||||
// oxlint-disable-next-line import/no-unassigned-import -- Loads the message panel stylesheet.
|
||||
import "./message-panel.css"
|
||||
|
||||
type LoadState =
|
||||
@@ -392,17 +393,19 @@ export function MessagePanel({
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(groups, ([packageName, group]) => ({
|
||||
const packageGroups = Array.from(groups, ([packageName, group]) => ({
|
||||
kind: group.kind,
|
||||
messages: group.messages,
|
||||
missingCount: group.messages.filter((message) => message.missing).length,
|
||||
packageName,
|
||||
})).sort(
|
||||
}))
|
||||
packageGroups.sort(
|
||||
(left, right) =>
|
||||
Number(right.kind === "application") -
|
||||
Number(left.kind === "application") ||
|
||||
left.packageName.localeCompare(right.packageName)
|
||||
)
|
||||
return packageGroups
|
||||
}, [missingOnly, query, state, visibleMessages])
|
||||
const navigationModel = React.useMemo(
|
||||
() => createMessageNavigationModel(visiblePackageGroups),
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# @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`.
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
"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,33 +0,0 @@
|
||||
import { type HugeiconsIconProps, HugeiconsIcon } from "@hugeicons/react"
|
||||
|
||||
export * from "@hugeicons/core-free-icons"
|
||||
|
||||
export type IconData = HugeiconsIconProps["icon"]
|
||||
export type IconSize = keyof typeof sizeValues
|
||||
|
||||
export type IconProps = Omit<HugeiconsIconProps, "icon" | "size"> & {
|
||||
data: IconData
|
||||
size?: IconSize
|
||||
}
|
||||
|
||||
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={className}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"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"]
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
"@workspace/i18n": "workspace:*",
|
||||
"@workspace/ui": "workspace:*",
|
||||
"lexical": "^0.48.0",
|
||||
"lucide-react": "^1.27.0",
|
||||
"react": "^19.2.6",
|
||||
"react-dom": "^19.2.6"
|
||||
},
|
||||
@@ -38,4 +37,4 @@
|
||||
"./locales": "./src/locales/catalogs.ts",
|
||||
"./locales/*": "./src/locales/*.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,12 @@
|
||||
import * as React from "react"
|
||||
import type { ComponentType, ReactElement, ReactNode } from "react"
|
||||
import type { Klass, LexicalNode } from "lexical"
|
||||
import { AlignLeft, CaseSensitive, Pilcrow, Plus } from "lucide-react"
|
||||
import {
|
||||
AlignLeft,
|
||||
CaseSensitive,
|
||||
Pilcrow,
|
||||
Plus,
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
import {
|
||||
boldAction,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Check, ChevronDown } from "lucide-react"
|
||||
import { Check, ChevronDown } from "@workspace/ui/components/icon"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { AlignCenter, AlignJustify, AlignLeft, AlignRight } from "lucide-react"
|
||||
import {
|
||||
AlignCenter,
|
||||
AlignJustify,
|
||||
AlignLeft,
|
||||
AlignRight,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import {
|
||||
$getSelection,
|
||||
$isRangeSelection,
|
||||
|
||||
@@ -8,7 +8,13 @@ import {
|
||||
} from "@lexical/rich-text"
|
||||
import type { HeadingTagType } from "@lexical/rich-text"
|
||||
import { $setBlocksType } from "@lexical/selection"
|
||||
import { Heading1, Heading2, Heading3, Pilcrow, Quote } from "lucide-react"
|
||||
import {
|
||||
Heading1,
|
||||
Heading2,
|
||||
Heading3,
|
||||
Pilcrow,
|
||||
Quote,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
|
||||
import type { LexicalActionDefinition } from "../types"
|
||||
import { lexicalMessages } from "../messages"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { TOGGLE_LINK_COMMAND } from "@lexical/link"
|
||||
import { REMOVE_LIST_COMMAND } from "@lexical/list"
|
||||
import { $patchStyleText, $setBlocksType } from "@lexical/selection"
|
||||
import { Eraser } from "lucide-react"
|
||||
import { Eraser } from "@workspace/ui/components/icon"
|
||||
import { $createParagraphNode, $getSelection, $isRangeSelection } from "lexical"
|
||||
import type { LexicalActionDefinition } from "../types"
|
||||
import { lexicalMessages } from "../messages"
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
$getSelectionStyleValueForProperty,
|
||||
$patchStyleText,
|
||||
} from "@lexical/selection"
|
||||
import { Baseline, Pipette } from "lucide-react"
|
||||
import { Baseline, Pipette } from "@workspace/ui/components/icon"
|
||||
import { $getSelection, $isRangeSelection, $setSelection } from "lexical"
|
||||
import type { LexicalEditor, RangeSelection } from "lexical"
|
||||
import { useRef, useState } from "react"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CalendarDays } from "lucide-react"
|
||||
import { CalendarDays } from "@workspace/ui/components/icon"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { DropdownMenuItem } from "@workspace/ui/components/dropdown-menu"
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
$getSelectionStyleValueForProperty,
|
||||
$patchStyleText,
|
||||
} from "@lexical/selection"
|
||||
import { ALargeSmall, ChevronDown } from "lucide-react"
|
||||
import { ALargeSmall, ChevronDown } from "@workspace/ui/components/icon"
|
||||
import { $getSelection, $isRangeSelection } from "lexical"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin"
|
||||
import { Redo2, Undo2 } from "lucide-react"
|
||||
import { Redo2, Undo2 } from "@workspace/ui/components/icon"
|
||||
import { REDO_COMMAND, UNDO_COMMAND } from "lexical"
|
||||
import type { LexicalActionDefinition } from "../types"
|
||||
import { lexicalMessages } from "../messages"
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
HorizontalRuleNode,
|
||||
} from "@lexical/extension"
|
||||
import { $insertNodeToNearestRoot } from "@lexical/utils"
|
||||
import { Minus } from "lucide-react"
|
||||
import { Minus } from "@workspace/ui/components/icon"
|
||||
import { $getSelection, $isRangeSelection } from "lexical"
|
||||
import type { LexicalActionDefinition } from "../types"
|
||||
import { lexicalMessages } from "../messages"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IndentDecrease, IndentIncrease } from "lucide-react"
|
||||
import { IndentDecrease, IndentIncrease } from "@workspace/ui/components/icon"
|
||||
import { INDENT_CONTENT_COMMAND, OUTDENT_CONTENT_COMMAND } from "lexical"
|
||||
import type { LexicalActionDefinition } from "../types"
|
||||
import { lexicalMessages } from "../messages"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { $isLinkNode, LinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link"
|
||||
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin"
|
||||
import { Link } from "lucide-react"
|
||||
import { Link } from "@workspace/ui/components/icon"
|
||||
import { $findMatchingParent, $getSelection, $isRangeSelection } from "lexical"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import {
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "@lexical/list"
|
||||
import { CheckListPlugin } from "@lexical/react/LexicalCheckListPlugin"
|
||||
import { ListPlugin } from "@lexical/react/LexicalListPlugin"
|
||||
import { List, ListChecks, ListOrdered } from "lucide-react"
|
||||
import { List, ListChecks, ListOrdered } from "@workspace/ui/components/icon"
|
||||
import { $getSelection, $isRangeSelection } from "lexical"
|
||||
import type { LexicalCommand, LexicalEditor } from "lexical"
|
||||
import type { LexicalActionDefinition, LexicalActionName } from "../types"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ImageIcon, VideoIcon } from "lucide-react"
|
||||
import { ImageIcon, VideoIcon } from "@workspace/ui/components/icon"
|
||||
import { $insertNodes } from "lexical"
|
||||
|
||||
import {
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Subscript,
|
||||
Superscript,
|
||||
Underline,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { $getSelection, $isRangeSelection, FORMAT_TEXT_COMMAND } from "lexical"
|
||||
import type { TextFormatType } from "lexical"
|
||||
import type { LexicalActionDefinition, LexicalActionName } from "../types"
|
||||
|
||||
@@ -22,7 +22,12 @@ import type {
|
||||
Spread,
|
||||
} from "lexical"
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { AlignCenter, AlignLeft, AlignRight, Trash2 } from "lucide-react"
|
||||
import {
|
||||
AlignCenter,
|
||||
AlignLeft,
|
||||
AlignRight,
|
||||
Trash2,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { useTranslate } from "@workspace/i18n"
|
||||
|
||||
import { ImageResizer } from "../components/image-resizer"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react"
|
||||
import { Trash2 } from "lucide-react"
|
||||
import { Trash2 } from "@workspace/ui/components/icon"
|
||||
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"
|
||||
import {
|
||||
$getNearestNodeFromDOMNode,
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
} from "react"
|
||||
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"
|
||||
import { DraggableBlockPlugin_EXPERIMENTAL } from "@lexical/react/LexicalDraggableBlockPlugin"
|
||||
import { GripVertical } from "lucide-react"
|
||||
import { GripVertical } from "@workspace/ui/components/icon"
|
||||
import { $getRoot, $isParagraphNode } from "lexical"
|
||||
|
||||
import { useLexicalMessage } from "../i18n"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useId, useRef, useState } from "react"
|
||||
import { $isLinkNode, $toggleLink } from "@lexical/link"
|
||||
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"
|
||||
import { Check, Pencil, Trash2 } from "lucide-react"
|
||||
import { Check, Pencil, Trash2 } from "@workspace/ui/components/icon"
|
||||
import {
|
||||
$findMatchingParent,
|
||||
$getNearestNodeFromDOMNode,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ComponentType, ReactNode } from "react"
|
||||
import type { Klass, LexicalEditor, LexicalNode } from "lexical"
|
||||
import type { LucideIcon } from "lucide-react"
|
||||
import type { IconComponent } from "@workspace/ui/components/icon"
|
||||
import type { LexicalActionState } from "./context"
|
||||
import type { LexicalEmbedDefinition } from "./embed"
|
||||
import type { LexicalMessage } from "./i18n"
|
||||
@@ -54,7 +54,7 @@ export interface LexicalActionDefinition<Value = string> {
|
||||
name: string
|
||||
label: LexicalMessage
|
||||
hidden?: boolean
|
||||
icon?: LucideIcon
|
||||
icon?: IconComponent
|
||||
group?: LexicalActionGroup
|
||||
execute: (context: LexicalActionContext, value?: Value) => void
|
||||
isActive?: (context: LexicalActionContext) => boolean
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"@base-ui/react": "^1.6.0",
|
||||
"@base-ui/utils": "^0.3.1",
|
||||
"@fontsource-variable/inter": "^5.3.0",
|
||||
"@icones/react": "^0.0.1",
|
||||
"@shadcn/react": "^0.2.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
@@ -17,7 +18,6 @@
|
||||
"date-fns": "^4.4.0",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"lucide-react": "^1.27.0",
|
||||
"react": "^19.2.6",
|
||||
"react-day-picker": "^10.0.1",
|
||||
"react-dom": "^19.2.6",
|
||||
@@ -44,4 +44,4 @@
|
||||
"./components/*": "./src/components/*.tsx",
|
||||
"./hooks/*": "./src/hooks/*.ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||
import { ChevronDownIcon, ChevronUpIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
||||
return (
|
||||
|
||||
@@ -3,7 +3,10 @@ import { mergeProps } from "@base-ui/react/merge-props"
|
||||
import { useRender } from "@base-ui/react/use-render"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
||||
import {
|
||||
ChevronRightIcon,
|
||||
MoreHorizontalIcon,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { LocalizedText, uiMessages, useMessage } from "./i18n"
|
||||
|
||||
function Breadcrumb({
|
||||
|
||||
@@ -14,7 +14,68 @@ import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronDownIcon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
type CalendarComponents = NonNullable<
|
||||
React.ComponentProps<typeof DayPicker>["components"]
|
||||
>
|
||||
|
||||
const CalendarLocaleContext = React.createContext<string | undefined>(undefined)
|
||||
|
||||
const CalendarRoot: NonNullable<CalendarComponents["Root"]> = ({
|
||||
className: rootClassName,
|
||||
rootRef,
|
||||
...rootProps
|
||||
}) => (
|
||||
<div
|
||||
data-slot="calendar"
|
||||
ref={rootRef}
|
||||
className={cn(rootClassName)}
|
||||
{...rootProps}
|
||||
/>
|
||||
)
|
||||
|
||||
const CalendarChevron: NonNullable<CalendarComponents["Chevron"]> = ({
|
||||
className: chevronClassName,
|
||||
orientation,
|
||||
...chevronProps
|
||||
}) => {
|
||||
if (orientation === "left") {
|
||||
return (
|
||||
<ChevronLeftIcon
|
||||
className={cn("size-4 rtl:rotate-180", chevronClassName)}
|
||||
{...chevronProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (orientation === "right") {
|
||||
return (
|
||||
<ChevronRightIcon
|
||||
className={cn("size-4 rtl:rotate-180", chevronClassName)}
|
||||
{...chevronProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ChevronDownIcon
|
||||
className={cn("size-4", chevronClassName)}
|
||||
{...chevronProps}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const CalendarWeekNumber: NonNullable<CalendarComponents["WeekNumber"]> = ({
|
||||
children,
|
||||
...weekNumberProps
|
||||
}) => (
|
||||
<td {...weekNumberProps}>
|
||||
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
)
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
@@ -37,162 +98,126 @@ function Calendar({
|
||||
const localeCode = locale?.code ?? contextLocale
|
||||
|
||||
return (
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn(
|
||||
"group/calendar bg-background p-3 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
|
||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
||||
className
|
||||
)}
|
||||
captionLayout={captionLayout}
|
||||
locale={locale}
|
||||
lang={lang ?? localeCode}
|
||||
formatters={{
|
||||
formatMonthDropdown: (date) =>
|
||||
date.toLocaleString(localeCode, { month: "short" }),
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
root: cn("w-fit", defaultClassNames.root),
|
||||
months: cn(
|
||||
"relative flex flex-col gap-4 md:flex-row",
|
||||
defaultClassNames.months
|
||||
),
|
||||
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
||||
nav: cn(
|
||||
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
||||
defaultClassNames.nav
|
||||
),
|
||||
button_previous: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
||||
defaultClassNames.button_previous
|
||||
),
|
||||
button_next: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
||||
defaultClassNames.button_next
|
||||
),
|
||||
month_caption: cn(
|
||||
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
||||
defaultClassNames.month_caption
|
||||
),
|
||||
dropdowns: cn(
|
||||
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
||||
defaultClassNames.dropdowns
|
||||
),
|
||||
dropdown_root: cn(
|
||||
"relative rounded-(--cell-radius)",
|
||||
defaultClassNames.dropdown_root
|
||||
),
|
||||
dropdown: cn(
|
||||
"absolute inset-0 bg-popover opacity-0",
|
||||
defaultClassNames.dropdown
|
||||
),
|
||||
caption_label: cn(
|
||||
"font-medium select-none",
|
||||
captionLayout === "label"
|
||||
? "text-sm"
|
||||
: "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
||||
defaultClassNames.caption_label
|
||||
),
|
||||
month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
|
||||
defaultClassNames.weekday
|
||||
),
|
||||
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
||||
week_number_header: cn(
|
||||
"w-(--cell-size) select-none",
|
||||
defaultClassNames.week_number_header
|
||||
),
|
||||
week_number: cn(
|
||||
"text-[0.8rem] text-muted-foreground select-none",
|
||||
defaultClassNames.week_number
|
||||
),
|
||||
day: cn(
|
||||
"group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius)",
|
||||
props.showWeekNumber
|
||||
? "[&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)"
|
||||
: "[&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)",
|
||||
defaultClassNames.day
|
||||
),
|
||||
range_start: cn(
|
||||
"relative isolate z-0 rounded-s-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:end-0 after:w-4 after:bg-muted",
|
||||
defaultClassNames.range_start
|
||||
),
|
||||
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
||||
range_end: cn(
|
||||
"relative isolate z-0 rounded-e-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:start-0 after:w-4 after:bg-muted",
|
||||
defaultClassNames.range_end
|
||||
),
|
||||
today: cn(
|
||||
"rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
|
||||
defaultClassNames.today
|
||||
),
|
||||
outside: cn(
|
||||
"text-muted-foreground aria-selected:text-muted-foreground",
|
||||
defaultClassNames.outside
|
||||
),
|
||||
disabled: cn(
|
||||
"text-muted-foreground opacity-50",
|
||||
defaultClassNames.disabled
|
||||
),
|
||||
hidden: cn("invisible", defaultClassNames.hidden),
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
Root: ({ className, rootRef, ...props }) => {
|
||||
return (
|
||||
<div
|
||||
data-slot="calendar"
|
||||
ref={rootRef}
|
||||
className={cn(className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
},
|
||||
Chevron: ({ className, orientation, ...props }) => {
|
||||
if (orientation === "left") {
|
||||
return (
|
||||
<ChevronLeftIcon
|
||||
className={cn("size-4 rtl:rotate-180", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (orientation === "right") {
|
||||
return (
|
||||
<ChevronRightIcon
|
||||
className={cn("size-4 rtl:rotate-180", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ChevronDownIcon className={cn("size-4", className)} {...props} />
|
||||
)
|
||||
},
|
||||
DayButton: ({ ...props }) => (
|
||||
<CalendarDayButton locale={localeCode} {...props} />
|
||||
),
|
||||
WeekNumber: ({ children, ...props }) => {
|
||||
return (
|
||||
<td {...props}>
|
||||
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
||||
{children}
|
||||
</div>
|
||||
</td>
|
||||
)
|
||||
},
|
||||
...components,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
<CalendarLocaleContext.Provider value={localeCode}>
|
||||
<DayPicker
|
||||
showOutsideDays={showOutsideDays}
|
||||
className={cn(
|
||||
"group/calendar bg-background p-3 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
|
||||
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
||||
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
||||
className
|
||||
)}
|
||||
captionLayout={captionLayout}
|
||||
locale={locale}
|
||||
lang={lang ?? localeCode}
|
||||
formatters={{
|
||||
formatMonthDropdown: (date) =>
|
||||
date.toLocaleString(localeCode, { month: "short" }),
|
||||
...formatters,
|
||||
}}
|
||||
classNames={{
|
||||
root: cn("w-fit", defaultClassNames.root),
|
||||
months: cn(
|
||||
"relative flex flex-col gap-4 md:flex-row",
|
||||
defaultClassNames.months
|
||||
),
|
||||
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
||||
nav: cn(
|
||||
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
||||
defaultClassNames.nav
|
||||
),
|
||||
button_previous: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
||||
defaultClassNames.button_previous
|
||||
),
|
||||
button_next: cn(
|
||||
buttonVariants({ variant: buttonVariant }),
|
||||
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
||||
defaultClassNames.button_next
|
||||
),
|
||||
month_caption: cn(
|
||||
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
||||
defaultClassNames.month_caption
|
||||
),
|
||||
dropdowns: cn(
|
||||
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
||||
defaultClassNames.dropdowns
|
||||
),
|
||||
dropdown_root: cn(
|
||||
"relative rounded-(--cell-radius)",
|
||||
defaultClassNames.dropdown_root
|
||||
),
|
||||
dropdown: cn(
|
||||
"absolute inset-0 bg-popover opacity-0",
|
||||
defaultClassNames.dropdown
|
||||
),
|
||||
caption_label: cn(
|
||||
"font-medium select-none",
|
||||
captionLayout === "label"
|
||||
? "text-sm"
|
||||
: "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
||||
defaultClassNames.caption_label
|
||||
),
|
||||
month_grid: cn(
|
||||
"w-full border-collapse",
|
||||
defaultClassNames.month_grid
|
||||
),
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
|
||||
defaultClassNames.weekday
|
||||
),
|
||||
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
||||
week_number_header: cn(
|
||||
"w-(--cell-size) select-none",
|
||||
defaultClassNames.week_number_header
|
||||
),
|
||||
week_number: cn(
|
||||
"text-[0.8rem] text-muted-foreground select-none",
|
||||
defaultClassNames.week_number
|
||||
),
|
||||
day: cn(
|
||||
"group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-e-(--cell-radius)",
|
||||
props.showWeekNumber
|
||||
? "[&:nth-child(2)[data-selected=true]_button]:rounded-s-(--cell-radius)"
|
||||
: "[&:first-child[data-selected=true]_button]:rounded-s-(--cell-radius)",
|
||||
defaultClassNames.day
|
||||
),
|
||||
range_start: cn(
|
||||
"relative isolate z-0 rounded-s-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:end-0 after:w-4 after:bg-muted",
|
||||
defaultClassNames.range_start
|
||||
),
|
||||
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
||||
range_end: cn(
|
||||
"relative isolate z-0 rounded-e-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:start-0 after:w-4 after:bg-muted",
|
||||
defaultClassNames.range_end
|
||||
),
|
||||
today: cn(
|
||||
"rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
|
||||
defaultClassNames.today
|
||||
),
|
||||
outside: cn(
|
||||
"text-muted-foreground aria-selected:text-muted-foreground",
|
||||
defaultClassNames.outside
|
||||
),
|
||||
disabled: cn(
|
||||
"text-muted-foreground opacity-50",
|
||||
defaultClassNames.disabled
|
||||
),
|
||||
hidden: cn("invisible", defaultClassNames.hidden),
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
Root: CalendarRoot,
|
||||
Chevron: CalendarChevron,
|
||||
DayButton: CalendarDayButton,
|
||||
WeekNumber: CalendarWeekNumber,
|
||||
...components,
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
</CalendarLocaleContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -200,10 +225,10 @@ function CalendarDayButton({
|
||||
className,
|
||||
day,
|
||||
modifiers,
|
||||
locale,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DayButton> & { locale?: string }) {
|
||||
}: React.ComponentProps<typeof DayButton>) {
|
||||
const defaultClassNames = getDefaultClassNames()
|
||||
const locale = React.useContext(CalendarLocaleContext)
|
||||
|
||||
const ref = React.useRef<HTMLButtonElement>(null)
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -5,7 +5,10 @@ import useEmblaCarousel, {
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
||||
import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { LocalizedText, uiMessages } from "./i18n"
|
||||
|
||||
type CarouselApi = UseEmblaCarouselType[1]
|
||||
@@ -60,10 +63,10 @@ function Carousel({
|
||||
const [canScrollPrev, setCanScrollPrev] = React.useState(false)
|
||||
const [canScrollNext, setCanScrollNext] = React.useState(false)
|
||||
|
||||
const onSelect = React.useCallback((api: CarouselApi) => {
|
||||
if (!api) return
|
||||
setCanScrollPrev(api.canScrollPrev())
|
||||
setCanScrollNext(api.canScrollNext())
|
||||
const onSelect = React.useCallback((carouselApi: CarouselApi) => {
|
||||
if (!carouselApi) return
|
||||
setCanScrollPrev(carouselApi.canScrollPrev())
|
||||
setCanScrollNext(carouselApi.canScrollNext())
|
||||
}, [])
|
||||
|
||||
const scrollPrev = React.useCallback(() => {
|
||||
@@ -103,20 +106,36 @@ function Carousel({
|
||||
}
|
||||
}, [api, onSelect])
|
||||
|
||||
const contextValue = React.useMemo<CarouselContextProps>(
|
||||
() => ({
|
||||
carouselRef,
|
||||
api,
|
||||
opts,
|
||||
orientation:
|
||||
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
||||
scrollPrev,
|
||||
scrollNext,
|
||||
canScrollPrev,
|
||||
canScrollNext,
|
||||
plugins,
|
||||
setApi,
|
||||
}),
|
||||
[
|
||||
api,
|
||||
canScrollNext,
|
||||
canScrollPrev,
|
||||
carouselRef,
|
||||
opts,
|
||||
orientation,
|
||||
plugins,
|
||||
scrollNext,
|
||||
scrollPrev,
|
||||
setApi,
|
||||
]
|
||||
)
|
||||
|
||||
return (
|
||||
<CarouselContext.Provider
|
||||
value={{
|
||||
carouselRef,
|
||||
api: api,
|
||||
opts,
|
||||
orientation:
|
||||
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
||||
scrollPrev,
|
||||
scrollNext,
|
||||
canScrollPrev,
|
||||
canScrollNext,
|
||||
}}
|
||||
>
|
||||
<CarouselContext.Provider value={contextValue}>
|
||||
<div
|
||||
onKeyDownCapture={handleKeyDown}
|
||||
className={cn("relative", className)}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { CheckIcon } from "lucide-react"
|
||||
import { CheckIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
||||
return (
|
||||
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
} from "@workspace/ui/components/input-group"
|
||||
import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
XIcon,
|
||||
CheckIcon,
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
const Combobox = ComboboxPrimitive.Root
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
} from "@workspace/ui/components/input-group"
|
||||
import { SearchIcon, CheckIcon } from "lucide-react"
|
||||
import { SearchIcon, CheckIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function Command({
|
||||
className,
|
||||
|
||||
@@ -4,7 +4,7 @@ import * as React from "react"
|
||||
import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronRightIcon, CheckIcon } from "lucide-react"
|
||||
import { ChevronRightIcon, CheckIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
|
||||
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { XIcon } from "lucide-react"
|
||||
import { XIcon } from "@workspace/ui/components/icon"
|
||||
import { LocalizedText, uiMessages } from "./i18n"
|
||||
|
||||
type DialogHandle<Payload> = DialogPrimitive.Handle<Payload>
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
getAnchoredSurfaceSideOffset,
|
||||
} from "@workspace/ui/components/anchored-surface"
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronRightIcon, CheckIcon } from "lucide-react"
|
||||
import { ChevronRightIcon, CheckIcon } from "@workspace/ui/components/icon"
|
||||
import { useRippleRef } from "../hooks/use-ripple"
|
||||
|
||||
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import * as React from "react"
|
||||
import { Icon, type IconName, type IconProps } from "@icones/react"
|
||||
|
||||
export type IconComponentProps = Omit<
|
||||
Extract<IconProps, { name: string }>,
|
||||
"name"
|
||||
>
|
||||
export type IconComponent = React.ComponentType<IconComponentProps>
|
||||
|
||||
function createIcon(name: IconName<"lucide">): IconComponent {
|
||||
const Component = (props: IconComponentProps) => (
|
||||
<Icon name={name} size={24} strokeWidth={2} {...props} />
|
||||
)
|
||||
|
||||
Component.displayName = `Icon(${name})`
|
||||
return Component
|
||||
}
|
||||
|
||||
export const ALargeSmall = createIcon("lucide:a-large-small")
|
||||
export const AlignCenter = createIcon("lucide:text-align-center")
|
||||
export const AlignCenterIcon = AlignCenter
|
||||
export const AlignJustify = createIcon("lucide:text-align-justify")
|
||||
export const AlignLeft = createIcon("lucide:text-align-start")
|
||||
export const AlignLeftIcon = AlignLeft
|
||||
export const AlignRight = createIcon("lucide:text-align-end")
|
||||
export const AlignRightIcon = AlignRight
|
||||
export const AppWindowIcon = createIcon("lucide:app-window")
|
||||
export const ArrowDownIcon = createIcon("lucide:arrow-down")
|
||||
export const ArrowLeftIcon = createIcon("lucide:arrow-left")
|
||||
export const Baseline = createIcon("lucide:baseline")
|
||||
export const Bold = createIcon("lucide:bold")
|
||||
export const CalendarDays = createIcon("lucide:calendar-days")
|
||||
export const CaseLower = createIcon("lucide:case-lower")
|
||||
export const CaseSensitive = createIcon("lucide:case-sensitive")
|
||||
export const CaseUpper = createIcon("lucide:case-upper")
|
||||
export const Check = createIcon("lucide:check")
|
||||
export const CheckIcon = Check
|
||||
export const CheckCheckIcon = createIcon("lucide:check-check")
|
||||
export const ChevronDown = createIcon("lucide:chevron-down")
|
||||
export const ChevronDownIcon = ChevronDown
|
||||
export const ChevronLeftIcon = createIcon("lucide:chevron-left")
|
||||
export const ChevronRightIcon = createIcon("lucide:chevron-right")
|
||||
export const ChevronUpIcon = createIcon("lucide:chevron-up")
|
||||
export const CircleAlertIcon = createIcon("lucide:circle-alert")
|
||||
export const CircleCheckIcon = createIcon("lucide:circle-check")
|
||||
export const ClipboardCopyIcon = createIcon("lucide:clipboard-copy")
|
||||
export const CloudIcon = createIcon("lucide:cloud")
|
||||
export const CloudUploadIcon = createIcon("lucide:cloud-upload")
|
||||
export const CopyIcon = createIcon("lucide:copy")
|
||||
export const CornerDownLeftIcon = createIcon("lucide:corner-down-left")
|
||||
export const Eraser = createIcon("lucide:eraser")
|
||||
export const ExternalLinkIcon = createIcon("lucide:external-link")
|
||||
export const EyeIcon = createIcon("lucide:eye")
|
||||
export const EyeOffIcon = createIcon("lucide:eye-off")
|
||||
export const FileCode2Icon = createIcon("lucide:file-code-corner")
|
||||
export const FileTextIcon = createIcon("lucide:file-text")
|
||||
export const FilmIcon = createIcon("lucide:film")
|
||||
export const FolderIcon = createIcon("lucide:folder")
|
||||
export const FolderInputIcon = createIcon("lucide:folder-input")
|
||||
export const FolderOpenIcon = createIcon("lucide:folder-open")
|
||||
export const FolderPlusIcon = createIcon("lucide:folder-plus")
|
||||
export const GlobeIcon = createIcon("lucide:globe")
|
||||
export const GripVertical = createIcon("lucide:grip-vertical")
|
||||
export const Heading1 = createIcon("lucide:heading-1")
|
||||
export const Heading2 = createIcon("lucide:heading-2")
|
||||
export const Heading3 = createIcon("lucide:heading-3")
|
||||
export const HeartIcon = createIcon("lucide:heart")
|
||||
export const HistoryIcon = createIcon("lucide:rotate-ccw-clock")
|
||||
export const ImageIcon = createIcon("lucide:image")
|
||||
export const ImagePlusIcon = createIcon("lucide:image-plus")
|
||||
export const ImagesIcon = createIcon("lucide:images")
|
||||
export const IndentDecrease = createIcon("lucide:list-indent-decrease")
|
||||
export const IndentIncrease = createIcon("lucide:list-indent-increase")
|
||||
export const InfoIcon = createIcon("lucide:info")
|
||||
export const Italic = createIcon("lucide:italic")
|
||||
export const LanguagesIcon = createIcon("lucide:languages")
|
||||
export const LibraryIcon = createIcon("lucide:library")
|
||||
export const Link = createIcon("lucide:link")
|
||||
export const LinkIcon = Link
|
||||
export const List = createIcon("lucide:list")
|
||||
export const ListChecks = createIcon("lucide:list-checks")
|
||||
export const ListOrdered = createIcon("lucide:list-ordered")
|
||||
export const LoaderCircleIcon = createIcon("lucide:loader-circle")
|
||||
export const Loader2Icon = LoaderCircleIcon
|
||||
export const Maximize2Icon = createIcon("lucide:maximize-2")
|
||||
export const MessageCircleIcon = createIcon("lucide:message-circle")
|
||||
export const Minimize2Icon = createIcon("lucide:minimize-2")
|
||||
export const Minus = createIcon("lucide:minus")
|
||||
export const MinusIcon = Minus
|
||||
export const MonitorIcon = createIcon("lucide:monitor")
|
||||
export const MoonIcon = createIcon("lucide:moon")
|
||||
export const MoreHorizontalIcon = createIcon("lucide:ellipsis")
|
||||
export const OctagonXIcon = createIcon("lucide:octagon-x")
|
||||
export const PackageIcon = createIcon("lucide:package")
|
||||
export const PanelLeftIcon = createIcon("lucide:panel-left")
|
||||
export const PaperclipIcon = createIcon("lucide:paperclip")
|
||||
export const Pencil = createIcon("lucide:pencil")
|
||||
export const PencilIcon = Pencil
|
||||
export const Pilcrow = createIcon("lucide:pilcrow")
|
||||
export const Pipette = createIcon("lucide:pipette")
|
||||
export const Plus = createIcon("lucide:plus")
|
||||
export const PlusIcon = Plus
|
||||
export const Quote = createIcon("lucide:quote")
|
||||
export const Redo2 = createIcon("lucide:redo-2")
|
||||
export const RotateCcwIcon = createIcon("lucide:rotate-ccw")
|
||||
export const SaveIcon = createIcon("lucide:save")
|
||||
export const SearchIcon = createIcon("lucide:search")
|
||||
export const SendHorizontalIcon = createIcon("lucide:send-horizontal")
|
||||
export const SettingsIcon = createIcon("lucide:settings")
|
||||
export const SmileIcon = createIcon("lucide:face-slightly-smiling")
|
||||
export const Strikethrough = createIcon("lucide:strikethrough")
|
||||
export const Subscript = createIcon("lucide:subscript")
|
||||
export const SunIcon = createIcon("lucide:sun")
|
||||
export const Superscript = createIcon("lucide:superscript")
|
||||
export const TelescopeIcon = createIcon("lucide:telescope")
|
||||
export const TerminalIcon = createIcon("lucide:terminal")
|
||||
export const Trash2 = createIcon("lucide:trash")
|
||||
export const Trash2Icon = Trash2
|
||||
export const TriangleAlertIcon = createIcon("lucide:triangle-alert")
|
||||
export const Underline = createIcon("lucide:underline")
|
||||
export const Undo2 = createIcon("lucide:undo-2")
|
||||
export const UnlinkIcon = createIcon("lucide:unlink")
|
||||
export const UploadIcon = createIcon("lucide:upload")
|
||||
export const VideoIcon = createIcon("lucide:video")
|
||||
export const XIcon = createIcon("lucide:x")
|
||||
|
||||
// Literal references let @icones/vite emit assets for compatibility components.
|
||||
export function IconAssetCatalog() {
|
||||
return (
|
||||
<>
|
||||
<Icon name="lucide:a-large-small" />
|
||||
<Icon name="lucide:text-align-center" />
|
||||
<Icon name="lucide:text-align-justify" />
|
||||
<Icon name="lucide:text-align-start" />
|
||||
<Icon name="lucide:text-align-end" />
|
||||
<Icon name="lucide:app-window" />
|
||||
<Icon name="lucide:arrow-down" />
|
||||
<Icon name="lucide:arrow-left" />
|
||||
<Icon name="lucide:baseline" />
|
||||
<Icon name="lucide:bold" />
|
||||
<Icon name="lucide:calendar-days" />
|
||||
<Icon name="lucide:case-lower" />
|
||||
<Icon name="lucide:case-sensitive" />
|
||||
<Icon name="lucide:case-upper" />
|
||||
<Icon name="lucide:check" />
|
||||
<Icon name="lucide:check-check" />
|
||||
<Icon name="lucide:chevron-down" />
|
||||
<Icon name="lucide:chevron-left" />
|
||||
<Icon name="lucide:chevron-right" />
|
||||
<Icon name="lucide:chevron-up" />
|
||||
<Icon name="lucide:circle-alert" />
|
||||
<Icon name="lucide:circle-check" />
|
||||
<Icon name="lucide:clipboard-copy" />
|
||||
<Icon name="lucide:cloud" />
|
||||
<Icon name="lucide:cloud-upload" />
|
||||
<Icon name="lucide:copy" />
|
||||
<Icon name="lucide:corner-down-left" />
|
||||
<Icon name="lucide:eraser" />
|
||||
<Icon name="lucide:external-link" />
|
||||
<Icon name="lucide:eye" />
|
||||
<Icon name="lucide:eye-off" />
|
||||
<Icon name="lucide:file-code-corner" />
|
||||
<Icon name="lucide:file-text" />
|
||||
<Icon name="lucide:film" />
|
||||
<Icon name="lucide:folder" />
|
||||
<Icon name="lucide:folder-input" />
|
||||
<Icon name="lucide:folder-open" />
|
||||
<Icon name="lucide:folder-plus" />
|
||||
<Icon name="lucide:globe" />
|
||||
<Icon name="lucide:grip-vertical" />
|
||||
<Icon name="lucide:heading-1" />
|
||||
<Icon name="lucide:heading-2" />
|
||||
<Icon name="lucide:heading-3" />
|
||||
<Icon name="lucide:heart" />
|
||||
<Icon name="lucide:rotate-ccw-clock" />
|
||||
<Icon name="lucide:image" />
|
||||
<Icon name="lucide:image-plus" />
|
||||
<Icon name="lucide:images" />
|
||||
<Icon name="lucide:list-indent-decrease" />
|
||||
<Icon name="lucide:list-indent-increase" />
|
||||
<Icon name="lucide:info" />
|
||||
<Icon name="lucide:italic" />
|
||||
<Icon name="lucide:languages" />
|
||||
<Icon name="lucide:library" />
|
||||
<Icon name="lucide:link" />
|
||||
<Icon name="lucide:list" />
|
||||
<Icon name="lucide:list-checks" />
|
||||
<Icon name="lucide:list-ordered" />
|
||||
<Icon name="lucide:loader-circle" />
|
||||
<Icon name="lucide:maximize-2" />
|
||||
<Icon name="lucide:message-circle" />
|
||||
<Icon name="lucide:minimize-2" />
|
||||
<Icon name="lucide:minus" />
|
||||
<Icon name="lucide:monitor" />
|
||||
<Icon name="lucide:moon" />
|
||||
<Icon name="lucide:ellipsis" />
|
||||
<Icon name="lucide:octagon-x" />
|
||||
<Icon name="lucide:package" />
|
||||
<Icon name="lucide:panel-left" />
|
||||
<Icon name="lucide:paperclip" />
|
||||
<Icon name="lucide:pencil" />
|
||||
<Icon name="lucide:pilcrow" />
|
||||
<Icon name="lucide:pipette" />
|
||||
<Icon name="lucide:plus" />
|
||||
<Icon name="lucide:quote" />
|
||||
<Icon name="lucide:redo-2" />
|
||||
<Icon name="lucide:rotate-ccw" />
|
||||
<Icon name="lucide:save" />
|
||||
<Icon name="lucide:search" />
|
||||
<Icon name="lucide:send-horizontal" />
|
||||
<Icon name="lucide:settings" />
|
||||
<Icon name="lucide:face-slightly-smiling" />
|
||||
<Icon name="lucide:strikethrough" />
|
||||
<Icon name="lucide:subscript" />
|
||||
<Icon name="lucide:sun" />
|
||||
<Icon name="lucide:superscript" />
|
||||
<Icon name="lucide:telescope" />
|
||||
<Icon name="lucide:terminal" />
|
||||
<Icon name="lucide:trash" />
|
||||
<Icon name="lucide:triangle-alert" />
|
||||
<Icon name="lucide:underline" />
|
||||
<Icon name="lucide:undo-2" />
|
||||
<Icon name="lucide:unlink" />
|
||||
<Icon name="lucide:upload" />
|
||||
<Icon name="lucide:video" />
|
||||
<Icon name="lucide:x" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import * as React from "react"
|
||||
import { OTPInput, OTPInputContext } from "input-otp"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { MinusIcon } from "lucide-react"
|
||||
import { MinusIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function InputOTP({
|
||||
className,
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuTrigger,
|
||||
} from "@workspace/ui/components/dropdown-menu"
|
||||
import { CheckIcon } from "lucide-react"
|
||||
import { CheckIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function Menubar({ className, ...props }: MenubarPrimitive.Props) {
|
||||
return (
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { ArrowDownIcon } from "lucide-react"
|
||||
import { ArrowDownIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function MessageScrollerProvider(
|
||||
props: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
import { ChevronDownIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
type NativeSelectProps = Omit<React.ComponentProps<"select">, "size"> & {
|
||||
size?: "sm" | "default"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/naviga
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronDownIcon } from "lucide-react"
|
||||
import { ChevronDownIcon } from "@workspace/ui/components/icon"
|
||||
|
||||
function NavigationMenu({
|
||||
align = "start",
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
MoreHorizontalIcon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { LocalizedText, uiMessages, useMessage } from "./i18n"
|
||||
|
||||
function Pagination({
|
||||
|
||||
@@ -4,7 +4,11 @@ import * as React from "react"
|
||||
import { Select as SelectPrimitive } from "@base-ui/react/select"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
CheckIcon,
|
||||
ChevronUpIcon,
|
||||
} from "@workspace/ui/components/icon"
|
||||
|
||||
const Select = SelectPrimitive.Root
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
|
||||
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { Button } from "@workspace/ui/components/button"
|
||||
import { XIcon } from "lucide-react"
|
||||
import { XIcon } from "@workspace/ui/components/icon"
|
||||
import { LocalizedText, uiMessages } from "./i18n"
|
||||
|
||||
function createSheetHandle<Payload>(): SheetPrimitive.Handle<Payload> {
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@workspace/ui/components/tooltip"
|
||||
import { PanelLeftIcon } from "lucide-react"
|
||||
import { PanelLeftIcon } from "@workspace/ui/components/icon"
|
||||
import { LocalizedText, uiMessages, useMessage } from "./i18n"
|
||||
|
||||
const SIDEBAR_COOKIE_NAME = "sidebar_state"
|
||||
@@ -89,7 +89,9 @@ function SidebarProvider({
|
||||
|
||||
// Helper to toggle the sidebar.
|
||||
const toggleSidebar = React.useCallback(() => {
|
||||
return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open)
|
||||
return isMobile
|
||||
? setOpenMobile((currentOpen) => !currentOpen)
|
||||
: setOpen((currentOpen) => !currentOpen)
|
||||
}, [isMobile, setOpen, setOpenMobile])
|
||||
|
||||
// Adds a keyboard shortcut to toggle the sidebar.
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { cn } from "@workspace/ui/lib/utils"
|
||||
import { Loader2Icon } from "lucide-react"
|
||||
import {
|
||||
Loader2Icon,
|
||||
type IconComponentProps,
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { uiMessages, useMessage } from "./i18n"
|
||||
|
||||
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
|
||||
function Spinner({ className, ...props }: IconComponentProps) {
|
||||
const loadingLabel = useMessage(uiMessages.loading)
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,10 +10,12 @@ import {
|
||||
TriangleAlertIcon,
|
||||
OctagonXIcon,
|
||||
Loader2Icon,
|
||||
} from "lucide-react"
|
||||
} from "@workspace/ui/components/icon"
|
||||
import { uiMessages, useMessage } from "./i18n"
|
||||
|
||||
const toast = ToastPrimitive.createToastManager()
|
||||
const defaultToastActionRender = <Button variant="outline" size="sm" />
|
||||
const defaultToastCloseRender = <Button variant="ghost" size="icon-sm" />
|
||||
|
||||
function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
|
||||
return <ToastPrimitive.Provider {...props} />
|
||||
@@ -101,7 +103,7 @@ function ToastDescription({
|
||||
|
||||
function ToastAction({
|
||||
className,
|
||||
render = <Button variant="outline" size="sm" />,
|
||||
render = defaultToastActionRender,
|
||||
...props
|
||||
}: ToastPrimitive.Action.Props) {
|
||||
return (
|
||||
@@ -118,7 +120,7 @@ function ToastClose({
|
||||
"aria-label": ariaLabel,
|
||||
className,
|
||||
children,
|
||||
render = <Button variant="ghost" size="icon-sm" />,
|
||||
render = defaultToastCloseRender,
|
||||
...props
|
||||
}: ToastPrimitive.Close.Props) {
|
||||
const closeLabel = useMessage(uiMessages.closeToast)
|
||||
|
||||
Reference in New Issue
Block a user