mirror of https://github.com/TZGyn/shortener
added form action to login
parent
92fd02b06f
commit
f83a24ad68
@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||||
|
import { Check, Minus } from "lucide-svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = CheckboxPrimitive.Props;
|
||||||
|
type $$Events = CheckboxPrimitive.Events;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let checked: $$Props["checked"] = false;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CheckboxPrimitive.Root
|
||||||
|
class={cn(
|
||||||
|
"box-content peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
bind:checked
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
>
|
||||||
|
<CheckboxPrimitive.Indicator
|
||||||
|
class={cn("flex items-center justify-center text-current h-4 w-4")}
|
||||||
|
let:isChecked
|
||||||
|
let:isIndeterminate
|
||||||
|
>
|
||||||
|
{#if isChecked}
|
||||||
|
<Check class="h-3.5 w-3.5" />
|
||||||
|
{:else if isIndeterminate}
|
||||||
|
<Minus class="h-3.5 w-3.5" />
|
||||||
|
{/if}
|
||||||
|
</CheckboxPrimitive.Indicator>
|
||||||
|
</CheckboxPrimitive.Root>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import Root from "./checkbox.svelte";
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
//
|
||||||
|
Root as Checkbox
|
||||||
|
};
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Button from "$lib/components/ui/button";
|
||||||
|
type $$Props = Button.Props;
|
||||||
|
type $$Events = Button.Events;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button.Root type="submit" {...$$restProps} on:click on:keydown>
|
||||||
|
<slot />
|
||||||
|
</Button.Root>
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import type { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||||
|
import { Checkbox } from "$lib/components/ui/checkbox";
|
||||||
|
type $$Props = CheckboxPrimitive.Props;
|
||||||
|
type $$Events = CheckboxPrimitive.Events;
|
||||||
|
|
||||||
|
export let onCheckedChange: $$Props["onCheckedChange"] = undefined;
|
||||||
|
|
||||||
|
const { name, setValue, attrStore, value } = getFormField();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { name: nameAttr, value: valueAttr, ...rest } = $attrStore;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Checkbox
|
||||||
|
{...rest}
|
||||||
|
checked={typeof $value === "boolean" ? $value : false}
|
||||||
|
onCheckedChange={(v) => {
|
||||||
|
onCheckedChange?.(v);
|
||||||
|
setValue(v);
|
||||||
|
}}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
/>
|
||||||
|
<input hidden {name} value={$value} />
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Form as FormPrimitive } from "formsnap";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormPrimitive.Description
|
||||||
|
class={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</FormPrimitive.Description>
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import type { HTMLInputAttributes } from "svelte/elements";
|
||||||
|
import { Input, type InputEvents } from "$lib/components/ui/input";
|
||||||
|
|
||||||
|
type $$Props = HTMLInputAttributes;
|
||||||
|
type $$Events = InputEvents;
|
||||||
|
|
||||||
|
const { attrStore, value } = getFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
{...$attrStore}
|
||||||
|
bind:value={$value}
|
||||||
|
{...$$restProps}
|
||||||
|
on:blur
|
||||||
|
on:change
|
||||||
|
on:click
|
||||||
|
on:focus
|
||||||
|
on:keydown
|
||||||
|
on:keypress
|
||||||
|
on:keyup
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:paste
|
||||||
|
on:input
|
||||||
|
/>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("space-y-2", className)} {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Label as LabelPrimitive } from "bits-ui";
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import { Label } from "$lib/components/ui/label";
|
||||||
|
|
||||||
|
type $$Props = LabelPrimitive.Props;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
const { errors, ids } = getFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Label
|
||||||
|
for={$ids.input}
|
||||||
|
class={cn($errors && "text-destructive", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</Label>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Form as FormPrimitive } from "formsnap";
|
||||||
|
import { buttonVariants } from "$lib/components/ui/button";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import { ChevronDown } from "lucide-svelte";
|
||||||
|
import type { HTMLSelectAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLSelectAttributes;
|
||||||
|
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormPrimitive.Select
|
||||||
|
class={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"appearance-none bg-transparent font-normal",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</FormPrimitive.Select>
|
||||||
|
<ChevronDown class="absolute right-3 top-2.5 h-4 w-4 opacity-50" />
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import type { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
||||||
|
import * as RadioGroup from "$lib/components/ui/radio-group";
|
||||||
|
|
||||||
|
type $$Props = RadioGroupPrimitive.Props;
|
||||||
|
const { attrStore, setValue, name, value } = getFormField();
|
||||||
|
|
||||||
|
export let onValueChange: $$Props["onValueChange"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<RadioGroup.Root
|
||||||
|
{...$attrStore}
|
||||||
|
onValueChange={(v) => {
|
||||||
|
onValueChange?.(v);
|
||||||
|
setValue(v);
|
||||||
|
}}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<input hidden {name} value={$value} />
|
||||||
|
</RadioGroup.Root>
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Select from "$lib/components/ui/select";
|
||||||
|
import type { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.TriggerProps & {
|
||||||
|
placeholder?: string;
|
||||||
|
};
|
||||||
|
type $$Events = SelectPrimitive.TriggerEvents;
|
||||||
|
const { attrStore } = getFormField();
|
||||||
|
export let placeholder = "";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Select.Trigger {...$$restProps} {...$attrStore} on:click on:keydown>
|
||||||
|
<Select.Value {placeholder} />
|
||||||
|
<slot />
|
||||||
|
</Select.Trigger>
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import * as Select from "$lib/components/ui/select";
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import type { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.Props;
|
||||||
|
const { setValue, name, value } = getFormField();
|
||||||
|
export let onSelectedChange: $$Props["onSelectedChange"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Select.Root
|
||||||
|
onSelectedChange={(v) => {
|
||||||
|
onSelectedChange?.(v);
|
||||||
|
setValue(v ? v.value : undefined);
|
||||||
|
}}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
<input hidden {name} value={$value} />
|
||||||
|
</Select.Root>
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import type { Switch as SwitchPrimitive } from "bits-ui";
|
||||||
|
import { Switch } from "$lib/components/ui/switch";
|
||||||
|
type $$Props = SwitchPrimitive.Props;
|
||||||
|
type $$Events = SwitchPrimitive.Events;
|
||||||
|
|
||||||
|
export let onCheckedChange: $$Props["onCheckedChange"] = undefined;
|
||||||
|
|
||||||
|
const { name, setValue, attrStore, value } = getFormField();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
{...$attrStore}
|
||||||
|
checked={typeof $value === "boolean" ? $value : false}
|
||||||
|
onCheckedChange={(v) => {
|
||||||
|
onCheckedChange?.(v);
|
||||||
|
setValue(v);
|
||||||
|
}}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
/>
|
||||||
|
<input hidden {name} value={$value} />
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { getFormField } from "formsnap";
|
||||||
|
import type { HTMLTextareaAttributes } from "svelte/elements";
|
||||||
|
import type { TextareaGetFormField } from ".";
|
||||||
|
import {
|
||||||
|
Textarea,
|
||||||
|
type TextareaEvents
|
||||||
|
} from "$lib/components/ui/textarea";
|
||||||
|
|
||||||
|
type $$Props = HTMLTextareaAttributes;
|
||||||
|
type $$Events = TextareaEvents;
|
||||||
|
|
||||||
|
const { attrStore, value } = getFormField() as TextareaGetFormField;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Textarea
|
||||||
|
{...$attrStore}
|
||||||
|
bind:value={$value}
|
||||||
|
{...$$restProps}
|
||||||
|
on:blur
|
||||||
|
on:change
|
||||||
|
on:click
|
||||||
|
on:focus
|
||||||
|
on:keydown
|
||||||
|
on:keypress
|
||||||
|
on:keyup
|
||||||
|
on:mouseover
|
||||||
|
on:mouseenter
|
||||||
|
on:mouseleave
|
||||||
|
on:paste
|
||||||
|
on:input
|
||||||
|
/>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Form as FormPrimitive } from "formsnap";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
type $$Props = HTMLAttributes<HTMLParagraphElement>;
|
||||||
|
let className: string | undefined | null = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormPrimitive.Validation
|
||||||
|
class={cn("text-sm font-medium text-destructive", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
import { Form as FormPrimitive, getFormField } from "formsnap";
|
||||||
|
import * as RadioGroupComp from "$lib/components/ui/radio-group";
|
||||||
|
import * as SelectComp from "$lib/components/ui/select";
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
import Item from "./form-item.svelte";
|
||||||
|
import Input from "./form-input.svelte";
|
||||||
|
import Textarea from "./form-textarea.svelte";
|
||||||
|
import Description from "./form-description.svelte";
|
||||||
|
import Label from "./form-label.svelte";
|
||||||
|
import Validation from "./form-validation.svelte";
|
||||||
|
import Checkbox from "./form-checkbox.svelte";
|
||||||
|
import Switch from "./form-switch.svelte";
|
||||||
|
import NativeSelect from "./form-native-select.svelte";
|
||||||
|
import RadioGroup from "./form-radio-group.svelte";
|
||||||
|
import Select from "./form-select.svelte";
|
||||||
|
import SelectTrigger from "./form-select-trigger.svelte";
|
||||||
|
import Button from "./form-button.svelte";
|
||||||
|
|
||||||
|
const Root = FormPrimitive.Root;
|
||||||
|
const Field = FormPrimitive.Field;
|
||||||
|
const Control = FormPrimitive.Control;
|
||||||
|
const RadioItem = RadioGroupComp.Item;
|
||||||
|
const NativeRadio = FormPrimitive.Radio;
|
||||||
|
const SelectContent = SelectComp.Content;
|
||||||
|
const SelectLabel = SelectComp.Label;
|
||||||
|
const SelectGroup = SelectComp.Group;
|
||||||
|
const SelectItem = SelectComp.Item;
|
||||||
|
const SelectSeparator = SelectComp.Separator;
|
||||||
|
|
||||||
|
export type TextareaGetFormField = Omit<
|
||||||
|
ReturnType<typeof getFormField>,
|
||||||
|
"value"
|
||||||
|
> & {
|
||||||
|
value: Writable<string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Field,
|
||||||
|
Control,
|
||||||
|
Item,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Button,
|
||||||
|
Switch,
|
||||||
|
Select,
|
||||||
|
Checkbox,
|
||||||
|
Textarea,
|
||||||
|
Validation,
|
||||||
|
RadioGroup,
|
||||||
|
RadioItem,
|
||||||
|
Description,
|
||||||
|
SelectContent,
|
||||||
|
SelectLabel,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectTrigger,
|
||||||
|
NativeSelect,
|
||||||
|
NativeRadio,
|
||||||
|
//
|
||||||
|
Root as Form,
|
||||||
|
Field as FormField,
|
||||||
|
Control as FormControl,
|
||||||
|
Item as FormItem,
|
||||||
|
Input as FormInput,
|
||||||
|
Textarea as FormTextarea,
|
||||||
|
Description as FormDescription,
|
||||||
|
Label as FormLabel,
|
||||||
|
Validation as FormValidation,
|
||||||
|
NativeSelect as FormNativeSelect,
|
||||||
|
NativeRadio as FormNativeRadio,
|
||||||
|
Checkbox as FormCheckbox,
|
||||||
|
Switch as FormSwitch,
|
||||||
|
RadioGroup as FormRadioGroup,
|
||||||
|
RadioItem as FormRadioItem,
|
||||||
|
Select as FormSelect,
|
||||||
|
SelectContent as FormSelectContent,
|
||||||
|
SelectLabel as FormSelectLabel,
|
||||||
|
SelectGroup as FormSelectGroup,
|
||||||
|
SelectItem as FormSelectItem,
|
||||||
|
SelectSeparator as FormSelectSeparator,
|
||||||
|
SelectTrigger as FormSelectTrigger,
|
||||||
|
Button as FormButton
|
||||||
|
};
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
import Root from "./radio-group.svelte";
|
||||||
|
import Item from "./radio-group-item.svelte";
|
||||||
|
const Input = RadioGroupPrimitive.Input;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Input,
|
||||||
|
Item,
|
||||||
|
//
|
||||||
|
Root as RadioGroup,
|
||||||
|
Input as RadioGroupInput,
|
||||||
|
Item as RadioGroupItem
|
||||||
|
};
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
||||||
|
import { Circle } from "lucide-svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = RadioGroupPrimitive.ItemProps;
|
||||||
|
type $$Events = RadioGroupPrimitive.ItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let value: $$Props["value"];
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<RadioGroupPrimitive.Item
|
||||||
|
{value}
|
||||||
|
class={cn(
|
||||||
|
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<RadioGroupPrimitive.ItemIndicator>
|
||||||
|
<Circle class="h-2.5 w-2.5 fill-current text-current" />
|
||||||
|
</RadioGroupPrimitive.ItemIndicator>
|
||||||
|
</div>
|
||||||
|
</RadioGroupPrimitive.Item>
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = RadioGroupPrimitive.Props;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let value: $$Props["value"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<RadioGroupPrimitive.Root
|
||||||
|
bind:value
|
||||||
|
class={cn("grid gap-2", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</RadioGroupPrimitive.Root>
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
import Root from "./select.svelte";
|
||||||
|
import Label from "./select-label.svelte";
|
||||||
|
import Item from "./select-item.svelte";
|
||||||
|
import Content from "./select-content.svelte";
|
||||||
|
import Trigger from "./select-trigger.svelte";
|
||||||
|
import Separator from "./select-separator.svelte";
|
||||||
|
|
||||||
|
const Group = SelectPrimitive.Group;
|
||||||
|
const Input = SelectPrimitive.Input;
|
||||||
|
const Value = SelectPrimitive.Value;
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Group,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Item,
|
||||||
|
Value,
|
||||||
|
Content,
|
||||||
|
Trigger,
|
||||||
|
Separator,
|
||||||
|
//
|
||||||
|
Root as Select,
|
||||||
|
Group as SelectGroup,
|
||||||
|
Input as SelectInput,
|
||||||
|
Label as SelectLabel,
|
||||||
|
Item as SelectItem,
|
||||||
|
Value as SelectValue,
|
||||||
|
Content as SelectContent,
|
||||||
|
Trigger as SelectTrigger,
|
||||||
|
Separator as SelectSeparator
|
||||||
|
};
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { cn, flyAndScale } from "$lib/utils";
|
||||||
|
import { scale } from "svelte/transition";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.ContentProps;
|
||||||
|
type $$Events = SelectPrimitive.ContentEvents;
|
||||||
|
export let inTransition: $$Props["inTransition"] = flyAndScale;
|
||||||
|
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
|
||||||
|
export let outTransition: $$Props["outTransition"] = scale;
|
||||||
|
export let outTransitionConfig: $$Props["outTransitionConfig"] = {
|
||||||
|
start: 0.95,
|
||||||
|
opacity: 0,
|
||||||
|
duration: 50
|
||||||
|
};
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
{inTransition}
|
||||||
|
{inTransitionConfig}
|
||||||
|
{outTransition}
|
||||||
|
{outTransitionConfig}
|
||||||
|
class={cn(
|
||||||
|
"relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md outline-none",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:keydown
|
||||||
|
>
|
||||||
|
<div class="w-full p-1">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</SelectPrimitive.Content>
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { Check } from "lucide-svelte";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.ItemProps;
|
||||||
|
type $$Events = SelectPrimitive.ItemEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let value: $$Props["value"];
|
||||||
|
export let label: $$Props["label"] = undefined;
|
||||||
|
export let disabled: $$Props["disabled"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
{value}
|
||||||
|
{disabled}
|
||||||
|
{label}
|
||||||
|
class={cn(
|
||||||
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
on:focusin
|
||||||
|
on:focusout
|
||||||
|
on:pointerleave
|
||||||
|
on:pointermove
|
||||||
|
>
|
||||||
|
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<Check class="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<slot />
|
||||||
|
</SelectPrimitive.Item>
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.LabelProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
class={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</SelectPrimitive.Label>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.SeparatorProps;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
class={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
import { ChevronDown } from "lucide-svelte";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.TriggerProps;
|
||||||
|
type $$Events = SelectPrimitive.TriggerEvents;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
class={cn(
|
||||||
|
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
let:builder
|
||||||
|
on:click
|
||||||
|
on:keydown
|
||||||
|
>
|
||||||
|
<slot {builder} />
|
||||||
|
<div>
|
||||||
|
<ChevronDown class="h-4 w-4 opacity-50" />
|
||||||
|
</div>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Select as SelectPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
type $$Props = SelectPrimitive.Props;
|
||||||
|
|
||||||
|
export let selected: $$Props["selected"] = undefined;
|
||||||
|
export let open: $$Props["open"] = undefined;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SelectPrimitive.Root bind:selected bind:open {...$$restProps}>
|
||||||
|
<slot />
|
||||||
|
</SelectPrimitive.Root>
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import Root from "./switch.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
//
|
||||||
|
Root as Switch
|
||||||
|
};
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Switch as SwitchPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = SwitchPrimitive.Props;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let checked: $$Props["checked"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SwitchPrimitive.Root
|
||||||
|
bind:checked
|
||||||
|
class={cn(
|
||||||
|
"peer inline-flex h-[24px] w-[44px] shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...$$restProps}
|
||||||
|
>
|
||||||
|
<SwitchPrimitive.Thumb
|
||||||
|
class={cn(
|
||||||
|
"pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</SwitchPrimitive.Root>
|
||||||
@ -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
|
||||||
|
};
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLTextareaAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils";
|
||||||
|
|
||||||
|
type $$Props = HTMLTextareaAttributes;
|
||||||
|
|
||||||
|
let className: $$Props["class"] = undefined;
|
||||||
|
export let value: $$Props["value"] = undefined;
|
||||||
|
export { className as class };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
class={cn(
|
||||||
|
"flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
bind:value
|
||||||
|
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}
|
||||||
|
/>
|
||||||
@ -1,53 +1,25 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Button } from '$lib/components/ui/button';
|
import * as Form from '$lib/components/ui/form';
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { formSchema, type FormSchema } from '../schema';
|
||||||
import { Label } from '$lib/components/ui/label';
|
import type { SuperValidated } from 'sveltekit-superforms';
|
||||||
import { Loader2, LoaderIcon } from 'lucide-svelte';
|
|
||||||
import { cn } from '$lib/utils';
|
|
||||||
|
|
||||||
let className: string | undefined | null = undefined;
|
export let form: SuperValidated<FormSchema>;
|
||||||
export { className as class };
|
|
||||||
|
|
||||||
let isLoading = false;
|
|
||||||
async function onSubmit() {
|
|
||||||
isLoading = true;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
isLoading = false;
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn('grid gap-6', className)} {...$$restProps}>
|
<Form.Root method="POST" {form} schema={formSchema} let:config>
|
||||||
<form on:submit|preventDefault={onSubmit}>
|
<Form.Field {config} name="email">
|
||||||
<div class="grid gap-4">
|
<Form.Item>
|
||||||
<div class="grid gap-1">
|
<Form.Label>Email</Form.Label>
|
||||||
<Label for="email">Email</Label>
|
<Form.Input placeholder="name@example.com" />
|
||||||
<Input
|
<Form.Validation />
|
||||||
id="email"
|
</Form.Item>
|
||||||
placeholder="name@example.com"
|
</Form.Field>
|
||||||
type="email"
|
<Form.Field {config} name="password">
|
||||||
autocapitalize="none"
|
<Form.Item>
|
||||||
autocomplete="email"
|
<Form.Label>Password</Form.Label>
|
||||||
autocorrect="off"
|
<Form.Input type="password" placeholder="••••••••" />
|
||||||
disabled={isLoading}
|
<Form.Validation />
|
||||||
/>
|
</Form.Item>
|
||||||
</div>
|
</Form.Field>
|
||||||
<div class="grid gap-1">
|
<Form.Button class="w-full">Sign In</Form.Button>
|
||||||
<Label for="password">Password</Label>
|
</Form.Root>
|
||||||
<Input
|
|
||||||
id="password"
|
|
||||||
placeholder="••••••••"
|
|
||||||
type="password"
|
|
||||||
disabled={isLoading}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Button disabled={isLoading} type="submit" class="flex gap-2">
|
|
||||||
{#if isLoading}
|
|
||||||
<Loader2 class="animate-spin" />
|
|
||||||
{/if}
|
|
||||||
Sign In
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|||||||
@ -0,0 +1,58 @@
|
|||||||
|
import type { PageServerLoad, Actions } from './$types';
|
||||||
|
import { fail } from '@sveltejs/kit';
|
||||||
|
import { superValidate } from 'sveltekit-superforms/server';
|
||||||
|
import { formSchema } from './schema';
|
||||||
|
import { db } from '$lib/db';
|
||||||
|
import { user as userSchema } from '$lib/db/schema';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
|
export const load = (async () => {
|
||||||
|
return {
|
||||||
|
form: superValidate(formSchema),
|
||||||
|
};
|
||||||
|
}) satisfies PageServerLoad;
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
default: async (event) => {
|
||||||
|
const form = await superValidate(event, formSchema);
|
||||||
|
|
||||||
|
console.log(form);
|
||||||
|
if (!form.valid) {
|
||||||
|
return fail(400, {
|
||||||
|
form,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const users = await db
|
||||||
|
.select()
|
||||||
|
.from(userSchema)
|
||||||
|
.where(eq(userSchema.email, form.data.email));
|
||||||
|
|
||||||
|
const user = users[0];
|
||||||
|
const matchPassword =
|
||||||
|
user && (await Bun.password.verify(form.data.password, user.password));
|
||||||
|
|
||||||
|
if (user && matchPassword) {
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return fail(400, {
|
||||||
|
form,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof SyntaxError) {
|
||||||
|
return fail(400, {
|
||||||
|
form,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log(error);
|
||||||
|
return fail(400, {
|
||||||
|
form,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const formSchema = z.object({
|
||||||
|
email: z.string().email(),
|
||||||
|
password: z.string().min(8),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FormSchema = typeof formSchema;
|
||||||
Loading…
Reference in New Issue