diff --git a/web/.prettierrc b/web/.prettierrc
index 7ff36ad..c8756c1 100644
--- a/web/.prettierrc
+++ b/web/.prettierrc
@@ -1,20 +1,22 @@
{
"useTabs": false,
+ "semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
- "plugins": [
- "prettier-plugin-svelte"
- ],
- "pluginSearchDirs": [
- "."
- ],
+ "jsxBracketSameLine": true,
"overrides": [
{
"files": "*.svelte",
"options": {
- "parser": "svelte"
+ "parser": "svelte",
+ "plugins": [
+ "prettier-plugin-svelte"
+ ]
}
}
+ ],
+ "pluginSearchDirs": [
+ "."
]
}
diff --git a/web/bun.lockb b/web/bun.lockb
index 56eee72..cbb3c04 100644
Binary files a/web/bun.lockb and b/web/bun.lockb differ
diff --git a/web/components.json b/web/components.json
index 83e6d22..0516b9c 100644
--- a/web/components.json
+++ b/web/components.json
@@ -3,7 +3,7 @@
"style": "default",
"tailwind": {
"config": "tailwind.config.js",
- "css": "src/app.postcss",
+ "css": "src/app.css",
"baseColor": "gray"
},
"aliases": {
diff --git a/web/package.json b/web/package.json
index 2d5de07..69a651e 100644
--- a/web/package.json
+++ b/web/package.json
@@ -24,16 +24,19 @@
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
+ "sveltekit-superforms": "^1.9.0",
"tailwindcss": "^3.3.2",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2",
- "vitest": "^0.34.0"
+ "vitest": "^0.34.0",
+ "zod": "^3.22.4"
},
"type": "module",
"dependencies": {
"bits-ui": "^0.9.0",
"clsx": "^2.0.0",
+ "formsnap": "^0.4.1",
"jsonwebtoken": "^9.0.2",
"lucide-svelte": "^0.292.0",
"tailwind-merge": "^2.0.0",
diff --git a/web/src/lib/components/ui/button/button.svelte b/web/src/lib/components/ui/button/button.svelte
index c08852d..f5def68 100644
--- a/web/src/lib/components/ui/button/button.svelte
+++ b/web/src/lib/components/ui/button/button.svelte
@@ -1,6 +1,7 @@
+
+
+
+ {#if isChecked}
+
+ {:else if isIndeterminate}
+
+ {/if}
+
+
diff --git a/web/src/lib/components/ui/checkbox/index.ts b/web/src/lib/components/ui/checkbox/index.ts
new file mode 100644
index 0000000..ba3b7d7
--- /dev/null
+++ b/web/src/lib/components/ui/checkbox/index.ts
@@ -0,0 +1,6 @@
+import Root from './checkbox.svelte';
+export {
+ Root,
+ //
+ Root as Checkbox,
+};
diff --git a/web/src/lib/components/ui/form/form-button.svelte b/web/src/lib/components/ui/form/form-button.svelte
new file mode 100644
index 0000000..f72c768
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-button.svelte
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/form/form-checkbox.svelte b/web/src/lib/components/ui/form/form-checkbox.svelte
new file mode 100644
index 0000000..9d57bf1
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-checkbox.svelte
@@ -0,0 +1,27 @@
+
+
+ {
+ onCheckedChange?.(v);
+ setValue(v);
+ }}
+ {...$$restProps}
+ on:click
+ on:keydown
+/>
+
diff --git a/web/src/lib/components/ui/form/form-description.svelte b/web/src/lib/components/ui/form/form-description.svelte
new file mode 100644
index 0000000..792d80a
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-description.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/form/form-input.svelte b/web/src/lib/components/ui/form/form-input.svelte
new file mode 100644
index 0000000..b7edb33
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-input.svelte
@@ -0,0 +1,28 @@
+
+
+
diff --git a/web/src/lib/components/ui/form/form-item.svelte b/web/src/lib/components/ui/form/form-item.svelte
new file mode 100644
index 0000000..9605359
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-item.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/form/form-label.svelte b/web/src/lib/components/ui/form/form-label.svelte
new file mode 100644
index 0000000..55b281d
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-label.svelte
@@ -0,0 +1,21 @@
+
+
+
diff --git a/web/src/lib/components/ui/form/form-native-select.svelte b/web/src/lib/components/ui/form/form-native-select.svelte
new file mode 100644
index 0000000..4115ebe
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-native-select.svelte
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/form/form-radio-group.svelte b/web/src/lib/components/ui/form/form-radio-group.svelte
new file mode 100644
index 0000000..bc69c50
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-radio-group.svelte
@@ -0,0 +1,22 @@
+
+
+ {
+ onValueChange?.(v);
+ setValue(v);
+ }}
+ {...$$restProps}
+>
+
+
+
diff --git a/web/src/lib/components/ui/form/form-select-trigger.svelte b/web/src/lib/components/ui/form/form-select-trigger.svelte
new file mode 100644
index 0000000..b4b1d9d
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-select-trigger.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/form/form-select.svelte b/web/src/lib/components/ui/form/form-select.svelte
new file mode 100644
index 0000000..dc5aef5
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-select.svelte
@@ -0,0 +1,20 @@
+
+
+ {
+ onSelectedChange?.(v);
+ setValue(v ? v.value : undefined);
+ }}
+ {...$$restProps}
+>
+
+
+
diff --git a/web/src/lib/components/ui/form/form-switch.svelte b/web/src/lib/components/ui/form/form-switch.svelte
new file mode 100644
index 0000000..958ac41
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-switch.svelte
@@ -0,0 +1,25 @@
+
+
+ {
+ onCheckedChange?.(v);
+ setValue(v);
+ }}
+ {...$$restProps}
+ on:click
+ on:keydown
+/>
+
diff --git a/web/src/lib/components/ui/form/form-textarea.svelte b/web/src/lib/components/ui/form/form-textarea.svelte
new file mode 100644
index 0000000..4b4a481
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-textarea.svelte
@@ -0,0 +1,32 @@
+
+
+
diff --git a/web/src/lib/components/ui/form/form-validation.svelte b/web/src/lib/components/ui/form/form-validation.svelte
new file mode 100644
index 0000000..fb741d0
--- /dev/null
+++ b/web/src/lib/components/ui/form/form-validation.svelte
@@ -0,0 +1,14 @@
+
+
+
diff --git a/web/src/lib/components/ui/form/index.ts b/web/src/lib/components/ui/form/index.ts
new file mode 100644
index 0000000..6ff040d
--- /dev/null
+++ b/web/src/lib/components/ui/form/index.ts
@@ -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,
+ 'value'
+> & {
+ value: Writable;
+};
+
+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,
+};
diff --git a/web/src/lib/components/ui/input/index.ts b/web/src/lib/components/ui/input/index.ts
new file mode 100644
index 0000000..72d9cde
--- /dev/null
+++ b/web/src/lib/components/ui/input/index.ts
@@ -0,0 +1,25 @@
+import Root from './input.svelte';
+
+type FormInputEvent = T & {
+ currentTarget: EventTarget & HTMLInputElement;
+};
+export type InputEvents = {
+ blur: FormInputEvent;
+ change: FormInputEvent;
+ click: FormInputEvent;
+ focus: FormInputEvent;
+ keydown: FormInputEvent;
+ keypress: FormInputEvent;
+ keyup: FormInputEvent;
+ mouseover: FormInputEvent;
+ mouseenter: FormInputEvent;
+ mouseleave: FormInputEvent;
+ paste: FormInputEvent;
+ input: FormInputEvent;
+};
+
+export {
+ Root,
+ //
+ Root as Input,
+};
diff --git a/web/src/lib/components/ui/input/input.svelte b/web/src/lib/components/ui/input/input.svelte
new file mode 100644
index 0000000..b62f390
--- /dev/null
+++ b/web/src/lib/components/ui/input/input.svelte
@@ -0,0 +1,33 @@
+
+
+
diff --git a/web/src/lib/components/ui/label/index.ts b/web/src/lib/components/ui/label/index.ts
new file mode 100644
index 0000000..36fb393
--- /dev/null
+++ b/web/src/lib/components/ui/label/index.ts
@@ -0,0 +1,7 @@
+import Root from './label.svelte';
+
+export {
+ Root,
+ //
+ Root as Label,
+};
diff --git a/web/src/lib/components/ui/label/label.svelte b/web/src/lib/components/ui/label/label.svelte
new file mode 100644
index 0000000..3651ae7
--- /dev/null
+++ b/web/src/lib/components/ui/label/label.svelte
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/radio-group/index.ts b/web/src/lib/components/ui/radio-group/index.ts
new file mode 100644
index 0000000..0e8b7bc
--- /dev/null
+++ b/web/src/lib/components/ui/radio-group/index.ts
@@ -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,
+};
diff --git a/web/src/lib/components/ui/radio-group/radio-group-item.svelte b/web/src/lib/components/ui/radio-group/radio-group-item.svelte
new file mode 100644
index 0000000..7d3221e
--- /dev/null
+++ b/web/src/lib/components/ui/radio-group/radio-group-item.svelte
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/radio-group/radio-group.svelte b/web/src/lib/components/ui/radio-group/radio-group.svelte
new file mode 100644
index 0000000..4f10eb6
--- /dev/null
+++ b/web/src/lib/components/ui/radio-group/radio-group.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/select/index.ts b/web/src/lib/components/ui/select/index.ts
new file mode 100644
index 0000000..d27bc48
--- /dev/null
+++ b/web/src/lib/components/ui/select/index.ts
@@ -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,
+};
diff --git a/web/src/lib/components/ui/select/select-content.svelte b/web/src/lib/components/ui/select/select-content.svelte
new file mode 100644
index 0000000..0439efe
--- /dev/null
+++ b/web/src/lib/components/ui/select/select-content.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/select/select-item.svelte b/web/src/lib/components/ui/select/select-item.svelte
new file mode 100644
index 0000000..51ee840
--- /dev/null
+++ b/web/src/lib/components/ui/select/select-item.svelte
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/select/select-label.svelte b/web/src/lib/components/ui/select/select-label.svelte
new file mode 100644
index 0000000..23defbb
--- /dev/null
+++ b/web/src/lib/components/ui/select/select-label.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/select/select-separator.svelte b/web/src/lib/components/ui/select/select-separator.svelte
new file mode 100644
index 0000000..d8c7df8
--- /dev/null
+++ b/web/src/lib/components/ui/select/select-separator.svelte
@@ -0,0 +1,14 @@
+
+
+
diff --git a/web/src/lib/components/ui/select/select-trigger.svelte b/web/src/lib/components/ui/select/select-trigger.svelte
new file mode 100644
index 0000000..cc62550
--- /dev/null
+++ b/web/src/lib/components/ui/select/select-trigger.svelte
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/ui/select/select.svelte b/web/src/lib/components/ui/select/select.svelte
new file mode 100644
index 0000000..92a5e7c
--- /dev/null
+++ b/web/src/lib/components/ui/select/select.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/switch/index.ts b/web/src/lib/components/ui/switch/index.ts
new file mode 100644
index 0000000..19be952
--- /dev/null
+++ b/web/src/lib/components/ui/switch/index.ts
@@ -0,0 +1,7 @@
+import Root from './switch.svelte';
+
+export {
+ Root,
+ //
+ Root as Switch,
+};
diff --git a/web/src/lib/components/ui/switch/switch.svelte b/web/src/lib/components/ui/switch/switch.svelte
new file mode 100644
index 0000000..b633976
--- /dev/null
+++ b/web/src/lib/components/ui/switch/switch.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/web/src/lib/components/ui/textarea/index.ts b/web/src/lib/components/ui/textarea/index.ts
new file mode 100644
index 0000000..04092b5
--- /dev/null
+++ b/web/src/lib/components/ui/textarea/index.ts
@@ -0,0 +1,28 @@
+import Root from './textarea.svelte';
+
+type FormTextareaEvent = T & {
+ currentTarget: EventTarget & HTMLTextAreaElement;
+};
+
+type TextareaEvents = {
+ blur: FormTextareaEvent;
+ change: FormTextareaEvent;
+ click: FormTextareaEvent;
+ focus: FormTextareaEvent;
+ keydown: FormTextareaEvent;
+ keypress: FormTextareaEvent;
+ keyup: FormTextareaEvent;
+ mouseover: FormTextareaEvent;
+ mouseenter: FormTextareaEvent;
+ mouseleave: FormTextareaEvent;
+ paste: FormTextareaEvent;
+ input: FormTextareaEvent;
+};
+
+export {
+ Root,
+ //
+ Root as Textarea,
+ type TextareaEvents,
+ type FormTextareaEvent,
+};
diff --git a/web/src/lib/components/ui/textarea/textarea.svelte b/web/src/lib/components/ui/textarea/textarea.svelte
new file mode 100644
index 0000000..357b394
--- /dev/null
+++ b/web/src/lib/components/ui/textarea/textarea.svelte
@@ -0,0 +1,31 @@
+
+
+