Skip to content

Commit 9f59edd

Browse files
committed
feat(ui-vite): replace native buttons with @leanspec/ui-components
- Replace all native button elements with Button component from @leanspec/ui-components - Update SpecsFilters, ThemeToggle, LanguageSwitcher for consistent styling - Update QuickSearch, TableOfContents, SubSpecTabs with Button variants - Update TagsEditor, Layout shortcut modal, DirectoryPicker with proper sizes - Update SettingsPage project actions and BoardView archived toggles - Import cn utility from @leanspec/ui-components where needed - Fix tsconfig deprecation warning (ignoreDeprecations: 6.0 -> 5.0) - All changes pass typecheck and build successfully Implements spec 197
1 parent 66c371c commit 9f59edd

File tree

14 files changed

+163
-179
lines changed

14 files changed

+163
-179
lines changed

packages/ui-components/tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"./src/*"
2929
]
3030
},
31-
"ignoreDeprecations": "6.0",
31+
"ignoreDeprecations": "5.0",
3232
"outDir": "./dist",
3333
"rootDir": "./src"
3434
},

packages/ui-vite/src/components/LanguageSwitcher.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { useEffect, useState } from 'react';
22
import { useTranslation } from 'react-i18next';
33
import { Languages } from 'lucide-react';
4-
import { Button } from '@leanspec/ui-components';
5-
import { cn } from '../lib/utils';
4+
import { Button, cn } from '@leanspec/ui-components';
65

76
const languages = [
87
{ code: 'en', labelKey: 'language.english', shortLabel: 'EN' },
@@ -47,16 +46,18 @@ export function LanguageSwitcher() {
4746
<div className="fixed inset-0 z-50" onClick={() => setOpen(false)} />
4847
<div className="absolute right-0 mt-2 w-32 z-50 rounded-md border bg-popover p-1 shadow-md">
4948
{languages.map((language) => (
50-
<button
49+
<Button
5150
key={language.code}
5251
onClick={() => changeLanguage(language.code)}
52+
variant="ghost"
53+
size="sm"
5354
className={cn(
54-
'w-full text-left px-2 py-1.5 text-sm rounded-sm hover:bg-accent transition-colors',
55+
'w-full justify-start h-8',
5556
i18n.language === language.code && 'bg-accent'
5657
)}
5758
>
5859
{t(language.labelKey)}
59-
</button>
60+
</Button>
6061
))}
6162
</div>
6263
</>

packages/ui-vite/src/components/Layout.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useGlobalShortcuts } from '../hooks/useKeyboardShortcuts';
66
import { ErrorBoundary } from './shared/ErrorBoundary';
77
import { PageTransition } from './shared/PageTransition';
88
import { BackToTop } from './shared/BackToTop';
9+
import { Button } from '@leanspec/ui-components';
910

1011
function KeyboardShortcutsHelp({ onClose }: { onClose: () => void }) {
1112
const shortcuts = [
@@ -30,12 +31,14 @@ function KeyboardShortcutsHelp({ onClose }: { onClose: () => void }) {
3031
</div>
3132
))}
3233
</div>
33-
<button
34+
<Button
3435
onClick={onClose}
35-
className="mt-4 w-full px-4 py-2 text-sm bg-secondary rounded-lg hover:bg-secondary/80 transition-colors"
36+
variant="secondary"
37+
size="sm"
38+
className="mt-4 w-full"
3639
>
3740
Close
38-
</button>
41+
</Button>
3942
</div>
4043
</div>
4144
);

packages/ui-vite/src/components/QuickSearch.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
CommandItem,
1111
CommandList,
1212
CommandSeparator,
13+
Button,
1314
} from '@leanspec/ui-components';
1415
import { api, type Spec } from '../lib/api';
1516
import { StatusBadge } from './StatusBadge';
@@ -129,17 +130,19 @@ export function QuickSearch() {
129130

130131
return (
131132
<>
132-
<button
133+
<Button
133134
onClick={() => setOpen(true)}
134-
className="inline-flex items-center gap-2 px-2 sm:px-3 py-2 text-sm text-muted-foreground hover:text-foreground transition-colors border rounded-md hover:border-foreground/20"
135+
variant="outline"
136+
size="sm"
137+
className="gap-2 text-muted-foreground hover:text-foreground"
135138
aria-label="Open quick search"
136139
>
137140
<Search className="h-4 w-4" />
138141
<span className="hidden sm:inline">Quick search...</span>
139142
<kbd className="hidden md:inline-flex pointer-events-none h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100">
140143
<span className="text-xs"></span>K
141144
</kbd>
142-
</button>
145+
</Button>
143146

144147
<CommandDialog
145148
open={open}

packages/ui-vite/src/components/ThemeToggle.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Sun, Moon, Monitor } from 'lucide-react';
22
import { useTheme } from '../contexts';
3-
import { cn } from '../lib/utils';
3+
import { cn, Button } from '@leanspec/ui-components';
44

55
export function ThemeToggle() {
66
const { theme, setTheme } = useTheme();
@@ -17,19 +17,21 @@ export function ThemeToggle() {
1717
const Icon = option.icon;
1818
const isActive = theme === option.value;
1919
return (
20-
<button
20+
<Button
2121
key={option.value}
2222
onClick={() => setTheme(option.value)}
2323
title={option.label}
24+
variant="ghost"
25+
size="icon"
2426
className={cn(
25-
'p-1.5 rounded-md transition-colors',
27+
'h-7 w-7',
2628
isActive
2729
? 'bg-background text-foreground shadow-sm'
2830
: 'text-muted-foreground hover:text-foreground'
2931
)}
3032
>
3133
<Icon className="w-4 h-4" />
32-
</button>
34+
</Button>
3335
);
3436
})}
3537
</div>

