your personal website on atproto - mirror blento.app
at improve-link-card 97 lines 2.0 kB view raw
1<script lang="ts"> 2 import { onDestroy, onMount } from 'svelte'; 3 import { Editor, type Extensions } from '@tiptap/core'; 4 import Placeholder from '@tiptap/extension-placeholder'; 5 import Paragraph from '@tiptap/extension-paragraph'; 6 import Document from '@tiptap/extension-document'; 7 import Text from '@tiptap/extension-text'; 8 import type { Item } from '$lib/types'; 9 10 let element: HTMLElement | undefined = $state(); 11 let editor: Editor | null = $state(null); 12 13 let { 14 contentDict = $bindable(), 15 key, 16 class: className, 17 placeholder = '', 18 defaultContent = '', 19 onupdate 20 }: { 21 contentDict: Record<string, any>; 22 key: string; 23 class?: string; 24 placeholder?: string; 25 defaultContent?: string; 26 onupdate?: (content: string) => void; 27 } = $props(); 28 29 const update = async () => { 30 if (!editor) return; 31 32 const text = editor.getText(); 33 34 contentDict[key] = text; 35 36 onupdate?.(text); 37 }; 38 39 onMount(async () => { 40 if (!element || editor) return; 41 42 let extensions: Extensions = [Document.configure(), Paragraph.configure(), Text.configure()]; 43 44 if (placeholder) { 45 extensions.push( 46 Placeholder.configure({ 47 placeholder: placeholder 48 }) 49 ); 50 } 51 52 editor = new Editor({ 53 element: element, 54 extensions: extensions, 55 onTransaction: () => { 56 editor = editor; 57 }, 58 onUpdate: () => { 59 update(); 60 }, 61 62 content: contentDict[key] ?? defaultContent, 63 64 editorProps: { 65 attributes: { 66 class: 'outline-none pointer-events-auto' 67 }, 68 handleKeyDown: (_view, event) => { 69 // Prevent newlines by blocking Enter key 70 if (event.key === 'Enter') { 71 return true; 72 } 73 return false; 74 } 75 } 76 }); 77 }); 78 79 onDestroy(() => { 80 if (editor) { 81 editor.destroy(); 82 } 83 }); 84</script> 85 86<span class={className} bind:this={element}></span> 87 88<style> 89 :global(.tiptap p.is-editor-empty:first-child::before) { 90 color: var(--color-base-500); 91 content: attr(data-placeholder); 92 opacity: 100%; 93 float: left; 94 height: 0; 95 pointer-events: none; 96 } 97</style>