@microlight/core 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/app/layout.js +2 -18
- package/dist/server/app/library/[[...f_path]]/ViewFolder.js +37 -66
- package/dist/server/app/monitoring/MonitoringDashboard.js +167 -215
- package/dist/server/app/tasks/[slug]/ViewTask.js +81 -173
- package/dist/server/app/tasks/[slug]/runs/[r_id]/ViewRun.js +68 -164
- package/dist/server/app/tasks/[slug]/runs/[r_id]/_components/DropdownActions/DropdownActions.js +19 -25
- package/dist/server/components/Link.js +6 -15
- package/dist/server/components/MLInput.js +19 -22
- package/dist/server/components/Navbar/Navbar.js +14 -51
- package/dist/server/components/Navbar/NavbarContainer.js +7 -20
- package/dist/server/components/PageHeader.js +22 -54
- package/dist/server/components/StatusChip.js +5 -5
- package/dist/server/components/ui/alert.js +61 -0
- package/dist/server/components/ui/badge.js +37 -0
- package/dist/server/components/ui/breadcrumb.js +82 -0
- package/dist/server/components/ui/button.js +65 -0
- package/dist/server/components/ui/card.js +81 -0
- package/dist/server/components/ui/dropdown-menu.js +222 -0
- package/dist/server/components/ui/input.js +21 -0
- package/dist/server/components/ui/label.js +20 -0
- package/dist/server/components/ui/select.js +165 -0
- package/dist/server/components/ui/stack.js +104 -0
- package/dist/server/components/ui/table.js +77 -0
- package/dist/server/components/ui/tabs.js +59 -0
- package/dist/server/components/ui/typography.js +229 -0
- package/dist/server/utils/css/cn.js +11 -0
- package/package.json +15 -3
- package/dist/server/components/Icon.js +0 -22
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Link as JoyLink } from '@mui/joy';
|
|
5
4
|
import NextLink from 'next/link';
|
|
6
5
|
import PropTypes from 'prop-types';
|
|
6
|
+
import { cn } from "../utils/css/cn";
|
|
7
7
|
export default function Link({
|
|
8
8
|
href = '/',
|
|
9
|
-
|
|
9
|
+
className = '',
|
|
10
10
|
target = '_self',
|
|
11
11
|
onClick = () => {},
|
|
12
12
|
children,
|
|
@@ -18,33 +18,24 @@ export default function Link({
|
|
|
18
18
|
if (onClick) {
|
|
19
19
|
onClick(e);
|
|
20
20
|
}
|
|
21
|
-
}}
|
|
21
|
+
}} className={className}>
|
|
22
22
|
{children}
|
|
23
23
|
</Component>
|
|
24
24
|
</NextLink>;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
backgroundColor: "transparent",
|
|
28
|
-
"&:hover": {
|
|
29
|
-
backgroundColor: "none"
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
return <JoyLink color="neutral" component={NextLink} underline="none" target={target} href={href} sx={{
|
|
33
|
-
...defaultSx,
|
|
34
|
-
...sx
|
|
35
|
-
}} onClick={e => {
|
|
26
|
+
return <NextLink href={href} target={target} className={cn("text-foreground no-underline bg-transparent hover:bg-transparent", className)} onClick={e => {
|
|
36
27
|
if (onClick) {
|
|
37
28
|
onClick(e);
|
|
38
29
|
}
|
|
39
30
|
}}>
|
|
40
31
|
{children}
|
|
41
|
-
</
|
|
32
|
+
</NextLink>;
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
// For Storybook Documentation
|
|
45
36
|
Link.propTypes = {
|
|
46
37
|
children: PropTypes.node.isRequired,
|
|
47
|
-
|
|
38
|
+
className: PropTypes.string,
|
|
48
39
|
onClick: PropTypes.func,
|
|
49
40
|
Component: PropTypes.elementType,
|
|
50
41
|
href: PropTypes.string.isRequired,
|
|
@@ -1,29 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Label } from "./ui/label";
|
|
2
|
+
import { Input } from "./ui/input";
|
|
3
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
|
2
4
|
export default function MLInput({
|
|
3
5
|
def,
|
|
4
6
|
slug,
|
|
5
7
|
searchParams
|
|
6
8
|
}) {
|
|
7
|
-
return
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
}}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{option.label || option.value}
|
|
23
|
-
</Option>)}
|
|
9
|
+
return <div className="mb-4">
|
|
10
|
+
<Label className="mb-1 block">
|
|
11
|
+
{def.name}:{def.required && <span className="text-destructive ml-1">*</span>}
|
|
12
|
+
</Label>
|
|
13
|
+
{['string', 'number', 'date'].includes(def.type) && <Input size="sm" name={slug} placeholder={def.placeholder} defaultValue={searchParams[slug] || def.default || ""} type={def.type === 'string' ? 'text' : def.type} required={def.required} />}
|
|
14
|
+
{def.type === 'file' && <Input size="sm" name={slug} placeholder={def.placeholder} type="file" required={def.required} />}
|
|
15
|
+
{def.type === 'dropdown' && <Select name={slug} defaultValue={searchParams[slug] || def.default || ""} required={def.required}>
|
|
16
|
+
<SelectTrigger size="sm">
|
|
17
|
+
<SelectValue placeholder={def.placeholder} />
|
|
18
|
+
</SelectTrigger>
|
|
19
|
+
<SelectContent>
|
|
20
|
+
{def.options?.map(option => <SelectItem key={option.value} value={option.value}>
|
|
21
|
+
{option.label || option.value}
|
|
22
|
+
</SelectItem>)}
|
|
23
|
+
</SelectContent>
|
|
24
24
|
</Select>}
|
|
25
|
-
</
|
|
26
|
-
{/* string input - {slug} <br/> */}
|
|
27
|
-
{/* <pre>{JSON.stringify(def,null,2)}</pre> */}
|
|
28
|
-
</>;
|
|
25
|
+
</div>;
|
|
29
26
|
}
|
|
@@ -2,68 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
import Link from 'next/link';
|
|
4
4
|
import { usePathname } from 'next/navigation';
|
|
5
|
-
|
|
6
|
-
import { Box, Sheet, Button } from '@mui/joy';
|
|
5
|
+
import { Button } from "../ui/button";
|
|
7
6
|
export default function Navbar({
|
|
8
7
|
user,
|
|
9
8
|
signOut
|
|
10
9
|
}) {
|
|
11
10
|
const pathname = usePathname();
|
|
12
|
-
return <
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
alignItems: 'center',
|
|
17
|
-
justifyContent: 'space-between',
|
|
18
|
-
// boxShadow: 'sm',
|
|
19
|
-
borderBottom: '1px solid',
|
|
20
|
-
borderColor: 'divider',
|
|
21
|
-
position: 'fixed',
|
|
22
|
-
top: 0,
|
|
23
|
-
left: 0,
|
|
24
|
-
right: 0,
|
|
25
|
-
height: '40px',
|
|
26
|
-
zIndex: 1000,
|
|
27
|
-
bgcolor: 'background.surface'
|
|
28
|
-
// bgcolor: 'white',
|
|
29
|
-
}}>
|
|
30
|
-
<Box sx={{
|
|
31
|
-
display: 'flex',
|
|
32
|
-
alignItems: 'center',
|
|
33
|
-
gap: 2
|
|
34
|
-
}}>
|
|
35
|
-
{/* <Logo offering='Transactions' /> */}
|
|
36
|
-
<Link href="/" style={{
|
|
37
|
-
textDecoration: 'none',
|
|
38
|
-
color: 'inherit'
|
|
39
|
-
}}>
|
|
40
|
-
<Box sx={{
|
|
41
|
-
fontWeight: 'bold'
|
|
42
|
-
}}>Microlight</Box>
|
|
11
|
+
return <nav className="px-2 flex items-center justify-between border-b border-border fixed top-0 left-0 right-0 h-10 z-[1000] bg-background">
|
|
12
|
+
<div className="flex items-center gap-4">
|
|
13
|
+
<Link href="/" className="no-underline text-inherit">
|
|
14
|
+
<span className="font-bold">Microlight</span>
|
|
43
15
|
</Link>
|
|
44
16
|
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}}>
|
|
49
|
-
<Link href="/library" style={{
|
|
50
|
-
textDecoration: 'none'
|
|
51
|
-
}}>
|
|
52
|
-
<Button variant={pathname?.startsWith('/library') ? 'solid' : 'plain'} size="sm" color="neutral">
|
|
17
|
+
<div className="flex gap-2">
|
|
18
|
+
<Link href="/library" className="no-underline">
|
|
19
|
+
<Button variant={pathname?.startsWith('/library') ? 'default' : 'ghost'} size="sm">
|
|
53
20
|
Library
|
|
54
21
|
</Button>
|
|
55
22
|
</Link>
|
|
56
23
|
|
|
57
|
-
<Link href="/monitoring"
|
|
58
|
-
|
|
59
|
-
}}>
|
|
60
|
-
<Button variant={pathname?.startsWith('/monitoring') ? 'solid' : 'plain'} size="sm" color="neutral">
|
|
24
|
+
<Link href="/monitoring" className="no-underline">
|
|
25
|
+
<Button variant={pathname?.startsWith('/monitoring') ? 'default' : 'ghost'} size="sm">
|
|
61
26
|
Monitoring
|
|
62
27
|
</Button>
|
|
63
28
|
</Link>
|
|
64
|
-
</
|
|
65
|
-
</
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
;
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</nav>;
|
|
32
|
+
}
|
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
import Navbar from "./Navbar";
|
|
2
|
-
import { Box } from "@mui/joy";
|
|
3
2
|
export default function NavbarContainer({
|
|
4
3
|
children
|
|
5
4
|
}) {
|
|
6
5
|
return <>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
px: {
|
|
15
|
-
xs: 1,
|
|
16
|
-
sm: 1.5
|
|
17
|
-
},
|
|
18
|
-
pt: 0.5,
|
|
19
|
-
pb: 0,
|
|
20
|
-
minWidth: 0
|
|
21
|
-
}}>
|
|
22
|
-
{children}
|
|
23
|
-
</Box>
|
|
24
|
-
</Box>
|
|
25
|
-
</>;
|
|
6
|
+
<Navbar />
|
|
7
|
+
<main className="pt-10 flex">
|
|
8
|
+
<div className="flex-1 px-2 sm:px-3 pt-1 pb-0 min-w-0">
|
|
9
|
+
{children}
|
|
10
|
+
</div>
|
|
11
|
+
</main>
|
|
12
|
+
</>;
|
|
26
13
|
}
|
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Box, Typography, Breadcrumbs } from '@mui/joy';
|
|
5
|
-
import { Link as JoyLink } from '@mui/joy';
|
|
6
4
|
import NextLink from 'next/link';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const BreadcrumbLink = ({
|
|
10
|
-
href,
|
|
11
|
-
children
|
|
12
|
-
}) => {
|
|
13
|
-
return <JoyLink color="primary" component={NextLink} href={href}>
|
|
14
|
-
{children}
|
|
15
|
-
</JoyLink>;
|
|
16
|
-
};
|
|
5
|
+
import { Typography } from "./ui/typography";
|
|
6
|
+
import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from "./ui/breadcrumb";
|
|
17
7
|
const BreadcrumbsCustom = function ({
|
|
18
8
|
breadcrumbs
|
|
19
9
|
}) {
|
|
20
|
-
return <
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{b.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
return <Breadcrumb>
|
|
11
|
+
<BreadcrumbList>
|
|
12
|
+
{breadcrumbs.map((b, index) => <React.Fragment key={index}>
|
|
13
|
+
{index > 0 && <BreadcrumbSeparator>›</BreadcrumbSeparator>}
|
|
14
|
+
<BreadcrumbItem>
|
|
15
|
+
{b.href ? <BreadcrumbLink asChild>
|
|
16
|
+
<NextLink href={b.href}>{b.text}</NextLink>
|
|
17
|
+
</BreadcrumbLink> : <BreadcrumbPage>{b.text}</BreadcrumbPage>}
|
|
18
|
+
</BreadcrumbItem>
|
|
19
|
+
</React.Fragment>)}
|
|
20
|
+
</BreadcrumbList>
|
|
21
|
+
</Breadcrumb>;
|
|
29
22
|
};
|
|
30
23
|
export default function PageHeader({
|
|
31
24
|
header = "PageHeader",
|
|
@@ -37,51 +30,26 @@ export default function PageHeader({
|
|
|
37
30
|
if (headerLevel) level = headerLevel;
|
|
38
31
|
const renderHeader = () => {
|
|
39
32
|
if (React.isValidElement(header)) {
|
|
40
|
-
return header;
|
|
33
|
+
return header;
|
|
41
34
|
} else if (typeof header === 'string') {
|
|
42
35
|
return <Typography level={level}>{header}</Typography>;
|
|
43
36
|
} else if (typeof header === 'object') {
|
|
44
37
|
const headerParts = Object.values(header);
|
|
45
38
|
return <Typography level={level}>
|
|
46
|
-
{headerParts.length === 1 ? <span>
|
|
47
|
-
{headerParts[0]}
|
|
48
|
-
</span> : headerParts.map((part, index) => <span key={index} style={{
|
|
49
|
-
opacity: index === 1 ? 1 : 0.5
|
|
50
|
-
}}>
|
|
39
|
+
{headerParts.length === 1 ? <span>{headerParts[0]}</span> : headerParts.map((part, index) => <span key={index} className={index === 1 ? 'opacity-100' : 'opacity-50'}>
|
|
51
40
|
{part}{index < headerParts.length - 1 && ' '}
|
|
52
41
|
</span>)}
|
|
53
42
|
</Typography>;
|
|
54
43
|
}
|
|
55
44
|
return null;
|
|
56
45
|
};
|
|
57
|
-
return <
|
|
58
|
-
|
|
59
|
-
flexDirection: {
|
|
60
|
-
xs: 'column',
|
|
61
|
-
sm: 'row'
|
|
62
|
-
},
|
|
63
|
-
alignItems: {
|
|
64
|
-
xs: 'flex-start',
|
|
65
|
-
sm: 'center'
|
|
66
|
-
},
|
|
67
|
-
gap: {
|
|
68
|
-
xs: 1,
|
|
69
|
-
sm: 1
|
|
70
|
-
},
|
|
71
|
-
pt: 0.5
|
|
72
|
-
}}>
|
|
73
|
-
<Box sx={{
|
|
74
|
-
flexGrow: 1
|
|
75
|
-
}}>
|
|
46
|
+
return <div data-cy='page-header' className="flex flex-col sm:flex-row items-start sm:items-center gap-2 pt-1">
|
|
47
|
+
<div className="flex-grow">
|
|
76
48
|
{breadcrumbs && <BreadcrumbsCustom breadcrumbs={breadcrumbs} />}
|
|
77
49
|
{renderHeader()}
|
|
78
|
-
</
|
|
79
|
-
{RightButtons && <
|
|
80
|
-
flexGrow: 0,
|
|
81
|
-
width: 'auto',
|
|
82
|
-
margin: "auto 0"
|
|
83
|
-
}}>
|
|
50
|
+
</div>
|
|
51
|
+
{RightButtons && <div className="flex-shrink-0 my-auto">
|
|
84
52
|
{typeof RightButtons === 'function' ? <RightButtons /> : RightButtons}
|
|
85
|
-
</
|
|
86
|
-
</
|
|
53
|
+
</div>}
|
|
54
|
+
</div>;
|
|
87
55
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
function
|
|
3
|
-
if (status === 'complete') return 'success';else if (status === 'pending') return 'warning';else if (status === 'running') return '
|
|
1
|
+
import { Badge } from "./ui/badge";
|
|
2
|
+
function chipStatusVariant(status) {
|
|
3
|
+
if (status === 'complete') return 'success';else if (status === 'pending') return 'warning';else if (status === 'running') return 'default';else return 'destructive';
|
|
4
4
|
}
|
|
5
5
|
export default function StatusChip({
|
|
6
6
|
status
|
|
7
7
|
}) {
|
|
8
|
-
return <
|
|
8
|
+
return <Badge variant={chipStatusVariant(status)}>
|
|
9
9
|
{status || 'pending'}
|
|
10
|
-
</
|
|
10
|
+
</Badge>;
|
|
11
11
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "../../utils/css/cn";
|
|
5
|
+
const alertVariants = cva("relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", {
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "bg-card text-card-foreground border-border",
|
|
9
|
+
destructive: "text-destructive bg-destructive/10 border-destructive/20 [&>svg]:text-destructive *:data-[slot=alert-description]:text-destructive/90",
|
|
10
|
+
warning: "text-amber-600 bg-amber-50 border-amber-200 [&>svg]:text-amber-600 *:data-[slot=alert-description]:text-amber-600/90 dark:text-amber-400 dark:bg-amber-950/20 dark:border-amber-800/20 dark:[&>svg]:text-amber-400",
|
|
11
|
+
success: "text-green-600 bg-green-50 border-green-200 [&>svg]:text-green-600 *:data-[slot=alert-description]:text-green-600/90 dark:text-green-400 dark:bg-green-950/20 dark:border-green-800/20 dark:[&>svg]:text-green-400"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
variant: "default"
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {{
|
|
21
|
+
* className?: string,
|
|
22
|
+
* variant?: 'default' | 'destructive' | 'warning' | 'success'
|
|
23
|
+
* } & React.HTMLAttributes<HTMLDivElement>} props
|
|
24
|
+
* @returns {React.JSX.Element}
|
|
25
|
+
*/
|
|
26
|
+
function Alert({
|
|
27
|
+
className,
|
|
28
|
+
variant,
|
|
29
|
+
...props
|
|
30
|
+
}) {
|
|
31
|
+
return <div data-slot="alert" role="alert" className={cn(alertVariants({
|
|
32
|
+
variant
|
|
33
|
+
}), className)} {...props} />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {{
|
|
38
|
+
* className?: string
|
|
39
|
+
* } & React.HTMLAttributes<HTMLDivElement>} props
|
|
40
|
+
* @returns {React.JSX.Element}
|
|
41
|
+
*/
|
|
42
|
+
function AlertTitle({
|
|
43
|
+
className,
|
|
44
|
+
...props
|
|
45
|
+
}) {
|
|
46
|
+
return <div data-slot="alert-title" className={cn("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", className)} {...props} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {{
|
|
51
|
+
* className?: string
|
|
52
|
+
* } & React.HTMLAttributes<HTMLDivElement>} props
|
|
53
|
+
* @returns {React.JSX.Element}
|
|
54
|
+
*/
|
|
55
|
+
function AlertDescription({
|
|
56
|
+
className,
|
|
57
|
+
...props
|
|
58
|
+
}) {
|
|
59
|
+
return <div data-slot="alert-description" className={cn("text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed", className)} {...props} />;
|
|
60
|
+
}
|
|
61
|
+
export { Alert, AlertTitle, AlertDescription };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "../../utils/css/cn";
|
|
5
|
+
const badgeVariants = cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", {
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
|
9
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
10
|
+
destructive: "border-transparent bg-red-100 text-red-800 hover:bg-red-200",
|
|
11
|
+
outline: "text-foreground",
|
|
12
|
+
warning: "border-transparent bg-yellow-100 text-yellow-800 hover:bg-yellow-200",
|
|
13
|
+
success: "border-transparent bg-green-100 text-green-800 hover:bg-green-200"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
variant: "default"
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {{
|
|
23
|
+
* className?: string,
|
|
24
|
+
* variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'warning' | 'success'
|
|
25
|
+
* } & React.HTMLAttributes<HTMLDivElement>} props
|
|
26
|
+
* @returns {React.JSX.Element}
|
|
27
|
+
*/
|
|
28
|
+
function Badge({
|
|
29
|
+
className,
|
|
30
|
+
variant,
|
|
31
|
+
...props
|
|
32
|
+
}) {
|
|
33
|
+
return <div className={cn(badgeVariants({
|
|
34
|
+
variant
|
|
35
|
+
}), className)} {...props} />;
|
|
36
|
+
}
|
|
37
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
|
4
|
+
import { cn } from "../../utils/css/cn";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {React.ComponentProps<"nav">} props
|
|
8
|
+
*/
|
|
9
|
+
function Breadcrumb({
|
|
10
|
+
...props
|
|
11
|
+
}) {
|
|
12
|
+
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {React.ComponentProps<"ol">} props
|
|
17
|
+
*/
|
|
18
|
+
function BreadcrumbList({
|
|
19
|
+
className,
|
|
20
|
+
...props
|
|
21
|
+
}) {
|
|
22
|
+
return <ol data-slot="breadcrumb-list" className={cn("text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5", className)} {...props} />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {React.ComponentProps<"li">} props
|
|
27
|
+
*/
|
|
28
|
+
function BreadcrumbItem({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}) {
|
|
32
|
+
return <li data-slot="breadcrumb-item" className={cn("inline-flex items-center gap-1.5", className)} {...props} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {React.ComponentProps<"a"> & { asChild?: boolean }} props
|
|
37
|
+
*/
|
|
38
|
+
function BreadcrumbLink({
|
|
39
|
+
asChild,
|
|
40
|
+
className,
|
|
41
|
+
...props
|
|
42
|
+
}) {
|
|
43
|
+
const Comp = asChild ? Slot : "a";
|
|
44
|
+
return <Comp data-slot="breadcrumb-link" className={cn("hover:text-foreground transition-colors", className)} {...props} />;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {React.ComponentProps<"span">} props
|
|
49
|
+
*/
|
|
50
|
+
function BreadcrumbPage({
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}) {
|
|
54
|
+
return <span data-slot="breadcrumb-page" role="link" aria-disabled="true" aria-current="page" className={cn("text-foreground font-normal", className)} {...props} />;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {React.ComponentProps<"li">} props
|
|
59
|
+
*/
|
|
60
|
+
function BreadcrumbSeparator({
|
|
61
|
+
children,
|
|
62
|
+
className,
|
|
63
|
+
...props
|
|
64
|
+
}) {
|
|
65
|
+
return <li data-slot="breadcrumb-separator" role="presentation" aria-hidden="true" className={cn("[&>svg]:size-3.5", className)} {...props}>
|
|
66
|
+
{children ?? <ChevronRight />}
|
|
67
|
+
</li>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @param {React.ComponentProps<"span">} props
|
|
72
|
+
*/
|
|
73
|
+
function BreadcrumbEllipsis({
|
|
74
|
+
className,
|
|
75
|
+
...props
|
|
76
|
+
}) {
|
|
77
|
+
return <span data-slot="breadcrumb-ellipsis" role="presentation" aria-hidden="true" className={cn("flex size-9 items-center justify-center", className)} {...props}>
|
|
78
|
+
<MoreHorizontal className="size-4" />
|
|
79
|
+
<span className="sr-only">More</span>
|
|
80
|
+
</span>;
|
|
81
|
+
}
|
|
82
|
+
export { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, BreadcrumbEllipsis };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import { cn } from "../../utils/css/cn";
|
|
7
|
+
const buttonVariants = cva("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", {
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
|
11
|
+
destructive: "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
12
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
13
|
+
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
14
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
15
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
19
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
20
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
21
|
+
icon: "size-9"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
size: "default"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {{
|
|
32
|
+
* className?: string,
|
|
33
|
+
* variant?: 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link',
|
|
34
|
+
* size?: 'default' | 'sm' | 'lg' | 'icon',
|
|
35
|
+
* asChild?: boolean,
|
|
36
|
+
* loading?: boolean,
|
|
37
|
+
* disabled?: boolean,
|
|
38
|
+
* children?: React.ReactNode
|
|
39
|
+
* } & React.ButtonHTMLAttributes<HTMLButtonElement>} props
|
|
40
|
+
* @returns {React.JSX.Element}
|
|
41
|
+
*/
|
|
42
|
+
function Button({
|
|
43
|
+
className,
|
|
44
|
+
variant,
|
|
45
|
+
size,
|
|
46
|
+
asChild = false,
|
|
47
|
+
loading = false,
|
|
48
|
+
children,
|
|
49
|
+
disabled,
|
|
50
|
+
...props
|
|
51
|
+
}) {
|
|
52
|
+
const Comp = asChild ? Slot : "button";
|
|
53
|
+
const content = <>
|
|
54
|
+
{loading && <Loader2 className="h-4 w-4 animate-spin" />}
|
|
55
|
+
{children}
|
|
56
|
+
</>;
|
|
57
|
+
return <Comp data-slot="button" className={cn(buttonVariants({
|
|
58
|
+
variant,
|
|
59
|
+
size,
|
|
60
|
+
className
|
|
61
|
+
}))} disabled={disabled || loading} {...props}>
|
|
62
|
+
{asChild ? <span className="inline-flex items-center gap-2">{content}</span> : content}
|
|
63
|
+
</Comp>;
|
|
64
|
+
}
|
|
65
|
+
export { Button, buttonVariants };
|