Basic pages

This commit is contained in:
Nabil Ould Hamou 2024-11-25 17:35:28 +01:00
parent 07b6e7b723
commit 203e20dafb
28 changed files with 784 additions and 13 deletions

14
components.json Normal file
View file

@ -0,0 +1,14 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/app.css",
"baseColor": "slate"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils"
},
"typescript": true
}

View file

@ -16,6 +16,8 @@
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"autoprefixer": "^10.4.20",
"bits-ui": "^0.21.16",
"clsx": "^2.1.1",
"eslint": "^9.7.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.36.0",
@ -26,9 +28,14 @@
"prettier-plugin-tailwindcss": "^0.6.5",
"svelte": "^5.0.0",
"svelte-check": "^4.0.0",
"tailwind-merge": "^2.5.5",
"tailwind-variants": "^0.3.0",
"tailwindcss": "^3.4.9",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0",
"vite": "^5.0.3"
},
"dependencies": {
"svelte-radix": "^2.0.1"
}
}

139
pnpm-lock.yaml generated
View file

@ -7,6 +7,10 @@ settings:
importers:
.:
dependencies:
svelte-radix:
specifier: ^2.0.1
version: 2.0.1(svelte@5.2.7)
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^3.0.0
@ -20,6 +24,12 @@ importers:
autoprefixer:
specifier: ^10.4.20
version: 10.4.20(postcss@8.4.49)
bits-ui:
specifier: ^0.21.16
version: 0.21.16(svelte@5.2.7)
clsx:
specifier: ^2.1.1
version: 2.1.1
eslint:
specifier: ^9.7.0
version: 9.15.0(jiti@1.21.6)
@ -50,6 +60,12 @@ importers:
svelte-check:
specifier: ^4.0.0
version: 4.1.0(svelte@5.2.7)(typescript@5.7.2)
tailwind-merge:
specifier: ^2.5.5
version: 2.5.5
tailwind-variants:
specifier: ^0.3.0
version: 0.3.0(tailwindcss@3.4.15)
tailwindcss:
specifier: ^3.4.9
version: 3.4.15
@ -245,6 +261,15 @@ packages:
resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.6.8':
resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
'@floating-ui/dom@1.6.12':
resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==}
'@floating-ui/utils@0.2.8':
resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
'@humanfs/core@0.19.1':
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
engines: {node: '>=18.18.0'}
@ -265,6 +290,9 @@ packages:
resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==}
engines: {node: '>=18.18'}
'@internationalized/date@3.6.0':
resolution: {integrity: sha512-+z6ti+CcJnRlLHok/emGEsWQhe7kfSmEW+/6qCzvKY67YPh7YOBfvc7+/+NXq+zJlbArg30tYpqLjNgcAYv2YQ==}
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@ -287,6 +315,11 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@melt-ui/svelte@0.76.2':
resolution: {integrity: sha512-7SbOa11tXUS95T3fReL+dwDs5FyJtCEqrqG3inRziDws346SYLsxOQ6HmX+4BkIsQh1R8U3XNa+EMmdMt38lMA==}
peerDependencies:
svelte: '>=3 <5'
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@ -425,6 +458,9 @@ packages:
svelte: ^5.0.0-next.96 || ^5.0.0
vite: ^5.0.0
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
@ -568,6 +604,11 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
bits-ui@0.21.16:
resolution: {integrity: sha512-XFZ7/bK7j/K+5iktxX/ZpmoFHjYjpPzP5EOO/4bWiaFg5TG1iMcfjDhlBTQnJxD6BoVoHuqeZPHZvaTgF4Iv3Q==}
peerDependencies:
svelte: ^4.0.0 || ^5.0.0-next.118
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
@ -606,6 +647,10 @@ packages:
resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==}
engines: {node: '>= 14.16.0'}
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
@ -649,6 +694,10 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
devalue@5.1.1:
resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==}
@ -804,6 +853,9 @@ packages:
flatted@3.3.2:
resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==}
focus-trap@7.6.2:
resolution: {integrity: sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==}
foreground-child@3.3.0:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
@ -1005,6 +1057,11 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanoid@5.0.8:
resolution: {integrity: sha512-TcJPw+9RV9dibz1hHUzlLVy8N4X9TnwirAjrU08Juo6BNKggzVfP2ZJ/3ZUSq15Xl5i85i+Z89XBO90pB2PghQ==}
engines: {node: ^18 || >=20}
hasBin: true
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@ -1338,10 +1395,28 @@ packages:
svelte:
optional: true
svelte-radix@2.0.1:
resolution: {integrity: sha512-YrX44Dj+Rp6YZuPSjdmyd6P8QTkb2NXwySUCZYzjwkP6Cl3dZaTBPPeaSOutP3v3ycQ2XwyNOpyn4p0QcN+uYQ==}
engines: {node: '>=18.0.0', npm: '>=7.0.0'}
peerDependencies:
svelte: ^5.0.0
svelte@5.2.7:
resolution: {integrity: sha512-cEhPGuLHiH2+Z8B1FwQgiZJgA39uUmJR4516TKrM5zrp0/cuwJkfhUfcTxhAkznanAF5fXUKzvYR4o+Ksx3ZCQ==}
engines: {node: '>=18'}
tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
tailwind-merge@2.5.5:
resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==}
tailwind-variants@0.3.0:
resolution: {integrity: sha512-ho2k5kn+LB1fT5XdNS3Clb96zieWxbStE9wNLK7D0AV64kdZMaYzAKo0fWl6fXLPY99ffF9oBJnIj5escEl/8A==}
engines: {node: '>=16.x', pnpm: '>=7.x'}
peerDependencies:
tailwindcss: '*'
tailwindcss@3.4.15:
resolution: {integrity: sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==}
engines: {node: '>=14.0.0'}
@ -1374,6 +1449,9 @@ packages:
ts-interface-checker@0.1.13:
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@ -1600,6 +1678,17 @@ snapshots:
dependencies:
levn: 0.4.1
'@floating-ui/core@1.6.8':
dependencies:
'@floating-ui/utils': 0.2.8
'@floating-ui/dom@1.6.12':
dependencies:
'@floating-ui/core': 1.6.8
'@floating-ui/utils': 0.2.8
'@floating-ui/utils@0.2.8': {}
'@humanfs/core@0.19.1': {}
'@humanfs/node@0.16.6':
@ -1613,6 +1702,10 @@ snapshots:
'@humanwhocodes/retry@0.4.1': {}
'@internationalized/date@3.6.0':
dependencies:
'@swc/helpers': 0.5.15
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@ -1639,6 +1732,16 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.0
'@melt-ui/svelte@0.76.2(svelte@5.2.7)':
dependencies:
'@floating-ui/core': 1.6.8
'@floating-ui/dom': 1.6.12
'@internationalized/date': 3.6.0
dequal: 2.0.3
focus-trap: 7.6.2
nanoid: 5.0.8
svelte: 5.2.7
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@ -1755,6 +1858,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
'@types/cookie@0.6.0': {}
'@types/estree@1.0.6': {}
@ -1901,6 +2008,13 @@ snapshots:
binary-extensions@2.3.0: {}
bits-ui@0.21.16(svelte@5.2.7):
dependencies:
'@internationalized/date': 3.6.0
'@melt-ui/svelte': 0.76.2(svelte@5.2.7)
nanoid: 5.0.8
svelte: 5.2.7
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
@ -1948,6 +2062,8 @@ snapshots:
dependencies:
readdirp: 4.0.2
clsx@2.1.1: {}
color-convert@2.0.1:
dependencies:
color-name: 1.1.4
@ -1976,6 +2092,8 @@ snapshots:
deepmerge@4.3.1: {}
dequal@2.0.3: {}
devalue@5.1.1: {}
didyoumean@1.2.2: {}
@ -2174,6 +2292,10 @@ snapshots:
flatted@3.3.2: {}
focus-trap@7.6.2:
dependencies:
tabbable: 6.2.0
foreground-child@3.3.0:
dependencies:
cross-spawn: 7.0.6
@ -2344,6 +2466,8 @@ snapshots:
nanoid@3.3.7: {}
nanoid@5.0.8: {}
natural-compare@1.4.0: {}
node-releases@2.0.18: {}
@ -2605,6 +2729,10 @@ snapshots:
optionalDependencies:
svelte: 5.2.7
svelte-radix@2.0.1(svelte@5.2.7):
dependencies:
svelte: 5.2.7
svelte@5.2.7:
dependencies:
'@ampproject/remapping': 2.3.0
@ -2621,6 +2749,15 @@ snapshots:
magic-string: 0.30.13
zimmerframe: 1.1.2
tabbable@6.2.0: {}
tailwind-merge@2.5.5: {}
tailwind-variants@0.3.0(tailwindcss@3.4.15):
dependencies:
tailwind-merge: 2.5.5
tailwindcss: 3.4.15
tailwindcss@3.4.15:
dependencies:
'@alloc/quick-lru': 5.2.0
@ -2673,6 +2810,8 @@ snapshots:
ts-interface-checker@0.1.13: {}
tslib@2.8.1: {}
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1

