add shadcn, badge, and few more new components

This commit is contained in:
Shahrad Elahi 2023-11-06 22:13:20 +03:30
parent 5d4a2c3d51
commit af3f37b14f
9 changed files with 258 additions and 2 deletions

View File

@ -0,0 +1,31 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
export let showInHover: boolean = false;
export let rootClass: string | undefined = undefined;
export let value: string;
let className: string | undefined = undefined;
export { className as class };
const handleCopy = () => {
navigator.clipboard.writeText(value);
};
</script>
<div class={cn('group flex items-center', rootClass)}>
<slot />
<i
aria-roledescription="Copy to clipboard"
role="button"
tabindex="0"
class={cn(
'ml-2 mb-0.5 far fa-copy cursor-pointer text-gray-400/80 hover:text-primary',
showInHover && 'group-hover:opacity-100 opacity-0',
className,
)}
on:click={handleCopy}
on:keydown={(e) => {
if (e.key === 'Enter') handleCopy();
}}
/>
</div>

View File

@ -0,0 +1,7 @@
import Root from './CopiableText.svelte';
export {
Root,
//
Root as CopiableText,
};

View File

@ -0,0 +1,73 @@
<script lang="ts">
import { cn } from '$lib/utils';
import { createEventDispatcher } from 'svelte';
import type { ZodEffects, ZodString } from 'zod';
export let editMode: boolean = false;
export let schema: ZodString | ZodEffects<any>;
export let rootClass: string | undefined = undefined;
export let inputClass: string | undefined = undefined;
export let value: string;
export let error: boolean = false;
export let asChild: boolean = false;
let className: string | undefined = undefined;
export { className as class };
const dispatch = createEventDispatcher();
const handleEnterEditMode = () => {
editMode = true;
};
const handleExitEditMode = async () => {
editMode = false;
dispatch('change', { value });
};
</script>
<div class={cn('group flex items-center leading-none gap-x-3', rootClass)}>
{#if asChild}
<slot {editMode} />
{:else}
<span class={cn(editMode ? 'hidden' : 'flex items-center gap-x-2', 'leading-none', className)}>
{value}
</span>
{/if}
<input
type="text"
class={cn(
editMode ? 'block' : 'hidden',
'w-full ring-2 ring-neutral-800 ring-offset-2 rounded transition-colors duration-200 ease-in-out outline-transparent',
inputClass,
error && 'ring-red-500 rounded',
)}
{value}
on:keydown={(e) => {
if (e.key === 'Enter') {
// @ts-ignore
let val = e.target.value ?? '';
if (!schema.safeParse(val).success) {
editMode = true;
error = true;
return;
}
value = val;
handleExitEditMode();
} else if (e.key === 'Escape') {
editMode = false;
}
}}
/>
<i
class="fal fa-pen-to-square text-sm opacity-0 group-hover:opacity-100 text-neutral-400 hover:text-primary cursor-pointer"
role="button"
tabindex="0"
aria-roledescription="Edit"
on:click={handleEnterEditMode}
on:keydown={(e) => {
if (e.key === 'Enter') handleEnterEditMode();
}}
/>
</div>

View File

@ -0,0 +1,7 @@
import Root from './EditableText.svelte';
export {
Root,
//
Root as EditableText
}

View File

@ -11,7 +11,7 @@
<h1>WireAdmin</h1>
</div>
<div class={'flex items-center gap-x-2'}>
<div class={'hidden md:flex items-center gap-x-2'}>
<a href={'https://github.com/shahradelahi/wireadmin'} title={'Giv me a star on Github'}>
<img
src={'https://img.shields.io/github/stars/shahradelahi/wireadmin.svg?style=social&label=Star'}
@ -20,7 +20,7 @@
</a>
{#if showLogout}
<a href={'/logout'} class={''} title="logout">
<a href={'/logout'} title="logout">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-red-500 hover:text-red-700"

View File

@ -0,0 +1,13 @@
<script lang="ts">
import { cn } from '$lib/utils';
import { badgeVariants, type Variant } from '.';
let className: string | undefined | null = undefined;
export let href: string | undefined = undefined;
export let variant: Variant = 'default';
export { className as class };
</script>
<svelte:element this={href ? 'a' : 'span'} {href} class={cn(badgeVariants({ variant, className }))} {...$$restProps}>
<slot />
</svelte:element>

View File

@ -0,0 +1,20 @@
import { tv, type VariantProps } from 'tailwind-variants';
export { default as Badge } from './badge.svelte';
export const badgeVariants = tv({
base: 'inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none select-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
variants: {
variant: {
default: 'bg-primary hover:bg-primary/80 border-transparent text-primary-foreground',
secondary: 'bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground',
success: 'bg-success hover:bg-success/80 border-transparent text-success-foreground',
destructive: 'bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
});
export type Variant = VariantProps<typeof badgeVariants>['variant'];

View File

@ -0,0 +1,92 @@
<script lang="ts">
import { CreateServerSchema, type CreateServerSchemaType } from './schema';
import type { SuperValidated } from 'sveltekit-superforms';
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '$lib/components/ui/dialog';
import {
Form,
FormButton,
FormDescription,
FormField,
FormInput,
FormLabel,
FormValidation,
} from '$lib/components/ui/form';
import { goto } from '$app/navigation';
import { FormItem } from '$lib/components/ui/form/index.js';
import SuperDebug from 'sveltekit-superforms/client/SuperDebug.svelte';
let form: SuperValidated<CreateServerSchemaType>;
export let isOpen = false;
</script>
<Dialog open={isOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Create Server</DialogTitle>
</DialogHeader>
<SuperDebug data={form} />
<Form
{form}
schema={CreateServerSchema}
class="space-y-3.5"
action="?/create"
method={'POST'}
let:config
options={{
onResult: ({ result }) => {
if (result.type === 'success') {
goto('/');
}
},
}}
>
<FormField {config} name={'name'}>
<FormItem>
<FormLabel>Name</FormLabel>
<FormInput placeholder={'e.g. CuteHub'} type={'text'} />
<FormValidation />
</FormItem>
</FormField>
<FormField {config} name={'address'}>
<FormItem>
<FormLabel>Address</FormLabel>
<FormInput placeholder={'e.g. 10.8.0.1'} type={'text'} />
<FormDescription>This is the Private IP Address of the server.</FormDescription>
<FormValidation />
</FormItem>
</FormField>
<FormField {config} name={'port'}>
<FormItem>
<FormLabel>Port</FormLabel>
<FormInput placeholder={'e.g. 51820'} type={'text'} />
<FormDescription>This is the port that the WireGuard server will listen on.</FormDescription>
<FormValidation />
</FormItem>
</FormField>
<FormField {config} name={'dns'}>
<FormItem>
<FormLabel>DNS</FormLabel>
<FormInput placeholder={'e.g. 1.1.1.1'} type={'text'} />
<FormDescription>Optional. This is the DNS server that will be pushed to clients.</FormDescription>
<FormValidation />
</FormItem>
</FormField>
<FormField {config} name={'mtu'}>
<FormItem>
<FormLabel>MTU</FormLabel>
<FormInput placeholder={'1350'} type={'text'} />
<FormDescription>Optional. Recommended to leave this blank.</FormDescription>
<FormValidation />
</FormItem>
</FormField>
<DialogFooter>
<FormButton type="submit">Create</FormButton>
</DialogFooter>
</Form>
</DialogContent>
</Dialog>

View File

@ -0,0 +1,13 @@
<script lang="ts">
import { Dialog, DialogContent } from '$lib/components/ui/dialog';
export let serverId: string;
export let open: boolean = false;
</script>
<Dialog {open}>
<DialogContent>
<h2>Create Peer</h2>
<p>Server Id: {serverId}</p>
</DialogContent>
</Dialog>