Refactor UI components to use consistent button and icon styles across the application
This commit is contained in:
parent
fdceed6067
commit
4f1d8feda5
12 changed files with 233 additions and 87 deletions
|
|
@ -176,7 +176,7 @@ export default function EPUBPage() {
|
|||
href="/app"
|
||||
className={buttonClass({ variant: 'secondary', size: 'md', className: 'gap-2' })}
|
||||
>
|
||||
<svg className="w-4 h-4 text-soft" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
Back to Documents
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ export default function PDFViewerPage() {
|
|||
onClick={handleBackToDocuments}
|
||||
className={buttonClass({ variant: 'secondary', size: 'md', className: 'gap-2' })}
|
||||
>
|
||||
<svg className="w-4 h-4 text-soft" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
Back to Documents
|
||||
|
|
@ -346,7 +346,7 @@ export default function PDFViewerPage() {
|
|||
<button
|
||||
type="button"
|
||||
onClick={requestForceReparse}
|
||||
className="inline-flex items-center rounded-md border border-line bg-surface px-3 py-1.5 text-xs font-medium text-foreground hover:text-accent transition-colors"
|
||||
className={buttonClass({ variant: 'secondary', size: 'sm' })}
|
||||
>
|
||||
Retry Parse
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ export function ColorPicker({ value, field, label, onChange }: ColorPickerProps)
|
|||
size="xs"
|
||||
aria-label="Open system color picker"
|
||||
>
|
||||
<PaletteIcon className="w-4 h-4 text-soft transform transition-transform duration-base ease-standard hover:text-accent" />
|
||||
<PaletteIcon className="w-4 h-4 transform transition-transform duration-base ease-standard" />
|
||||
</IconButton>
|
||||
<input
|
||||
ref={nativeRef}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ export function HomeContent() {
|
|||
const appActions = (
|
||||
<div className="flex flex-col gap-0.5 w-full">
|
||||
<SettingsTrigger
|
||||
variant="sidebar"
|
||||
triggerLabel="Settings"
|
||||
className="w-full justify-start gap-2 px-2 py-1 text-[12px] border-transparent hover:border-accent"
|
||||
onOpen={() => setSettingsOpen(true)}
|
||||
/>
|
||||
<UserMenu variant="sidebar" />
|
||||
|
|
|
|||
|
|
@ -181,12 +181,27 @@ type AdminSubTab = 'providers' | 'features';
|
|||
export function SettingsTrigger({
|
||||
className = '',
|
||||
triggerLabel,
|
||||
variant = 'button',
|
||||
onOpen,
|
||||
}: {
|
||||
className?: string;
|
||||
triggerLabel?: string;
|
||||
variant?: 'button' | 'sidebar';
|
||||
onOpen: () => void;
|
||||
}) {
|
||||
if (variant === 'sidebar') {
|
||||
return (
|
||||
<SidebarNavItem
|
||||
compact
|
||||
onClick={onOpen}
|
||||
className={className}
|
||||
aria-label="Settings"
|
||||
icon={<SettingsIcon className="w-3.5 h-3.5" />}
|
||||
label={triggerLabel ?? 'Settings'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
|
@ -959,16 +974,18 @@ export function SettingsModal({
|
|||
}}
|
||||
className="flex-1"
|
||||
/>
|
||||
<button
|
||||
<IconButton
|
||||
onClick={() => setIsCustomExpanded(!isCustomExpanded)}
|
||||
className="shrink-0 p-1.5 rounded-lg border border-line hover:border-accent-line transition-colors"
|
||||
tone="surface"
|
||||
size="sm"
|
||||
className="shrink-0"
|
||||
style={{ color: colors.muted, backgroundColor: colors.base }}
|
||||
aria-label={isCustomExpanded ? 'Collapse color picker' : 'Expand color picker'}
|
||||
>
|
||||
<svg className={`w-3.5 h-3.5 transition-transform duration-base ${isCustomExpanded ? 'rotate-180' : ''}`} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
{isCustomExpanded && (
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { useAuthSession } from '@/hooks/useAuthSession';
|
|||
import { getAuthClient } from '@/lib/client/auth-client';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { UserIcon } from '@/components/icons/Icons';
|
||||
import { IconButton, buttonClass } from '@/components/ui';
|
||||
import { IconButton, SidebarNavItem, SidebarNavLink, buttonClass } from '@/components/ui';
|
||||
|
||||
type UserMenuVariant = 'toolbar' | 'sidebar';
|
||||
|
||||
|
|
@ -31,21 +31,24 @@ export function UserMenu({
|
|||
router.push('/signin');
|
||||
};
|
||||
|
||||
const rowClass =
|
||||
'w-full inline-flex items-center gap-2 px-2 py-1 rounded-md text-[12px] border border-transparent transition duration-base ease-standard text-left hover:border-accent hover:bg-accent-wash hover:text-accent';
|
||||
|
||||
if (!session || session.user.isAnonymous) {
|
||||
if (variant === 'sidebar') {
|
||||
return (
|
||||
<div className={`flex w-full flex-col gap-0.5 ${className}`}>
|
||||
<Link href="/signin" className={rowClass}>
|
||||
<UserIcon className="h-3.5 w-3.5 text-soft" />
|
||||
<span className="truncate">Connect</span>
|
||||
<Link href="/signin" legacyBehavior passHref>
|
||||
<SidebarNavLink
|
||||
compact
|
||||
icon={<UserIcon className="h-3.5 w-3.5" />}
|
||||
label="Connect"
|
||||
/>
|
||||
</Link>
|
||||
{enableUserSignups && (
|
||||
<Link href="/signup" className={rowClass}>
|
||||
<UserIcon className="h-3.5 w-3.5 text-soft" />
|
||||
<span className="truncate">Create account</span>
|
||||
<Link href="/signup" legacyBehavior passHref>
|
||||
<SidebarNavLink
|
||||
compact
|
||||
icon={<UserIcon className="h-3.5 w-3.5" />}
|
||||
label="Create account"
|
||||
/>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -72,20 +75,24 @@ export function UserMenu({
|
|||
|
||||
if (variant === 'sidebar') {
|
||||
return (
|
||||
<button
|
||||
<SidebarNavItem
|
||||
compact
|
||||
onClick={handleDisconnectAccount}
|
||||
className={`${rowClass} ${className}`}
|
||||
className={className}
|
||||
title="Disconnect account"
|
||||
aria-label="Disconnect account"
|
||||
icon={<UserIcon className="h-3.5 w-3.5" />}
|
||||
label={session.user.email || 'Account'}
|
||||
trailing={(
|
||||
<svg className="h-3.5 w-3.5 shrink-0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
)}
|
||||
>
|
||||
<UserIcon className="h-3.5 w-3.5 text-soft" />
|
||||
<span className="truncate flex-1">{session.user.email || 'Account'}</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||
<polyline points="16 17 21 12 16 7"></polyline>
|
||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
{session.user.email || 'Account'}
|
||||
</SidebarNavItem>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -182,6 +182,16 @@ function SidebarUploadLoader({
|
|||
);
|
||||
}
|
||||
|
||||
function DocumentListStateLoader() {
|
||||
return (
|
||||
<div
|
||||
className="h-full w-full min-h-0 bg-surface-sunken animate-pulse"
|
||||
aria-label="Loading documents"
|
||||
aria-busy="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
|
||||
const cachedState = cachedDocumentListState;
|
||||
const [sortBy, setSortBy] = useState<SortBy>(cachedState?.sortBy ?? DEFAULT_STATE.sortBy);
|
||||
|
|
@ -670,7 +680,11 @@ function DocumentListInner({ brand, appActions }: DocumentListInnerProps) {
|
|||
|
||||
{isLoading ? (
|
||||
<div className="flex-1 min-h-0 overflow-hidden">
|
||||
<DocumentListSkeleton viewMode={fallbackViewMode} iconSize={iconSize} />
|
||||
{isInitialized ? (
|
||||
<DocumentListSkeleton viewMode={fallbackViewMode} iconSize={iconSize} />
|
||||
) : (
|
||||
<DocumentListStateLoader />
|
||||
)}
|
||||
</div>
|
||||
) : allDocuments.length === 0 ? (
|
||||
<div className="flex-1 min-h-0 flex items-center justify-center p-6">
|
||||
|
|
|
|||
|
|
@ -79,20 +79,20 @@ function FolderRow({
|
|||
countClassName="group-hover/folder:-translate-x-6 group-focus-within/folder:-translate-x-6"
|
||||
isDropTarget={isDropTarget}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete();
|
||||
}}
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 h-5 w-5 inline-flex items-center justify-center rounded text-soft opacity-0 group-hover/folder:opacity-100 group-focus-within/folder:opacity-100 hover:text-accent hover:bg-accent-wash transition"
|
||||
size="xs"
|
||||
className="absolute right-1 top-1/2 -translate-y-1/2 opacity-0 group-hover/folder:opacity-100 group-focus-within/folder:opacity-100"
|
||||
aria-label={`Delete ${folder.name}`}
|
||||
title={`Delete ${folder.name}`}
|
||||
>
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</IconButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import {
|
|||
HamburgerIcon,
|
||||
} from './finderIcons';
|
||||
import { ChevronUpDownIcon } from '@/components/icons/Icons';
|
||||
import { SearchField, Toolbar, ToolbarButton, ToolbarGroup, ToolbarSegment, listboxOptionClass, listboxOptionsClass, toolbarButtonStyles } from '@/components/ui';
|
||||
import { SearchField, Toolbar, ToolbarButton, ToolbarGroup, ToolbarSegment, listboxCompactOptionClass, listboxCompactOptionsClass, toolbarButtonStyles } from '@/components/ui';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
interface FinderToolbarProps {
|
||||
|
|
@ -146,17 +146,17 @@ export function FinderToolbar({
|
|||
</ToolbarButton>
|
||||
<Listbox value={sortBy} onChange={onSortByChange}>
|
||||
<ListboxButton
|
||||
className={toolbarButtonStyles({ className: 'gap-1 min-w-[90px] justify-between' })}
|
||||
className={toolbarButtonStyles({ className: 'gap-1 min-w-[86px] justify-between' })}
|
||||
>
|
||||
<span>{currentSort.label}</span>
|
||||
<ChevronUpDownIcon className="h-3 w-3 opacity-60" />
|
||||
</ListboxButton>
|
||||
<ListboxOptions anchor="bottom end" className={listboxOptionsClass}>
|
||||
<ListboxOptions anchor="bottom end" className={listboxCompactOptionsClass}>
|
||||
{SORT_OPTIONS.map((opt) => (
|
||||
<ListboxOption
|
||||
key={opt.value}
|
||||
value={opt.value}
|
||||
className={({ active, selected }) => listboxOptionClass(active || selected)}
|
||||
className={({ active, selected }) => listboxCompactOptionClass(active, selected)}
|
||||
>
|
||||
{opt.label}
|
||||
</ListboxOption>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import toast from 'react-hot-toast';
|
|||
import { useTTS } from '@/contexts/TTSContext';
|
||||
import { useConfig } from '@/contexts/ConfigContext';
|
||||
import { RefreshIcon, InfoIcon } from '@/components/icons/Icons';
|
||||
import { buttonClass } from '@/components/ui';
|
||||
import { Button, IconButton, popoverPanelClass } from '@/components/ui';
|
||||
import { ReaderSidebarShell } from '@/components/reader/ReaderSidebarShell';
|
||||
import { compareSegmentLocators, locatorGroupKey, locatorIdentityKey } from '@/lib/shared/tts-locator';
|
||||
import { buildSegmentKey, buildSegmentKeyPrefix } from '@/lib/shared/tts-segment-plan';
|
||||
|
|
@ -632,33 +632,27 @@ export function SegmentsSidebar({ isOpen, setIsOpen, documentId, epubBookRef }:
|
|||
|
||||
const headerActions = (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
<Button
|
||||
onClick={() => void handleClearCache()}
|
||||
aria-label="Clear segments cache"
|
||||
title="Clear cache for listed segments"
|
||||
disabled={isClearingSegments}
|
||||
className={buttonClass({
|
||||
variant: 'secondary',
|
||||
size: 'xs',
|
||||
className: 'h-8 px-2 text-soft',
|
||||
})}
|
||||
variant="secondary"
|
||||
size="xs"
|
||||
className="h-8 px-2 text-soft"
|
||||
>
|
||||
{isClearingSegments ? 'Clearing…' : 'Clear'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
</Button>
|
||||
<IconButton
|
||||
onClick={handleRefresh}
|
||||
aria-label="Refresh segments"
|
||||
title="Refresh"
|
||||
className={buttonClass({
|
||||
variant: 'secondary',
|
||||
size: 'icon',
|
||||
className: 'h-8 w-8 text-soft',
|
||||
})}
|
||||
tone="surface"
|
||||
size="md"
|
||||
className="h-8 w-8 text-soft"
|
||||
>
|
||||
<RefreshIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</IconButton>
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
@ -893,9 +887,10 @@ function SegmentMetadataPopover({ row }: { row: TTSSegmentRow }) {
|
|||
return (
|
||||
<Popover className="relative shrink-0">
|
||||
<PopoverButton
|
||||
as={IconButton}
|
||||
size="sm"
|
||||
aria-label="Segment metadata"
|
||||
title="Metadata"
|
||||
className="inline-flex items-center justify-center w-7 h-7 rounded-md border border-transparent text-soft hover:bg-accent-wash hover:border-line hover:text-accent transition-colors"
|
||||
>
|
||||
<InfoIcon className="w-3.5 h-3.5" />
|
||||
</PopoverButton>
|
||||
|
|
@ -910,7 +905,7 @@ function SegmentMetadataPopover({ row }: { row: TTSSegmentRow }) {
|
|||
>
|
||||
<PopoverPanel
|
||||
anchor="bottom end"
|
||||
className="z-[60] w-[300px] mt-1 rounded-lg border border-line bg-surface shadow-elev-3 p-3"
|
||||
className={`${popoverPanelClass} z-[60] w-[300px] mt-1`}
|
||||
>
|
||||
<dl className="space-y-2">
|
||||
<Row label="locator">
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ export const listboxPanelClass =
|
|||
export const listboxOptionsClass =
|
||||
cn(listboxPanelClass, 'w-[var(--button-width)] [--anchor-gap:0.25rem]');
|
||||
|
||||
export const listboxCompactOptionsClass =
|
||||
'z-50 min-w-[8rem] rounded-md bg-surface p-1 shadow-elev-2 ring-1 ring-line focus:outline-none [--anchor-gap:0.25rem]';
|
||||
|
||||
export const listboxOptionClass = (active: boolean, selected = false, inset: 'check' | 'none' = 'check') =>
|
||||
cn(
|
||||
'relative cursor-pointer select-none rounded-sm py-1.5 text-sm',
|
||||
|
|
@ -20,6 +23,16 @@ export const listboxOptionClass = (active: boolean, selected = false, inset: 'ch
|
|||
selected ? 'bg-accent text-background font-medium' : active ? 'bg-accent-wash text-foreground' : 'text-foreground',
|
||||
);
|
||||
|
||||
export const listboxCompactOptionClass = (active: boolean, selected = false) =>
|
||||
cn(
|
||||
'relative cursor-pointer select-none rounded-sm px-2 py-1 text-xs',
|
||||
active
|
||||
? 'bg-accent-wash text-accent'
|
||||
: selected
|
||||
? 'bg-surface-sunken text-accent font-medium'
|
||||
: 'text-foreground',
|
||||
);
|
||||
|
||||
export function Select({
|
||||
value,
|
||||
onChange,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
|
||||
import type { AnchorHTMLAttributes, ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
|
||||
import { cn } from './cn';
|
||||
import { focusRing, motionColors } from './tokens';
|
||||
|
||||
|
|
@ -50,6 +50,82 @@ export function SidebarNavGroup({
|
|||
);
|
||||
}
|
||||
|
||||
export function sidebarNavItemClass({
|
||||
active = false,
|
||||
compact = false,
|
||||
isDropTarget = false,
|
||||
className,
|
||||
}: {
|
||||
active?: boolean;
|
||||
compact?: boolean;
|
||||
isDropTarget?: boolean;
|
||||
className?: string;
|
||||
} = {}) {
|
||||
return cn(
|
||||
'group w-full min-w-0 border text-left font-medium',
|
||||
'inline-flex items-center transition duration-base ease-standard',
|
||||
focusRing,
|
||||
motionColors,
|
||||
compact ? 'gap-1.5 rounded-md px-2 py-1 text-xs' : 'gap-2 rounded-md px-2.5 py-1.5 text-sm',
|
||||
active
|
||||
? 'border-accent-line bg-surface-sunken text-accent'
|
||||
: 'border-transparent bg-transparent text-foreground hover:border-accent-line hover:bg-accent-wash hover:text-accent',
|
||||
isDropTarget ? 'ring-1 ring-accent-line' : '',
|
||||
className,
|
||||
);
|
||||
}
|
||||
|
||||
export function sidebarNavIconClass({
|
||||
active = false,
|
||||
compact = false,
|
||||
}: {
|
||||
active?: boolean;
|
||||
compact?: boolean;
|
||||
} = {}) {
|
||||
return cn(
|
||||
'shrink-0 inline-flex items-center justify-center transition-colors duration-base',
|
||||
compact ? 'h-4 w-4' : 'h-5 w-5',
|
||||
active ? 'text-accent' : 'text-soft group-hover:text-accent',
|
||||
);
|
||||
}
|
||||
|
||||
function SidebarNavItemContent({
|
||||
active = false,
|
||||
icon,
|
||||
label,
|
||||
count,
|
||||
countClassName,
|
||||
trailing,
|
||||
compact = false,
|
||||
children,
|
||||
}: {
|
||||
active?: boolean;
|
||||
icon?: ReactNode;
|
||||
label?: ReactNode;
|
||||
count?: number;
|
||||
countClassName?: string;
|
||||
trailing?: ReactNode;
|
||||
compact?: boolean;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
{icon ? (
|
||||
<span className={sidebarNavIconClass({ active, compact })}>
|
||||
{icon}
|
||||
</span>
|
||||
) : null}
|
||||
{label ?? children ? <span className="min-w-0 flex-1 truncate">{label ?? children}</span> : null}
|
||||
{typeof count === 'number' && count > 0 ? (
|
||||
<span className={cn('text-[10px] text-soft tabular-nums transition-transform duration-base ease-standard', countClassName)}>
|
||||
{count}
|
||||
</span>
|
||||
) : null}
|
||||
{trailing}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function SidebarNavItem({
|
||||
active = false,
|
||||
icon,
|
||||
|
|
@ -76,38 +152,62 @@ export function SidebarNavItem({
|
|||
return (
|
||||
<button
|
||||
type={type}
|
||||
className={cn(
|
||||
'group w-full min-w-0 border text-left font-medium',
|
||||
'inline-flex items-center transition duration-base ease-standard',
|
||||
focusRing,
|
||||
motionColors,
|
||||
compact ? 'gap-1.5 rounded-md px-2 py-1 text-xs' : 'gap-2 rounded-md px-2.5 py-1.5 text-sm',
|
||||
active
|
||||
? 'border-accent-line bg-surface-sunken text-accent'
|
||||
: 'border-transparent bg-transparent text-foreground hover:border-accent-line hover:bg-accent-wash hover:text-accent',
|
||||
isDropTarget ? 'ring-1 ring-accent-line' : '',
|
||||
className,
|
||||
)}
|
||||
className={sidebarNavItemClass({ active, compact, isDropTarget, className })}
|
||||
{...props}
|
||||
>
|
||||
{icon ? (
|
||||
<span
|
||||
className={cn(
|
||||
'shrink-0 inline-flex items-center justify-center transition-colors duration-base',
|
||||
compact ? 'h-4 w-4' : 'h-5 w-5',
|
||||
active ? 'text-accent' : 'text-soft group-hover:text-accent',
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
) : null}
|
||||
{label ?? children ? <span className="min-w-0 flex-1 truncate">{label ?? children}</span> : null}
|
||||
{typeof count === 'number' && count > 0 ? (
|
||||
<span className={cn('text-[10px] text-soft tabular-nums transition-transform duration-base ease-standard', countClassName)}>
|
||||
{count}
|
||||
</span>
|
||||
) : null}
|
||||
{trailing}
|
||||
<SidebarNavItemContent
|
||||
active={active}
|
||||
compact={compact}
|
||||
icon={icon}
|
||||
label={label}
|
||||
count={count}
|
||||
countClassName={countClassName}
|
||||
trailing={trailing}
|
||||
>
|
||||
{children}
|
||||
</SidebarNavItemContent>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function SidebarNavLink({
|
||||
active = false,
|
||||
icon,
|
||||
label,
|
||||
count,
|
||||
countClassName,
|
||||
trailing,
|
||||
isDropTarget = false,
|
||||
className,
|
||||
compact = false,
|
||||
children,
|
||||
...props
|
||||
}: AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||
active?: boolean;
|
||||
icon?: ReactNode;
|
||||
label?: ReactNode;
|
||||
count?: number;
|
||||
countClassName?: string;
|
||||
trailing?: ReactNode;
|
||||
isDropTarget?: boolean;
|
||||
compact?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
className={sidebarNavItemClass({ active, compact, isDropTarget, className })}
|
||||
{...props}
|
||||
>
|
||||
<SidebarNavItemContent
|
||||
active={active}
|
||||
compact={compact}
|
||||
icon={icon}
|
||||
label={label}
|
||||
count={count}
|
||||
countClassName={countClassName}
|
||||
trailing={trailing}
|
||||
>
|
||||
{children}
|
||||
</SidebarNavItemContent>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue