Skip to content

Commit d5a3e97

Browse files
committed
➕ Add shadcn ui
1 parent 49913c5 commit d5a3e97

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1111
-5
lines changed

app.vue

-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
11
<template>
2-
<div>
3-
4-
</div>
52
<NuxtPage />
63
</template>

assets/css/tailwind.css

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;
4+
5+
@layer base {
6+
:root {
7+
--background: 0 0% 100%;
8+
--foreground: 222.2 84% 4.9%;
9+
10+
--muted: 210 40% 96.1%;
11+
--muted-foreground: 215.4 16.3% 46.9%;
12+
13+
--popover: 0 0% 100%;
14+
--popover-foreground: 222.2 84% 4.9%;
15+
16+
--card: 0 0% 100%;
17+
--card-foreground: 222.2 84% 4.9%;
18+
19+
--border: 214.3 31.8% 91.4%;
20+
--input: 214.3 31.8% 91.4%;
21+
22+
--primary: 222.2 47.4% 11.2%;
23+
--primary-foreground: 210 40% 98%;
24+
25+
--secondary: 210 40% 96.1%;
26+
--secondary-foreground: 222.2 47.4% 11.2%;
27+
28+
--accent: 210 40% 96.1%;
29+
--accent-foreground: 222.2 47.4% 11.2%;
30+
31+
--destructive: 0 84.2% 60.2%;
32+
--destructive-foreground: 210 40% 98%;
33+
34+
--ring: 222.2 84% 4.9%;
35+
36+
--radius: 0.5rem;
37+
}
38+
39+
.dark {
40+
--background: 222.2 84% 4.9%;
41+
--foreground: 210 40% 98%;
42+
43+
--muted: 217.2 32.6% 17.5%;
44+
--muted-foreground: 215 20.2% 65.1%;
45+
46+
--popover: 222.2 84% 4.9%;
47+
--popover-foreground: 210 40% 98%;
48+
49+
--card: 222.2 84% 4.9%;
50+
--card-foreground: 210 40% 98%;
51+
52+
--border: 217.2 32.6% 17.5%;
53+
--input: 217.2 32.6% 17.5%;
54+
55+
--primary: 210 40% 98%;
56+
--primary-foreground: 222.2 47.4% 11.2%;
57+
58+
--secondary: 217.2 32.6% 17.5%;
59+
--secondary-foreground: 210 40% 98%;
60+
61+
--accent: 217.2 32.6% 17.5%;
62+
--accent-foreground: 210 40% 98%;
63+
64+
--destructive: 0 62.8% 30.6%;
65+
--destructive-foreground: 210 40% 98%;
66+
67+
--ring: 212.7 26.8% 83.9%;
68+
}
69+
}
70+
71+
@layer base {
72+
* {
73+
@apply border-border;
74+
}
75+
body {
76+
@apply bg-background text-foreground;
77+
}
78+
}

bun.lockb

37.4 KB
Binary file not shown.

components.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "https://shadcn-vue.com/schema.json",
3+
"style": "default",
4+
"typescript": true,
5+
"tsConfigPath": ".nuxt/tsconfig.json",
6+
"tailwind": {
7+
"config": "tailwind.config.js",
8+
"css": "assets/css/tailwind.css",
9+
"baseColor": "slate",
10+
"cssVariables": true
11+
},
12+
"framework": "nuxt",
13+
"aliases": {
14+
"components": "@/components",
15+
"utils": "@/lib/utils"
16+
}
17+
}

