feat(ui-kits): add shadcn, MUI, Mantine, Bootstrap and Chakra kits

The four derived kits reuse the shadcn markup with their own tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Marcos Paulo
2026-10-28 20:47:02 -03:00
parent b64a779619
commit 8d3306ea1f
3 changed files with 447 additions and 78 deletions
+200
View File
@@ -0,0 +1,200 @@
//! Kits drawn with the shadcn kit's markup in another library's palette, radius and control
//! height (the markup reads them from variables and tokens), each with that library's code for
//! the components it has. A component a library lacks keeps no code link.
/// (component, import, code) for one library.
pub type Code = &'static [(&'static str, &'static str, &'static str)];
pub const MUI: Code = &[
("Button / Default", r#"import Button from "@mui/material/Button""#, r#"<Button variant="contained">{Label}</Button>"#),
("Button / Outline", r#"import Button from "@mui/material/Button""#, r#"<Button variant="outlined">{Label}</Button>"#),
("Button / Secondary", r#"import Button from "@mui/material/Button""#, r#"<Button variant="contained" color="inherit">{Label}</Button>"#),
("Button / Ghost", r#"import Button from "@mui/material/Button""#, r#"<Button variant="text">{Label}</Button>"#),
("Button / Destructive", r#"import Button from "@mui/material/Button""#, r#"<Button variant="contained" color="error">{Label}</Button>"#),
("Button / Link", r#"import Link from "@mui/material/Link""#, r##"<Link href="#">{Label}</Link>"##),
("Button / Small", r#"import Button from "@mui/material/Button""#, r#"<Button variant="contained" size="small">{Label}</Button>"#),
("Button / Icon", "import IconButton from \"@mui/material/IconButton\"\nimport AddIcon from \"@mui/icons-material/Add\"", "<IconButton><AddIcon /></IconButton>"),
("Button / With Icon", "import Button from \"@mui/material/Button\"\nimport AddIcon from \"@mui/icons-material/Add\"", r#"<Button variant="contained" startIcon={<AddIcon />}>{Label}</Button>"#),
("Input", r#"import TextField from "@mui/material/TextField""#, r#"<TextField size="small" placeholder="{Placeholder}" />"#),
("Search", r#"import TextField from "@mui/material/TextField""#, r#"<TextField size="small" type="search" placeholder="{Placeholder}" />"#),
("Textarea", r#"import TextField from "@mui/material/TextField""#, r#"<TextField multiline minRows={3} placeholder="{Placeholder}" />"#),
("Select", "import Select from \"@mui/material/Select\"\nimport MenuItem from \"@mui/material/MenuItem\"", "<Select size=\"small\" value=\"one\">\n <MenuItem value=\"one\">{Value}</MenuItem>\n</Select>"),
("Checkbox", "import Checkbox from \"@mui/material/Checkbox\"\nimport FormControlLabel from \"@mui/material/FormControlLabel\"", r#"<FormControlLabel control={<Checkbox defaultChecked />} label="{Label}" />"#),
("Checkbox / Off", "import Checkbox from \"@mui/material/Checkbox\"\nimport FormControlLabel from \"@mui/material/FormControlLabel\"", r#"<FormControlLabel control={<Checkbox />} label="{Label}" />"#),
("Radio", "import Radio from \"@mui/material/Radio\"\nimport FormControlLabel from \"@mui/material/FormControlLabel\"", r#"<FormControlLabel control={<Radio checked />} label="{Label}" />"#),
("Radio / Off", "import Radio from \"@mui/material/Radio\"\nimport FormControlLabel from \"@mui/material/FormControlLabel\"", r#"<FormControlLabel control={<Radio />} label="{Label}" />"#),
("Switch", r#"import Switch from "@mui/material/Switch""#, "<Switch defaultChecked />"),
("Switch / Off", r#"import Switch from "@mui/material/Switch""#, "<Switch />"),
("Slider", r#"import Slider from "@mui/material/Slider""#, "<Slider defaultValue={60} />"),
("Form Field", r#"import TextField from "@mui/material/TextField""#, r#"<TextField label="{Label}" defaultValue="{Value}" helperText="{Description}" />"#),
("Form Field / Error", r#"import TextField from "@mui/material/TextField""#, r#"<TextField error label="{Label}" defaultValue="{Value}" helperText="{Message}" />"#),
("Badge", r#"import Chip from "@mui/material/Chip""#, r#"<Chip label="{Label}" color="primary" size="small" />"#),
("Badge / Secondary", r#"import Chip from "@mui/material/Chip""#, r#"<Chip label="{Label}" size="small" />"#),
("Badge / Outline", r#"import Chip from "@mui/material/Chip""#, r#"<Chip label="{Label}" variant="outlined" size="small" />"#),
("Badge / Destructive", r#"import Chip from "@mui/material/Chip""#, r#"<Chip label="{Label}" color="error" size="small" />"#),
("Avatar", r#"import Avatar from "@mui/material/Avatar""#, "<Avatar>{Initials}</Avatar>"),
("Card", "import Card from \"@mui/material/Card\"\nimport CardHeader from \"@mui/material/CardHeader\"", "<Card variant=\"outlined\">\n <CardHeader title=\"{Title}\" subheader=\"{Description}\" />\n</Card>"),
("Alert", "import Alert from \"@mui/material/Alert\"\nimport AlertTitle from \"@mui/material/AlertTitle\"", "<Alert severity=\"info\">\n <AlertTitle>{Title}</AlertTitle>\n {Description}\n</Alert>"),
("Alert / Destructive", "import Alert from \"@mui/material/Alert\"\nimport AlertTitle from \"@mui/material/AlertTitle\"", "<Alert severity=\"error\">\n <AlertTitle>{Title}</AlertTitle>\n {Description}\n</Alert>"),
("Progress", r#"import LinearProgress from "@mui/material/LinearProgress""#, r#"<LinearProgress variant="determinate" value={60} />"#),
("Skeleton", r#"import Skeleton from "@mui/material/Skeleton""#, r#"<Skeleton variant="text" width={200} />"#),
("Separator", r#"import Divider from "@mui/material/Divider""#, "<Divider />"),
("Dropdown Menu", "import Menu from \"@mui/material/Menu\"\nimport MenuItem from \"@mui/material/MenuItem\"", "<Menu open anchorEl={anchor}>\n <MenuItem>{Item 1}</MenuItem>\n <MenuItem>{Item 2}</MenuItem>\n <MenuItem>{Item 3}</MenuItem>\n</Menu>"),
("Tooltip", "import Tooltip from \"@mui/material/Tooltip\"\nimport Button from \"@mui/material/Button\"", r#"<Tooltip title="{Text}"><Button>Hover</Button></Tooltip>"#),
("Toast", r#"import Snackbar from "@mui/material/Snackbar""#, r#"<Snackbar open message="{Title}" />"#),
("Dialog", "import Button from \"@mui/material/Button\"\nimport Dialog from \"@mui/material/Dialog\"\nimport DialogActions from \"@mui/material/DialogActions\"\nimport DialogContent from \"@mui/material/DialogContent\"\nimport DialogContentText from \"@mui/material/DialogContentText\"\nimport DialogTitle from \"@mui/material/DialogTitle\"", "<Dialog open>\n <DialogTitle>{Title}</DialogTitle>\n <DialogContent>\n <DialogContentText>{Description}</DialogContentText>\n </DialogContent>\n <DialogActions>\n <Button>Cancel</Button>\n <Button color=\"error\">Delete</Button>\n </DialogActions>\n</Dialog>"),
("Sheet", r#"import Drawer from "@mui/material/Drawer""#, "<Drawer anchor=\"right\" open>\n {Title}\n</Drawer>"),
("Tabs", "import Tabs from \"@mui/material/Tabs\"\nimport Tab from \"@mui/material/Tab\"", "<Tabs value={0}>\n <Tab label=\"{Tab 1}\" />\n <Tab label=\"{Tab 2}\" />\n <Tab label=\"{Tab 3}\" />\n</Tabs>"),
("Breadcrumb", "import Breadcrumbs from \"@mui/material/Breadcrumbs\"\nimport Link from \"@mui/material/Link\"\nimport Typography from \"@mui/material/Typography\"", "<Breadcrumbs>\n <Link href=\"/\">{Item 1}</Link>\n <Link href=\"#\">{Item 2}</Link>\n <Typography>{Current}</Typography>\n</Breadcrumbs>"),
("Pagination", r#"import Pagination from "@mui/material/Pagination""#, "<Pagination count={3} />"),
("Table / Header", "import TableHead from \"@mui/material/TableHead\"\nimport TableRow from \"@mui/material/TableRow\"\nimport TableCell from \"@mui/material/TableCell\"", "<TableHead>\n <TableRow>\n <TableCell>{Col 1}</TableCell>\n <TableCell>{Col 2}</TableCell>\n <TableCell>{Col 3}</TableCell>\n <TableCell align=\"right\">{Col 4}</TableCell>\n </TableRow>\n</TableHead>"),
("Table / Row", "import TableRow from \"@mui/material/TableRow\"\nimport TableCell from \"@mui/material/TableCell\"", "<TableRow>\n <TableCell>{Cell 1}</TableCell>\n <TableCell>{Cell 2}</TableCell>\n <TableCell>{Cell 3}</TableCell>\n <TableCell align=\"right\">{Cell 4}</TableCell>\n</TableRow>"),
("Sidebar / Item", "import ListItemButton from \"@mui/material/ListItemButton\"\nimport ListItemText from \"@mui/material/ListItemText\"", r#"<ListItemButton><ListItemText primary="{Label}" /></ListItemButton>"#),
("Sidebar / Item Active", "import ListItemButton from \"@mui/material/ListItemButton\"\nimport ListItemText from \"@mui/material/ListItemText\"", r#"<ListItemButton selected><ListItemText primary="{Label}" /></ListItemButton>"#),
];
const MANTINE_CORE: &str = r#"import { Button } from "@mantine/core""#;
pub const MANTINE: Code = &[
("Button / Default", MANTINE_CORE, "<Button>{Label}</Button>"),
("Button / Outline", MANTINE_CORE, r#"<Button variant="outline">{Label}</Button>"#),
("Button / Secondary", MANTINE_CORE, r#"<Button variant="light">{Label}</Button>"#),
("Button / Ghost", MANTINE_CORE, r#"<Button variant="subtle">{Label}</Button>"#),
("Button / Destructive", MANTINE_CORE, r#"<Button color="red">{Label}</Button>"#),
("Button / Link", r#"import { Anchor } from "@mantine/core""#, r##"<Anchor href="#">{Label}</Anchor>"##),
("Button / Small", MANTINE_CORE, r#"<Button size="xs">{Label}</Button>"#),
("Button / Icon", "import { ActionIcon } from \"@mantine/core\"\nimport { IconPlus } from \"@tabler/icons-react\"", r#"<ActionIcon variant="default"><IconPlus size={16} /></ActionIcon>"#),
("Button / With Icon", "import { Button } from \"@mantine/core\"\nimport { IconPlus } from \"@tabler/icons-react\"", r#"<Button leftSection={<IconPlus size={16} />}>{Label}</Button>"#),
("Input", r#"import { TextInput } from "@mantine/core""#, r#"<TextInput placeholder="{Placeholder}" />"#),
("Search", "import { TextInput } from \"@mantine/core\"\nimport { IconSearch } from \"@tabler/icons-react\"", r#"<TextInput leftSection={<IconSearch size={16} />} placeholder="{Placeholder}" />"#),
("Textarea", r#"import { Textarea } from "@mantine/core""#, r#"<Textarea placeholder="{Placeholder}" />"#),
("Select", r#"import { Select } from "@mantine/core""#, r#"<Select placeholder="{Value}" data={["One", "Two"]} />"#),
("Checkbox", r#"import { Checkbox } from "@mantine/core""#, r#"<Checkbox defaultChecked label="{Label}" />"#),
("Checkbox / Off", r#"import { Checkbox } from "@mantine/core""#, r#"<Checkbox label="{Label}" />"#),
("Radio", r#"import { Radio } from "@mantine/core""#, r#"<Radio defaultChecked label="{Label}" />"#),
("Radio / Off", r#"import { Radio } from "@mantine/core""#, r#"<Radio label="{Label}" />"#),
("Switch", r#"import { Switch } from "@mantine/core""#, "<Switch defaultChecked />"),
("Switch / Off", r#"import { Switch } from "@mantine/core""#, "<Switch />"),
("Slider", r#"import { Slider } from "@mantine/core""#, "<Slider defaultValue={60} />"),
("Form Field", r#"import { TextInput } from "@mantine/core""#, r#"<TextInput label="{Label}" description="{Description}" defaultValue="{Value}" />"#),
("Form Field / Error", r#"import { TextInput } from "@mantine/core""#, r#"<TextInput label="{Label}" error="{Message}" defaultValue="{Value}" />"#),
("Badge", r#"import { Badge } from "@mantine/core""#, "<Badge>{Label}</Badge>"),
("Badge / Secondary", r#"import { Badge } from "@mantine/core""#, r#"<Badge variant="light">{Label}</Badge>"#),
("Badge / Outline", r#"import { Badge } from "@mantine/core""#, r#"<Badge variant="outline">{Label}</Badge>"#),
("Badge / Destructive", r#"import { Badge } from "@mantine/core""#, r#"<Badge color="red">{Label}</Badge>"#),
("Avatar", r#"import { Avatar } from "@mantine/core""#, r#"<Avatar radius="xl">{Initials}</Avatar>"#),
("Card", r#"import { Card, Text } from "@mantine/core""#, "<Card withBorder>\n <Text fw={600}>{Title}</Text>\n <Text size=\"sm\" c=\"dimmed\">{Description}</Text>\n</Card>"),
("Alert", r#"import { Alert } from "@mantine/core""#, r#"<Alert title="{Title}">{Description}</Alert>"#),
("Alert / Destructive", r#"import { Alert } from "@mantine/core""#, r#"<Alert color="red" title="{Title}">{Description}</Alert>"#),
("Progress", r#"import { Progress } from "@mantine/core""#, "<Progress value={60} />"),
("Skeleton", r#"import { Skeleton } from "@mantine/core""#, "<Skeleton height={12} width={200} />"),
("Separator", r#"import { Divider } from "@mantine/core""#, "<Divider />"),
("Dropdown Menu", r#"import { Button, Menu } from "@mantine/core""#, "<Menu opened>\n <Menu.Target><Button>Open</Button></Menu.Target>\n <Menu.Dropdown>\n <Menu.Label>{Label}</Menu.Label>\n <Menu.Item>{Item 1}</Menu.Item>\n <Menu.Item>{Item 2}</Menu.Item>\n <Menu.Item>{Item 3}</Menu.Item>\n </Menu.Dropdown>\n</Menu>"),
("Tooltip", r#"import { Button, Tooltip } from "@mantine/core""#, r#"<Tooltip label="{Text}"><Button>Hover</Button></Tooltip>"#),
("Toast", r#"import { notifications } from "@mantine/notifications""#, r#"notifications.show({ title: "{Title}", message: "{Description}" })"#),
("Dialog", r#"import { Button, Group, Modal, Text } from "@mantine/core""#, "<Modal opened onClose={close} title=\"{Title}\">\n <Text size=\"sm\">{Description}</Text>\n <Group justify=\"flex-end\" mt=\"md\">\n <Button variant=\"default\">Cancel</Button>\n <Button color=\"red\">Delete</Button>\n </Group>\n</Modal>"),
("Sheet", r#"import { Drawer } from "@mantine/core""#, "<Drawer opened position=\"right\" onClose={close} title=\"{Title}\">\n {Description}\n</Drawer>"),
("Tabs", r#"import { Tabs } from "@mantine/core""#, "<Tabs defaultValue=\"1\">\n <Tabs.List>\n <Tabs.Tab value=\"1\">{Tab 1}</Tabs.Tab>\n <Tabs.Tab value=\"2\">{Tab 2}</Tabs.Tab>\n <Tabs.Tab value=\"3\">{Tab 3}</Tabs.Tab>\n </Tabs.List>\n</Tabs>"),
("Breadcrumb", r#"import { Anchor, Breadcrumbs, Text } from "@mantine/core""#, "<Breadcrumbs>\n <Anchor href=\"/\">{Item 1}</Anchor>\n <Anchor href=\"#\">{Item 2}</Anchor>\n <Text>{Current}</Text>\n</Breadcrumbs>"),
("Pagination", r#"import { Pagination } from "@mantine/core""#, "<Pagination total={3} />"),
("Table / Header", r#"import { Table } from "@mantine/core""#, "<Table.Thead>\n <Table.Tr>\n <Table.Th>{Col 1}</Table.Th>\n <Table.Th>{Col 2}</Table.Th>\n <Table.Th>{Col 3}</Table.Th>\n <Table.Th ta=\"right\">{Col 4}</Table.Th>\n </Table.Tr>\n</Table.Thead>"),
("Table / Row", r#"import { Table } from "@mantine/core""#, "<Table.Tr>\n <Table.Td>{Cell 1}</Table.Td>\n <Table.Td>{Cell 2}</Table.Td>\n <Table.Td>{Cell 3}</Table.Td>\n <Table.Td ta=\"right\">{Cell 4}</Table.Td>\n</Table.Tr>"),
("Sidebar / Item", r#"import { NavLink } from "@mantine/core""#, r#"<NavLink label="{Label}" />"#),
("Sidebar / Item Active", r#"import { NavLink } from "@mantine/core""#, r#"<NavLink active label="{Label}" />"#),
];
const RB_BUTTON: &str = r#"import { Button } from "react-bootstrap""#;
const RB_FORM: &str = r#"import { Form } from "react-bootstrap""#;
const RB_BADGE: &str = r#"import { Badge } from "react-bootstrap""#;
pub const BOOTSTRAP: Code = &[
("Button / Default", RB_BUTTON, r#"<Button variant="primary">{Label}</Button>"#),
("Button / Outline", RB_BUTTON, r#"<Button variant="outline-primary">{Label}</Button>"#),
("Button / Secondary", RB_BUTTON, r#"<Button variant="secondary">{Label}</Button>"#),
("Button / Ghost", RB_BUTTON, r#"<Button variant="light">{Label}</Button>"#),
("Button / Destructive", RB_BUTTON, r#"<Button variant="danger">{Label}</Button>"#),
("Button / Link", RB_BUTTON, r#"<Button variant="link">{Label}</Button>"#),
("Button / Small", RB_BUTTON, r#"<Button size="sm">{Label}</Button>"#),
("Button / Icon", RB_BUTTON, r#"<Button variant="outline-secondary" aria-label="Add">+</Button>"#),
("Button / With Icon", RB_BUTTON, "<Button>+ {Label}</Button>"),
("Input", RB_FORM, r#"<Form.Control placeholder="{Placeholder}" />"#),
("Search", RB_FORM, r#"<Form.Control type="search" placeholder="{Placeholder}" />"#),
("Textarea", RB_FORM, r#"<Form.Control as="textarea" rows={3} placeholder="{Placeholder}" />"#),
("Select", RB_FORM, "<Form.Select>\n <option>{Value}</option>\n</Form.Select>"),
("Checkbox", RB_FORM, r#"<Form.Check type="checkbox" defaultChecked label="{Label}" />"#),
("Checkbox / Off", RB_FORM, r#"<Form.Check type="checkbox" label="{Label}" />"#),
("Radio", RB_FORM, r#"<Form.Check type="radio" defaultChecked label="{Label}" />"#),
("Radio / Off", RB_FORM, r#"<Form.Check type="radio" label="{Label}" />"#),
("Switch", RB_FORM, r#"<Form.Check type="switch" defaultChecked />"#),
("Switch / Off", RB_FORM, r#"<Form.Check type="switch" />"#),
("Slider", RB_FORM, "<Form.Range defaultValue={60} />"),
("Form Field", RB_FORM, "<Form.Group>\n <Form.Label>{Label}</Form.Label>\n <Form.Control defaultValue=\"{Value}\" />\n <Form.Text muted>{Description}</Form.Text>\n</Form.Group>"),
("Form Field / Error", RB_FORM, "<Form.Group>\n <Form.Label>{Label}</Form.Label>\n <Form.Control isInvalid defaultValue=\"{Value}\" />\n <Form.Control.Feedback type=\"invalid\">{Message}</Form.Control.Feedback>\n</Form.Group>"),
("Badge", RB_BADGE, r#"<Badge bg="primary">{Label}</Badge>"#),
("Badge / Secondary", RB_BADGE, r#"<Badge bg="secondary">{Label}</Badge>"#),
("Badge / Outline", RB_BADGE, r#"<Badge bg="light" text="dark">{Label}</Badge>"#),
("Badge / Destructive", RB_BADGE, r#"<Badge bg="danger">{Label}</Badge>"#),
("Card", r#"import { Card } from "react-bootstrap""#, "<Card>\n <Card.Body>\n <Card.Title>{Title}</Card.Title>\n <Card.Text>{Description}</Card.Text>\n </Card.Body>\n</Card>"),
("Alert", r#"import { Alert } from "react-bootstrap""#, "<Alert variant=\"info\">\n <Alert.Heading>{Title}</Alert.Heading>\n {Description}\n</Alert>"),
("Alert / Destructive", r#"import { Alert } from "react-bootstrap""#, "<Alert variant=\"danger\">\n <Alert.Heading>{Title}</Alert.Heading>\n {Description}\n</Alert>"),
("Progress", r#"import { ProgressBar } from "react-bootstrap""#, "<ProgressBar now={60} />"),
("Skeleton", r#"import { Placeholder } from "react-bootstrap""#, "<Placeholder as=\"p\" animation=\"glow\">\n <Placeholder xs={6} />\n</Placeholder>"),
("Separator", "", "<hr />"),
("Dropdown Menu", r#"import { Dropdown } from "react-bootstrap""#, "<Dropdown show>\n <Dropdown.Toggle>Open</Dropdown.Toggle>\n <Dropdown.Menu>\n <Dropdown.Header>{Label}</Dropdown.Header>\n <Dropdown.Item>{Item 1}</Dropdown.Item>\n <Dropdown.Item>{Item 2}</Dropdown.Item>\n <Dropdown.Item>{Item 3}</Dropdown.Item>\n </Dropdown.Menu>\n</Dropdown>"),
("Tooltip", r#"import { Button, OverlayTrigger, Tooltip } from "react-bootstrap""#, "<OverlayTrigger overlay={<Tooltip>{Text}</Tooltip>}>\n <Button>Hover</Button>\n</OverlayTrigger>"),
("Toast", r#"import { Toast } from "react-bootstrap""#, "<Toast>\n <Toast.Header><strong>{Title}</strong></Toast.Header>\n <Toast.Body>{Description}</Toast.Body>\n</Toast>"),
("Dialog", r#"import { Button, Modal } from "react-bootstrap""#, "<Modal show>\n <Modal.Header closeButton>\n <Modal.Title>{Title}</Modal.Title>\n </Modal.Header>\n <Modal.Body>{Description}</Modal.Body>\n <Modal.Footer>\n <Button variant=\"secondary\">Cancel</Button>\n <Button variant=\"danger\">Delete</Button>\n </Modal.Footer>\n</Modal>"),
("Sheet", r#"import { Offcanvas } from "react-bootstrap""#, "<Offcanvas show placement=\"end\">\n <Offcanvas.Header>\n <Offcanvas.Title>{Title}</Offcanvas.Title>\n </Offcanvas.Header>\n <Offcanvas.Body>{Description}</Offcanvas.Body>\n</Offcanvas>"),
("Tabs", r#"import { Nav } from "react-bootstrap""#, "<Nav variant=\"pills\" defaultActiveKey=\"1\">\n <Nav.Item><Nav.Link eventKey=\"1\">{Tab 1}</Nav.Link></Nav.Item>\n <Nav.Item><Nav.Link eventKey=\"2\">{Tab 2}</Nav.Link></Nav.Item>\n <Nav.Item><Nav.Link eventKey=\"3\">{Tab 3}</Nav.Link></Nav.Item>\n</Nav>"),
("Breadcrumb", r#"import { Breadcrumb } from "react-bootstrap""#, "<Breadcrumb>\n <Breadcrumb.Item href=\"/\">{Item 1}</Breadcrumb.Item>\n <Breadcrumb.Item href=\"#\">{Item 2}</Breadcrumb.Item>\n <Breadcrumb.Item active>{Current}</Breadcrumb.Item>\n</Breadcrumb>"),
("Pagination", r#"import { Pagination } from "react-bootstrap""#, "<Pagination>\n <Pagination.Prev />\n <Pagination.Item active>{1}</Pagination.Item>\n <Pagination.Item>{2}</Pagination.Item>\n <Pagination.Item>{3}</Pagination.Item>\n <Pagination.Next />\n</Pagination>"),
("Table / Header", "", "<thead>\n <tr>\n <th>{Col 1}</th>\n <th>{Col 2}</th>\n <th>{Col 3}</th>\n <th className=\"text-end\">{Col 4}</th>\n </tr>\n</thead>"),
("Table / Row", "", "<tr>\n <td>{Cell 1}</td>\n <td>{Cell 2}</td>\n <td>{Cell 3}</td>\n <td className=\"text-end\">{Cell 4}</td>\n</tr>"),
("Sidebar / Item", r#"import { Nav } from "react-bootstrap""#, "<Nav.Link>{Label}</Nav.Link>"),
("Sidebar / Item Active", r#"import { Nav } from "react-bootstrap""#, "<Nav.Link active>{Label}</Nav.Link>"),
];
const CHAKRA_BUTTON: &str = r#"import { Button } from "@chakra-ui/react""#;
pub const CHAKRA: Code = &[
("Button / Default", CHAKRA_BUTTON, "<Button>{Label}</Button>"),
("Button / Outline", CHAKRA_BUTTON, r#"<Button variant="outline">{Label}</Button>"#),
("Button / Secondary", CHAKRA_BUTTON, r#"<Button variant="subtle">{Label}</Button>"#),
("Button / Ghost", CHAKRA_BUTTON, r#"<Button variant="ghost">{Label}</Button>"#),
("Button / Destructive", CHAKRA_BUTTON, r#"<Button colorPalette="red">{Label}</Button>"#),
("Button / Link", r#"import { Link } from "@chakra-ui/react""#, r##"<Link href="#">{Label}</Link>"##),
("Button / Small", CHAKRA_BUTTON, r#"<Button size="sm">{Label}</Button>"#),
("Button / Icon", "import { IconButton } from \"@chakra-ui/react\"\nimport { LuPlus } from \"react-icons/lu\"", r#"<IconButton aria-label="Add" variant="outline"><LuPlus /></IconButton>"#),
("Button / With Icon", "import { Button } from \"@chakra-ui/react\"\nimport { LuPlus } from \"react-icons/lu\"", "<Button>\n <LuPlus /> {Label}\n</Button>"),
("Input", r#"import { Input } from "@chakra-ui/react""#, r#"<Input placeholder="{Placeholder}" />"#),
("Search", r#"import { Input } from "@chakra-ui/react""#, r#"<Input type="search" placeholder="{Placeholder}" />"#),
("Textarea", r#"import { Textarea } from "@chakra-ui/react""#, r#"<Textarea placeholder="{Placeholder}" />"#),
("Select", r#"import { NativeSelect } from "@chakra-ui/react""#, "<NativeSelect.Root>\n <NativeSelect.Field>\n <option>{Value}</option>\n </NativeSelect.Field>\n <NativeSelect.Indicator />\n</NativeSelect.Root>"),
("Checkbox", r#"import { Checkbox } from "@chakra-ui/react""#, "<Checkbox.Root defaultChecked>\n <Checkbox.HiddenInput />\n <Checkbox.Control />\n <Checkbox.Label>{Label}</Checkbox.Label>\n</Checkbox.Root>"),
("Checkbox / Off", r#"import { Checkbox } from "@chakra-ui/react""#, "<Checkbox.Root>\n <Checkbox.HiddenInput />\n <Checkbox.Control />\n <Checkbox.Label>{Label}</Checkbox.Label>\n</Checkbox.Root>"),
("Radio", r#"import { RadioGroup } from "@chakra-ui/react""#, "<RadioGroup.Root defaultValue=\"a\">\n <RadioGroup.Item value=\"a\">\n <RadioGroup.ItemHiddenInput />\n <RadioGroup.ItemIndicator />\n <RadioGroup.ItemText>{Label}</RadioGroup.ItemText>\n </RadioGroup.Item>\n</RadioGroup.Root>"),
("Switch", r#"import { Switch } from "@chakra-ui/react""#, "<Switch.Root defaultChecked>\n <Switch.HiddenInput />\n <Switch.Control />\n</Switch.Root>"),
("Switch / Off", r#"import { Switch } from "@chakra-ui/react""#, "<Switch.Root>\n <Switch.HiddenInput />\n <Switch.Control />\n</Switch.Root>"),
("Slider", r#"import { Slider } from "@chakra-ui/react""#, "<Slider.Root defaultValue={[60]}>\n <Slider.Control>\n <Slider.Track><Slider.Range /></Slider.Track>\n <Slider.Thumbs />\n </Slider.Control>\n</Slider.Root>"),
("Form Field", r#"import { Field, Input } from "@chakra-ui/react""#, "<Field.Root>\n <Field.Label>{Label}</Field.Label>\n <Input defaultValue=\"{Value}\" />\n <Field.HelperText>{Description}</Field.HelperText>\n</Field.Root>"),
("Form Field / Error", r#"import { Field, Input } from "@chakra-ui/react""#, "<Field.Root invalid>\n <Field.Label>{Label}</Field.Label>\n <Input defaultValue=\"{Value}\" />\n <Field.ErrorText>{Message}</Field.ErrorText>\n</Field.Root>"),
("Badge", r#"import { Badge } from "@chakra-ui/react""#, r#"<Badge variant="solid">{Label}</Badge>"#),
("Badge / Secondary", r#"import { Badge } from "@chakra-ui/react""#, r#"<Badge variant="subtle">{Label}</Badge>"#),
("Badge / Outline", r#"import { Badge } from "@chakra-ui/react""#, r#"<Badge variant="outline">{Label}</Badge>"#),
("Badge / Destructive", r#"import { Badge } from "@chakra-ui/react""#, r#"<Badge variant="solid" colorPalette="red">{Label}</Badge>"#),
("Avatar", r#"import { Avatar } from "@chakra-ui/react""#, "<Avatar.Root>\n <Avatar.Fallback>{Initials}</Avatar.Fallback>\n</Avatar.Root>"),
("Card", r#"import { Card } from "@chakra-ui/react""#, "<Card.Root variant=\"outline\">\n <Card.Body>\n <Card.Title>{Title}</Card.Title>\n <Card.Description>{Description}</Card.Description>\n </Card.Body>\n</Card.Root>"),
("Alert", r#"import { Alert } from "@chakra-ui/react""#, "<Alert.Root status=\"info\">\n <Alert.Content>\n <Alert.Title>{Title}</Alert.Title>\n <Alert.Description>{Description}</Alert.Description>\n </Alert.Content>\n</Alert.Root>"),
("Alert / Destructive", r#"import { Alert } from "@chakra-ui/react""#, "<Alert.Root status=\"error\">\n <Alert.Content>\n <Alert.Title>{Title}</Alert.Title>\n <Alert.Description>{Description}</Alert.Description>\n </Alert.Content>\n</Alert.Root>"),
("Progress", r#"import { Progress } from "@chakra-ui/react""#, "<Progress.Root value={60}>\n <Progress.Track><Progress.Range /></Progress.Track>\n</Progress.Root>"),
("Skeleton", r#"import { SkeletonText } from "@chakra-ui/react""#, "<SkeletonText noOfLines={2} />"),
("Separator", r#"import { Separator } from "@chakra-ui/react""#, "<Separator />"),
("Dropdown Menu", r#"import { Button, Menu } from "@chakra-ui/react""#, "<Menu.Root open>\n <Menu.Trigger asChild><Button>Open</Button></Menu.Trigger>\n <Menu.Content>\n <Menu.ItemGroupLabel>{Label}</Menu.ItemGroupLabel>\n <Menu.Item value=\"1\">{Item 1}</Menu.Item>\n <Menu.Item value=\"2\">{Item 2}</Menu.Item>\n <Menu.Item value=\"3\">{Item 3}</Menu.Item>\n </Menu.Content>\n</Menu.Root>"),
("Toast", r#"import { toaster } from "@/components/ui/toaster""#, r#"toaster.create({ title: "{Title}", description: "{Description}" })"#),
("Dialog", r#"import { Button, Dialog } from "@chakra-ui/react""#, "<Dialog.Root open>\n <Dialog.Content>\n <Dialog.Header><Dialog.Title>{Title}</Dialog.Title></Dialog.Header>\n <Dialog.Body>{Description}</Dialog.Body>\n <Dialog.Footer>\n <Button variant=\"outline\">Cancel</Button>\n <Button colorPalette=\"red\">Delete</Button>\n </Dialog.Footer>\n </Dialog.Content>\n</Dialog.Root>"),
("Sheet", r#"import { Drawer } from "@chakra-ui/react""#, "<Drawer.Root open placement=\"end\">\n <Drawer.Content>\n <Drawer.Header><Drawer.Title>{Title}</Drawer.Title></Drawer.Header>\n <Drawer.Body>{Description}</Drawer.Body>\n </Drawer.Content>\n</Drawer.Root>"),
("Tabs", r#"import { Tabs } from "@chakra-ui/react""#, "<Tabs.Root defaultValue=\"1\">\n <Tabs.List>\n <Tabs.Trigger value=\"1\">{Tab 1}</Tabs.Trigger>\n <Tabs.Trigger value=\"2\">{Tab 2}</Tabs.Trigger>\n <Tabs.Trigger value=\"3\">{Tab 3}</Tabs.Trigger>\n </Tabs.List>\n</Tabs.Root>"),
("Breadcrumb", r#"import { Breadcrumb } from "@chakra-ui/react""#, "<Breadcrumb.Root>\n <Breadcrumb.List>\n <Breadcrumb.Item><Breadcrumb.Link href=\"/\">{Item 1}</Breadcrumb.Link></Breadcrumb.Item>\n <Breadcrumb.Separator />\n <Breadcrumb.Item><Breadcrumb.Link href=\"#\">{Item 2}</Breadcrumb.Link></Breadcrumb.Item>\n <Breadcrumb.Separator />\n <Breadcrumb.Item><Breadcrumb.CurrentLink>{Current}</Breadcrumb.CurrentLink></Breadcrumb.Item>\n </Breadcrumb.List>\n</Breadcrumb.Root>"),
("Table / Header", r#"import { Table } from "@chakra-ui/react""#, "<Table.Header>\n <Table.Row>\n <Table.ColumnHeader>{Col 1}</Table.ColumnHeader>\n <Table.ColumnHeader>{Col 2}</Table.ColumnHeader>\n <Table.ColumnHeader>{Col 3}</Table.ColumnHeader>\n <Table.ColumnHeader textAlign=\"end\">{Col 4}</Table.ColumnHeader>\n </Table.Row>\n</Table.Header>"),
("Table / Row", r#"import { Table } from "@chakra-ui/react""#, "<Table.Row>\n <Table.Cell>{Cell 1}</Table.Cell>\n <Table.Cell>{Cell 2}</Table.Cell>\n <Table.Cell>{Cell 3}</Table.Cell>\n <Table.Cell textAlign=\"end\">{Cell 4}</Table.Cell>\n</Table.Row>"),
];
+121 -78
View File
@@ -17,6 +17,11 @@ pub struct Kit {
/// The kit page's background variable.
pub surface: &'static str,
pub components: &'static [KitComponent],
/// Screen layouts built from the kit's components: (name, markup with `<x-paper-instance>`).
pub layouts: &'static [(&'static str, &'static str)],
/// The library's code per component, for a kit drawn with another kit's markup; empty means
/// each component's own.
pub code: derived::Code,
}
pub struct KitComponent {
@@ -27,10 +32,13 @@ pub struct KitComponent {
pub egui: &'static str,
}
const EGUI_BUTTON: &str = "if ui.button({Label}).clicked() {\n // TODO: action\n}";
const EGUI_INPUT: &str = "ui.add(egui::TextEdit::singleline(&mut text).hint_text({Placeholder}));";
mod derived;
mod shadcn;
pub const KITS: [Kit; 4] = [
pub(crate) const EGUI_BUTTON: &str = "if ui.button({Label}).clicked() {\n // TODO: action\n}";
pub(crate) const EGUI_INPUT: &str = "ui.add(egui::TextEdit::singleline(&mut text).hint_text({Placeholder}));";
pub const KITS: [Kit; 8] = [
Kit {
id: "shadcn",
name: "shadcn/ui",
@@ -39,88 +47,21 @@ pub const KITS: [Kit; 4] = [
("background", "#ffffff"), ("foreground", "#09090b"), ("card", "#ffffff"), ("primary", "#18181b"),
("primary-foreground", "#fafafa"), ("secondary", "#f4f4f5"), ("secondary-foreground", "#18181b"),
("muted", "#f4f4f5"), ("muted-foreground", "#71717a"), ("border", "#e4e4e7"), ("input", "#e4e4e7"),
("destructive", "#dc2626"), ("ring", "#a1a1aa"),
("destructive", "#dc2626"), ("destructive-foreground", "#fafafa"), ("ring", "#a1a1aa"), ("accent", "#f4f4f5"),
("accent-foreground", "#18181b"), ("popover", "#ffffff"), ("overlay", "#00000080"),
],
dark: &[
("background", "#09090b"), ("foreground", "#fafafa"), ("card", "#18181b"), ("primary", "#fafafa"),
("primary-foreground", "#18181b"), ("secondary", "#27272a"), ("secondary-foreground", "#fafafa"),
("muted", "#27272a"), ("muted-foreground", "#a1a1aa"), ("border", "#27272a"), ("input", "#3f3f46"),
("destructive", "#ef4444"), ("ring", "#52525b"),
("destructive", "#ef4444"), ("destructive-foreground", "#fafafa"), ("ring", "#52525b"), ("accent", "#27272a"),
("accent-foreground", "#fafafa"), ("popover", "#18181b"), ("overlay", "#000000b3"),
],
tokens: &[("radius", "10px"), ("control-height", "36px"), ("text-sm", "14px"), ("space-4", "16px"), ("space-6", "24px")],
tokens: &[("radius-control", "8px"), ("radius-card", "12px"), ("control-height", "36px"), ("text-sm", "14px"), ("space-4", "16px"), ("space-6", "24px")],
surface: "background",
components: &[
KitComponent {
name: "Button / Default",
html: r#"<div layer-name="Button / Default" style="display:flex;align-items:center;justify-content:center;height:36px;padding:0 16px;border-radius:8px;background:var(--primary)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--primary-foreground)">Button</span></div>"#,
import: r#"import { Button } from "@/components/ui/button""#,
react: "<Button>{Label}</Button>",
egui: EGUI_BUTTON,
},
KitComponent {
name: "Button / Outline",
html: r#"<div layer-name="Button / Outline" style="display:flex;align-items:center;justify-content:center;height:36px;padding:0 16px;border-radius:8px;background:var(--background);border:1px solid var(--border)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Button</span></div>"#,
import: r#"import { Button } from "@/components/ui/button""#,
react: r#"<Button variant="outline">{Label}</Button>"#,
egui: EGUI_BUTTON,
},
KitComponent {
name: "Button / Secondary",
html: r#"<div layer-name="Button / Secondary" style="display:flex;align-items:center;justify-content:center;height:36px;padding:0 16px;border-radius:8px;background:var(--secondary)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--secondary-foreground)">Button</span></div>"#,
import: r#"import { Button } from "@/components/ui/button""#,
react: r#"<Button variant="secondary">{Label}</Button>"#,
egui: EGUI_BUTTON,
},
KitComponent {
name: "Button / Ghost",
html: r#"<div layer-name="Button / Ghost" style="display:flex;align-items:center;justify-content:center;height:36px;padding:0 16px;border-radius:8px"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Button</span></div>"#,
import: r#"import { Button } from "@/components/ui/button""#,
react: r#"<Button variant="ghost">{Label}</Button>"#,
egui: EGUI_BUTTON,
},
KitComponent {
name: "Button / Destructive",
html: r#"<div layer-name="Button / Destructive" style="display:flex;align-items:center;justify-content:center;height:36px;padding:0 16px;border-radius:8px;background:var(--destructive)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:#ffffff">Delete</span></div>"#,
import: r#"import { Button } from "@/components/ui/button""#,
react: r#"<Button variant="destructive">{Label}</Button>"#,
egui: EGUI_BUTTON,
},
KitComponent {
name: "Input",
html: r#"<div layer-name="Input" style="display:flex;align-items:center;width:240px;height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--input);background:var(--background)"><span layer-name="Placeholder" style="font-size:14px;color:var(--muted-foreground)">Email</span></div>"#,
import: r#"import { Input } from "@/components/ui/input""#,
react: r#"<Input placeholder="{Placeholder}" />"#,
egui: EGUI_INPUT,
},
KitComponent {
name: "Badge",
html: r#"<div layer-name="Badge" style="display:flex;align-items:center;padding:2px 8px;border-radius:6px;background:var(--primary)"><span layer-name="Label" style="font-size:12px;font-weight:500;color:var(--primary-foreground)">Badge</span></div>"#,
import: r#"import { Badge } from "@/components/ui/badge""#,
react: "<Badge>{Label}</Badge>",
egui: "ui.label(egui::RichText::new({Label}).small().strong());",
},
KitComponent {
name: "Card",
html: r#"<div layer-name="Card" style="display:flex;flex-direction:column;gap:6px;width:320px;padding:24px;border-radius:12px;border:1px solid var(--border);background:var(--card)"><span layer-name="Title" style="font-size:16px;font-weight:600;color:var(--foreground)">Card title</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground)">A short description of what this card holds.</span></div>"#,
import: r#"import { Card, CardDescription, CardHeader, CardTitle } from "@/components/ui/card""#,
react: "<Card>\n <CardHeader>\n <CardTitle>{Title}</CardTitle>\n <CardDescription>{Description}</CardDescription>\n </CardHeader>\n</Card>",
egui: "egui::Frame::group(ui.style()).show(ui, |ui| {\n ui.strong({Title});\n ui.label({Description});\n});",
},
KitComponent {
name: "Switch",
html: r#"<div layer-name="Switch" style="display:flex;align-items:center;justify-content:flex-end;width:32px;height:18px;padding:2px;border-radius:9px;background:var(--primary)"><div style="width:14px;height:14px;border-radius:7px;background:var(--primary-foreground)"></div></div>"#,
import: r#"import { Switch } from "@/components/ui/switch""#,
react: "<Switch defaultChecked />",
egui: "ui.toggle_value(&mut on, \"\");",
},
KitComponent {
name: "Alert",
html: r#"<div layer-name="Alert" style="display:flex;flex-direction:column;gap:4px;width:360px;padding:12px 16px;border-radius:10px;border:1px solid var(--border);background:var(--background)"><span layer-name="Title" style="font-size:14px;font-weight:500;color:var(--foreground)">Heads up</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground)">You can add components to your app using the CLI.</span></div>"#,
import: r#"import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert""#,
react: "<Alert>\n <AlertTitle>{Title}</AlertTitle>\n <AlertDescription>{Description}</AlertDescription>\n</Alert>",
egui: "ui.group(|ui| {\n ui.strong({Title});\n ui.label({Description});\n});",
},
],
components: shadcn::COMPONENTS,
layouts: shadcn::LAYOUTS,
code: &[],
},
Kit {
id: "antd",
@@ -203,6 +144,8 @@ pub const KITS: [Kit; 4] = [
egui: "ui.group(|ui| {\n ui.strong({Title});\n ui.label({Description});\n});",
},
],
layouts: &[],
code: &[],
},
Kit {
id: "reactbits",
@@ -269,6 +212,8 @@ pub const KITS: [Kit; 4] = [
egui: EGUI_BUTTON,
},
],
layouts: &[],
code: &[],
},
Kit {
id: "egui",
@@ -342,6 +287,104 @@ pub const KITS: [Kit; 4] = [
egui: "ui.collapsing({Title}, |ui| {\n // body\n});",
},
],
layouts: &[],
code: &[],
},
Kit {
id: "mui",
name: "Material UI",
about: "Material Design 2 in React (@mui/material v6): blue primary, 4px radius, 36px controls.",
light: &[
("background", "#ffffff"), ("foreground", "#212121"), ("card", "#ffffff"), ("primary", "#1976d2"),
("primary-foreground", "#ffffff"), ("secondary", "#e0e0e0"), ("secondary-foreground", "#212121"),
("muted", "#f5f5f5"), ("muted-foreground", "#666666"), ("border", "#e0e0e0"), ("input", "#c4c4c4"),
("destructive", "#d32f2f"), ("destructive-foreground", "#ffffff"), ("ring", "#1976d2"), ("accent", "#f5f5f5"),
("accent-foreground", "#212121"), ("popover", "#ffffff"), ("overlay", "#00000080"),
],
dark: &[
("background", "#121212"), ("foreground", "#ffffff"), ("card", "#1e1e1e"), ("primary", "#90caf9"),
("primary-foreground", "#0d0d0d"), ("secondary", "#2e2e2e"), ("secondary-foreground", "#ffffff"),
("muted", "#2a2a2a"), ("muted-foreground", "#b3b3b3"), ("border", "#2e2e2e"), ("input", "#5c5c5c"),
("destructive", "#f44336"), ("destructive-foreground", "#0d0d0d"), ("ring", "#90caf9"), ("accent", "#2a2a2a"),
("accent-foreground", "#ffffff"), ("popover", "#1e1e1e"), ("overlay", "#00000080"),
],
tokens: &[("radius-control", "4px"), ("radius-card", "4px"), ("control-height", "36px"), ("text-sm", "14px"), ("space-4", "16px"), ("space-6", "24px")],
surface: "background",
components: shadcn::COMPONENTS,
layouts: shadcn::LAYOUTS,
code: derived::MUI,
},
Kit {
id: "mantine",
name: "Mantine",
about: "Mantine v7 React components: blue primary, 4px controls in 8px cards, 36px controls.",
light: &[
("background", "#ffffff"), ("foreground", "#000000"), ("card", "#ffffff"), ("primary", "#1971c2"),
("primary-foreground", "#ffffff"), ("secondary", "#e7f5ff"), ("secondary-foreground", "#1864ab"),
("muted", "#f1f3f5"), ("muted-foreground", "#495057"), ("border", "#dee2e6"), ("input", "#ced4da"),
("destructive", "#c92a2a"), ("destructive-foreground", "#ffffff"), ("ring", "#1971c2"), ("accent", "#f8f9fa"),
("accent-foreground", "#000000"), ("popover", "#ffffff"), ("overlay", "#00000080"),
],
dark: &[
("background", "#242424"), ("foreground", "#c9c9c9"), ("card", "#2e2e2e"), ("primary", "#1971c2"),
("primary-foreground", "#ffffff"), ("secondary", "#1e3a52"), ("secondary-foreground", "#a5d8ff"),
("muted", "#2e2e2e"), ("muted-foreground", "#a6a7ab"), ("border", "#424242"), ("input", "#424242"),
("destructive", "#c92a2a"), ("destructive-foreground", "#ffffff"), ("ring", "#1971c2"), ("accent", "#2e2e2e"),
("accent-foreground", "#c9c9c9"), ("popover", "#2e2e2e"), ("overlay", "#000000b3"),
],
tokens: &[("radius-control", "4px"), ("radius-card", "8px"), ("control-height", "36px"), ("text-sm", "14px"), ("space-4", "16px"), ("space-6", "24px")],
surface: "background",
components: shadcn::COMPONENTS,
layouts: shadcn::LAYOUTS,
code: derived::MANTINE,
},
Kit {
id: "bootstrap",
name: "Bootstrap",
about: "Bootstrap 5 through react-bootstrap: blue primary, 6px radius, 38px controls.",
light: &[
("background", "#ffffff"), ("foreground", "#212529"), ("card", "#ffffff"), ("primary", "#0b5ed7"),
("primary-foreground", "#ffffff"), ("secondary", "#6c757d"), ("secondary-foreground", "#ffffff"),
("muted", "#e9ecef"), ("muted-foreground", "#6c757d"), ("border", "#dee2e6"), ("input", "#dee2e6"),
("destructive", "#bb2d3b"), ("destructive-foreground", "#ffffff"), ("ring", "#86b7fe"), ("accent", "#f8f9fa"),
("accent-foreground", "#212529"), ("popover", "#ffffff"), ("overlay", "#00000080"),
],
dark: &[
("background", "#212529"), ("foreground", "#dee2e6"), ("card", "#2b3035"), ("primary", "#0b5ed7"),
("primary-foreground", "#ffffff"), ("secondary", "#6c757d"), ("secondary-foreground", "#ffffff"),
("muted", "#343a40"), ("muted-foreground", "#adb5bd"), ("border", "#495057"), ("input", "#495057"),
("destructive", "#bb2d3b"), ("destructive-foreground", "#ffffff"), ("ring", "#86b7fe"), ("accent", "#343a40"),
("accent-foreground", "#dee2e6"), ("popover", "#2b3035"), ("overlay", "#000000b3"),
],
tokens: &[("radius-control", "6px"), ("radius-card", "6px"), ("control-height", "38px"), ("text-sm", "14px"), ("space-4", "16px"), ("space-6", "24px")],
surface: "background",
components: shadcn::COMPONENTS,
layouts: shadcn::LAYOUTS,
code: derived::BOOTSTRAP,
},
Kit {
id: "chakra",
name: "Chakra UI",
about: "Chakra UI v3 with the teal colour palette: 6px controls in 8px cards, 40px controls.",
light: &[
("background", "#ffffff"), ("foreground", "#09090b"), ("card", "#ffffff"), ("primary", "#0f766e"),
("primary-foreground", "#ffffff"), ("secondary", "#f4f4f5"), ("secondary-foreground", "#18181b"),
("muted", "#f4f4f5"), ("muted-foreground", "#52525b"), ("border", "#e4e4e7"), ("input", "#e4e4e7"),
("destructive", "#dc2626"), ("destructive-foreground", "#ffffff"), ("ring", "#0f766e"), ("accent", "#f4f4f5"),
("accent-foreground", "#18181b"), ("popover", "#ffffff"), ("overlay", "#00000080"),
],
dark: &[
("background", "#111111"), ("foreground", "#fafafa"), ("card", "#18181b"), ("primary", "#2dd4bf"),
("primary-foreground", "#042f2e"), ("secondary", "#27272a"), ("secondary-foreground", "#fafafa"),
("muted", "#27272a"), ("muted-foreground", "#a1a1aa"), ("border", "#27272a"), ("input", "#3f3f46"),
("destructive", "#ef4444"), ("destructive-foreground", "#fafafa"), ("ring", "#2dd4bf"), ("accent", "#27272a"),
("accent-foreground", "#fafafa"), ("popover", "#18181b"), ("overlay", "#000000b3"),
],
tokens: &[("radius-control", "6px"), ("radius-card", "8px"), ("control-height", "40px"), ("text-sm", "14px"), ("space-4", "16px"), ("space-6", "24px")],
surface: "background",
components: shadcn::COMPONENTS,
layouts: shadcn::LAYOUTS,
code: derived::CHAKRA,
},
];
+126
View File
@@ -0,0 +1,126 @@
//! The shadcn/ui kit: the base most designs start from (re-colour it through its variables). The
//! common controls, overlays, navigation and data pieces, and screen layouts built from them.
use super::{EGUI_BUTTON, EGUI_INPUT, KitComponent};
const BUTTON: &str = r#"import { Button } from "@/components/ui/button""#;
const TABLE: &str = r#"import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table""#;
const CARD: &str = r#"import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card""#;
const SIDEBAR: &str = r#"import { SidebarMenuButton, SidebarMenuItem } from "@/components/ui/sidebar""#;
/// One component; `egui` is empty where egui has no counterpart.
const fn c(name: &'static str, html: &'static str, import: &'static str, react: &'static str, egui: &'static str) -> KitComponent {
KitComponent { name, html, import, react, egui }
}
pub const COMPONENTS: &[KitComponent] = &[
// ---- buttons
c("Button / Default", r#"<div layer-name="Button / Default" style="display:flex;align-items:center;justify-content:center;height:var(--control-height);padding:0 16px;border-radius:var(--radius-control);background:var(--primary)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--primary-foreground)">Button</span></div>"#, BUTTON, "<Button>{Label}</Button>", EGUI_BUTTON),
c("Button / Outline", r#"<div layer-name="Button / Outline" style="display:flex;align-items:center;justify-content:center;height:var(--control-height);padding:0 16px;border-radius:var(--radius-control);background:var(--background);border:1px solid var(--border)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Button</span></div>"#, BUTTON, r#"<Button variant="outline">{Label}</Button>"#, EGUI_BUTTON),
c("Button / Secondary", r#"<div layer-name="Button / Secondary" style="display:flex;align-items:center;justify-content:center;height:var(--control-height);padding:0 16px;border-radius:var(--radius-control);background:var(--secondary)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--secondary-foreground)">Button</span></div>"#, BUTTON, r#"<Button variant="secondary">{Label}</Button>"#, EGUI_BUTTON),
c("Button / Ghost", r#"<div layer-name="Button / Ghost" style="display:flex;align-items:center;justify-content:center;height:var(--control-height);padding:0 16px;border-radius:var(--radius-control)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Button</span></div>"#, BUTTON, r#"<Button variant="ghost">{Label}</Button>"#, EGUI_BUTTON),
c("Button / Destructive", r#"<div layer-name="Button / Destructive" style="display:flex;align-items:center;justify-content:center;height:var(--control-height);padding:0 16px;border-radius:var(--radius-control);background:var(--destructive)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--destructive-foreground)">Delete</span></div>"#, BUTTON, r#"<Button variant="destructive">{Label}</Button>"#, EGUI_BUTTON),
c("Button / Link", r#"<div layer-name="Button / Link" style="display:flex;align-items:center;height:var(--control-height)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--primary);text-decoration:underline">Learn more</span></div>"#, BUTTON, r#"<Button variant="link">{Label}</Button>"#, "ui.link({Label});"),
c("Button / Small", r#"<div layer-name="Button / Small" style="display:flex;align-items:center;justify-content:center;height:32px;padding:0 12px;border-radius:var(--radius-control);background:var(--primary)"><span layer-name="Label" style="font-size:13px;font-weight:500;color:var(--primary-foreground)">Button</span></div>"#, BUTTON, r#"<Button size="sm">{Label}</Button>"#, EGUI_BUTTON),
c("Button / Icon", r#"<div layer-name="Button / Icon" style="display:flex;align-items:center;justify-content:center;width:var(--control-height);height:var(--control-height);border-radius:var(--radius-control);border:1px solid var(--border);background:var(--background)"><svg layer-name="Icon / plus" width="16" height="16" viewBox="0 0 24 24" style="color:var(--foreground)"><path d="M5 12h14M12 5v14" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>"#, BUTTON, r#"<Button variant="outline" size="icon"><Plus /></Button>"#, r#"if ui.button("+").clicked() {}"#),
c("Button / With Icon", r#"<div layer-name="Button / With Icon" style="display:flex;align-items:center;justify-content:center;gap:8px;height:var(--control-height);padding:0 16px;border-radius:var(--radius-control);background:var(--primary)"><svg layer-name="Icon / plus" width="16" height="16" viewBox="0 0 24 24" style="color:var(--primary-foreground)"><path d="M5 12h14M12 5v14" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--primary-foreground)">New item</span></div>"#, BUTTON, "<Button>\n <Plus /> {Label}\n</Button>", EGUI_BUTTON),
// ---- form controls
c("Input", r#"<div layer-name="Input" style="display:flex;align-items:center;width:240px;height:var(--control-height);padding:0 12px;border-radius:var(--radius-control);border:1px solid var(--input);background:var(--background)"><span layer-name="Placeholder" style="font-size:14px;color:var(--muted-foreground)">Email</span></div>"#, r#"import { Input } from "@/components/ui/input""#, r#"<Input placeholder="{Placeholder}" />"#, EGUI_INPUT),
c("Search", r#"<div layer-name="Search" style="display:flex;align-items:center;gap:8px;width:240px;height:var(--control-height);padding:0 12px;border-radius:var(--radius-control);border:1px solid var(--input);background:var(--background)"><svg layer-name="Icon / search" width="16" height="16" viewBox="0 0 24 24" style="color:var(--muted-foreground)"><path d="M11 3a8 8 0 1 0 0 16 8 8 0 0 0 0-16zM21 21l-4.3-4.3" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Placeholder" style="font-size:14px;color:var(--muted-foreground)">Search…</span></div>"#, r#"import { Input } from "@/components/ui/input""#, r#"<Input type="search" placeholder="{Placeholder}" />"#, EGUI_INPUT),
c("Textarea", r#"<div layer-name="Textarea" style="display:flex;width:320px;height:80px;padding:8px 12px;border-radius:var(--radius-control);border:1px solid var(--input);background:var(--background)"><span layer-name="Placeholder" style="font-size:14px;color:var(--muted-foreground)">Type your message here.</span></div>"#, r#"import { Textarea } from "@/components/ui/textarea""#, r#"<Textarea placeholder="{Placeholder}" />"#, "ui.add(egui::TextEdit::multiline(&mut text).hint_text({Placeholder}));"),
c("Select", r#"<div layer-name="Select" style="display:flex;align-items:center;justify-content:space-between;width:240px;height:var(--control-height);padding:0 12px;border-radius:var(--radius-control);border:1px solid var(--input);background:var(--background)"><span layer-name="Value" style="font-size:14px;color:var(--foreground)">Select a fruit</span><svg layer-name="Icon / chevron-down" width="16" height="16" viewBox="0 0 24 24" style="color:var(--muted-foreground)"><path d="m6 9 6 6 6-6" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>"#, r#"import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select""#, "<Select>\n <SelectTrigger className=\"w-[240px]\">\n <SelectValue placeholder=\"{Value}\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"one\">One</SelectItem>\n </SelectContent>\n</Select>", "egui::ComboBox::from_id_salt(\"select\")\n .selected_text({Value})\n .show_ui(ui, |ui| {});"),
c("Checkbox", r#"<div layer-name="Checkbox" style="display:flex;align-items:center;gap:8px"><div layer-name="Box" style="display:flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:4px;background:var(--primary)"><svg layer-name="Icon / check" width="12" height="12" viewBox="0 0 24 24" style="color:var(--primary-foreground)"><path d="M20 6 9 17l-5-5" stroke="currentColor" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg></div><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Accept terms</span></div>"#, r#"import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label""#, "<div className=\"flex items-center gap-2\">\n <Checkbox id=\"terms\" defaultChecked />\n <Label htmlFor=\"terms\">{Label}</Label>\n</div>", "ui.checkbox(&mut checked, {Label});"),
c("Checkbox / Off", r#"<div layer-name="Checkbox / Off" style="display:flex;align-items:center;gap:8px"><div layer-name="Box" style="width:16px;height:16px;border-radius:4px;border:1px solid var(--input);background:var(--background)"></div><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Remember me</span></div>"#, r#"import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label""#, "<div className=\"flex items-center gap-2\">\n <Checkbox id=\"remember\" />\n <Label htmlFor=\"remember\">{Label}</Label>\n</div>", "ui.checkbox(&mut checked, {Label});"),
c("Radio", r#"<div layer-name="Radio" style="display:flex;align-items:center;gap:8px"><div layer-name="Ring" style="display:flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:var(--radius-control);border:1px solid var(--primary)"><div layer-name="Dot" style="width:8px;height:8px;border-radius:4px;background:var(--primary)"></div></div><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Default</span></div>"#, r#"import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { Label } from "@/components/ui/label""#, "<RadioGroup defaultValue=\"default\">\n <div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"default\" id=\"r1\" />\n <Label htmlFor=\"r1\">{Label}</Label>\n </div>\n</RadioGroup>", "ui.radio_value(&mut choice, 0, {Label});"),
c("Radio / Off", r#"<div layer-name="Radio / Off" style="display:flex;align-items:center;gap:8px"><div layer-name="Ring" style="width:16px;height:16px;border-radius:var(--radius-control);border:1px solid var(--input)"></div><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Compact</span></div>"#, r#"import { RadioGroupItem } from "@/components/ui/radio-group"
import { Label } from "@/components/ui/label""#, "<div className=\"flex items-center gap-2\">\n <RadioGroupItem value=\"compact\" id=\"r2\" />\n <Label htmlFor=\"r2\">{Label}</Label>\n</div>", "ui.radio_value(&mut choice, 1, {Label});"),
c("Switch", r#"<div layer-name="Switch" style="display:flex;align-items:center;justify-content:flex-end;width:36px;height:20px;padding:4px;border-radius:var(--radius-card);background:var(--primary)"><div layer-name="Knob" style="width:12px;height:12px;border-radius:6px;background:var(--primary-foreground)"></div></div>"#, r#"import { Switch } from "@/components/ui/switch""#, "<Switch defaultChecked />", "ui.toggle_value(&mut on, \"\");"),
c("Switch / Off", r#"<div layer-name="Switch / Off" style="display:flex;align-items:center;width:36px;height:20px;padding:4px;border-radius:var(--radius-card);background:var(--input)"><div layer-name="Knob" style="width:12px;height:12px;border-radius:6px;background:var(--background)"></div></div>"#, r#"import { Switch } from "@/components/ui/switch""#, "<Switch />", "ui.toggle_value(&mut on, \"\");"),
c("Slider", r#"<div layer-name="Slider" style="display:flex;align-items:center;width:240px;height:16px"><x-paper-instance component="Progress" style="flex-grow:1"/><div layer-name="Thumb" style="position:absolute;left:136px;top:0px;width:16px;height:16px;border-radius:var(--radius-control);border:2px solid var(--primary);background:var(--background)"></div></div>"#, r#"import { Slider } from "@/components/ui/slider""#, "<Slider defaultValue={[60]} max={100} step={1} />", "ui.add(egui::Slider::new(&mut value, 0.0..=100.0));"),
c("Form Field", r#"<div layer-name="Form Field" style="display:flex;flex-direction:column;gap:8px;width:320px"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--foreground)">Username</span><div layer-name="Input" style="display:flex;align-items:center;height:var(--control-height);padding:0 12px;border-radius:var(--radius-control);border:1px solid var(--input);background:var(--background)"><span layer-name="Value" style="font-size:14px;color:var(--foreground)">shadcn</span></div><span layer-name="Description" style="font-size:13px;color:var(--muted-foreground)">This is your public display name.</span></div>"#, r#"import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label""#, "<div className=\"grid gap-2\">\n <Label htmlFor=\"field\">{Label}</Label>\n <Input id=\"field\" defaultValue=\"{Value}\" />\n <p className=\"text-sm text-muted-foreground\">{Description}</p>\n</div>", "ui.label({Label});\nui.text_edit_singleline(&mut value);"),
c("Form Field / Error", r#"<div layer-name="Form Field / Error" style="display:flex;flex-direction:column;gap:8px;width:320px"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--destructive)">Email</span><div layer-name="Input" style="display:flex;align-items:center;height:var(--control-height);padding:0 12px;border-radius:var(--radius-control);border:1px solid var(--destructive);background:var(--background)"><span layer-name="Value" style="font-size:14px;color:var(--foreground)">name@</span></div><span layer-name="Message" style="font-size:13px;font-weight:500;color:var(--destructive)">Enter a valid email address.</span></div>"#, r#"import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label""#, "<div className=\"grid gap-2\">\n <Label htmlFor=\"email\" className=\"text-destructive\">{Label}</Label>\n <Input id=\"email\" aria-invalid defaultValue=\"{Value}\" />\n <p className=\"text-sm font-medium text-destructive\">{Message}</p>\n</div>", ""),
// ---- display
c("Badge", r#"<div layer-name="Badge" style="display:flex;align-items:center;padding:4px 8px;border-radius:6px;background:var(--primary)"><span layer-name="Label" style="font-size:12px;font-weight:500;color:var(--primary-foreground)">Badge</span></div>"#, r#"import { Badge } from "@/components/ui/badge""#, "<Badge>{Label}</Badge>", "ui.label(egui::RichText::new({Label}).small().strong());"),
c("Badge / Secondary", r#"<div layer-name="Badge / Secondary" style="display:flex;align-items:center;padding:4px 8px;border-radius:6px;background:var(--secondary)"><span layer-name="Label" style="font-size:12px;font-weight:500;color:var(--secondary-foreground)">Draft</span></div>"#, r#"import { Badge } from "@/components/ui/badge""#, r#"<Badge variant="secondary">{Label}</Badge>"#, ""),
c("Badge / Outline", r#"<div layer-name="Badge / Outline" style="display:flex;align-items:center;padding:4px 8px;border-radius:6px;border:1px solid var(--border)"><span layer-name="Label" style="font-size:12px;font-weight:500;color:var(--foreground)">Outline</span></div>"#, r#"import { Badge } from "@/components/ui/badge""#, r#"<Badge variant="outline">{Label}</Badge>"#, ""),
c("Badge / Destructive", r#"<div layer-name="Badge / Destructive" style="display:flex;align-items:center;padding:4px 8px;border-radius:6px;background:var(--destructive)"><span layer-name="Label" style="font-size:12px;font-weight:500;color:var(--destructive-foreground)">Failed</span></div>"#, r#"import { Badge } from "@/components/ui/badge""#, r#"<Badge variant="destructive">{Label}</Badge>"#, ""),
c("Avatar", r#"<div layer-name="Avatar" style="display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:16px;background:var(--muted)"><span layer-name="Initials" style="font-size:13px;font-weight:500;color:var(--foreground)">EV</span></div>"#, r#"import { Avatar, AvatarFallback } from "@/components/ui/avatar""#, "<Avatar>\n <AvatarFallback>{Initials}</AvatarFallback>\n</Avatar>", ""),
c("Card", r#"<div layer-name="Card" style="display:flex;flex-direction:column;gap:4px;width:320px;padding:24px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--card)"><span layer-name="Title" style="font-size:16px;font-weight:600;color:var(--foreground)">Card title</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground)">A short description of what this card holds.</span></div>"#, CARD, "<Card>\n <CardHeader>\n <CardTitle>{Title}</CardTitle>\n <CardDescription>{Description}</CardDescription>\n </CardHeader>\n</Card>", "egui::Frame::group(ui.style()).show(ui, |ui| {\n ui.strong({Title});\n ui.label({Description});\n});"),
c("Stat Card", r#"<div layer-name="Stat Card" style="display:flex;flex-direction:column;gap:4px;width:240px;padding:20px 24px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--card)"><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--muted-foreground)">Total revenue</span><span layer-name="Value" style="font-size:24px;font-weight:700;color:var(--foreground)">$45,231.89</span><span layer-name="Delta" style="font-size:12px;color:var(--muted-foreground)">+20.1% from last month</span></div>"#, CARD, "<Card>\n <CardHeader>\n <CardDescription>{Label}</CardDescription>\n <CardTitle className=\"text-2xl\">{Value}</CardTitle>\n </CardHeader>\n <CardContent className=\"text-xs text-muted-foreground\">{Delta}</CardContent>\n</Card>", ""),
c("Alert", r#"<div layer-name="Alert" style="display:flex;flex-direction:column;gap:4px;width:360px;padding:12px 16px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--background)"><span layer-name="Title" style="font-size:14px;font-weight:500;color:var(--foreground)">Heads up</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground)">You can add components to your app using the CLI.</span></div>"#, r#"import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert""#, "<Alert>\n <AlertTitle>{Title}</AlertTitle>\n <AlertDescription>{Description}</AlertDescription>\n</Alert>", "ui.group(|ui| {\n ui.strong({Title});\n ui.label({Description});\n});"),
c("Alert / Destructive", r#"<div layer-name="Alert / Destructive" style="display:flex;flex-direction:column;gap:4px;width:360px;padding:12px 16px;border-radius:var(--radius-card);border:1px solid var(--destructive);background:var(--background)"><span layer-name="Title" style="font-size:14px;font-weight:500;color:var(--destructive)">Error</span><span layer-name="Description" style="font-size:14px;color:var(--destructive)">Your session has expired. Sign in again.</span></div>"#, r#"import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert""#, "<Alert variant=\"destructive\">\n <AlertTitle>{Title}</AlertTitle>\n <AlertDescription>{Description}</AlertDescription>\n</Alert>", ""),
c("Progress", r#"<div layer-name="Progress" style="display:flex;width:240px;height:8px;border-radius:4px;background:var(--muted)"><div layer-name="Indicator" style="width:144px;height:8px;border-radius:4px;background:var(--primary)"></div></div>"#, r#"import { Progress } from "@/components/ui/progress""#, "<Progress value={60} />", "ui.add(egui::ProgressBar::new(0.6));"),
c("Skeleton", r#"<div layer-name="Skeleton" style="display:flex;align-items:center;gap:12px"><div layer-name="Circle" style="width:40px;height:40px;border-radius:20px;background:var(--muted)"></div><div layer-name="Lines" style="display:flex;flex-direction:column;gap:8px"><div layer-name="Line 1" style="width:200px;height:12px;border-radius:6px;background:var(--muted)"></div><div layer-name="Line 2" style="width:150px;height:12px;border-radius:6px;background:var(--muted)"></div></div></div>"#, r#"import { Skeleton } from "@/components/ui/skeleton""#, "<div className=\"flex items-center gap-3\">\n <Skeleton className=\"size-10 rounded-full\" />\n <div className=\"grid gap-2\">\n <Skeleton className=\"h-3 w-[200px]\" />\n <Skeleton className=\"h-3 w-[150px]\" />\n </div>\n</div>", "ui.spinner();"),
c("Separator", r#"<div layer-name="Separator" style="width:320px;height:1px;background:var(--border)"></div>"#, r#"import { Separator } from "@/components/ui/separator""#, "<Separator />", "ui.separator();"),
c("Empty State", r#"<div layer-name="Empty State" style="display:flex;flex-direction:column;align-items:center;gap:8px;width:400px;padding:40px 24px;border-radius:var(--radius-card);border:1px dashed var(--border)"><svg layer-name="Icon / inbox" width="32" height="32" viewBox="0 0 24 24" style="color:var(--muted-foreground)"><path d="M22 12h-6l-2 3h-4l-2-3H2M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" stroke="currentColor" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Title" style="font-size:16px;font-weight:600;color:var(--foreground)">No projects yet</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground);text-align:center">Create your first project to get started.</span></div>"#, "", "", ""),
// ---- overlays
c("Dropdown Menu", r#"<div layer-name="Dropdown Menu" style="display:flex;flex-direction:column;width:200px;padding:4px;border-radius:var(--radius-control);border:1px solid var(--border);background:var(--popover)"><span layer-name="Label" style="padding:8px;font-size:14px;font-weight:600;color:var(--foreground)">My account</span><div layer-name="Separator" style="height:1px;background:var(--border)"></div><div layer-name="Item / Active" style="display:flex;padding:8px;border-radius:6px;background:var(--accent)"><span layer-name="Item 1" style="font-size:14px;color:var(--accent-foreground)">Profile</span></div><div layer-name="Item 2 Row" style="display:flex;padding:8px"><span layer-name="Item 2" style="font-size:14px;color:var(--foreground)">Settings</span></div><div layer-name="Item 3 Row" style="display:flex;padding:8px"><span layer-name="Item 3" style="font-size:14px;color:var(--foreground)">Sign out</span></div></div>"#, r#"import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "@/components/ui/dropdown-menu""#, "<DropdownMenu>\n <DropdownMenuTrigger asChild><Button variant=\"outline\">Open</Button></DropdownMenuTrigger>\n <DropdownMenuContent className=\"w-[200px]\">\n <DropdownMenuLabel>{Label}</DropdownMenuLabel>\n <DropdownMenuSeparator />\n <DropdownMenuItem>{Item 1}</DropdownMenuItem>\n <DropdownMenuItem>{Item 2}</DropdownMenuItem>\n <DropdownMenuItem>{Item 3}</DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>", "ui.menu_button(\"Open\", |ui| {\n if ui.button({Item 1}).clicked() {}\n});"),
c("Tooltip", r#"<div layer-name="Tooltip" style="display:flex;padding:4px 12px;border-radius:6px;background:var(--primary)"><span layer-name="Text" style="font-size:12px;color:var(--primary-foreground)">Add to library</span></div>"#, r#"import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip""#, "<Tooltip>\n <TooltipTrigger asChild><Button variant=\"outline\">Hover</Button></TooltipTrigger>\n <TooltipContent>{Text}</TooltipContent>\n</Tooltip>", "response.on_hover_text({Text});"),
c("Toast", r#"<div layer-name="Toast" style="display:flex;align-items:center;gap:16px;width:360px;padding:16px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--popover)"><div layer-name="Text" style="display:flex;flex-direction:column;gap:4px;flex-grow:1"><span layer-name="Title" style="font-size:14px;font-weight:600;color:var(--foreground)">Event created</span><span layer-name="Description" style="font-size:13px;color:var(--muted-foreground)">Sunday, December 3 at 9:00 AM</span></div><div layer-name="Action Button" style="display:flex;align-items:center;height:28px;padding:0 12px;border-radius:6px;background:var(--primary)"><span layer-name="Action" style="font-size:12px;font-weight:500;color:var(--primary-foreground)">Undo</span></div></div>"#, r#"import { toast } from "sonner""#, "toast(\"{Title}\", {\n description: \"{Description}\",\n action: { label: \"{Action}\", onClick: () => {} },\n})", ""),
c("Dialog", r#"<div layer-name="Dialog" style="display:flex;flex-direction:column;gap:16px;width:440px;padding:24px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--background)"><div layer-name="Header" style="display:flex;flex-direction:column;gap:4px"><span layer-name="Title" style="font-size:18px;font-weight:600;color:var(--foreground)">Delete project?</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground)">This removes the project and its files. It cannot be undone.</span></div><div layer-name="Actions" style="display:flex;justify-content:flex-end;gap:8px"><x-paper-instance component="Button / Outline" overrides='{"Label":"Cancel"}'/><x-paper-instance component="Button / Destructive"/></div></div>"#, r#"import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Button } from "@/components/ui/button""#, "<Dialog open>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>{Title}</DialogTitle>\n <DialogDescription>{Description}</DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button variant=\"outline\">{Cancel}</Button>\n <Button variant=\"destructive\">{Confirm}</Button>\n </DialogFooter>\n </DialogContent>\n</Dialog>", "egui::Window::new({Title}).collapsible(false).show(ctx, |ui| {\n ui.label({Description});\n});"),
c("Sheet", r#"<div layer-name="Sheet" style="display:flex;flex-direction:column;gap:16px;width:400px;height:640px;padding:24px;border-left:1px solid var(--border);background:var(--background)"><div layer-name="Header" style="display:flex;flex-direction:column;gap:4px"><span layer-name="Title" style="font-size:18px;font-weight:600;color:var(--foreground)">Edit profile</span><span layer-name="Description" style="font-size:14px;color:var(--muted-foreground)">Make changes to your profile here.</span></div></div>"#, r#"import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet""#, "<Sheet open>\n <SheetContent>\n <SheetHeader>\n <SheetTitle>{Title}</SheetTitle>\n <SheetDescription>{Description}</SheetDescription>\n </SheetHeader>\n </SheetContent>\n</Sheet>", "egui::SidePanel::right(\"sheet\").show(ctx, |ui| {\n ui.heading({Title});\n});"),
// ---- navigation
c("Tabs", r#"<div layer-name="Tabs" style="display:flex;align-items:center;gap:4px;padding:4px;border-radius:var(--radius-control);background:var(--muted)"><div layer-name="Tab 1 Pill" style="display:flex;align-items:center;height:28px;padding:0 12px;border-radius:6px;background:var(--background)"><span layer-name="Tab 1" style="font-size:14px;font-weight:500;color:var(--foreground)">Account</span></div><div layer-name="Tab 2 Pill" style="display:flex;align-items:center;height:28px;padding:0 12px"><span layer-name="Tab 2" style="font-size:14px;font-weight:500;color:var(--foreground)">Password</span></div><div layer-name="Tab 3 Pill" style="display:flex;align-items:center;height:28px;padding:0 12px"><span layer-name="Tab 3" style="font-size:14px;font-weight:500;color:var(--foreground)">Billing</span></div></div>"#, r#"import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs""#, "<Tabs defaultValue=\"one\">\n <TabsList>\n <TabsTrigger value=\"one\">{Tab 1}</TabsTrigger>\n <TabsTrigger value=\"two\">{Tab 2}</TabsTrigger>\n <TabsTrigger value=\"three\">{Tab 3}</TabsTrigger>\n </TabsList>\n</Tabs>", "ui.horizontal(|ui| {\n ui.selectable_value(&mut tab, 0, {Tab 1});\n ui.selectable_value(&mut tab, 1, {Tab 2});\n});"),
c("Breadcrumb", r#"<div layer-name="Breadcrumb" style="display:flex;align-items:center;gap:8px"><span layer-name="Item 1" style="font-size:14px;color:var(--muted-foreground)">Home</span><svg layer-name="Icon / chevron-right" width="14" height="14" viewBox="0 0 24 24" style="color:var(--muted-foreground)"><path d="m9 18 6-6-6-6" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Item 2" style="font-size:14px;color:var(--muted-foreground)">Projects</span><svg layer-name="Icon / chevron-right" width="14" height="14" viewBox="0 0 24 24" style="color:var(--muted-foreground)"><path d="m9 18 6-6-6-6" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Current" style="font-size:14px;color:var(--foreground)">Settings</span></div>"#, r#"import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "@/components/ui/breadcrumb""#, "<Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem><BreadcrumbLink href=\"/\">{Item 1}</BreadcrumbLink></BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem><BreadcrumbLink href=\"#\">{Item 2}</BreadcrumbLink></BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem><BreadcrumbPage>{Current}</BreadcrumbPage></BreadcrumbItem>\n </BreadcrumbList>\n</Breadcrumb>", ""),
c("Pagination", r#"<div layer-name="Pagination" style="display:flex;align-items:center;gap:4px"><x-paper-instance component="Button / Ghost" overrides='{"Label":"Previous"}'/><x-paper-instance component="Button / Outline" style="width:var(--control-height);padding:0px" overrides='{"Label":"1"}'/><x-paper-instance component="Button / Ghost" style="width:var(--control-height);padding:0px" overrides='{"Label":"2"}'/><x-paper-instance component="Button / Ghost" style="width:var(--control-height);padding:0px" overrides='{"Label":"3"}'/><x-paper-instance component="Button / Ghost" overrides='{"Label":"Next"}'/></div>"#, r#"import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious } from "@/components/ui/pagination""#, "<Pagination>\n <PaginationContent>\n <PaginationItem><PaginationPrevious href=\"#\" /></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\" isActive>1</PaginationLink></PaginationItem>\n <PaginationItem><PaginationLink href=\"#\">2</PaginationLink></PaginationItem>\n <PaginationItem><PaginationNext href=\"#\" /></PaginationItem>\n </PaginationContent>\n</Pagination>", ""),
c("Sidebar / Item", r#"<div layer-name="Sidebar / Item" style="display:flex;align-items:center;gap:8px;width:224px;height:32px;padding:0 8px;border-radius:6px"><svg layer-name="Icon / house" width="16" height="16" viewBox="0 0 24 24" style="color:var(--muted-foreground)"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Label" style="font-size:14px;color:var(--foreground)">Dashboard</span></div>"#, SIDEBAR, "<SidebarMenuItem>\n <SidebarMenuButton>{Label}</SidebarMenuButton>\n</SidebarMenuItem>", "ui.selectable_label(false, {Label});"),
c("Sidebar / Item Active", r#"<div layer-name="Sidebar / Item Active" style="display:flex;align-items:center;gap:8px;width:224px;height:32px;padding:0 8px;border-radius:6px;background:var(--accent)"><svg layer-name="Icon / house" width="16" height="16" viewBox="0 0 24 24" style="color:var(--accent-foreground)"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg><span layer-name="Label" style="font-size:14px;font-weight:500;color:var(--accent-foreground)">Dashboard</span></div>"#, SIDEBAR, "<SidebarMenuItem>\n <SidebarMenuButton isActive>{Label}</SidebarMenuButton>\n</SidebarMenuItem>", "ui.selectable_label(true, {Label});"),
// ---- data
c("Table / Header", r#"<div layer-name="Table / Header" style="display:flex;align-items:center;width:720px;height:40px;padding:0 8px;border-bottom:1px solid var(--border)"><span layer-name="Col 1" style="flex-grow:1;font-size:14px;font-weight:500;color:var(--muted-foreground)">Name</span><span layer-name="Col 2" style="width:140px;font-size:14px;font-weight:500;color:var(--muted-foreground)">Status</span><span layer-name="Col 3" style="width:180px;font-size:14px;font-weight:500;color:var(--muted-foreground)">Email</span><span layer-name="Col 4" style="width:100px;font-size:14px;font-weight:500;color:var(--muted-foreground);text-align:right">Amount</span></div>"#, TABLE, "<TableHeader>\n <TableRow>\n <TableHead>{Col 1}</TableHead>\n <TableHead>{Col 2}</TableHead>\n <TableHead>{Col 3}</TableHead>\n <TableHead className=\"text-right\">{Col 4}</TableHead>\n </TableRow>\n</TableHeader>", ""),
c("Table / Row", r#"<div layer-name="Table / Row" style="display:flex;align-items:center;width:720px;height:48px;padding:0 8px;border-bottom:1px solid var(--border)"><span layer-name="Cell 1" style="flex-grow:1;font-size:14px;font-weight:500;color:var(--foreground)">Elena Vasquez</span><span layer-name="Cell 2" style="width:140px;font-size:14px;color:var(--foreground)">Active</span><span layer-name="Cell 3" style="width:180px;font-size:14px;color:var(--muted-foreground)">elena@example.com</span><span layer-name="Cell 4" style="width:100px;font-size:14px;color:var(--foreground);text-align:right">$316.00</span></div>"#, TABLE, "<TableRow>\n <TableCell className=\"font-medium\">{Cell 1}</TableCell>\n <TableCell>{Cell 2}</TableCell>\n <TableCell>{Cell 3}</TableCell>\n <TableCell className=\"text-right\">{Cell 4}</TableCell>\n</TableRow>", ""),
c("List Item", r#"<div layer-name="List Item" style="display:flex;align-items:center;gap:12px;width:400px;padding:12px 0"><div layer-name="Avatar" style="display:flex;align-items:center;justify-content:center;width:var(--control-height);height:var(--control-height);border-radius:18px;background:var(--muted)"><span layer-name="Initials" style="font-size:13px;font-weight:500;color:var(--foreground)">OM</span></div><div layer-name="Text" style="display:flex;flex-direction:column;flex-grow:1"><span layer-name="Title" style="font-size:14px;font-weight:500;color:var(--foreground)">Olivia Martin</span><span layer-name="Subtitle" style="font-size:13px;color:var(--muted-foreground)">olivia.martin@email.com</span></div><span layer-name="Meta" style="font-size:14px;font-weight:500;color:var(--foreground)">+$1,999.00</span></div>"#, "", "", ""),
];
/// A sidebar of kit items for the layouts.
macro_rules! sidebar {
() => {
r#"<div layer-name="Sidebar" style="display:flex;flex-direction:column;gap:4px;width:256px;padding:16px;border-right:1px solid var(--border);background:var(--card)"><span layer-name="Product" style="padding:8px;font-size:16px;font-weight:700;color:var(--foreground)">Acme</span><x-paper-instance component="Sidebar / Item Active"/><x-paper-instance component="Sidebar / Item" overrides='{"Label":"Projects"}'/><x-paper-instance component="Sidebar / Item" overrides='{"Label":"Team"}'/><x-paper-instance component="Sidebar / Item" overrides='{"Label":"Settings"}'/></div>"#
};
}
/// Top bar: breadcrumb, search, account.
macro_rules! topbar {
() => {
r#"<div layer-name="Top Bar" style="display:flex;align-items:center;gap:12px;height:56px;padding:0 24px;border-bottom:1px solid var(--border)"><x-paper-instance component="Breadcrumb"/><div layer-name="Spacer" style="flex-grow:1"></div><x-paper-instance component="Search"/><x-paper-instance component="Avatar"/></div>"#
};
}
pub const LAYOUTS: &[(&str, &str)] = &[
("Layout / Dashboard", concat!(
r#"<div layer-name="Layout / Dashboard" style="display:flex;width:1440px;height:900px;background:var(--background)">"#, sidebar!(),
r#"<div layer-name="Main" style="display:flex;flex-direction:column;flex-grow:1">"#, topbar!(),
r#"<div layer-name="Content" style="display:flex;flex-direction:column;gap:24px;padding:32px"><div layer-name="Header" style="display:flex;align-items:center"><span layer-name="Title" style="flex-grow:1;font-size:30px;font-weight:700;color:var(--foreground)">Dashboard</span><x-paper-instance component="Button / With Icon"/></div>"#,
r#"<div layer-name="Stats" style="display:flex;gap:16px"><x-paper-instance component="Stat Card" style="flex-grow:1"/><x-paper-instance component="Stat Card" style="flex-grow:1" overrides='{"Label":"Subscriptions","Value":"+2,350","Delta":"+180.1% from last month"}'/><x-paper-instance component="Stat Card" style="flex-grow:1" overrides='{"Label":"Sales","Value":"+12,234","Delta":"+19% from last month"}'/><x-paper-instance component="Stat Card" style="flex-grow:1" overrides='{"Label":"Active now","Value":"+573","Delta":"+201 since last hour"}'/></div>"#,
r#"<div layer-name="Recent" style="display:flex;flex-direction:column;padding:24px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--card)"><span layer-name="Title" style="font-size:16px;font-weight:600;color:var(--foreground)">Recent sales</span><x-paper-instance component="List Item" style="width:100%"/><x-paper-instance component="List Item" style="width:100%" overrides='{"Initials":"JL","Title":"Jackson Lee","Subtitle":"jackson.lee@email.com","Meta":"+$39.00"}'/><x-paper-instance component="List Item" style="width:100%" overrides='{"Initials":"IN","Title":"Isabella Nguyen","Subtitle":"isabella.nguyen@email.com","Meta":"+$299.00"}'/></div></div></div></div>"#
)),
("Layout / Table", concat!(
r#"<div layer-name="Layout / Table" style="display:flex;width:1440px;height:900px;background:var(--background)">"#, sidebar!(),
r#"<div layer-name="Main" style="display:flex;flex-direction:column;flex-grow:1">"#, topbar!(),
r#"<div layer-name="Content" style="display:flex;flex-direction:column;gap:16px;padding:32px"><div layer-name="Header" style="display:flex;align-items:center;gap:8px"><span layer-name="Title" style="flex-grow:1;font-size:30px;font-weight:700;color:var(--foreground)">Customers</span><x-paper-instance component="Button / Outline" overrides='{"Label":"Export"}'/><x-paper-instance component="Button / With Icon" overrides='{"Label":"Add customer"}'/></div><x-paper-instance component="Tabs" overrides='{"Tab 1":"All","Tab 2":"Active","Tab 3":"Archived"}'/>"#,
r#"<div layer-name="Table" style="display:flex;flex-direction:column;padding:0 8px;border-radius:var(--radius-card);border:1px solid var(--border)"><x-paper-instance component="Table / Header" style="width:100%"/><x-paper-instance component="Table / Row" style="width:100%"/><x-paper-instance component="Table / Row" style="width:100%" overrides='{"Cell 1":"Jackson Lee","Cell 2":"Invited","Cell 3":"jackson@example.com","Cell 4":"$84.00"}'/><x-paper-instance component="Table / Row" style="width:100%" overrides='{"Cell 1":"Isabella Nguyen","Cell 2":"Active","Cell 3":"isabella@example.com","Cell 4":"$1,240.00"}'/><x-paper-instance component="Table / Row" style="width:100%" overrides='{"Cell 1":"William Kim","Cell 2":"Paused","Cell 3":"will@example.com","Cell 4":"$99.00"}'/></div>"#,
r#"<div layer-name="Footer" style="display:flex;align-items:center"><span layer-name="Count" style="flex-grow:1;font-size:14px;color:var(--muted-foreground)">Showing 1–4 of 48</span><x-paper-instance component="Pagination"/></div></div></div></div>"#
)),
("Layout / Settings", concat!(
r#"<div layer-name="Layout / Settings" style="display:flex;width:1440px;height:900px;background:var(--background)">"#, sidebar!(),
r#"<div layer-name="Main" style="display:flex;flex-direction:column;flex-grow:1">"#, topbar!(),
r#"<div layer-name="Content" style="display:flex;flex-direction:column;gap:24px;padding:32px;width:720px"><div layer-name="Heading" style="display:flex;flex-direction:column;gap:4px"><span layer-name="Title" style="font-size:30px;font-weight:700;color:var(--foreground)">Settings</span><span layer-name="Subtitle" style="font-size:14px;color:var(--muted-foreground)">Manage your account settings and preferences.</span></div><x-paper-instance component="Separator" style="width:100%"/>"#,
r#"<x-paper-instance component="Form Field"/><x-paper-instance component="Form Field" overrides='{"Label":"Email","Value":"m@example.com","Description":"Shown on invoices and receipts."}'/><x-paper-instance component="Textarea" overrides='{"Placeholder":"Tell us a little about yourself"}'/><div layer-name="Notifications" style="display:flex;align-items:center;gap:12px"><x-paper-instance component="Switch"/><span layer-name="Label" style="font-size:14px;color:var(--foreground)">Email me about account activity</span></div><x-paper-instance component="Button / Default" overrides='{"Label":"Update profile"}'/></div></div></div>"#
)),
("Layout / Sign In", concat!(
r#"<div layer-name="Layout / Sign In" style="display:flex;align-items:center;justify-content:center;width:1440px;height:900px;background:var(--muted)">"#,
r#"<div layer-name="Card" style="display:flex;flex-direction:column;gap:20px;width:400px;padding:32px;border-radius:var(--radius-card);border:1px solid var(--border);background:var(--card)"><div layer-name="Heading" style="display:flex;flex-direction:column;gap:4px"><span layer-name="Title" style="font-size:24px;font-weight:700;color:var(--foreground)">Sign in</span><span layer-name="Subtitle" style="font-size:14px;color:var(--muted-foreground)">Enter your email below to sign in to your account.</span></div>"#,
r#"<x-paper-instance component="Form Field" style="width:100%" overrides='{"Label":"Email","Value":"m@example.com","Description":"We never share it."}'/><x-paper-instance component="Form Field" style="width:100%" overrides='{"Label":"Password","Value":"••••••••","Description":"At least 8 characters."}'/><x-paper-instance component="Checkbox / Off"/><x-paper-instance component="Button / Default" style="width:100%" overrides='{"Label":"Sign in"}'/><x-paper-instance component="Button / Outline" style="width:100%" overrides='{"Label":"Continue with Google"}'/></div></div>"#
)),
("Layout / Dialog", concat!(
r#"<div layer-name="Layout / Dialog" style="display:flex;width:1440px;height:900px;background:var(--background)">"#, sidebar!(),
r#"<div layer-name="Main" style="display:flex;flex-direction:column;flex-grow:1">"#, topbar!(),
r#"<div layer-name="Content" style="display:flex;flex-direction:column;gap:24px;padding:32px"><x-paper-instance component="Empty State" style="width:100%"/></div></div>"#,
r#"<div layer-name="Overlay" style="position:absolute;left:0px;top:0px;display:flex;align-items:center;justify-content:center;width:1440px;height:900px;background:var(--overlay)"><x-paper-instance component="Dialog"/></div></div>"#
)),
];