View file

@ -1,3 +1,78 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 84% 4.9%;
--card: 0 0% 100%;
--card-foreground: 222.2 84% 4.9%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 210 40% 98%;
--ring: 222.2 84% 4.9%;
--radius: 0.5rem;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--muted: 217.2 32.6% 17.5%;
--muted-foreground: 215 20.2% 65.1%;
--popover: 222.2 84% 4.9%;
--popover-foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
--card-foreground: 210 40% 98%;
--border: 217.2 32.6% 17.5%;
--input: 217.2 32.6% 17.5%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 11.2%;
--secondary: 217.2 32.6% 17.5%;
--secondary-foreground: 210 40% 98%;
--accent: 217.2 32.6% 17.5%;
--accent-foreground: 210 40% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 210 40% 98%;
--ring: 212.7 26.8% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View file

@ -0,0 +1,15 @@
<script lang="ts">
import * as Card from "$lib/components/ui/card";
export let username;
export let messageContent;
</script>
<Card.Root>
<Card.Header>
<Card.Title>{username}</Card.Title>
</Card.Header>
<Card.Content>
{messageContent}
</Card.Content>
</Card.Root>

View file

@ -0,0 +1,25 @@
<script lang="ts">
import { Button as ButtonPrimitive } from "bits-ui";
import { type Events, type Props, buttonVariants } from "./index.js";
import { cn } from "$lib/utils.js";
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>

