mirror of
https://github.com/wireadmin/wireadmin
synced 2025-03-09 13:20:39 +00:00
add shadcn
, badge, and few more new components
This commit is contained in:
parent
5d4a2c3d51
commit
af3f37b14f
31
web/src/lib/components/copiable-text/CopiableText.svelte
Normal file
31
web/src/lib/components/copiable-text/CopiableText.svelte
Normal 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>
|
7
web/src/lib/components/copiable-text/index.ts
Normal file
7
web/src/lib/components/copiable-text/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Root from './CopiableText.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as CopiableText,
|
||||
};
|
73
web/src/lib/components/editable-text/EditableText.svelte
Normal file
73
web/src/lib/components/editable-text/EditableText.svelte
Normal 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>
|
7
web/src/lib/components/editable-text/index.ts
Normal file
7
web/src/lib/components/editable-text/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Root from './EditableText.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as EditableText
|
||||
}
|
@ -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"
|
||||
|
13
web/src/lib/components/ui/badge/badge.svelte
Normal file
13
web/src/lib/components/ui/badge/badge.svelte
Normal 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>
|
20
web/src/lib/components/ui/badge/index.ts
Normal file
20
web/src/lib/components/ui/badge/index.ts
Normal 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'];
|
92
web/src/routes/CreateServerDialog.svelte
Normal file
92
web/src/routes/CreateServerDialog.svelte
Normal 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>
|
13
web/src/routes/[serverId]/CreatePeerDialog.svelte
Normal file
13
web/src/routes/[serverId]/CreatePeerDialog.svelte
Normal 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>
|
Loading…
Reference in New Issue
Block a user