components/ui/button/Button.vue

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue';
3+
import { Primitive, type PrimitiveProps } from 'radix-vue';
4+
import { type ButtonVariants, buttonVariants } from '.';
5+
import { cn } from '@/lib/utils';
6+
7+
interface Props extends PrimitiveProps {
8+
variant?: ButtonVariants['variant'];
9+
size?: ButtonVariants['size'];
10+
class?: HTMLAttributes['class'];
11+
}
12+
13+
const props = withDefaults(defineProps<Props>(), {
14+
as: 'button',
15+
});
16+
</script>
17+
18+
<template>
19+
<Primitive
20+
:as="as"
21+
:as-child="asChild"
22+
:class="cn(buttonVariants({ variant, size }), props.class)"
23+
>
24+
<slot />
25+
</Primitive>
26+
</template>

components/ui/button/index.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { type VariantProps, cva } from 'class-variance-authority';
2+
3+
export { default as Button } from './Button.vue';
4+
5+
export const buttonVariants = cva(
6+
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
7+
{
8+
variants: {
9+
variant: {
10+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
11+
destructive:
12+
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
13+
outline:
14+
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
15+
secondary:
16+
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
17+
ghost: 'hover:bg-accent hover:text-accent-foreground',
18+
link: 'text-primary underline-offset-4 hover:underline',
19+
},
20+
size: {
21+
default: 'h-10 px-4 py-2',
22+
xs: 'h-7 rounded px-2',
23+
sm: 'h-9 rounded-md px-3',
24+
lg: 'h-11 rounded-md px-8',
25+
icon: 'h-10 w-10',
26+
},
27+
},
28+
defaultVariants: {
29+
variant: 'default',
30+
size: 'default',
31+
},
32+
},
33+
);
34+
35+
export type ButtonVariants = VariantProps<typeof buttonVariants>;

components/ui/dialog/Dialog.vue

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script setup lang="ts">
2+
import {
3+
DialogRoot,
4+
type DialogRootEmits,
5+
type DialogRootProps,
6+
useForwardPropsEmits,
7+
} from 'radix-vue';
8+
9+
const props = defineProps<DialogRootProps>();
10+
const emits = defineEmits<DialogRootEmits>();
11+
12+
const forwarded = useForwardPropsEmits(props, emits);
13+
</script>
14+
15+
<template>
16+
<DialogRoot v-bind="forwarded">
17+
<slot />
18+
</DialogRoot>
19+
</template>

components/ui/dialog/DialogClose.vue

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script setup lang="ts">
2+
import { DialogClose, type DialogCloseProps } from 'radix-vue';
3+
4+
const props = defineProps<DialogCloseProps>();
5+
</script>
6+
7+
<template>
8+
<DialogClose v-bind="props">
9+
<slot />
10+
</DialogClose>
11+
</template>
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<script setup lang="ts">
2+
import { type HTMLAttributes, computed } from 'vue';
3+
import {
4+
DialogClose,
5+
DialogContent,
6+
type DialogContentEmits,
7+
type DialogContentProps,
8+
DialogOverlay,
9+
DialogPortal,
10+
useForwardPropsEmits,
11+
} from 'radix-vue';
12+
import { X } from 'lucide-vue-next';
13+
import { cn } from '@/lib/utils';
14+
15+
const props = defineProps<
16+
DialogContentProps & { class?: HTMLAttributes['class'] }
17+
>();
18+
const emits = defineEmits<DialogContentEmits>();
19+
20+
const delegatedProps = computed(() => {
21+
const { class: _, ...delegated } = props;
22+
23+
return delegated;
24+
});
25+
26+
const forwarded = useForwardPropsEmits(delegatedProps, emits);
27+
</script>
28+
29+
<template>
30+
<DialogPortal>
31+
<DialogOverlay
32+
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
33+
/>
34+
<DialogContent
35+
v-bind="forwarded"
36+
:class="
37+
cn(
38+
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
39+
props.class,
40+
)
41+
"
42+
>
43+
<slot />
44+
45+
<DialogClose
46+
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
47+
>
48+
<X class="w-4 h-4" />
49+
<span class="sr-only">Close</span>
50+
</DialogClose>
51+
</DialogContent>
52+
</DialogPortal>
53+
</template>
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script setup lang="ts">
2+
import { type HTMLAttributes, computed } from 'vue';
3+
import {
4+
DialogDescription,
5+
type DialogDescriptionProps,
6+
useForwardProps,
7+
} from 'radix-vue';
8+
import { cn } from '@/lib/utils';
9+
10+
const props = defineProps<
11+
DialogDescriptionProps & { class?: HTMLAttributes['class'] }
12+
>();
13+
14+
const delegatedProps = computed(() => {
15+
const { class: _, ...delegated } = props;
16+
17+
return delegated;
18+
});
19+
20+
const forwardedProps = useForwardProps(delegatedProps);
21+
</script>
22+
23+
<template>
24+
<DialogDescription
25+
v-bind="forwardedProps"
26+
:class="cn('text-sm text-muted-foreground', props.class)"
27+
>
28+
<slot />
29+
</DialogDescription>
30+
</template>