packages/ui-vite/src/components/metadata-editors/TagsEditor.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,20 @@ export function TagsEditor({ specName, value, onChange, disabled = false, classN
5858
<Badge
5959
key={tag}
6060
variant="outline"
61-
className="text-xs pr-1 gap-1"
61+
className="text-xs pr-0.5 gap-1 h-6"
6262
>
6363
{tag}
6464
{!disabled && (
65-
<button
65+
<Button
6666
onClick={() => handleRemove(tag)}
6767
disabled={updating}
68-
className="ml-1 rounded-full hover:bg-muted p-0.5 transition-colors"
68+
variant="ghost"
69+
size="icon"
70+
className="h-4 w-4 p-0 rounded-full hover:bg-muted ml-0.5"
6971
aria-label={`Remove ${tag}`}
7072
>
7173
<X className="h-3 w-3" />
72-
</button>
74+
</Button>
7375
)}
7476
</Badge>
7577
))}

packages/ui-vite/src/components/projects/DirectoryPicker.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,26 +98,30 @@ export function DirectoryPicker({
9898
className="flex-1 overflow-x-auto whitespace-nowrap flex items-center scrollbar-hide px-1 min-w-0"
9999
title={currentPath}
100100
>
101-
<button
101+
<Button
102102
onClick={() => handleNavigate('/')}
103-
className="hover:bg-accent p-1 rounded-sm transition-colors shrink-0"
103+
variant="ghost"
104+
size="icon"
105+
className="h-7 w-7 shrink-0"
104106
title="Go to root"
105107
>
106108
<Home className="h-4 w-4 text-muted-foreground" />
107-
</button>
109+
</Button>
108110

109111
{segments.map((segment, index) => (
110112
<div key={segment.path} className="flex items-center shrink-0">
111113
<ChevronRight className="h-3 w-3 text-muted-foreground mx-0.5 shrink-0" />
112-
<button
114+
<Button
113115
onClick={() => handleNavigate(segment.path)}
116+
variant="ghost"
117+
size="sm"
114118
className={cn(
115-
'px-1.5 py-0.5 rounded-sm transition-colors text-sm hover:bg-accent hover:text-accent-foreground',
119+
'h-7 text-sm',
116120
index === segments.length - 1 ? 'font-medium text-foreground' : 'text-muted-foreground'
117121
)}
118122
>
119123
{segment.name}
120-
</button>
124+
</Button>
121125
</div>
122126
))}
123127
</div>
@@ -144,14 +148,16 @@ export function DirectoryPicker({
144148
<div className="p-4 text-center text-sm text-muted-foreground">Empty directory</div>
145149
) : (
146150
displayItems.map((item: DirectoryListResponse['items'][number]) => (
147-
<button
151+
<Button
148152
key={item.path}
149153
onClick={() => handleNavigate(item.path)}
150-
className="flex items-center gap-3 w-full px-3 py-2 text-sm rounded-sm hover:bg-accent hover:text-accent-foreground text-left group transition-colors"
154+
variant="ghost"
155+
size="sm"
156+
className="w-full justify-start gap-3 h-9 group"
151157
>
152158
<Folder className="h-4 w-4 text-blue-500 fill-blue-500/20 group-hover:fill-blue-500/30 transition-colors shrink-0" />
153159
<span className="truncate">{item.name}</span>
154-
</button>
160+
</Button>
155161
))
156162
)}
157163
</div>

packages/ui-vite/src/components/spec-detail/SubSpecTabs.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import type { Components } from 'react-markdown';
55
import remarkGfm from 'remark-gfm';
66
import GithubSlugger from 'github-slugger';
77
import { BookOpen, CheckSquare, Code, FileText, GitBranch, Map, Palette, TestTube, Wrench } from 'lucide-react';
8-
import { Card, Button, Separator } from '@leanspec/ui-components';
9-
import { cn } from '../../lib/utils';
8+
import { Card, Button, Separator, cn } from '@leanspec/ui-components';
109
import { MermaidDiagram } from '../MermaidDiagram';
1110

1211
export interface SubSpec {
@@ -94,19 +93,20 @@ export function SubSpecTabs({ mainContent, subSpecs = [] }: SubSpecTabsProps) {
9493
const overviewCardVisible = hasSubSpecs && subSpecs.length > 2 && activeTab === 'readme';
9594

9695
const renderTabButton = (value: string, label: string, Icon?: React.ComponentType<{ className?: string }>, color?: string) => (
97-
<button
96+
<Button
9897
key={value}
9998
onClick={() => setActiveTab(value)}
99+
variant="ghost"
100100
className={cn(
101-
'flex items-center gap-2 px-4 py-2 text-sm border-b-2 -mb-px transition-colors rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2',
101+
'border-b-2 -mb-px rounded-none gap-2',
102102
activeTab === value
103103
? 'border-primary text-foreground'
104104
: 'border-transparent text-muted-foreground hover:text-foreground'
105105
)}
106106
>
107107
{Icon && <Icon className={cn('h-4 w-4', color)} />}
108108
<span className="truncate">{label}</span>
109-
</button>
109+
</Button>
110110
);
111111

112112
return (

packages/ui-vite/src/components/spec-detail/TableOfContents.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import {
66
DialogContent,
77
DialogHeader,
88
DialogTitle,
9+
cn,
910
} from '@leanspec/ui-components';
1011
import { extractHeadings, type HeadingItem } from '../../lib/markdown-utils';
11-
import { cn } from '../../lib/utils';
1212

1313
function scrollToHeading(id: string) {
1414
const element = document.getElementById(id);
@@ -30,20 +30,23 @@ function TOCList({ headings, onHeadingClick }: TOCListProps) {
3030
return (
3131
<nav className="space-y-1">
3232
{headings.map((heading) => (
33-
<button
33+
<Button
3434
key={`${heading.id}-${heading.level}`}
3535
onClick={() => onHeadingClick(heading.id)}
36+
variant="ghost"
37+
size="sm"
3638
className={cn(
37-
'w-full text-left px-2 py-1.5 text-sm rounded-md hover:bg-muted transition-colors flex items-start gap-2 text-muted-foreground hover:text-foreground',
39+
'w-full justify-start h-auto py-1.5',
3840
heading.level === 2 && 'font-medium text-foreground',
3941
heading.level === 3 && 'pl-6',
4042
heading.level === 4 && 'pl-10',
4143
heading.level === 5 && 'pl-14',
42-
heading.level >= 6 && 'pl-16'
44+
heading.level >= 6 && 'pl-16',
45+
'text-muted-foreground hover:text-foreground'
4346
)}
4447
>
45-
<span className="flex-1 truncate">{heading.text}</span>
46-
</button>
48+
<span className="flex-1 truncate text-left">{heading.text}</span>
49+
</Button>
4750
))}
4851
</nav>
4952
);

packages/ui-vite/src/components/specs/BoardView.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Link } from 'react-router-dom';
33
import { Clock, PlayCircle, CheckCircle2, Archive, MoreHorizontal } from 'lucide-react';
44
import type { Spec } from '../../lib/api';
55
import { PriorityBadge } from '../PriorityBadge';
6-
import { cn } from '../../lib/utils';
6+
import { cn, Button } from '@leanspec/ui-components';
77

88
type SpecStatus = 'planned' | 'in-progress' | 'complete' | 'archived';
99

@@ -142,12 +142,14 @@ export function BoardView({ specs, onStatusChange, showArchived, onToggleArchive
142142
</span>
143143
</div>
144144
{status === 'archived' && (
145-
<button
145+
<Button
146146
onClick={onToggleArchived}
147-
className="text-muted-foreground hover:text-foreground"
147+
variant="ghost"
148+
size="icon"
149+
className="h-8 w-8 text-muted-foreground"
148150
>
149151
<MoreHorizontal className="w-4 h-4" />
150-
</button>
152+
</Button>
151153
)}
152154
</div>
153155

@@ -202,16 +204,17 @@ export function BoardView({ specs, onStatusChange, showArchived, onToggleArchive
202204
})}
203205

204206
{!showArchived && (
205-
<button
207+
<Button
206208
onClick={onToggleArchived}
207-
className="flex-shrink-0 w-10 flex flex-col items-center py-4 gap-2 rounded-lg bg-secondary/30 hover:bg-secondary/50 transition-colors border border-transparent"
209+
variant="ghost"
210+
className="flex-shrink-0 w-10 flex-col items-center py-4 gap-2 rounded-lg h-auto"
208211
title="Show Archived"
209212
>
210213
<Archive className="w-4 h-4 text-muted-foreground" />
211214
<div className="writing-vertical-rl text-xs font-medium text-muted-foreground tracking-wider uppercase">
212215
Archived
213216
</div>
214-
</button>
217+
</Button>
215218
)}
216219
</div>
217220
);

0 commit comments

Comments
 (0)