feat: Add Tailwind CSS and UI components
- Updated project to use Tailwind CSS for styling. - Added new UI components including Navbar, Sidebar, Footer, Accordion, and Button. - Created markdown content page for documentation. - Improved overall structure and design of the application.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "../../../utils";
|
||||
import { slide } from "svelte/transition";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
|
||||
export let isOpen = false;
|
||||
</script>
|
||||
|
||||
{#if isOpen}
|
||||
<div
|
||||
transition:slide={{ duration: 200 }}
|
||||
class={cn("overflow-hidden text-sm transition-all", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<div class="pb-4 pt-0">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
24
sweb/src/lib/components/ui/accordion/accordion-item.svelte
Normal file
24
sweb/src/lib/components/ui/accordion/accordion-item.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "../../../utils";
|
||||
import { getAccordionContext } from "./context";
|
||||
|
||||
export let value: string;
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
|
||||
const context = getAccordionContext();
|
||||
|
||||
// Compute isOpen based on the current value
|
||||
$: isOpen = Array.isArray(context.value)
|
||||
? context.value.includes(value)
|
||||
: context.value === value;
|
||||
</script>
|
||||
|
||||
<div
|
||||
data-state={isOpen ? "open" : "closed"}
|
||||
class={cn("border-b", className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot {isOpen} />
|
||||
</div>
|
||||
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "../../../utils";
|
||||
import { getAccordionContext } from "./context";
|
||||
|
||||
export let value: string;
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
|
||||
const context = getAccordionContext();
|
||||
|
||||
function handleClick() {
|
||||
context.onValueChange(value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded={value ? "true" : "false"}
|
||||
class={cn(
|
||||
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||
className,
|
||||
)}
|
||||
on:click={handleClick}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
45
sweb/src/lib/components/ui/accordion/accordion.svelte
Normal file
45
sweb/src/lib/components/ui/accordion/accordion.svelte
Normal file
@@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "../../../utils";
|
||||
import { setAccordionContext } from "./context";
|
||||
|
||||
export let value: string | undefined = undefined;
|
||||
export let collapsible = false;
|
||||
export let multiple = false;
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
$: accordionValue = multiple
|
||||
? value
|
||||
? Array.isArray(value)
|
||||
? value
|
||||
: [value]
|
||||
: []
|
||||
: value;
|
||||
|
||||
function onValueChange(itemValue: string) {
|
||||
if (multiple) {
|
||||
const newValue = Array.isArray(accordionValue) ? [...accordionValue] : [];
|
||||
const index = newValue.indexOf(itemValue);
|
||||
if (index > -1) {
|
||||
newValue.splice(index, 1);
|
||||
} else {
|
||||
newValue.push(itemValue);
|
||||
}
|
||||
value = newValue as any; // Type assertion to handle the string[] to string assignment
|
||||
} else {
|
||||
value =
|
||||
accordionValue === itemValue && collapsible ? undefined : itemValue;
|
||||
}
|
||||
}
|
||||
|
||||
setAccordionContext({
|
||||
value: accordionValue,
|
||||
onValueChange,
|
||||
collapsible,
|
||||
multiple,
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={cn("flex flex-col space-y-1.5", className)} {...$$restProps}>
|
||||
<slot />
|
||||
</div>
|
||||
13
sweb/src/lib/components/ui/accordion/context.ts
Normal file
13
sweb/src/lib/components/ui/accordion/context.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createContext } from "../../../create-context";
|
||||
|
||||
export type AccordionContextValue = {
|
||||
value: string | string[] | undefined;
|
||||
onValueChange: (value: string) => void;
|
||||
collapsible: boolean;
|
||||
multiple: boolean;
|
||||
};
|
||||
|
||||
export const {
|
||||
get: getAccordionContext,
|
||||
set: setAccordionContext
|
||||
} = createContext<AccordionContextValue>();
|
||||
16
sweb/src/lib/components/ui/accordion/index.ts
Normal file
16
sweb/src/lib/components/ui/accordion/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import Root from './accordion.svelte';
|
||||
import Item from './accordion-item.svelte';
|
||||
import Content from './accordion-content.svelte';
|
||||
import Trigger from './accordion-trigger.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Item,
|
||||
Content,
|
||||
Trigger,
|
||||
//
|
||||
Root as Accordion,
|
||||
Item as AccordionItem,
|
||||
Content as AccordionContent,
|
||||
Trigger as AccordionTrigger
|
||||
};
|
||||
32
sweb/src/lib/components/ui/button/button.svelte
Normal file
32
sweb/src/lib/components/ui/button/button.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "../../../utils";
|
||||
|
||||
export let variant: "primary" | "secondary" | "ghost" = "primary";
|
||||
export let size: "default" | "sm" | "lg" | "icon" = "default";
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
|
||||
const sizeClasses = {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3 text-xs",
|
||||
lg: "h-11 rounded-md px-8 text-base",
|
||||
icon: "h-10 w-10 p-2",
|
||||
};
|
||||
</script>
|
||||
|
||||
<button
|
||||
class={cn(
|
||||
"btn",
|
||||
variant === "primary"
|
||||
? "btn-primary"
|
||||
: variant === "secondary"
|
||||
? "btn-secondary"
|
||||
: "btn-ghost",
|
||||
sizeClasses[size],
|
||||
className,
|
||||
)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
7
sweb/src/lib/components/ui/button/index.ts
Normal file
7
sweb/src/lib/components/ui/button/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import Root from './button.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Button
|
||||
};
|
||||
17
sweb/src/lib/create-context.ts
Normal file
17
sweb/src/lib/create-context.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { getContext, hasContext, setContext } from 'svelte';
|
||||
|
||||
export function createContext<T>() {
|
||||
const key = Symbol();
|
||||
|
||||
return {
|
||||
get: () => {
|
||||
if (!hasContext(key)) {
|
||||
throw new Error('Context was not set');
|
||||
}
|
||||
return getContext<T>(key);
|
||||
},
|
||||
set: (context: T) => {
|
||||
setContext(key, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
6
sweb/src/lib/utils.ts
Normal file
6
sweb/src/lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
Reference in New Issue
Block a user