@exitvibing/hqui 0.1.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/README.md +45 -0
- package/dist/index.css +1 -0
- package/dist/index.d.mts +180 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.js +1049 -0
- package/dist/index.mjs +988 -0
- package/docs/components.md +304 -0
- package/docs/design-system.md +85 -0
- package/docs/extended-components.md +201 -0
- package/docs/setup.md +146 -0
- package/package.json +33 -0
- package/src/components/Badge.tsx +40 -0
- package/src/components/Button.tsx +50 -0
- package/src/components/Card.tsx +72 -0
- package/src/components/Checkbox.tsx +47 -0
- package/src/components/HighlightText.tsx +24 -0
- package/src/components/Input.tsx +21 -0
- package/src/components/ProgressBar.tsx +64 -0
- package/src/components/Separator.tsx +25 -0
- package/src/components/Switch.tsx +43 -0
- package/src/components/Table.tsx +87 -0
- package/src/components/Tabs.tsx +122 -0
- package/src/components/ThemeToggle.tsx +40 -0
- package/src/components/Tooltip.tsx +120 -0
- package/src/components/extended/ArrowButton.tsx +28 -0
- package/src/components/extended/ChooseList.tsx +38 -0
- package/src/components/extended/Counter.tsx +74 -0
- package/src/components/extended/Popup.tsx +78 -0
- package/src/components/extended/StatusBar.tsx +45 -0
- package/src/components/extended/WeekViewCalendar.tsx +126 -0
- package/src/index.css +119 -0
- package/src/index.ts +25 -0
- package/src/lib/cn.ts +6 -0
- package/tailwind.config.ts +64 -0
- package/tsconfig.json +22 -0
package/docs/setup.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun add hqui
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
**Peer dependencies** (install if not already in your project):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bun add react react-dom
|
|
13
|
+
bun add -d tailwindcss@3 postcss autoprefixer
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Import CSS
|
|
17
|
+
|
|
18
|
+
In your app entry file (e.g. `main.tsx`):
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import "hqui/dist/index.css";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This is required. It provides CSS variables for colors, theme support, and base styles.
|
|
25
|
+
|
|
26
|
+
## Tailwind Config
|
|
27
|
+
|
|
28
|
+
Create `tailwind.config.ts`:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import type { Config } from "tailwindcss";
|
|
32
|
+
|
|
33
|
+
const config: Config = {
|
|
34
|
+
darkMode: ["class"],
|
|
35
|
+
content: ["./src/**/*.{ts,tsx}", "./node_modules/hqui/dist/**/*.{js,mjs}"],
|
|
36
|
+
theme: {
|
|
37
|
+
extend: {
|
|
38
|
+
colors: {
|
|
39
|
+
background: "hsl(var(--background))",
|
|
40
|
+
foreground: "hsl(var(--foreground))",
|
|
41
|
+
primary: {
|
|
42
|
+
DEFAULT: "hsl(var(--primary))",
|
|
43
|
+
foreground: "hsl(var(--primary-foreground))",
|
|
44
|
+
},
|
|
45
|
+
secondary: {
|
|
46
|
+
DEFAULT: "hsl(var(--secondary))",
|
|
47
|
+
foreground: "hsl(var(--secondary-foreground))",
|
|
48
|
+
},
|
|
49
|
+
muted: {
|
|
50
|
+
DEFAULT: "hsl(var(--muted))",
|
|
51
|
+
foreground: "hsl(var(--muted-foreground))",
|
|
52
|
+
},
|
|
53
|
+
accent: {
|
|
54
|
+
DEFAULT: "hsl(var(--accent))",
|
|
55
|
+
foreground: "hsl(var(--accent-foreground))",
|
|
56
|
+
},
|
|
57
|
+
destructive: {
|
|
58
|
+
DEFAULT: "hsl(var(--destructive))",
|
|
59
|
+
foreground: "hsl(var(--destructive-foreground))",
|
|
60
|
+
},
|
|
61
|
+
border: "hsl(var(--border))",
|
|
62
|
+
input: "hsl(var(--input))",
|
|
63
|
+
ring: "hsl(var(--ring))",
|
|
64
|
+
card: {
|
|
65
|
+
DEFAULT: "hsl(var(--card))",
|
|
66
|
+
foreground: "hsl(var(--card-foreground))",
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
borderRadius: {
|
|
70
|
+
lg: "var(--radius)",
|
|
71
|
+
md: "calc(var(--radius) - 2px)",
|
|
72
|
+
sm: "calc(var(--radius) - 4px)",
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
plugins: [],
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export default config;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## PostCSS Config
|
|
83
|
+
|
|
84
|
+
Create `postcss.config.js`:
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
export default {
|
|
88
|
+
plugins: {
|
|
89
|
+
tailwindcss: {},
|
|
90
|
+
autoprefixer: {},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Entry CSS
|
|
96
|
+
|
|
97
|
+
Your app's CSS file should start with:
|
|
98
|
+
|
|
99
|
+
```css
|
|
100
|
+
@tailwind base;
|
|
101
|
+
@tailwind components;
|
|
102
|
+
@tailwind utilities;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Dark Mode
|
|
106
|
+
|
|
107
|
+
Add `class="dark"` to `<html>` for dark mode. Or use the `ThemeToggle` component.
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<html class="dark"></html>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Vite Config (if needed)
|
|
114
|
+
|
|
115
|
+
If hqui is linked locally (e.g. monorepo), add dedupe to avoid duplicate React:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { defineConfig } from "vite";
|
|
119
|
+
import react from "@vitejs/plugin-react";
|
|
120
|
+
|
|
121
|
+
export default defineConfig({
|
|
122
|
+
plugins: [react()],
|
|
123
|
+
resolve: {
|
|
124
|
+
dedupe: ["react", "react-dom", "lucide-react"],
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## cn() Utility
|
|
130
|
+
|
|
131
|
+
All components use `cn()` for class merging. You can import it too:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { cn } from "hqui";
|
|
135
|
+
|
|
136
|
+
cn("px-4 py-2", "px-6"); // "px-6 py-2" (later wins)
|
|
137
|
+
cn("bg-red-500", isActive && "bg-blue-500");
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Build
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
bun run build
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Outputs: `dist/index.js`, `dist/index.mjs`, `dist/index.d.ts`, `dist/index.css`
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@exitvibing/hqui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React Tailwind UI library",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup src/index.ts --format cjs,esm --dts && bunx tailwindcss -i ./src/index.css -o ./dist/index.css --minify",
|
|
10
|
+
"dev": "bunx tailwindcss -i ./src/index.css -o ./dist/index.css --watch"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"react": "^18.0.0",
|
|
14
|
+
"react-dom": "^18.0.0",
|
|
15
|
+
"tailwindcss": ">=3.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.11.24",
|
|
19
|
+
"@types/react": "^18.2.61",
|
|
20
|
+
"@types/react-dom": "^18.2.19",
|
|
21
|
+
"prettier": "^3.8.1",
|
|
22
|
+
"react": "^18.2.0",
|
|
23
|
+
"react-dom": "^18.2.0",
|
|
24
|
+
"tailwindcss": "^3.4.1",
|
|
25
|
+
"tsup": "^8.0.2",
|
|
26
|
+
"typescript": "^5.3.3"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"clsx": "^2.1.1",
|
|
30
|
+
"lucide-react": "^0.575.0",
|
|
31
|
+
"tailwind-merge": "^3.5.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React, { HTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
const colorVariants = {
|
|
5
|
+
default: "bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
6
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
7
|
+
outline: "border border-border text-foreground",
|
|
8
|
+
white: "bg-foreground/10 text-foreground border border-foreground/20",
|
|
9
|
+
destructive:
|
|
10
|
+
"bg-[hsl(var(--red))] text-white shadow hover:bg-[hsl(var(--red))]/80",
|
|
11
|
+
purple:
|
|
12
|
+
"bg-[hsl(var(--purple))]/15 text-[hsl(var(--purple))] border border-[hsl(var(--purple))]/20",
|
|
13
|
+
orange:
|
|
14
|
+
"bg-[hsl(var(--orange))]/15 text-[hsl(var(--orange))] border border-[hsl(var(--orange))]/20",
|
|
15
|
+
blue: "bg-[hsl(var(--blue))]/15 text-[hsl(var(--blue))] border border-[hsl(var(--blue))]/20",
|
|
16
|
+
green:
|
|
17
|
+
"bg-[hsl(var(--green))]/15 text-[hsl(var(--green))] border border-[hsl(var(--green))]/20",
|
|
18
|
+
red: "bg-[hsl(var(--red))]/15 text-[hsl(var(--red))] border border-[hsl(var(--red))]/20",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
|
|
22
|
+
variant?: keyof typeof colorVariants;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
|
|
26
|
+
({ className, variant = "default", ...props }, ref) => {
|
|
27
|
+
return (
|
|
28
|
+
<span
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
"inline-flex items-center rounded-md px-2 py-0.5 text-xs font-semibold transition-colors",
|
|
32
|
+
colorVariants[variant],
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
Badge.displayName = "Badge";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { ButtonHTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
const variants = {
|
|
5
|
+
primary: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
6
|
+
secondary:
|
|
7
|
+
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
8
|
+
destructive:
|
|
9
|
+
"bg-[hsl(var(--red))] text-white shadow-sm hover:bg-[hsl(var(--red))]/90",
|
|
10
|
+
outline:
|
|
11
|
+
"border border-border bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
12
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
13
|
+
purple:
|
|
14
|
+
"bg-[hsl(var(--purple))] text-white shadow-sm hover:bg-[hsl(var(--purple))]/90",
|
|
15
|
+
orange:
|
|
16
|
+
"bg-[hsl(var(--orange))] text-white shadow-sm hover:bg-[hsl(var(--orange))]/90",
|
|
17
|
+
blue: "bg-[hsl(var(--blue))] text-white shadow-sm hover:bg-[hsl(var(--blue))]/90",
|
|
18
|
+
green:
|
|
19
|
+
"bg-[hsl(var(--green))] text-white shadow-sm hover:bg-[hsl(var(--green))]/90",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const sizes = {
|
|
23
|
+
sm: "h-8 rounded-md px-3 text-xs gap-1.5",
|
|
24
|
+
default: "h-9 px-4 py-2 text-sm gap-2",
|
|
25
|
+
lg: "h-10 rounded-md px-6 text-base gap-2",
|
|
26
|
+
icon: "h-9 w-9 p-0",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
30
|
+
variant?: keyof typeof variants;
|
|
31
|
+
size?: keyof typeof sizes;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
35
|
+
({ className, variant = "primary", size = "default", ...props }, ref) => {
|
|
36
|
+
return (
|
|
37
|
+
<button
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(
|
|
40
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
41
|
+
variants[variant],
|
|
42
|
+
sizes[size],
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
Button.displayName = "Button";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { HTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
export const Card = forwardRef<HTMLDivElement, HTMLAttributes<HTMLDivElement>>(
|
|
5
|
+
({ className, ...props }, ref) => (
|
|
6
|
+
<div
|
|
7
|
+
ref={ref}
|
|
8
|
+
className={cn(
|
|
9
|
+
"rounded-xl border border-border bg-card text-card-foreground shadow-sm transition-shadow hover:shadow-md",
|
|
10
|
+
className,
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
),
|
|
15
|
+
);
|
|
16
|
+
Card.displayName = "Card";
|
|
17
|
+
|
|
18
|
+
export const CardHeader = forwardRef<
|
|
19
|
+
HTMLDivElement,
|
|
20
|
+
HTMLAttributes<HTMLDivElement>
|
|
21
|
+
>(({ className, ...props }, ref) => (
|
|
22
|
+
<div
|
|
23
|
+
ref={ref}
|
|
24
|
+
className={cn("flex flex-col space-y-1.5 p-6 pb-4", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
));
|
|
28
|
+
CardHeader.displayName = "CardHeader";
|
|
29
|
+
|
|
30
|
+
export const CardTitle = forwardRef<
|
|
31
|
+
HTMLHeadingElement,
|
|
32
|
+
HTMLAttributes<HTMLHeadingElement>
|
|
33
|
+
>(({ className, ...props }, ref) => (
|
|
34
|
+
<h3
|
|
35
|
+
ref={ref}
|
|
36
|
+
className={cn("font-semibold leading-none tracking-tight", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
));
|
|
40
|
+
CardTitle.displayName = "CardTitle";
|
|
41
|
+
|
|
42
|
+
export const CardDescription = forwardRef<
|
|
43
|
+
HTMLParagraphElement,
|
|
44
|
+
HTMLAttributes<HTMLParagraphElement>
|
|
45
|
+
>(({ className, ...props }, ref) => (
|
|
46
|
+
<p
|
|
47
|
+
ref={ref}
|
|
48
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
));
|
|
52
|
+
CardDescription.displayName = "CardDescription";
|
|
53
|
+
|
|
54
|
+
export const CardContent = forwardRef<
|
|
55
|
+
HTMLDivElement,
|
|
56
|
+
HTMLAttributes<HTMLDivElement>
|
|
57
|
+
>(({ className, ...props }, ref) => (
|
|
58
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
59
|
+
));
|
|
60
|
+
CardContent.displayName = "CardContent";
|
|
61
|
+
|
|
62
|
+
export const CardFooter = forwardRef<
|
|
63
|
+
HTMLDivElement,
|
|
64
|
+
HTMLAttributes<HTMLDivElement>
|
|
65
|
+
>(({ className, ...props }, ref) => (
|
|
66
|
+
<div
|
|
67
|
+
ref={ref}
|
|
68
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
));
|
|
72
|
+
CardFooter.displayName = "CardFooter";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React, { InputHTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { Check } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
|
|
5
|
+
export interface CheckboxProps extends Omit<
|
|
6
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
7
|
+
"type"
|
|
8
|
+
> {
|
|
9
|
+
label?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
13
|
+
({ className, label, id, ...props }, ref) => {
|
|
14
|
+
const defaultId = React.useId();
|
|
15
|
+
const inputId = id || defaultId;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<label
|
|
19
|
+
htmlFor={inputId}
|
|
20
|
+
className={cn(
|
|
21
|
+
"flex items-center gap-2 cursor-pointer select-none",
|
|
22
|
+
className,
|
|
23
|
+
)}
|
|
24
|
+
>
|
|
25
|
+
<div className="relative flex items-center justify-center">
|
|
26
|
+
<input
|
|
27
|
+
id={inputId}
|
|
28
|
+
type="checkbox"
|
|
29
|
+
className="peer h-4 w-4 shrink-0 rounded-sm border border-foreground/30 shadow appearance-none focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 checked:bg-foreground checked:border-foreground"
|
|
30
|
+
ref={ref}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
<Check
|
|
34
|
+
className="absolute h-3 w-3 text-background pointer-events-none opacity-0 peer-checked:opacity-100 transition-opacity"
|
|
35
|
+
strokeWidth={3}
|
|
36
|
+
/>
|
|
37
|
+
</div>
|
|
38
|
+
{label && (
|
|
39
|
+
<span className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
|
40
|
+
{label}
|
|
41
|
+
</span>
|
|
42
|
+
)}
|
|
43
|
+
</label>
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
Checkbox.displayName = "Checkbox";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { HTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface HighlightTextProps extends HTMLAttributes<HTMLSpanElement> {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const HighlightText = forwardRef<HTMLSpanElement, HighlightTextProps>(
|
|
9
|
+
({ className, children, ...props }, ref) => {
|
|
10
|
+
return (
|
|
11
|
+
<span
|
|
12
|
+
ref={ref}
|
|
13
|
+
className={cn(
|
|
14
|
+
"bg-foreground text-background px-1.5 py-0.5 rounded font-medium",
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
{children}
|
|
20
|
+
</span>
|
|
21
|
+
);
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
HighlightText.displayName = "HighlightText";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React, { InputHTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {}
|
|
5
|
+
|
|
6
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
7
|
+
({ className, type = "text", ...props }, ref) => {
|
|
8
|
+
return (
|
|
9
|
+
<input
|
|
10
|
+
type={type}
|
|
11
|
+
className={cn(
|
|
12
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
ref={ref}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
Input.displayName = "Input";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { HTMLAttributes, forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
const colorClasses = {
|
|
5
|
+
white: "bg-foreground",
|
|
6
|
+
purple: "bg-[hsl(var(--purple))]",
|
|
7
|
+
orange: "bg-[hsl(var(--orange))]",
|
|
8
|
+
blue: "bg-[hsl(var(--blue))]",
|
|
9
|
+
green: "bg-[hsl(var(--green))]",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export interface ProgressBarProps extends HTMLAttributes<HTMLDivElement> {
|
|
13
|
+
value?: number;
|
|
14
|
+
max?: number;
|
|
15
|
+
showLabel?: boolean;
|
|
16
|
+
color?: keyof typeof colorClasses;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
|
|
20
|
+
(
|
|
21
|
+
{
|
|
22
|
+
className,
|
|
23
|
+
value = 0,
|
|
24
|
+
max = 100,
|
|
25
|
+
showLabel = false,
|
|
26
|
+
color = "white",
|
|
27
|
+
...props
|
|
28
|
+
},
|
|
29
|
+
ref,
|
|
30
|
+
) => {
|
|
31
|
+
const percentage = Math.min(Math.max(0, (value / max) * 100), 100);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={cn("flex items-center gap-3", showLabel && "w-full")}>
|
|
35
|
+
<div
|
|
36
|
+
ref={ref}
|
|
37
|
+
role="progressbar"
|
|
38
|
+
aria-valuemin={0}
|
|
39
|
+
aria-valuemax={max}
|
|
40
|
+
aria-valuenow={value}
|
|
41
|
+
className={cn(
|
|
42
|
+
"relative h-2 w-full overflow-hidden rounded-full bg-secondary",
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
className={cn(
|
|
49
|
+
"h-full rounded-full transition-all duration-500 ease-out",
|
|
50
|
+
colorClasses[color],
|
|
51
|
+
)}
|
|
52
|
+
style={{ width: `${percentage}%` }}
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
{showLabel && (
|
|
56
|
+
<span className="text-xs font-medium text-muted-foreground tabular-nums w-10 text-right">
|
|
57
|
+
{Math.round(percentage)}%
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
ProgressBar.displayName = "ProgressBar";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React, { forwardRef } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface SeparatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
orientation?: "horizontal" | "vertical";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const Separator = forwardRef<HTMLDivElement, SeparatorProps>(
|
|
9
|
+
({ className, orientation = "horizontal", ...props }, ref) => {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
ref={ref}
|
|
13
|
+
role="separator"
|
|
14
|
+
aria-orientation={orientation}
|
|
15
|
+
className={cn(
|
|
16
|
+
"shrink-0 bg-border",
|
|
17
|
+
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
Separator.displayName = "Separator";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { forwardRef, InputHTMLAttributes } from "react";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
|
|
4
|
+
export interface SwitchProps extends Omit<
|
|
5
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
6
|
+
"type" | "role"
|
|
7
|
+
> {
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
|
|
12
|
+
({ className, label, id, ...props }, ref) => {
|
|
13
|
+
const defaultId = React.useId();
|
|
14
|
+
const inputId = id || defaultId;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<label
|
|
18
|
+
htmlFor={inputId}
|
|
19
|
+
className={cn(
|
|
20
|
+
"inline-flex items-center gap-2 cursor-pointer select-none",
|
|
21
|
+
className,
|
|
22
|
+
)}
|
|
23
|
+
>
|
|
24
|
+
<div className="relative">
|
|
25
|
+
<input
|
|
26
|
+
id={inputId}
|
|
27
|
+
type="checkbox"
|
|
28
|
+
role="switch"
|
|
29
|
+
className="peer sr-only"
|
|
30
|
+
ref={ref}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
<div className="h-5 w-9 rounded-full border-2 border-transparent bg-input transition-colors peer-checked:bg-foreground peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-background peer-disabled:cursor-not-allowed peer-disabled:opacity-50" />
|
|
34
|
+
<div className="absolute left-0.5 top-0.5 h-4 w-4 rounded-full bg-background shadow-lg transition-transform peer-checked:translate-x-4 peer-checked:bg-background" />
|
|
35
|
+
</div>
|
|
36
|
+
{label && (
|
|
37
|
+
<span className="text-sm font-medium leading-none">{label}</span>
|
|
38
|
+
)}
|
|
39
|
+
</label>
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
Switch.displayName = "Switch";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
HTMLAttributes,
|
|
3
|
+
TdHTMLAttributes,
|
|
4
|
+
ThHTMLAttributes,
|
|
5
|
+
forwardRef,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { cn } from "../lib/cn";
|
|
8
|
+
|
|
9
|
+
export const Table = forwardRef<
|
|
10
|
+
HTMLTableElement,
|
|
11
|
+
React.HTMLAttributes<HTMLTableElement>
|
|
12
|
+
>(({ className, ...props }, ref) => (
|
|
13
|
+
<div className="relative w-full overflow-auto rounded-lg border border-border">
|
|
14
|
+
<table
|
|
15
|
+
ref={ref}
|
|
16
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
));
|
|
21
|
+
Table.displayName = "Table";
|
|
22
|
+
|
|
23
|
+
export const TableHeader = forwardRef<
|
|
24
|
+
HTMLTableSectionElement,
|
|
25
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
26
|
+
>(({ className, ...props }, ref) => (
|
|
27
|
+
<thead
|
|
28
|
+
ref={ref}
|
|
29
|
+
className={cn("bg-muted/50 [&_tr]:border-b", className)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
));
|
|
33
|
+
TableHeader.displayName = "TableHeader";
|
|
34
|
+
|
|
35
|
+
export const TableBody = forwardRef<
|
|
36
|
+
HTMLTableSectionElement,
|
|
37
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
38
|
+
>(({ className, ...props }, ref) => (
|
|
39
|
+
<tbody
|
|
40
|
+
ref={ref}
|
|
41
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
));
|
|
45
|
+
TableBody.displayName = "TableBody";
|
|
46
|
+
|
|
47
|
+
export const TableRow = forwardRef<
|
|
48
|
+
HTMLTableRowElement,
|
|
49
|
+
React.HTMLAttributes<HTMLTableRowElement>
|
|
50
|
+
>(({ className, ...props }, ref) => (
|
|
51
|
+
<tr
|
|
52
|
+
ref={ref}
|
|
53
|
+
className={cn(
|
|
54
|
+
"border-b border-border transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
|
55
|
+
className,
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
));
|
|
60
|
+
TableRow.displayName = "TableRow";
|
|
61
|
+
|
|
62
|
+
export const TableHead = forwardRef<
|
|
63
|
+
HTMLTableCellElement,
|
|
64
|
+
ThHTMLAttributes<HTMLTableCellElement>
|
|
65
|
+
>(({ className, ...props }, ref) => (
|
|
66
|
+
<th
|
|
67
|
+
ref={ref}
|
|
68
|
+
className={cn(
|
|
69
|
+
"h-10 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
));
|
|
75
|
+
TableHead.displayName = "TableHead";
|
|
76
|
+
|
|
77
|
+
export const TableCell = forwardRef<
|
|
78
|
+
HTMLTableCellElement,
|
|
79
|
+
TdHTMLAttributes<HTMLTableCellElement>
|
|
80
|
+
>(({ className, ...props }, ref) => (
|
|
81
|
+
<td
|
|
82
|
+
ref={ref}
|
|
83
|
+
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
));
|
|
87
|
+
TableCell.displayName = "TableCell";
|