Files
2026-06-02 18:10:19 +03:00

578 lines
18 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import {
ArrowRight,
Trash,
Plus,
DotsThree,
MagnifyingGlass,
Folder,
FolderOpen,
PencilSimple,
Copy,
Tag,
Info,
FilePdf,
FileText,
CheckCircle,
Warning,
WarningOctagon,
} from '@phosphor-icons/react';
import { useTheme } from './components/theme';
import {
AlertDialog,
Badge,
Button,
Callout,
Card,
Checkbox,
Chip,
Control,
Dialog,
DialogClose,
IconButton,
Knob,
List,
MenuRow,
MenuSeparator,
MenuSurface,
Progress,
RadioGroup,
RadioItem,
Row,
ScrollArea,
SearchField,
SegmentedControl,
Select,
Slider,
Spinner,
Stepper,
Switch,
TBody,
THead,
Table,
Tabs,
TabsContent,
TabsList,
Td,
TextArea,
TextField,
Th,
Tooltip,
Tr,
Window,
} from './components/ui';
const Section = ({ label, children }: { label: string; children: React.ReactNode }) => (
<section>
<div className="sec-label">{label}</div>
{children}
</section>
);
const App = () => {
const { theme, setTheme } = useTheme();
const [seg, setSeg] = useState('list');
const [tab, setTab] = useState('general');
const [count, setCount] = useState(4);
const [chips, setChips] = useState(['design', '2026', 'invoices']);
return (
<div className="modern-sk-felt">
<div className="wrap">
<header>
<div className="topbar">
<div>
<div className="word">
MODERN<b>SK</b> · KITCHEN SINK
</div>
<p className="sub">
Every component, live and interactive, built on Radix Primitives
styled from the global tokens in{' '}
<span className="modern-sk-mono">tokens.css</span> +{' '}
<span className="modern-sk-mono">components.css</span>. Click, toggle,
focus it all responds.
</p>
</div>
<div style={{ flexShrink: 0 }}>
<SegmentedControl
value={theme}
onValueChange={(v) => setTheme(v as 'dark' | 'light')}
items={[
{ value: 'dark', label: 'Dark' },
{ value: 'light', label: 'Light' },
]}
/>
</div>
</div>
</header>
{/* BUTTONS */}
<Section label="Buttons">
<div className="stack">
<div>
<div className="cap">Variants</div>
<div className="cluster">
<Button variant="primary">
<ArrowRight size={16} />
Primary
</Button>
<Button>Push button</Button>
<Button variant="ember">
<Trash size={16} />
Delete
</Button>
<Button variant="ghost">Cancel</Button>
<Button disabled>Disabled</Button>
</div>
</div>
<div>
<div className="cap">Sizes &amp; icon</div>
<div className="cluster">
<Button variant="primary" size="sm">
Small
</Button>
<Button size="sm">Small</Button>
<Button iconOnly aria-label="Add">
<Plus size={16} />
</Button>
<Button iconOnly aria-label="More">
<DotsThree size={16} weight="bold" />
</Button>
</div>
</div>
</div>
</Section>
{/* FIELDS */}
<Section label="Text fields &amp; selects">
<div className="two">
<div className="stack">
<div>
<div className="lab">Name</div>
<TextField defaultValue="Quarterly Report" />
</div>
<div>
<div className="lab">Location</div>
<TextField placeholder="Choose a folder…" />
</div>
<div>
<div className="lab">Search</div>
<SearchField
icon={<MagnifyingGlass size={16} />}
placeholder="Search files…"
/>
</div>
</div>
<div className="stack">
<div>
<div className="lab">View</div>
<Select
defaultValue="name"
aria-label="Sort order"
items={[
{ value: 'name', label: 'Sort by name' },
{ value: 'date', label: 'Sort by date' },
{ value: 'size', label: 'Sort by size' },
]}
/>
</div>
<div>
<div className="lab">Notes</div>
<TextArea
placeholder="Add a note…"
defaultValue="Everything right at your hands."
/>
</div>
</div>
</div>
</Section>
{/* TOGGLES */}
<Section label="Switches · checkboxes · radios">
<div className="three">
<div>
<div className="cap">Switch</div>
<div className="stack">
<Control control={<Switch defaultChecked />}>
Sync across devices
</Control>
<Control control={<Switch />}>Show hidden files</Control>
</div>
</div>
<div>
<div className="cap">Checkbox</div>
<div className="stack">
<Control control={<Checkbox defaultChecked />}>
Include subfolders
</Control>
<Control control={<Checkbox />}>Follow symlinks</Control>
</div>
</div>
<div>
<div className="cap">Radio</div>
<RadioGroup defaultValue="list" className="stack">
<Control control={<RadioItem value="list" />}>List view</Control>
<Control control={<RadioItem value="grid" />}>Grid view</Control>
</RadioGroup>
</div>
</div>
</Section>
{/* CONTROLS */}
<Section label="Segmented · slider · stepper · tabs · progress">
<div className="two">
<div className="stack">
<div>
<div className="cap">Segmented</div>
<SegmentedControl
value={seg}
onValueChange={setSeg}
items={[
{ value: 'icons', label: 'Icons' },
{ value: 'list', label: 'List' },
{ value: 'columns', label: 'Columns' },
{ value: 'gallery', label: 'Gallery' },
]}
/>
</div>
<div>
<div className="cap">Slider &amp; stepper</div>
<div className="cluster">
<Slider defaultValue={[62]} max={100} step={1} />
<Slider defaultValue={[40]} max={100} step={20} marks />
<Stepper
onDecrement={() => setCount((n) => Math.max(0, n - 1))}
onIncrement={() => setCount((n) => n + 1)}
/>
<span className="modern-sk-mono" style={{ color: 'var(--fg-2)' }}>
{count}
</span>
</div>
</div>
<div>
<div className="cap">Knobs circular sliders</div>
<div className="cluster" style={{ gap: 40, alignItems: 'flex-start' }}>
<Knob defaultValue={62} aria-label="Volume" />
<Knob defaultValue={3} min={1} max={5} step={1} aria-label="Quality" />
<Knob defaultValue={40} accent="ember" aria-label="Warmth" />
</div>
</div>
</div>
<div className="stack">
<div>
<div className="cap">Tabs</div>
<Tabs value={tab} onValueChange={setTab}>
<TabsList
items={[
{ value: 'general', label: 'General' },
{ value: 'sharing', label: 'Sharing' },
{ value: 'tags', label: 'Tags' },
]}
/>
<TabsContent value={tab} />
</Tabs>
</div>
<div>
<div className="cap">Progress</div>
<div style={{ marginTop: 6 }}>
<Progress value={64} />
</div>
</div>
</div>
</div>
</Section>
{/* BADGES */}
<Section label="Badges · chips · tags">
<div className="stack">
<div className="cluster">
<Badge variant="lime">Synced</Badge>
<Badge variant="ember">3 conflicts</Badge>
<Badge variant="neutral">Draft</Badge>
<Badge variant="outline">v2.4</Badge>
<Badge variant="neutral" dot style={{ color: 'var(--lime)' }}>
Online
</Badge>
</div>
<div className="cluster">
{chips.map((c) => (
<Chip
key={c}
onRemove={() => setChips((cs) => cs.filter((x) => x !== c))}
>
{c}
</Chip>
))}
</div>
</div>
</Section>
{/* SURFACES */}
<Section label="Cards · list rows · menu">
<div className="three">
<Card>
<div style={{ fontSize: 24, color: 'var(--lime)' }}>
<Folder weight="fill" />
</div>
<div style={{ fontWeight: 600, marginTop: 10 }}>Projects</div>
<div
style={{ color: 'var(--fg-3)', fontSize: 13, marginTop: 2 }}
>
24 items · 1.2 GB
</div>
<div style={{ marginTop: 14 }}>
<Progress value={48} />
</div>
</Card>
<List>
<Row selected>
<Folder weight="fill" size={22} color="#bef264" />
<span className="nm">Projects</span>
<span className="meta">24</span>
</Row>
<Row>
<Folder weight="fill" size={22} color="#e9572b" />
<span className="nm">Invoices</span>
<span className="meta">8</span>
</Row>
<Row>
<FilePdf weight="fill" size={22} color="var(--fg-2)" />
<span className="nm">Contract.pdf</span>
<span className="meta">2.4 MB</span>
</Row>
<Row>
<FileText weight="fill" size={22} color="var(--fg-2)" />
<span className="nm">notes.md</span>
<span className="meta">12 KB</span>
</Row>
</List>
<MenuSurface>
<MenuRow icon={<FolderOpen size={16} />} shortcut="⌘O">
Open
</MenuRow>
<MenuRow icon={<PencilSimple size={16} />} shortcut="⏎">
Rename
</MenuRow>
<MenuRow icon={<Copy size={16} />} shortcut="⌘D">
Duplicate
</MenuRow>
<MenuSeparator />
<MenuRow icon={<Tag size={16} />}>Add Tag</MenuRow>
<MenuRow icon={<Info size={16} />} shortcut="⌘I">
Get Info
</MenuRow>
</MenuSurface>
</div>
</Section>
{/* WINDOW */}
<Section label="Window — components composed">
<Window
title="New Bookmark"
style={{ maxWidth: 600 }}
badge={
<Badge variant="neutral" dot style={{ color: 'var(--lime)' }}>
Saved
</Badge>
}
>
<div className="panel">
<div className="stack">
<div>
<div className="lab">Name</div>
<TextField defaultValue="Projects" />
</div>
<div>
<div className="lab">Location</div>
<SearchField
icon={<Folder size={16} />}
defaultValue="~/Documents/2026"
/>
</div>
<div style={{ marginTop: 2 }}>
<Control control={<Switch defaultChecked />}>
Sync across devices
</Control>
</div>
<div
className="cluster"
style={{
justifyContent: 'flex-end',
marginTop: 6,
gap: 12,
}}
>
<Button variant="ghost">Cancel</Button>
<Button variant="primary">Add Bookmark</Button>
</div>
</div>
</div>
</Window>
<div style={{ marginTop: 14 }}>
<Tooltip
content={
<>
<Info
size={14}
style={{ marginRight: 5, color: 'var(--lime)' }}
/>
Tooltip appears over floating layers
</>
}
>
<Button variant="ghost" size="sm">
Hover for tooltip
</Button>
</Tooltip>
</div>
</Section>
{/* ICON BUTTONS · SPINNER */}
<Section label="Icon buttons · spinner">
<div className="cluster">
<IconButton variant="primary" aria-label="Add">
<Plus size={16} weight="bold" />
</IconButton>
<IconButton aria-label="Edit">
<PencilSimple size={15} />
</IconButton>
<IconButton variant="ember" aria-label="Delete">
<Trash size={15} />
</IconButton>
<IconButton variant="ghost" aria-label="More">
<DotsThree size={18} weight="bold" />
</IconButton>
<IconButton size="lg" aria-label="Open">
<FolderOpen size={18} />
</IconButton>
<span style={{ width: 16 }} />
<Spinner size="sm" />
<Spinner />
<Spinner size="lg" />
</div>
</Section>
{/* CALLOUTS */}
<Section label="Callouts">
<div className="stack">
<Callout variant="info" icon={<Info size={17} weight="fill" />}>
<strong>Heads up.</strong> Files sync automatically when youre
online no manual save needed.
</Callout>
<Callout variant="success" icon={<CheckCircle size={17} weight="fill" />}>
<strong>All set.</strong> Your 24 projects are backed up and
encrypted.
</Callout>
<Callout variant="warning" icon={<Warning size={17} weight="fill" />}>
<strong>Low space.</strong> 1.2 GB left on this device.
</Callout>
<Callout variant="danger" icon={<WarningOctagon size={17} weight="fill" />}>
<strong>3 conflicts.</strong> Some files changed in two places at
once.
</Callout>
</div>
</Section>
{/* TABLE */}
<Section label="Table">
<Table>
<THead>
<Tr>
<Th>Name</Th>
<Th>Owner</Th>
<Th>Status</Th>
<Th style={{ textAlign: 'right' }}>Size</Th>
</Tr>
</THead>
<TBody>
<Tr selected>
<Td>Quarterly Report.pdf</Td>
<Td className="muted">You</Td>
<Td>
<Badge variant="lime">Synced</Badge>
</Td>
<Td className="num">2.4 MB</Td>
</Tr>
<Tr>
<Td>Invoices</Td>
<Td className="muted">Mara K.</Td>
<Td>
<Badge variant="ember">3 conflicts</Badge>
</Td>
<Td className="num">812 KB</Td>
</Tr>
<Tr>
<Td>notes.md</Td>
<Td className="muted">You</Td>
<Td>
<Badge variant="neutral">Draft</Badge>
</Td>
<Td className="num">12 KB</Td>
</Tr>
</TBody>
</Table>
</Section>
{/* SCROLL AREA */}
<Section label="Scroll area">
<Card style={{ padding: 0, maxWidth: 320 }}>
<ScrollArea style={{ height: 160 }}>
<List style={{ border: 'none', boxShadow: 'none', borderRadius: 0 }}>
{Array.from({ length: 12 }).map((_, i) => (
<Row key={i}>
<FileText weight="fill" size={20} color="var(--fg-2)" />
<span className="nm">document-{i + 1}.txt</span>
<span className="meta">{(i + 1) * 7} KB</span>
</Row>
))}
</List>
</ScrollArea>
</Card>
</Section>
{/* DIALOGS */}
<Section label="Dialog · alert dialog">
<div className="cluster">
<Dialog
title="Rename file"
description="Choose a new name for this item."
trigger={<Button variant="primary">Open dialog</Button>}
footer={
<>
<DialogClose asChild>
<Button variant="ghost">Cancel</Button>
</DialogClose>
<DialogClose asChild>
<Button variant="primary">Save</Button>
</DialogClose>
</>
}
>
<div className="lab">Name</div>
<TextField defaultValue="Quarterly Report.pdf" autoFocus />
</Dialog>
<AlertDialog
title="Delete 3 files?"
description="This permanently removes the selected files. This action cannot be undone."
cancelLabel="Keep files"
actionLabel="Delete"
destructive
trigger={
<Button variant="ember">
<Trash size={16} />
Delete
</Button>
}
/>
</div>
</Section>
</div>
</div>
);
};
export default App;