components/ui/dialog/DialogFooter.vue

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue';
3+
import { cn } from '@/lib/utils';
4+
5+
const props = defineProps<{ class?: HTMLAttributes['class'] }>();
6+
</script>
7+
8+
<template>
9+
<div
10+
:class="
11+
cn(
12+
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
13+
props.class,
14+
)
15+
"
16+
>
17+
<slot />
18+
</div>
19+
</template>

components/ui/dialog/DialogHeader.vue

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script setup lang="ts">
2+
import type { HTMLAttributes } from 'vue';
3+
import { cn } from '@/lib/utils';
4+
5+
const props = defineProps<{
6+
class?: HTMLAttributes['class'];
7+
}>();
8+
</script>
9+
10+
<template>
11+
<div
12+
:class="cn('flex flex-col gap-y-1.5 text-center sm:text-left', props.class)"
13+
>
14+
<slot />
15+
</div>
16+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<script setup lang="ts">
2+
import { type HTMLAttributes, computed } from 'vue';
3+
import {
4+
DialogClose,
5+
DialogContent,
6+
type DialogContentEmits,
7+
type DialogContentProps,
8+
DialogOverlay,
9+
DialogPortal,
10+
useForwardPropsEmits,
11+
} from 'radix-vue';
12+
import { X } from 'lucide-vue-next';
13+
import { cn } from '@/lib/utils';
14+
15+
const props = defineProps<
16+
DialogContentProps & { class?: HTMLAttributes['class'] }
17+
>();
18+
const emits = defineEmits<DialogContentEmits>();
19+
20+
const delegatedProps = computed(() => {
21+
const { class: _, ...delegated } = props;
22+
23+
return delegated;
24+
});
25+
26+
const forwarded = useForwardPropsEmits(delegatedProps, emits);
27+
</script>
28+
29+
<template>
30+
<DialogPortal>
31+
<DialogOverlay
32+
class="fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
33+
>
34+
<DialogContent
35+
:class="
36+
cn(
37+
'relative z-50 grid w-full max-w-lg my-8 gap-4 border border-border bg-background p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
38+
props.class,
39+
)
40+
"
41+
v-bind="forwarded"
42+
@pointer-down-outside="
43+
(event) => {
44+
const originalEvent = event.detail.originalEvent;
45+
const target = originalEvent.target as HTMLElement;
46+
if (
47+
originalEvent.offsetX > target.clientWidth ||
48+
originalEvent.offsetY > target.clientHeight
49+
) {
50+
event.preventDefault();
51+
}
52+
}
53+
"
54+
>
55+
<slot />
56+
57+
<DialogClose
58+
class="absolute top-3 right-3 p-0.5 transition-colors rounded-md hover:bg-secondary"
59+
>
60+
<X class="w-4 h-4" />
61+
<span class="sr-only">Close</span>
62+
</DialogClose>
63+
</DialogContent>
64+
</DialogOverlay>
65+
</DialogPortal>
66+
</template>

0 commit comments

Comments
 (0)