notes

Log | Files | Refs

svelte.txt (2243B)


      1 # Svelte
      2 
      3 ## Attachment factories
      4 
      5 A useful pattern is for a function, such as tooltip in this example, to return
      6 an attachment:
      7 
      8 <script lang="ts">
      9 	import tippy from 'tippy.js';
     10 	import type { Attachment } from 'svelte/attachments';
     11 
     12 	let content = $state('Hello!');
     13 
     14 	function tooltip(content: string): Attachment {
     15 		return (element) => {
     16 			const tooltip = tippy(element, { content });
     17 			return tooltip.destroy;
     18 		};
     19 	}
     20 </script>
     21 
     22 <input bind:value={content} />
     23 
     24 <button {@attach tooltip(content)}>
     25 	Hover me
     26 </button>
     27 
     28 ## Interesting syntax here (root.svelte by rich harris):
     29 
     30 <script lang="ts">
     31 	import { afterNavigate } from '$app/navigation';
     32 	import type { Page } from '@sveltejs/kit';
     33 	import type { RenderNode } from '../types.js';
     34 
     35 	interface Props {
     36 		page: Page;
     37 		tree: RenderNode;
     38 		components: any[];
     39 		resetters: Array<(() => void) | undefined>;
     40 		form?: any;
     41 		error?: App.Error;
     42 	}
     43 
     44 	const { page, components, resetters, tree, form, error }: Props = $props();
     45 
     46 	let mounted = $state(false);
     47 	let navigated = $state(false);
     48 	let title = $state('');
     49 
     50 	afterNavigate(() => {
     51 		if (mounted) {
     52 			navigated = true;
     53 			title = document.title || 'untitled page';
     54 		} else {
     55 			mounted = true;
     56 		}
     57 	});
     58 </script>
     59 
     60 {#snippet node(n: RenderNode, depth: number)}
     61 	{const Component = $derived(n.component)}
     62 	{const Error = $derived(n.error)}
     63 	{const data = $derived(n.data)}
     64 
     65 	<svelte:boundary onerror={(_, reset) => (resetters[depth] = reset)}>
     66 		{#if n.child}
     67 			<!-- svelte-ignore binding_property_non_reactive -->
     68 			<Component bind:this={components[depth]} {data} {form} params={page.params}>
     69 				{@render node(n.child, depth + 1)}
     70 			</Component>
     71 		{:else}
     72 			<!-- svelte-ignore binding_property_non_reactive -->
     73 			<Component bind:this={components[depth]} {data} {form} params={page.params} {error} />
     74 		{/if}
     75 
     76 		{#snippet failed(error)}
     77 			<Error {error} />
     78 		{/snippet}
     79 	</svelte:boundary>
     80 {/snippet}
     81 
     82 {@render node(tree, 0)}
     83 
     84 {#if mounted}
     85 	<div
     86 		id="svelte-announcer"
     87 		aria-live="assertive"
     88 		aria-atomic="true"
     89 		style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"
     90 	>
     91 		{#if navigated}
     92 			{title}
     93 		{/if}
     94 	</div>
     95 {/if}