Merge pull request #2 from NabilOuldHamou/graphical-rework
Refonte Graphique du site
This commit is contained in:
commit
1e2c511bb2
46 changed files with 1309 additions and 196 deletions
13
components.json
Normal file
13
components.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/app.css",
|
||||||
|
"baseColor": "zinc"
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "$lib/components",
|
||||||
|
"utils": "$lib/utils"
|
||||||
|
}
|
||||||
|
}
|
417
package-lock.json
generated
417
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -32,6 +32,14 @@
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"svelte-icons": "^2.1.0"
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
"bits-ui": "^0.13.3",
|
||||||
|
"clsx": "^2.1.0",
|
||||||
|
"mode-watcher": "^0.1.2",
|
||||||
|
"radix-icons-svelte": "^1.2.1",
|
||||||
|
"svelte-icons": "^2.1.0",
|
||||||
|
"tailwind-merge": "^2.2.0",
|
||||||
|
"tailwind-variants": "^0.1.20",
|
||||||
|
"vaul-svelte": "^0.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
76
src/app.css
76
src/app.css
|
@ -1,8 +1,78 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&display=swap');
|
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
.content-container {
|
@layer base {
|
||||||
height: calc(100vh - theme('spacing.16'));
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--muted: 240 4.8% 95.9%;
|
||||||
|
--muted-foreground: 240 3.8% 46.1%;
|
||||||
|
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--border: 240 5.9% 90%;
|
||||||
|
--input: 240 5.9% 90%;
|
||||||
|
|
||||||
|
--primary: 240 5.9% 10%;
|
||||||
|
--primary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--secondary: 240 4.8% 95.9%;
|
||||||
|
--secondary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--accent: 240 4.8% 95.9%;
|
||||||
|
--accent-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--destructive: 0 72.2% 50.6%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 240 10% 3.9%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 240 10% 3.9%;
|
||||||
|
--foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--muted: 240 3.7% 15.9%;
|
||||||
|
--muted-foreground: 240 5% 64.9%;
|
||||||
|
|
||||||
|
--popover: 240 10% 3.9%;
|
||||||
|
--popover-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--card: 240 10% 3.9%;
|
||||||
|
--card-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--border: 240 3.7% 15.9%;
|
||||||
|
--input: 240 3.7% 15.9%;
|
||||||
|
|
||||||
|
--primary: 0 0% 98%;
|
||||||
|
--primary-foreground: 240 5.9% 10%;
|
||||||
|
|
||||||
|
--secondary: 240 3.7% 15.9%;
|
||||||
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--accent: 240 3.7% 15.9%;
|
||||||
|
--accent-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
|
--ring: 240 4.9% 83.9%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -2,25 +2,39 @@ const projects = [
|
||||||
{
|
{
|
||||||
"name": "Portfolio",
|
"name": "Portfolio",
|
||||||
"stack": ["SvelteKit", "TypeScript", "TailwindCSS"],
|
"stack": ["SvelteKit", "TypeScript", "TailwindCSS"],
|
||||||
"description": "SvelteKit project to learn svelte and also update my personal website.",
|
"shortDesc": "Personal portfolio made using SvelteKit for learning purposes.",
|
||||||
|
"description": "This project is the website that you are actually using. This is my personal portfolio and used " +
|
||||||
|
"this project as an occasion to learn SvelteKit. In order to have an easier time with the design and make something " +
|
||||||
|
"good looking I also used TailwindCSS and Shadcn-svelte which is a binding for Shadcn/ui in Svelte. This project was " +
|
||||||
|
"the perfect occasion for me to learn and get more familiar with SvelteKit which is now my favorite framework for web developpment.",
|
||||||
"url": "https://github.com/nabilouldhamou/portfolio"
|
"url": "https://github.com/nabilouldhamou/portfolio"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Green Plates",
|
"name": "Green Plates",
|
||||||
"stack": ["GoLang", "GinGonic", "SvelteKit", "TypeScript"],
|
"stack": ["Go", "GinGonic", "SvelteKit", "TypeScript"],
|
||||||
"description": "Recipe website with custom made backend using GoLang.",
|
"shortDesc": "Recipe website with custom made backend using Go.",
|
||||||
|
"description": "Green Plates is a recipe sharing website made in collaboration with my friend @BenGregory23. " +
|
||||||
|
"This website is also made with SvelteKit for the frontend but also features a custom made high performance backend using Go and GinGonic. " +
|
||||||
|
"The website is still in a work in progress but the backend 99% done, only missing file fetching after the files are uploaded to the server.",
|
||||||
"url": "https://github.com/NabilOuldHamou?tab=repositories&q=green-plates&type=&language=&sort="
|
"url": "https://github.com/NabilOuldHamou?tab=repositories&q=green-plates&type=&language=&sort="
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AnimeWorld",
|
"name": "AnimeWorld",
|
||||||
"stack": ["Symfony 6", "PHP", "Nginx"],
|
"stack": ["Symfony 6", "PHP", "Nginx"],
|
||||||
"description": "University group project for the Web Server Programming class. I took the initiative to deploy the website on my VPS.",
|
"shortDesc": "University group project for my Web Server Programming class.",
|
||||||
|
"description": "For my Web Server Programming class, we were tasked with creating a website using Symfony 6 and other libraries, " +
|
||||||
|
"we decided to make an anime rating website, where users can create their own accounts are rate animes using a grade from 0 to 5 and also leave a comment. " +
|
||||||
|
"This project was the occasion for me to endorse a bit as a group leader by planning and taking care of the GitHub repository by giving some rules to follow to not compromise the repository. " +
|
||||||
|
"This was also the occasion for me to work once again on deploying a website on a Linux VPS by using Nginx and having to come up with the configuration needed for our website and also configure mailer, DNS and other domain related stuff.",
|
||||||
"url": "https://github.com/luxray555/projetsymfony"
|
"url": "https://github.com/luxray555/projetsymfony"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "EMG reader",
|
"name": "EMG reader",
|
||||||
"stack": ["C", "Arduino"],
|
"stack": ["C", "Arduino"],
|
||||||
"description": "EMG reader for a friend in Biomedical Masters. The source code is private.",
|
"shortDesc": "EMG reader for a friend in Biomedical Masters.",
|
||||||
|
"description": "A friend in Biomedical Masters degree asked for my help to conceive a program, that would allow him to read the data from EMG using an Arduino board. " +
|
||||||
|
"I proceeded to write a program that can read the data from the EMG and send it to a computer where that data can be read and processed accordingly. " +
|
||||||
|
"My friend did not release the source code thus the project is considered proprietary.",
|
||||||
"url": ""
|
"url": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
29
src/lib/components/HamburgerMenu.svelte
Normal file
29
src/lib/components/HamburgerMenu.svelte
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import * as Drawer from "@/components/ui/drawer";
|
||||||
|
import {HamburgerMenu} from "radix-icons-svelte";
|
||||||
|
import routes from "@/Routes";
|
||||||
|
export let path;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Drawer.Root>
|
||||||
|
<Drawer.Trigger asChild let:builder>
|
||||||
|
<Button class="sm:hidden" variant="ghost" builders={[builder]}><HamburgerMenu class="w-6 h-6" /></Button>
|
||||||
|
</Drawer.Trigger>
|
||||||
|
<Drawer.Content>
|
||||||
|
<div class="mx-auto w-full max-w-sm mb-10">
|
||||||
|
<Drawer.Header>
|
||||||
|
<Drawer.Title>nbiloh.me</Drawer.Title>
|
||||||
|
</Drawer.Header>
|
||||||
|
<div class="p-4 pb-0">
|
||||||
|
<div class="flex flex-col gap-4 items-center justify-center space-x-2 text-muted-foreground">
|
||||||
|
{#each routes as route}
|
||||||
|
<Drawer.Close asChild let:builder>
|
||||||
|
<Button builders={[builder]} variant="ghost" class={`cursor-pointer ${path === route.href ? 'text-foreground font-bold' : 'hover:text-foreground'}`} href={route.href}>{route.name}</Button>
|
||||||
|
</Drawer.Close>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Drawer.Content>
|
||||||
|
</Drawer.Root>
|
17
src/lib/components/LightSwitch.svelte
Normal file
17
src/lib/components/LightSwitch.svelte
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Sun, Moon } from "radix-icons-svelte";
|
||||||
|
|
||||||
|
import {ModeWatcher, toggleMode} from "mode-watcher";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModeWatcher />
|
||||||
|
<Button on:click={toggleMode} variant="outline" size="icon">
|
||||||
|
<Sun
|
||||||
|
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0"
|
||||||
|
/>
|
||||||
|
<Moon
|
||||||
|
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100"
|
||||||
|
/>
|
||||||
|
<span class="sr-only">Toggle theme</span>
|
||||||
|
</Button>
|
|
@ -1,15 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import routes from '$lib/Routes'
|
import routes from '$lib/Routes'
|
||||||
export let path;
|
import LightSwitch from "@/components/LightSwitch.svelte";
|
||||||
|
import HamburgerMenu from "@/components/HamburgerMenu.svelte";
|
||||||
|
export let path: string;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class='h-14 w-full top-0 left-0 sticky flex justify-center items-center bg-dark-charcoal-gray'>
|
<div class='h-14 w-full top-0 left-0 sticky flex justify-center items-center bg-background'>
|
||||||
<div class='pt-4 w-4/5 flex justify-between'>
|
<div class='pt-4 w-4/5 flex justify-between items-center'>
|
||||||
<img alt="Logo" src="/images/console.png" class='w-12 h-12' />
|
<a href="/" class="font-bold">nbiloh.me</a>
|
||||||
<div class='flex items-center justify-between sm:gap-x-8 gap-x-4 font-medium text-lg'>
|
<div class='hidden sm:visible sm:flex items-center justify-between sm:gap-x-8 gap-x-4 font-medium text-lg text-muted-foreground'>
|
||||||
{#each routes as route}
|
{#each routes as route}
|
||||||
<a class={`cursor-pointer ${path === route.href ? 'text-accent' : 'hover:text-accent'}`} href={route.href}>{route.name}</a>
|
<a class={`cursor-pointer ${path === route.href ? 'text-foreground' : 'hover:text-foreground'}`} href={route.href}>{route.name}</a>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
<HamburgerMenu path={path} />
|
||||||
|
<LightSwitch />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,14 +1,57 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import * as Card from "@/components/ui/card";
|
||||||
|
import {Button, buttonVariants} from "@/components/ui/button";
|
||||||
|
import * as Dialog from "@/components/ui/dialog"
|
||||||
|
import {EyeNone, GithubLogo} from "radix-icons-svelte";
|
||||||
export let name: string;
|
export let name: string;
|
||||||
export let stack: string;
|
export let stack: string;
|
||||||
|
export let shortDescription: string;
|
||||||
export let description: string;
|
export let description: string;
|
||||||
export let url: string;
|
export let url: string;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<a target='_blank' href='{url}'>
|
<Card.Root class="max-w-xs w-[320px]">
|
||||||
<div class='rounded-md border border-gray-200 max-w-xl text-center flex flex-col items-center justify-center flex-wrap hover:border-accent transition ease-in-out duration-500'>
|
<Card.Header>
|
||||||
<h1 class='text-blue-300 text-xl font-medium'>{name}</h1>
|
<Card.Title class="font-bold">{name}</Card.Title>
|
||||||
<h3 class='text-accent w-11/12 break-words'>{stack}</h3>
|
<Card.Description>{stack}</Card.Description>
|
||||||
<p class='w-4/5'>{description}</p>
|
</Card.Header>
|
||||||
</div>
|
<Card.Content>
|
||||||
</a>
|
<p>{shortDescription}</p>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Footer class="flex justify-between items-center">
|
||||||
|
{#if url === ""}
|
||||||
|
<Button disabled="true" variant="outline">
|
||||||
|
<EyeNone class="mr-2 h-4 w-4" />
|
||||||
|
Proprietary
|
||||||
|
</Button>
|
||||||
|
{:else }
|
||||||
|
<Button href={url} target="_blank" variant="outline">
|
||||||
|
<GithubLogo class="mr-2 h-4 w-4" />
|
||||||
|
View on GitHub
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
<Dialog.Root>
|
||||||
|
<Dialog.Trigger class={buttonVariants({ variant: "default" })}>View More</Dialog.Trigger>
|
||||||
|
<Dialog.Content>
|
||||||
|
<Dialog.Header>
|
||||||
|
<Dialog.Title>{name}</Dialog.Title>
|
||||||
|
<Dialog.Description>{stack}</Dialog.Description>
|
||||||
|
</Dialog.Header>
|
||||||
|
<p>{description}</p>
|
||||||
|
<Dialog.Footer>
|
||||||
|
{#if url === ""}
|
||||||
|
<Button disabled="true" variant="outline">
|
||||||
|
<EyeNone class="mr-2 h-4 w-4" />
|
||||||
|
Proprietary
|
||||||
|
</Button>
|
||||||
|
{:else }
|
||||||
|
<Button href={url} target="_blank" variant="outline">
|
||||||
|
<GithubLogo class="mr-2 h-4 w-4" />
|
||||||
|
View on GitHub
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</Dialog.Footer>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
||||||
|
</Card.Footer>
|
||||||
|
</Card.Root>
|
19
src/lib/components/ui/avatar/avatar-fallback.svelte
Normal file
19
src/lib/components/ui/avatar/avatar-fallback.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = AvatarPrimitive.FallbackProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Fallback
|
||||||
|
class={cn(
|
||||||
|
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AvatarPrimitive.Fallback>
|
18
src/lib/components/ui/avatar/avatar-image.svelte
Normal file
18
src/lib/components/ui/avatar/avatar-image.svelte
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = AvatarPrimitive.ImageProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let src: $$Props["src"] = undefined;
|
||||||
|
export let alt: $$Props["alt"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Image
|
||||||
|
{src}
|
||||||
|
{alt}
|
||||||
|
class={cn("aspect-square h-full w-full", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
21
src/lib/components/ui/avatar/avatar.svelte
Normal file
21
src/lib/components/ui/avatar/avatar.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = AvatarPrimitive.Props;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let delayMs: $$Props["delayMs"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Root
|
||||||
|
{delayMs}
|
||||||
|
class={cn(
|
||||||
|
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</AvatarPrimitive.Root>
|
13
src/lib/components/ui/avatar/index.ts
Normal file
13
src/lib/components/ui/avatar/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Root from "./avatar.svelte";
|
||||||
|
import Image from "./avatar-image.svelte";
|
||||||
|
import Fallback from "./avatar-fallback.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Image,
|
||||||
|
Fallback,
|
||||||
|
//
|
||||||
|
Root as Avatar,
|
||||||
|
Image as AvatarImage,
|
||||||
|
Fallback as AvatarFallback
|
||||||
|
};
|
25
src/lib/components/ui/button/button.svelte
Normal file
25
src/lib/components/ui/button/button.svelte
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Button as ButtonPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import { buttonVariants, type Props, type Events } from ".";
|
||||||
|
|
||||||
|
type $$Props = Props;
|
||||||
|
type $$Events = Events;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let variant: $$Props["variant"] = "default";
|
||||||
|
export let size: $$Props["size"] = "default";
|
||||||
|
export let builders: $$Props["builders"] = [];
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ButtonPrimitive.Root
|
||||||
|
{builders}
|
||||||
|
class={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
type="button"
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</ButtonPrimitive.Root>
|
52
src/lib/components/ui/button/index.ts
Normal file
52
src/lib/components/ui/button/index.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import type { Button as ButtonPrimitive } from "bits-ui";
|
||||||
|
import { tv, type VariantProps } from "tailwind-variants";
|
||||||
|
import Root from "./button.svelte";
|
||||||
|
|
||||||
|
const buttonVariants = tv({
|
||||||
|
base: "inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
||||||
|
outline:
|
||||||
|
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline"
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2",
|
||||||
|
sm: "h-8 rounded-md px-3 text-xs",
|
||||||
|
lg: "h-10 rounded-md px-8",
|
||||||
|
icon: "h-9 w-9"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
type Variant = VariantProps<typeof buttonVariants>["variant"];
|
||||||
|
type Size = VariantProps<typeof buttonVariants>["size"];
|
||||||
|
|
||||||
|
type Props = ButtonPrimitive.Props & {
|
||||||
|
variant?: Variant;
|
||||||
|
size?: Size;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Events = ButtonPrimitive.Events;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
type Props,
|
||||||
|
type Events,
|
||||||
|
//
|
||||||
|
Root as Button,
|
||||||
|
type Props as ButtonProps,
|
||||||
|
type Events as ButtonEvents,
|
||||||
|
buttonVariants
|
||||||
|
};
|
13
src/lib/components/ui/card/card-content.svelte
Normal file
13
src/lib/components/ui/card/card-content.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("p-6 pt-0", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
src/lib/components/ui/card/card-description.svelte
Normal file
13
src/lib/components/ui/card/card-description.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLParagraphElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p class={cn("text-sm text-muted-foreground", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</p>
|
13
src/lib/components/ui/card/card-footer.svelte
Normal file
13
src/lib/components/ui/card/card-footer.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex items-center p-6 pt-0", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
13
src/lib/components/ui/card/card-header.svelte
Normal file
13
src/lib/components/ui/card/card-header.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("flex flex-col space-y-1.5 p-6", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
21
src/lib/components/ui/card/card-title.svelte
Normal file
21
src/lib/components/ui/card/card-title.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HeadingLevel } from ".";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||||
|
tag?: HeadingLevel;
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let tag: $$Props["tag"] = "h3";
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element
|
||||||
|
this={tag}
|
||||||
|
class={cn("font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</svelte:element>
|
25
src/lib/components/ui/card/card.svelte
Normal file
25
src/lib/components/ui/card/card.svelte
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<div
|
||||||
|
class={cn(
|
||||||
|
"rounded-xl border bg-card text-card-foreground shadow",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
24
src/lib/components/ui/card/index.ts
Normal file
24
src/lib/components/ui/card/index.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import Root from "./card.svelte";
|
||||||
|
import Content from "./card-content.svelte";
|
||||||
|
import Description from "./card-description.svelte";
|
||||||
|
import Footer from "./card-footer.svelte";
|
||||||
|
import Header from "./card-header.svelte";
|
||||||
|
import Title from "./card-title.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle
|
||||||
|
};
|
||||||
|
|
||||||
|
export type HeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
36
src/lib/components/ui/dialog/dialog-content.svelte
Normal file
36
src/lib/components/ui/dialog/dialog-content.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import * as Dialog from ".";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils";
|
||||||
|
import { Cross2 } from "radix-icons-svelte";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.ContentProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = flyAndScale;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
duration: 200
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Overlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg md:w-full",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<DialogPrimitive.Close
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<Cross2 class="h-4 w-4" />
|
||||||
|
<span class="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</Dialog.Portal>
|
16
src/lib/components/ui/dialog/dialog-description.svelte
Normal file
16
src/lib/components/ui/dialog/dialog-description.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.DescriptionProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
class={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Description>
|
19
src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
19
src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn(
|
||||||
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
16
src/lib/components/ui/dialog/dialog-header.svelte
Normal file
16
src/lib/components/ui/dialog/dialog-header.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
24
src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
24
src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import { fade } from "svelte/transition";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.OverlayProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let transition: $$Props["transition"] = fade;
|
||||||
|
export let transitionConfig: $$Props["transitionConfig"] = {
|
||||||
|
duration: 150
|
||||||
|
};
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
{transition}
|
||||||
|
{transitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
9
src/lib/components/ui/dialog/dialog-portal.svelte
Normal file
9
src/lib/components/ui/dialog/dialog-portal.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.PortalProps;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Portal {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Portal>
|
16
src/lib/components/ui/dialog/dialog-title.svelte
Normal file
16
src/lib/components/ui/dialog/dialog-title.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = DialogPrimitive.TitleProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DialogPrimitive.Title>
|
34
src/lib/components/ui/dialog/index.ts
Normal file
34
src/lib/components/ui/dialog/index.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
const Root = DialogPrimitive.Root;
|
||||||
|
const Trigger = DialogPrimitive.Trigger;
|
||||||
|
|
||||||
|
import Title from "./dialog-title.svelte";
|
||||||
|
import Portal from "./dialog-portal.svelte";
|
||||||
|
import Footer from "./dialog-footer.svelte";
|
||||||
|
import Header from "./dialog-header.svelte";
|
||||||
|
import Overlay from "./dialog-overlay.svelte";
|
||||||
|
import Content from "./dialog-content.svelte";
|
||||||
|
import Description from "./dialog-description.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Title,
|
||||||
|
Portal,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Trigger,
|
||||||
|
Overlay,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
//
|
||||||
|
Root as Dialog,
|
||||||
|
Title as DialogTitle,
|
||||||
|
Portal as DialogPortal,
|
||||||
|
Footer as DialogFooter,
|
||||||
|
Header as DialogHeader,
|
||||||
|
Trigger as DialogTrigger,
|
||||||
|
Overlay as DialogOverlay,
|
||||||
|
Content as DialogContent,
|
||||||
|
Description as DialogDescription
|
||||||
|
};
|
24
src/lib/components/ui/drawer/drawer-content.svelte
Normal file
24
src/lib/components/ui/drawer/drawer-content.svelte
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||||
|
import DrawerOverlay from "./drawer-overlay.svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = DrawerPrimitive.ContentProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerPrimitive.Portal>
|
||||||
|
<DrawerOverlay />
|
||||||
|
<DrawerPrimitive.Content
|
||||||
|
class={cn(
|
||||||
|
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<div class="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||||
|
<slot />
|
||||||
|
</DrawerPrimitive.Content>
|
||||||
|
</DrawerPrimitive.Portal>
|
18
src/lib/components/ui/drawer/drawer-description.svelte
Normal file
18
src/lib/components/ui/drawer/drawer-description.svelte
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = DrawerPrimitive.DescriptionProps;
|
||||||
|
|
||||||
|
export let el: $$Props["el"] = undefined;
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerPrimitive.Description
|
||||||
|
bind:el
|
||||||
|
class={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DrawerPrimitive.Description>
|
20
src/lib/components/ui/drawer/drawer-footer.svelte
Normal file
20
src/lib/components/ui/drawer/drawer-footer.svelte
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement> & {
|
||||||
|
el?: HTMLDivElement;
|
||||||
|
};
|
||||||
|
|
||||||
|
export let el: $$Props["el"] = undefined;
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={el}
|
||||||
|
class={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
19
src/lib/components/ui/drawer/drawer-header.svelte
Normal file
19
src/lib/components/ui/drawer/drawer-header.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement> & {
|
||||||
|
el?: HTMLDivElement;
|
||||||
|
};
|
||||||
|
export let el: $$Props["el"] = undefined;
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={el}
|
||||||
|
class={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</div>
|
18
src/lib/components/ui/drawer/drawer-overlay.svelte
Normal file
18
src/lib/components/ui/drawer/drawer-overlay.svelte
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = DrawerPrimitive.OverlayProps;
|
||||||
|
|
||||||
|
export let el: $$Props["el"] = undefined;
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerPrimitive.Overlay
|
||||||
|
bind:el
|
||||||
|
class={cn("fixed inset-0 z-50 bg-black/80", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DrawerPrimitive.Overlay>
|
18
src/lib/components/ui/drawer/drawer-title.svelte
Normal file
18
src/lib/components/ui/drawer/drawer-title.svelte
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = DrawerPrimitive.TitleProps;
|
||||||
|
|
||||||
|
export let el: $$Props["el"] = undefined;
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerPrimitive.Title
|
||||||
|
bind:el
|
||||||
|
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DrawerPrimitive.Title>
|
17
src/lib/components/ui/drawer/drawer.svelte
Normal file
17
src/lib/components/ui/drawer/drawer.svelte
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||||
|
|
||||||
|
type $$Props = DrawerPrimitive.Props;
|
||||||
|
export let shouldScaleBackground: $$Props["shouldScaleBackground"] = true;
|
||||||
|
export let open: $$Props["open"] = false;
|
||||||
|
export let activeSnapPoint: $$Props["activeSnapPoint"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<DrawerPrimitive.Root
|
||||||
|
{shouldScaleBackground}
|
||||||
|
bind:open
|
||||||
|
bind:activeSnapPoint
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</DrawerPrimitive.Root>
|
37
src/lib/components/ui/drawer/index.ts
Normal file
37
src/lib/components/ui/drawer/index.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Drawer as DrawerPrimitive } from "vaul-svelte";
|
||||||
|
|
||||||
|
import Root from "./drawer.svelte";
|
||||||
|
import Content from "./drawer-content.svelte";
|
||||||
|
import Description from "./drawer-description.svelte";
|
||||||
|
import Overlay from "./drawer-overlay.svelte";
|
||||||
|
import Footer from "./drawer-footer.svelte";
|
||||||
|
import Header from "./drawer-header.svelte";
|
||||||
|
import Title from "./drawer-title.svelte";
|
||||||
|
|
||||||
|
const Trigger = DrawerPrimitive.Trigger;
|
||||||
|
const Portal = DrawerPrimitive.Portal;
|
||||||
|
const Close = DrawerPrimitive.Close;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Overlay,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
Trigger,
|
||||||
|
Portal,
|
||||||
|
Close,
|
||||||
|
//
|
||||||
|
Root as Drawer,
|
||||||
|
Content as DrawerContent,
|
||||||
|
Description as DrawerDescription,
|
||||||
|
Overlay as DrawerOverlay,
|
||||||
|
Footer as DrawerFooter,
|
||||||
|
Header as DrawerHeader,
|
||||||
|
Title as DrawerTitle,
|
||||||
|
Trigger as DrawerTrigger,
|
||||||
|
Portal as DrawerPortal,
|
||||||
|
Close as DrawerClose
|
||||||
|
};
|
62
src/lib/utils.ts
Normal file
62
src/lib/utils.ts
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import { type ClassValue, clsx } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
import { cubicOut } from "svelte/easing";
|
||||||
|
import type { TransitionConfig } from "svelte/transition";
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlyAndScaleParams = {
|
||||||
|
y?: number;
|
||||||
|
x?: number;
|
||||||
|
start?: number;
|
||||||
|
duration?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const flyAndScale = (
|
||||||
|
node: Element,
|
||||||
|
params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
|
||||||
|
): TransitionConfig => {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const transform = style.transform === "none" ? "" : style.transform;
|
||||||
|
|
||||||
|
const scaleConversion = (
|
||||||
|
valueA: number,
|
||||||
|
scaleA: [number, number],
|
||||||
|
scaleB: [number, number]
|
||||||
|
) => {
|
||||||
|
const [minA, maxA] = scaleA;
|
||||||
|
const [minB, maxB] = scaleB;
|
||||||
|
|
||||||
|
const percentage = (valueA - minA) / (maxA - minA);
|
||||||
|
const valueB = percentage * (maxB - minB) + minB;
|
||||||
|
|
||||||
|
return valueB;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styleToString = (
|
||||||
|
style: Record<string, number | string | undefined>
|
||||||
|
): string => {
|
||||||
|
return Object.keys(style).reduce((str, key) => {
|
||||||
|
if (style[key] === undefined) return str;
|
||||||
|
return str + `${key}:${style[key]};`;
|
||||||
|
}, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
duration: params.duration ?? 200,
|
||||||
|
delay: 0,
|
||||||
|
css: (t) => {
|
||||||
|
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
|
||||||
|
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
|
||||||
|
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
|
||||||
|
|
||||||
|
return styleToString({
|
||||||
|
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
|
||||||
|
opacity: t
|
||||||
|
});
|
||||||
|
},
|
||||||
|
easing: cubicOut
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
<div class='flex flex-col items-center justify-center w-full h-full'>
|
<div class='flex flex-col items-center justify-center w-full h-full'>
|
||||||
<h1 class='text-lg text-accent font-bold'>404</h1>
|
<h1 class='text-lg text-foreground font-bold'>404</h1>
|
||||||
<h3>This page does not exist.</h3>
|
<h3>This page does not exist.</h3>
|
||||||
</div>
|
</div>
|
|
@ -10,11 +10,3 @@
|
||||||
<slot />
|
<slot />
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<style lang="postcss">
|
|
||||||
:global(html) {
|
|
||||||
font-family: theme(fontFamily.fira-code);
|
|
||||||
background-color: theme(colors.dark-charcoal-gray);
|
|
||||||
color: theme(colors.white-smoke);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,18 +1,44 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import FaGithub from 'svelte-icons/fa/FaGithub.svelte'
|
import {Button} from '@/components/ui/button'
|
||||||
import FaTwitter from 'svelte-icons/fa/FaTwitter.svelte';
|
import { GithubLogo } from "radix-icons-svelte";
|
||||||
import FaRegFileAlt from 'svelte-icons/fa/FaRegFileAlt.svelte';
|
import { TwitterLogo } from "radix-icons-svelte";
|
||||||
|
import { FileText } from "radix-icons-svelte";
|
||||||
|
import * as Card from "@/components/ui/card";
|
||||||
|
import * as Avatar from "@/components/ui/avatar"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class='flex flex-col justify-center items-center h-full font-bold text-center cursor-default'>
|
<div class='flex flex-col justify-center items-center h-full text-center cursor-default mt-20'>
|
||||||
<div class='w-3/5'>
|
|
||||||
<h1 class='text-xl sm:text-4xl'>Hi I'm Nabil 👋</h1>
|
|
||||||
<p class='text-lg sm:text-2xl'>A <span class='text-accent'>Computer Science</span> student 🧑🏽💻 interested in <span class='text-accent'>embedded systems engineering</span> 🔋 Currently based in France 🇫🇷</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class='w-3/5 h-10 flex mt-8 justify-evenly'>
|
<Card.Root class="max-w-xs sm:max-w-sm ease-in-out duration-500">
|
||||||
<a class='hover:text-accent cursor-pointer' href="https://github.com/nabilouldhamou" target="_blank"><FaGithub /></a>
|
<Card.Header class="flex items-center">
|
||||||
<a class='hover:text-accent cursor-pointer' href="https://twitter.com/nbil_o" target="_blank"><FaTwitter /></a>
|
<Card.Title>Nabil Ould Hamou</Card.Title>
|
||||||
<a class='hover:text-accent cursor-pointer' href="/CV.pdf" target="_blank"><FaRegFileAlt /></a>
|
<Card.Description>Computer Science Student</Card.Description>
|
||||||
|
<Avatar.Root class="h-32 w-32">
|
||||||
|
<Avatar.Image src="https://github.com/nabilouldhamou.png" alt="@nabilouldhamou" />
|
||||||
|
<Avatar.Fallback>NOH</Avatar.Fallback>
|
||||||
|
</Avatar.Root>
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Content>
|
||||||
|
<p class="text-muted-foreground">Hi I am <span class="text-foreground font-bold">Nabil</span> 👋</p>
|
||||||
|
<p class="text-muted-foreground">I am a <span class="text-foreground">computer science student</span> 👨💻 interested in <span class="text-foreground">embedded systems engineering</span> 📟.</p>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Footer class="flex flex-col justify-center gap-2">
|
||||||
|
<div class="flex justify-center gap-1.5 sm:gap-3">
|
||||||
|
<Button href="https://github.com/nabilouldhamou" target='_blank' variant="outline">
|
||||||
|
<GithubLogo class="mr-2 h-4 w-4" />
|
||||||
|
Github
|
||||||
|
</Button>
|
||||||
|
<Button href="/CV.pdf" target='_blank' variant="outline">
|
||||||
|
<FileText class="mr-2 h-4 w-4" />
|
||||||
|
CV
|
||||||
|
</Button>
|
||||||
|
<Button href="https://x.com/nbil_o" target='_blank' variant="outline">
|
||||||
|
<TwitterLogo class="mr-2 h-4 w-4" />
|
||||||
|
Twitter
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="font-bold">Contact: ouldhamounabil@gmail.com</p>
|
||||||
|
</Card.Footer>
|
||||||
|
</Card.Root>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -1,7 +1,7 @@
|
||||||
<div class='flex flex-col justify-center items-center pt-6'>
|
<div class='flex flex-col justify-center items-center pt-6'>
|
||||||
<div class='text-justify w-4/5 lg:w-3/5'>
|
<div class='text-justify w-4/5 lg:w-3/5'>
|
||||||
<h3 class='text-accent md:text-4xl text-3xl pb-5 font-bold'>About Me</h3>
|
<h1 class='md:text-4xl text-3xl pb-5 font-bold'>About Me</h1>
|
||||||
<p class='sm:text-xl text-md'>Hello I am <span class='text-accent'>Nabil Ould Hamou</span>, a <span class='text-accent'>computer science student</span>.
|
<p class='text-muted-foreground text-lg'>I am <span class='text-foreground font-bold'>Nabil Ould Hamou</span>, a <span class='text-foreground font-bold'>computer science student</span>.
|
||||||
I am currently studying computer science and making my way to become an embedded systems engineer. Since a very young age I always
|
I am currently studying computer science and making my way to become an embedded systems engineer. Since a very young age I always
|
||||||
loved tinkering, especially with electronics, anything from game consoles to computers. This curiosity led me into pursuing my dream
|
loved tinkering, especially with electronics, anything from game consoles to computers. This curiosity led me into pursuing my dream
|
||||||
of become a computer scientist.</p>
|
of become a computer scientist.</p>
|
||||||
|
|
|
@ -3,13 +3,15 @@
|
||||||
import projects from '$lib/Projects'
|
import projects from '$lib/Projects'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class='flex flex-col items-center'>
|
<div class='flex flex-col justify-center items-center pt-6'>
|
||||||
|
<div class='w-4/5 lg:w-3/5'>
|
||||||
|
<h1 class='md:text-4xl text-3xl pb-5 font-bold'>Projects</h1>
|
||||||
|
|
||||||
<h1 class='text-accent text-4xl font-bold my-4'>Projects</h1>
|
</div>
|
||||||
|
|
||||||
<div class='flex flex-col gap-6 w-4/5 mb-6 items-center justify-center'>
|
<div class='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mb-6'>
|
||||||
{#each projects as project}
|
{#each projects as project}
|
||||||
<Project name={project.name} stack={project.stack} description={project.description} url={project.url} />
|
<Project name={project.name} stack={project.stack} shortDescription={project.shortDesc} description={project.description} url={project.url} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -11,7 +11,10 @@ const config = {
|
||||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
adapter: adapter()
|
adapter: adapter(),
|
||||||
|
alias: {
|
||||||
|
"@/*": "./src/lib",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,64 @@
|
||||||
|
import { fontFamily } from "tailwindcss/defaultTheme";
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
const config = {
|
||||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
darkMode: ["class"],
|
||||||
purge: ["./src/**/*.svelte"],
|
content: ["./src/**/*.{html,js,svelte,ts}"],
|
||||||
darkMode: true,
|
safelist: ["dark"],
|
||||||
theme: {
|
theme: {
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: "2rem",
|
||||||
|
screens: {
|
||||||
|
"2xl": "1400px"
|
||||||
|
}
|
||||||
|
},
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
'dark-charcoal-gray': '#111111',
|
border: "hsl(var(--border) / <alpha-value>)",
|
||||||
'white-smoke': '#efefef',
|
input: "hsl(var(--input) / <alpha-value>)",
|
||||||
'accent': '#f9f871',
|
ring: "hsl(var(--ring) / <alpha-value>)",
|
||||||
'lighter-gray': '#333333',
|
background: "hsl(var(--background) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--foreground) / <alpha-value>)",
|
||||||
|
primary: {
|
||||||
|
DEFAULT: "hsl(var(--primary) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--primary-foreground) / <alpha-value>)"
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--secondary-foreground) / <alpha-value>)"
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--destructive-foreground) / <alpha-value>)"
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: "hsl(var(--muted) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--muted-foreground) / <alpha-value>)"
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: "hsl(var(--accent) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--accent-foreground) / <alpha-value>)"
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: "hsl(var(--popover) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--popover-foreground) / <alpha-value>)"
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: "hsl(var(--card) / <alpha-value>)",
|
||||||
|
foreground: "hsl(var(--card-foreground) / <alpha-value>)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: "var(--radius)",
|
||||||
|
md: "calc(var(--radius) - 2px)",
|
||||||
|
sm: "calc(var(--radius) - 4px)"
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
'fira-code': ['Fira Code', 'monospace']
|
sans: [...fontFamily.sans]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
|
|
Loading…
Add table
Reference in a new issue