This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / strings / StringForm.svelte
3.9 kB 134 lines
1<script module lang="ts"> 2 export interface StringFormData { 3 filename: string; 4 description: string; 5 content: string; 6 } 7</script> 8 9<script lang="ts"> 10 import { untrack } from "svelte"; 11 import Button from "$lib/components/ui/Button.svelte"; 12 import Spinner from "$lib/components/ui/Spinner.svelte"; 13 import ErrorAlert from "$lib/components/ui/Error.svelte"; 14 import ArrowUp from "$icon/arrow-up"; 15 import X from "$icon/x"; 16 17 interface Props { 18 mode?: "create" | "edit"; 19 filename?: string; 20 description?: string; 21 content?: string; 22 onsubmit: (data: StringFormData) => Promise<void>; 23 oncancel?: () => void; 24 } 25 26 let { 27 mode = "create", 28 filename: initialFilename = "", 29 description: initialDescription = "", 30 content: initialContent = "", 31 onsubmit, 32 oncancel 33 }: Props = $props(); 34 35 // seed once; the props can't change while the form is open. 36 let filename = $state(untrack(() => initialFilename)); 37 let description = $state(untrack(() => initialDescription)); 38 let content = $state(untrack(() => initialContent)); 39 let isPublishing = $state(false); 40 let error = $state<string | null>(null); 41 42 const lineCount = $derived(content === "" ? 0 : content.split("\n").length); 43 const byteCount = $derived(new TextEncoder().encode(content).length); 44 45 const handleSubmit = async (e: SubmitEvent) => { 46 e.preventDefault(); 47 isPublishing = true; 48 error = null; 49 try { 50 await onsubmit({ filename, description, content }); 51 } catch (err) { 52 error = err instanceof Error ? err.message : "Failed to publish string"; 53 } finally { 54 isPublishing = false; 55 } 56 }; 57</script> 58 59<div class="rounded border border-border-default bg-background-default"> 60 <form onsubmit={handleSubmit}> 61 <div class="flex flex-col gap-2 p-4"> 62 <div class="flex flex-col gap-2 md:flex-row"> 63 <input 64 type="text" 65 id="filename" 66 name="filename" 67 placeholder="Filename" 68 required 69 bind:value={filename} 70 disabled={isPublishing} 71 class="rounded border border-border-default bg-background-default px-2 py-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50 md:max-w-64" 72 /> 73 <input 74 type="text" 75 id="description" 76 name="description" 77 placeholder="Description ..." 78 maxlength={280} 79 bind:value={description} 80 disabled={isPublishing} 81 class="flex-1 rounded border border-border-default bg-background-default px-2 py-2 text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50" 82 /> 83 </div> 84 85 <textarea 86 id="content" 87 name="content" 88 placeholder="Paste your string here!" 89 required 90 rows={20} 91 spellcheck={false} 92 bind:value={content} 93 disabled={isPublishing} 94 class="w-full resize-y rounded border border-border-default bg-background-default px-2 py-2 font-mono text-sm text-foreground-default placeholder:text-foreground-placeholder focus:ring-1 focus:ring-border-strong focus:outline-none disabled:opacity-50" 95 ></textarea> 96 </div> 97 98 <div class="flex items-center justify-between px-4 py-3"> 99 <div class="text-sm text-foreground-subtle"> 100 <span>{lineCount} line{lineCount !== 1 ? "s" : ""}</span> 101 <span class="px-1 select-none">·</span> 102 <span>{byteCount} byte{byteCount !== 1 ? "s" : ""}</span> 103 </div> 104 105 <div class="flex items-center gap-2"> 106 {#if mode === "edit"} 107 <Button 108 type="button" 109 variant="default" 110 icon={X} 111 disabled={isPublishing} 112 onclick={oncancel} 113 > 114 Cancel 115 </Button> 116 {/if} 117 <Button 118 type="submit" 119 variant="primary" 120 icon={isPublishing ? Spinner : ArrowUp} 121 disabled={isPublishing} 122 > 123 Publish 124 </Button> 125 </div> 126 </div> 127 128 {#if error} 129 <div class="px-4 pb-4"> 130 <ErrorAlert label={error} /> 131 </div> 132 {/if} 133 </form> 134</div>