View file

@ -0,0 +1,49 @@
import { type VariantProps, tv } from "tailwind-variants";
import type { Button as ButtonPrimitive } from "bits-ui";
import Root from "./button.svelte";
const buttonVariants = tv({
base: "ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border-input bg-background hover:bg-accent hover:text-accent-foreground border",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
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,
};

View file

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div class={cn("p-6", className)} {...$$restProps}>
<slot />
</div>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLParagraphElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<p class={cn("text-muted-foreground text-sm", className)} {...$$restProps}>
<slot />
</p>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
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>

View file

@ -0,0 +1,13 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
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 pb-0", className)} {...$$restProps}>
<slot />
</div>

View file

@ -0,0 +1,21 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import type { HeadingLevel } from "./index.js";
import { cn } from "$lib/utils.js";
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("text-lg font-semibold leading-none tracking-tight", className)}
{...$$restProps}
>
<slot />
</svelte:element>

View file

@ -0,0 +1,16 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
type $$Props = HTMLAttributes<HTMLDivElement>;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<div
class={cn("bg-card text-card-foreground rounded-lg border shadow-sm", className)}
{...$$restProps}
>
<slot />
</div>

View 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";

View file

@ -0,0 +1,29 @@
import Root from "./input.svelte";
export type FormInputEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLInputElement;
};
export type InputEvents = {
blur: FormInputEvent<FocusEvent>;
change: FormInputEvent<Event>;
click: FormInputEvent<MouseEvent>;
focus: FormInputEvent<FocusEvent>;
focusin: FormInputEvent<FocusEvent>;
focusout: FormInputEvent<FocusEvent>;
keydown: FormInputEvent<KeyboardEvent>;
keypress: FormInputEvent<KeyboardEvent>;
keyup: FormInputEvent<KeyboardEvent>;
mouseover: FormInputEvent<MouseEvent>;
mouseenter: FormInputEvent<MouseEvent>;
mouseleave: FormInputEvent<MouseEvent>;
mousemove: FormInputEvent<MouseEvent>;
paste: FormInputEvent<ClipboardEvent>;
input: FormInputEvent<InputEvent>;
wheel: FormInputEvent<WheelEvent>;
};
export {
Root,
//
Root as Input,
};

View file

@ -0,0 +1,42 @@
<script lang="ts">
import type { HTMLInputAttributes } from "svelte/elements";
import type { InputEvents } from "./index.js";
import { cn } from "$lib/utils.js";
type $$Props = HTMLInputAttributes;
type $$Events = InputEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
// Workaround for https://github.com/sveltejs/svelte/issues/9305
// Fixed in Svelte 5, but not backported to 4.x.
export let readonly: $$Props["readonly"] = undefined;
</script>
<input
class={cn(
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
{readonly}
on:blur
on:change
on:click
on:focus
on:focusin
on:focusout
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:mousemove
on:paste
on:input
on:wheel|passive
{...$$restProps}
/>

View file

@ -0,0 +1,7 @@
import Root from "./label.svelte";
export {
Root,
//
Root as Label,
};

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { Label as LabelPrimitive } from "bits-ui";
import { cn } from "$lib/utils.js";
type $$Props = LabelPrimitive.Props;
type $$Events = LabelPrimitive.Events;
let className: $$Props["class"] = undefined;
export { className as class };
</script>
<LabelPrimitive.Root
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
className
)}
{...$$restProps}
on:mousedown
>
<slot />
</LabelPrimitive.Root>

View file

@ -0,0 +1,28 @@
import Root from "./textarea.svelte";
type FormTextareaEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLTextAreaElement;
};
type TextareaEvents = {
blur: FormTextareaEvent<FocusEvent>;
change: FormTextareaEvent<Event>;
click: FormTextareaEvent<MouseEvent>;
focus: FormTextareaEvent<FocusEvent>;
keydown: FormTextareaEvent<KeyboardEvent>;
keypress: FormTextareaEvent<KeyboardEvent>;
keyup: FormTextareaEvent<KeyboardEvent>;
mouseover: FormTextareaEvent<MouseEvent>;
mouseenter: FormTextareaEvent<MouseEvent>;
mouseleave: FormTextareaEvent<MouseEvent>;
paste: FormTextareaEvent<ClipboardEvent>;
input: FormTextareaEvent<InputEvent>;
};
export {
Root,
//
Root as Textarea,
type TextareaEvents,
type FormTextareaEvent,
};

View file

@ -0,0 +1,38 @@
<script lang="ts">
import type { HTMLTextareaAttributes } from "svelte/elements";
import type { TextareaEvents } from "./index.js";
import { cn } from "$lib/utils.js";
type $$Props = HTMLTextareaAttributes;
type $$Events = TextareaEvents;
let className: $$Props["class"] = undefined;
export let value: $$Props["value"] = undefined;
export { className as class };
// Workaround for https://github.com/sveltejs/svelte/issues/9305
// Fixed in Svelte 5, but not backported to 4.x.
export let readonly: $$Props["readonly"] = undefined;
</script>
<textarea
class={cn(
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
bind:value
{readonly}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps}
></textarea>

62
src/lib/utils.ts Normal file
View 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
};
};

View file

@ -3,4 +3,6 @@
let { children } = $props();
</script>
{@render children()}
<main class="h-screen">
{@render children()}
</main>

View file

@ -1,2 +1,28 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
<script lang="ts">
import { Label } from "$lib/components/ui/label";
import { Button } from "$lib/components/ui/button";
import { Input } from "$lib/components/ui/input";
import * as Card from "$lib/components/ui/card";
</script>
<div class="w-full h-full flex justify-center items-center">
<Card.Root class="w-[450px]">
<Card.Header>
<Card.Title>🌳</Card.Title>
<Card.Description>Un chat collaboratif</Card.Description>
</Card.Header>
<Card.Content>
<div class="grid w-full max-w-sm items-center gap-1.5">
<Label for="username">Nom d'utilisateur</Label>
<Input type="text" id="username" />
</div>
<div class="pt-4 grid w-full max-w-sm items-center gap-1.5">
<Label for="password">Mot de passe</Label>
<Input type="password" id="password" />
</div>
</Card.Content>
<Card.Footer>
<Button>Se connecter</Button>
</Card.Footer>
</Card.Root>
</div>

View file

View file

@ -0,0 +1,3 @@
<script></script>
Salut

View file

@ -0,0 +1,19 @@
<script lang="ts">
import Textarea from "$lib/components/ui/textarea/textarea.svelte";
import { Button } from "$lib/components/ui/button/index.js";
import PaperPlane from "svelte-radix/PaperPlane.svelte";
import Message from "$lib/components/Message.svelte";
</script>
<div class="h-full">
<div class="px-10 flex flex-col gap-5 h-[90%]">
<Message username="Yanax" messageContent="Salut les amis" />
<Message username="Luxray" messageContent="Salut Yanax" />
</div>
<div class="fixed bottom-5 px-10 w-full flex gap-2">
<Textarea class="h-20 resize-none" placeholder="Messsage..." />
<Button size="icon" class="h-20 w-20">
<PaperPlane class="h-18 w-18" />
</Button>
</div>
</div>

View file

@ -8,6 +8,10 @@ const config = {
// for more information about preprocessors
preprocess: [vitePreprocess(), mdsvex()],
alias: {
"@/*": "./path/to/lib/*",
},
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.

View file

@ -1,11 +1,64 @@
import type { Config } from 'tailwindcss';
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
import { fontFamily } from "tailwindcss/defaultTheme";
import type { Config } from "tailwindcss";
const config: Config = {
darkMode: ["class"],
content: ["./src/**/*.{html,js,svelte,ts}"],
safelist: ["dark"],
theme: {
extend: {}
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
},
extend: {
colors: {
border: "hsl(var(--border) / <alpha-value>)",
input: "hsl(var(--input) / <alpha-value>)",
ring: "hsl(var(--ring) / <alpha-value>)",
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: {
sans: [...fontFamily.sans]
}
}
},
};
plugins: []
} satisfies Config;
export default config;