chore: rewrite existing shared/ui stories using snippet template pattern

This commit is contained in:
Ilia Mashkov
2026-02-22 11:25:02 +03:00
parent 30bbfa7e11
commit 0c8b8e989f
15 changed files with 403 additions and 304 deletions

View File

@@ -65,9 +65,13 @@ const selectedFilter = createFilter({
</script> </script>
<Story name="Default"> <Story name="Default">
<CheckboxFilter filter={defaultFilter} displayedLabel="Zoo" /> {#snippet template(args)}
<CheckboxFilter filter={defaultFilter} displayedLabel="Zoo" {...args} />
{/snippet}
</Story> </Story>
<Story name="Selected"> <Story name="Selected">
<CheckboxFilter filter={selectedFilter} displayedLabel="Shopping list" /> {#snippet template(args)}
<CheckboxFilter filter={selectedFilter} displayedLabel="Shopping list" {...args} />
{/snippet}
</Story> </Story>

View File

@@ -51,7 +51,9 @@ const customLabelsControl = createTypographyControl({ value: 50, min: 0, max: 10
control: defaultControl, control: defaultControl,
}} }}
> >
<ComboControl control={defaultControl} /> {#snippet template(args)}
<ComboControl control={defaultControl} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -60,7 +62,9 @@ const customLabelsControl = createTypographyControl({ value: 50, min: 0, max: 10
control: atMinimumControl, control: atMinimumControl,
}} }}
> >
<ComboControl control={atMinimumControl} /> {#snippet template(args)}
<ComboControl control={atMinimumControl} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -69,7 +73,9 @@ const customLabelsControl = createTypographyControl({ value: 50, min: 0, max: 10
control: atMaximumControl, control: atMaximumControl,
}} }}
> >
<ComboControl control={atMaximumControl} /> {#snippet template(args)}
<ComboControl control={atMaximumControl} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -78,7 +84,9 @@ const customLabelsControl = createTypographyControl({ value: 50, min: 0, max: 10
control: withFloatControl, control: withFloatControl,
}} }}
> >
<ComboControl control={withFloatControl} /> {#snippet template(args)}
<ComboControl control={withFloatControl} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -90,10 +98,13 @@ const customLabelsControl = createTypographyControl({ value: 50, min: 0, max: 10
controlLabel: 'Open font size controls', controlLabel: 'Open font size controls',
}} }}
> >
<ComboControl {#snippet template(args)}
control={customLabelsControl} <ComboControl
decreaseLabel="Decrease font size" control={customLabelsControl}
increaseLabel="Increase font size" decreaseLabel="Decrease font size"
controlLabel="Open font size controls" increaseLabel="Increase font size"
/> controlLabel="Open font size controls"
{...args}
/>
{/snippet}
</Story> </Story>

View File

@@ -52,7 +52,9 @@ const largeRangeControl = createTypographyControl({ min: 0, max: 1000, step: 10,
label: 'Size', label: 'Size',
}} }}
> >
<ComboControlV2 control={horizontalControl} orientation="horizontal" label="Size" /> {#snippet template(args)}
<ComboControlV2 control={horizontalControl} orientation="horizontal" label="Size" {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -63,7 +65,9 @@ const largeRangeControl = createTypographyControl({ min: 0, max: 1000, step: 10,
label: 'Size', label: 'Size',
}} }}
> >
<ComboControlV2 control={verticalControl} orientation="vertical" class="h-48" label="Size" /> {#snippet template(args)}
<ComboControlV2 control={verticalControl} orientation="vertical" class="h-48" label="Size" {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -74,7 +78,9 @@ const largeRangeControl = createTypographyControl({ min: 0, max: 1000, step: 10,
label: 'Opacity', label: 'Opacity',
}} }}
> >
<ComboControlV2 control={floatControl} orientation="vertical" class="h-48" label="Opacity" /> {#snippet template(args)}
<ComboControlV2 control={floatControl} orientation="vertical" class="h-48" label="Opacity" {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -85,7 +91,9 @@ const largeRangeControl = createTypographyControl({ min: 0, max: 1000, step: 10,
label: 'Size', label: 'Size',
}} }}
> >
<ComboControlV2 control={atMinControl} orientation="horizontal" label="Size" /> {#snippet template(args)}
<ComboControlV2 control={atMinControl} orientation="horizontal" label="Size" {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -96,7 +104,9 @@ const largeRangeControl = createTypographyControl({ min: 0, max: 1000, step: 10,
label: 'Size', label: 'Size',
}} }}
> >
<ComboControlV2 control={atMaxControl} orientation="horizontal" label="Size" /> {#snippet template(args)}
<ComboControlV2 control={atMaxControl} orientation="horizontal" label="Size" {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -107,5 +117,7 @@ const largeRangeControl = createTypographyControl({ min: 0, max: 1000, step: 10,
label: 'Scale', label: 'Scale',
}} }}
> >
<ComboControlV2 control={largeRangeControl} orientation="horizontal" label="Scale" /> {#snippet template(args)}
<ComboControlV2 control={largeRangeControl} orientation="horizontal" label="Scale" {...args} />
{/snippet}
</Story> </Story>

View File

@@ -55,7 +55,9 @@ let longValue = $state(
letterSpacing: 0, letterSpacing: 0,
}} }}
> >
<ContentEditable bind:text={value} /> {#snippet template(args)}
<ContentEditable bind:text={value} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -67,7 +69,9 @@ let longValue = $state(
letterSpacing: 0, letterSpacing: 0,
}} }}
> >
<ContentEditable bind:text={smallValue} /> {#snippet template(args)}
<ContentEditable bind:text={smallValue} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -79,7 +83,9 @@ let longValue = $state(
letterSpacing: 0, letterSpacing: 0,
}} }}
> >
<ContentEditable bind:text={largeValue} /> {#snippet template(args)}
<ContentEditable bind:text={largeValue} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -91,7 +97,9 @@ let longValue = $state(
letterSpacing: 0.3, letterSpacing: 0.3,
}} }}
> >
<ContentEditable bind:text={spacedValue} /> {#snippet template(args)}
<ContentEditable bind:text={spacedValue} {...args} />
{/snippet}
</Story> </Story>
<Story <Story
@@ -103,5 +111,7 @@ let longValue = $state(
letterSpacing: 0, letterSpacing: 0,
}} }}
> >
<ContentEditable bind:text={longValue} /> {#snippet template(args)}
<ContentEditable bind:text={longValue} {...args} />
{/snippet}
</Story> </Story>

View File

@@ -59,7 +59,7 @@ const { Story } = defineMeta({
</script> </script>
{/* @ts-ignore */ null} {/* @ts-ignore */ null}
<Story name="With hidden content"> <Story name="With hidden content">
{#snippet children(args)} {#snippet template(args)}
<div class="p-12 bg-slate-100 min-h-[300px] flex justify-center items-start"> <div class="p-12 bg-slate-100 min-h-[300px] flex justify-center items-start">
<ExpandableWrapper <ExpandableWrapper
{...args} {...args}
@@ -71,7 +71,7 @@ const { Story } = defineMeta({
{/* @ts-ignore */ null} {/* @ts-ignore */ null}
<Story name="Disabled" args={{ disabled: true }}> <Story name="Disabled" args={{ disabled: true }}>
{#snippet children(args)} {#snippet template(args)}
<div class="p-12 bg-slate-100 min-h-[300px] flex justify-center items-start"> <div class="p-12 bg-slate-100 min-h-[300px] flex justify-center items-start">
<ExpandableWrapper <ExpandableWrapper
{...args} {...args}
@@ -84,7 +84,7 @@ const { Story } = defineMeta({
{/* @ts-ignore */ null} {/* @ts-ignore */ null}
<Story name="With badge" args={{ badge: badgeSnippet }}> <Story name="With badge" args={{ badge: badgeSnippet }}>
{#snippet children(args)} {#snippet template(args)}
<div class="p-12 bg-slate-100 min-h-[300px] flex justify-center items-start"> <div class="p-12 bg-slate-100 min-h-[300px] flex justify-center items-start">
<ExpandableWrapper <ExpandableWrapper
{...args} {...args}

View File

@@ -17,15 +17,19 @@ const { Story } = defineMeta({
</script> </script>
<Story name="Default"> <Story name="Default">
<Footnote> {#snippet template(args)}
Footnote <Footnote {...args}>
</Footnote> Footnote
</Footnote>
{/snippet}
</Story> </Story>
<Story name="With custom render"> <Story name="With custom render">
<Footnote> {#snippet template(args)}
{#snippet render({ class: className })} <Footnote {...args}>
<span class={className}>Footnote</span> {#snippet render({ class: className })}
{/snippet} <span class={className}>Footnote</span>
</Footnote> {/snippet}
</Footnote>
{/snippet}
</Story> </Story>

View File

@@ -77,11 +77,13 @@ import XIcon from '@lucide/svelte/icons/x';
icon: chevronRightIcon, icon: chevronRightIcon,
}} }}
> >
<IconButton onclick={() => console.log('Default clicked')}> {#snippet template(args)}
{#snippet icon({ className })} <IconButton onclick={() => console.log('Default clicked')} {...args}>
<ChevronRight class={className} /> {#snippet icon({ className })}
{/snippet} <ChevronRight class={className} />
</IconButton> {/snippet}
</IconButton>
{/snippet}
</Story> </Story>
<Story <Story
@@ -91,11 +93,13 @@ import XIcon from '@lucide/svelte/icons/x';
disabled: true, disabled: true,
}} }}
> >
<div class="flex flex-col gap-4 items-center"> {#snippet template(args)}
<IconButton disabled> <div class="flex flex-col gap-4 items-center">
{#snippet icon({ className })} <IconButton disabled {...args}>
<ChevronRight class={className} /> {#snippet icon({ className })}
{/snippet} <ChevronRight class={className} />
</IconButton> {/snippet}
</div> </IconButton>
</div>
{/snippet}
</Story> </Story>

View File

@@ -50,33 +50,47 @@ const placeholder = 'Enter text';
<!-- Default Story --> <!-- Default Story -->
<Story name="Default" args={{ placeholder }}> <Story name="Default" args={{ placeholder }}>
<Input bind:value={valueDefault} {placeholder} /> {#snippet template(args)}
<Input bind:value={valueDefault} {placeholder} {...args} />
{/snippet}
</Story> </Story>
<!-- Size Variants --> <!-- Size Variants -->
<Story name="Small" args={{ placeholder }}> <Story name="Small" args={{ placeholder }}>
<Input bind:value={valueSm} {placeholder} size="sm" /> {#snippet template(args)}
<Input bind:value={valueSm} {placeholder} size="sm" {...args} />
{/snippet}
</Story> </Story>
<Story name="Medium" args={{ placeholder }}> <Story name="Medium" args={{ placeholder }}>
<Input bind:value={valueMd} {placeholder} size="md" /> {#snippet template(args)}
<Input bind:value={valueMd} {placeholder} size="md" {...args} />
{/snippet}
</Story> </Story>
<Story name="Large" args={{ placeholder }}> <Story name="Large" args={{ placeholder }}>
<Input bind:value={valueLg} {placeholder} size="lg" /> {#snippet template(args)}
<Input bind:value={valueLg} {placeholder} size="lg" {...args} />
{/snippet}
</Story> </Story>
<!-- Ghost Variant with Sizes --> <!-- Ghost Variant with Sizes -->
<Story name="Ghost Small" args={{ placeholder }}> <Story name="Ghost Small" args={{ placeholder }}>
<Input bind:value={valueGhostSm} {placeholder} variant="ghost" size="sm" /> {#snippet template(args)}
<Input bind:value={valueGhostSm} {placeholder} variant="ghost" size="sm" {...args} />
{/snippet}
</Story> </Story>
<Story name="Ghost Medium" args={{ placeholder }}> <Story name="Ghost Medium" args={{ placeholder }}>
<Input bind:value={valueGhostMd} {placeholder} variant="ghost" size="md" /> {#snippet template(args)}
<Input bind:value={valueGhostMd} {placeholder} variant="ghost" size="md" {...args} />
{/snippet}
</Story> </Story>
<Story name="Ghost Large" args={{ placeholder }}> <Story name="Ghost Large" args={{ placeholder }}>
<Input bind:value={valueGhostLg} {placeholder} variant="ghost" size="lg" /> {#snippet template(args)}
<Input bind:value={valueGhostLg} {placeholder} variant="ghost" size="lg" {...args} />
{/snippet}
</Story> </Story>
<!-- Size Comparison --> <!-- Size Comparison -->

View File

@@ -29,5 +29,7 @@ const { Story } = defineMeta({
</script> </script>
<Story name="Default"> <Story name="Default">
<Loader /> {#snippet template(args)}
<Loader {...args} />
{/snippet}
</Story> </Story>

View File

@@ -17,5 +17,7 @@ const { Story } = defineMeta({
</script> </script>
<Story name="Default"> <Story name="Default">
<Logo /> {#snippet template(args)}
<Logo {...args} />
{/snippet}
</Story> </Story>

View File

@@ -39,5 +39,7 @@ let defaultSearchValue = $state('');
placeholder: 'Type here...', placeholder: 'Type here...',
}} }}
> >
<SearchBar bind:value={defaultSearchValue} placeholder="Type here..." /> {#snippet template(args)}
<SearchBar bind:value={defaultSearchValue} placeholder="Type here..." {...args} />
{/snippet}
</Story> </Story>

View File

@@ -192,21 +192,23 @@ import SettingsIcon from '@lucide/svelte/icons/settings';
content: welcomeContent, content: welcomeContent,
}} }}
> >
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> {#snippet template(args)}
<Section index={1}> <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
{#snippet title({ className })} <Section index={1} {...args}>
<h2 class={className}>Welcome</h2> {#snippet title({ className })}
{/snippet} <h2 class={className}>Welcome</h2>
{#snippet content({ className })} {/snippet}
<div class={cn(className, 'min-w-128')}> {#snippet content({ className })}
<p class="text-lg text-text-muted"> <div class={cn(className, 'min-w-128')}>
This is the default section layout with a title and content area. The section uses a 2-column <p class="text-lg text-text-muted">
grid layout with the title on the left and content on the right. This is the default section layout with a title and content area. The section uses a
</p> 2-column grid layout with the title on the left and content on the right.
</div> </p>
{/snippet} </div>
</Section> {/snippet}
</div> </Section>
</div>
{/snippet}
</Story> </Story>
<Story <Story
@@ -216,50 +218,53 @@ import SettingsIcon from '@lucide/svelte/icons/settings';
content: stickyContent, content: stickyContent,
}} }}
> >
<div class="h-[200vh]"> {#snippet template(args)}
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> <div class="h-[200vh]">
<Section <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
id="sticky-section" <Section
index={1} id="sticky-section"
stickyTitle={true} index={1}
stickyOffset="20px" stickyTitle={true}
> stickyOffset="20px"
{#snippet title({ className })} {...args}
<h2 class={className}>Sticky Title</h2> >
{/snippet} {#snippet title({ className })}
{#snippet content({ className })} <h2 class={className}>Sticky Title</h2>
<div class={cn(className, 'min-w-128')}> {/snippet}
<p class="text-lg text-text-muted mb-4"> {#snippet content({ className })}
This section has a sticky title that stays fixed while you scroll through the content. Try <div class={cn(className, 'min-w-128')}>
scrolling down to see the effect. <p class="text-lg text-text-muted mb-4">
</p> This section has a sticky title that stays fixed while you scroll through the content.
<div class="space-y-4"> Try scrolling down to see the effect.
<p class="text-text-muted">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
</p>
<p class="text-text-muted">
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
</p>
<p class="text-text-muted">
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat
nulla pariatur.
</p>
<p class="text-text-muted">
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollim anim id est laborum.
</p>
<p class="text-text-muted">
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
laudantium.
</p> </p>
<div class="space-y-4">
<p class="text-text-muted">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua.
</p>
<p class="text-text-muted">
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat.
</p>
<p class="text-text-muted">
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu
fugiat nulla pariatur.
</p>
<p class="text-text-muted">
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollim anim id est laborum.
</p>
<p class="text-text-muted">
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium
doloremque laudantium.
</p>
</div>
</div> </div>
</div> {/snippet}
{/snippet} </Section>
</Section> </div>
</div> </div>
</div> {/snippet}
</Story> </Story>
<Story <Story
@@ -271,103 +276,110 @@ import SettingsIcon from '@lucide/svelte/icons/settings';
content: searchFontsContent, content: searchFontsContent,
}} }}
> >
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> {#snippet template(args)}
<Section index={1}> <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
{#snippet title({ className })} <Section index={1} {...args}>
<h2 class={className}>Search Fonts</h2> {#snippet title({ className })}
{/snippet} <h2 class={className}>Search Fonts</h2>
{#snippet icon({ className })} {/snippet}
<SearchIcon class={className} /> {#snippet icon({ className })}
{/snippet} <SearchIcon class={className} />
{#snippet description({ className })} {/snippet}
<span class={className}>Find your perfect typeface</span> {#snippet description({ className })}
{/snippet} <span class={className}>Find your perfect typeface</span>
{#snippet content({ className })} {/snippet}
<div class={cn(className, 'min-w-128')}> {#snippet content({ className })}
<p class="text-lg text-text-muted"> <div class={cn(className, 'min-w-128')}>
Use the search bar to find fonts by name, or use the filters to browse by category, subset, or <p class="text-lg text-text-muted">
provider. Use the search bar to find fonts by name, or use the filters to browse by category, subset,
</p> or provider.
</div> </p>
{/snippet} </div>
</Section> {/snippet}
</div> </Section>
</div>
{/snippet}
</Story> </Story>
<Story name="Multiple Sections" tags={['!autodocs']}> <Story name="Multiple Sections" tags={['!autodocs']}>
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> {#snippet template(args)}
<Section <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
id="section-1" <Section
index={1} id="section-1"
stickyTitle={true} index={1}
> stickyTitle={true}
{#snippet title({ className })} {...args}
<h2 class={className}>Typography</h2> >
{/snippet} {#snippet title({ className })}
{#snippet icon({ className })} <h2 class={className}>Typography</h2>
<SettingsIcon class={className} /> {/snippet}
{/snippet} {#snippet icon({ className })}
{#snippet description({ className })} <SettingsIcon class={className} />
<span class={className}>Adjust text appearance</span> {/snippet}
{/snippet} {#snippet description({ className })}
{#snippet content({ className })} <span class={className}>Adjust text appearance</span>
<div class={cn(className, 'min-w-128')}> {/snippet}
<p class="text-lg text-text-muted"> {#snippet content({ className })}
Control the size, weight, and line height of your text. These settings apply across the <div class={cn(className, 'min-w-128')}>
comparison view. <p class="text-lg text-text-muted">
</p> Control the size, weight, and line height of your text. These settings apply across the
</div> comparison view.
{/snippet} </p>
</Section> </div>
{/snippet}
</Section>
<Section <Section
id="section-2" id="section-2"
index={2} index={2}
stickyTitle={true} stickyTitle={true}
> {...args}
{#snippet title({ className })} >
<h2 class={className}>Font Search</h2> {#snippet title({ className })}
{/snippet} <h2 class={className}>Font Search</h2>
{#snippet icon({ className })} {/snippet}
<SearchIcon class={className} /> {#snippet icon({ className })}
{/snippet} <SearchIcon class={className} />
{#snippet description({ className })} {/snippet}
<span class={className}>Browse available typefaces</span> {#snippet description({ className })}
{/snippet} <span class={className}>Browse available typefaces</span>
{#snippet content({ className })} {/snippet}
<div class={cn(className, 'min-w-128')}> {#snippet content({ className })}
<p class="text-lg text-text-muted"> <div class={cn(className, 'min-w-128')}>
Search through our collection of fonts from Google Fonts and Fontshare. Use filters to narrow <p class="text-lg text-text-muted">
down your selection. Search through our collection of fonts from Google Fonts and Fontshare. Use filters to
</p> narrow down your selection.
</div> </p>
{/snippet} </div>
</Section> {/snippet}
</Section>
<Section <Section
id="section-3" id="section-3"
index={3} index={3}
stickyTitle={true} stickyTitle={true}
> {...args}
{#snippet title({ className })} >
<h2 class={className}>Sample List</h2> {#snippet title({ className })}
{/snippet} <h2 class={className}>Sample List</h2>
{#snippet icon({ className })} {/snippet}
<ListIcon class={className} /> {#snippet icon({ className })}
{/snippet} <ListIcon class={className} />
{#snippet description({ className })} {/snippet}
<span class={className}>Preview font samples</span> {#snippet description({ className })}
{/snippet} <span class={className}>Preview font samples</span>
{#snippet content({ className })} {/snippet}
<div class={cn(className, 'min-w-128')}> {#snippet content({ className })}
<p class="text-lg text-text-muted"> <div class={cn(className, 'min-w-128')}>
Browse through font samples with your custom text. The list is virtualized for optimal <p class="text-lg text-text-muted">
performance. Browse through font samples with your custom text. The list is virtualized for optimal
</p> performance.
</div> </p>
{/snippet} </div>
</Section> {/snippet}
</div> </Section>
</div>
{/snippet}
</Story> </Story>
<Story <Story
@@ -377,44 +389,48 @@ import SettingsIcon from '@lucide/svelte/icons/settings';
content: longContent, content: longContent,
}} }}
> >
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> {#snippet template(args)}
<Section <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
index={1} <Section
stickyTitle={true} index={1}
stickyOffset="0px" stickyTitle={true}
> stickyOffset="0px"
{#snippet title({ className })} {...args}
<h2 class={className}>Long Content</h2> >
{/snippet} {#snippet title({ className })}
{#snippet content({ className })} <h2 class={className}>Long Content</h2>
<div class={cn(className, 'min-w-128')}> {/snippet}
<div class="space-y-6"> {#snippet content({ className })}
<p class="text-lg text-text-muted"> <div class={cn(className, 'min-w-128')}>
This section demonstrates how the sticky title behaves with longer content. As you scroll <div class="space-y-6">
through this content, the title remains visible at the top of the viewport. <p class="text-lg text-text-muted">
</p> This section demonstrates how the sticky title behaves with longer content. As you
<div class="h-64 bg-background-40 rounded-lg flex items-center justify-center"> scroll through this content, the title remains visible at the top of the viewport.
<span class="text-text-muted">Content block 1</span> </p>
</div> <div class="h-64 bg-background-40 rounded-lg flex items-center justify-center">
<p class="text-text-muted"> <span class="text-text-muted">Content block 1</span>
The sticky position is achieved using CSS position: sticky with a configurable top offset. </div>
This is useful for long sections where you want to maintain context while scrolling. <p class="text-text-muted">
</p> The sticky position is achieved using CSS position: sticky with a configurable top
<div class="h-64 bg-background-40 rounded-lg flex items-center justify-center"> offset. This is useful for long sections where you want to maintain context while
<span class="text-text-muted">Content block 2</span> scrolling.
</div> </p>
<p class="text-text-muted"> <div class="h-64 bg-background-40 rounded-lg flex items-center justify-center">
The Intersection Observer API is used to detect when the section title scrolls out of view, <span class="text-text-muted">Content block 2</span>
triggering the optional onTitleStatusChange callback. </div>
</p> <p class="text-text-muted">
<div class="h-64 bg-background-40 rounded-lg flex items-center justify-center"> The Intersection Observer API is used to detect when the section title scrolls out of
<span class="text-text-muted">Content block 3</span> view, triggering the optional onTitleStatusChange callback.
</p>
<div class="h-64 bg-background-40 rounded-lg flex items-center justify-center">
<span class="text-text-muted">Content block 3</span>
</div>
</div> </div>
</div> </div>
</div> {/snippet}
{/snippet} </Section>
</Section> </div>
</div> {/snippet}
</Story> </Story>
<Story <Story
@@ -424,20 +440,22 @@ import SettingsIcon from '@lucide/svelte/icons/settings';
content: minimalContent, content: minimalContent,
}} }}
> >
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> {#snippet template(args)}
<Section> <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
{#snippet title({ className })} <Section {...args}>
<h2 class={className}>Minimal Section</h2> {#snippet title({ className })}
{/snippet} <h2 class={className}>Minimal Section</h2>
{#snippet content({ className })} {/snippet}
<div class={cn(className, 'min-w-128')}> {#snippet content({ className })}
<p class="text-text-muted"> <div class={cn(className, 'min-w-128')}>
A minimal section without index, icon, or description. Just the essentials. <p class="text-text-muted">
</p> A minimal section without index, icon, or description. Just the essentials.
</div> </p>
{/snippet} </div>
</Section> {/snippet}
</div> </Section>
</div>
{/snippet}
</Story> </Story>
<Story <Story
@@ -448,28 +466,30 @@ import SettingsIcon from '@lucide/svelte/icons/settings';
content: customContent, content: customContent,
}} }}
> >
<div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto"> {#snippet template(args)}
<Section index={42}> <div class="grid grid-cols-1 lg:grid-cols-[auto_1fr] gap-x-6 sm:gap-x-8 md:gap-x-10 lg:gap-x-12 p-8 max-w-6xl mx-auto">
{#snippet title({ className })} <Section index={42} {...args}>
<h2 class={className}>Custom Content</h2> {#snippet title({ className })}
{/snippet} <h2 class={className}>Custom Content</h2>
{#snippet description({ className })} {/snippet}
<span class={className}>With interactive elements</span> {#snippet description({ className })}
{/snippet} <span class={className}>With interactive elements</span>
{#snippet content({ className })} {/snippet}
<div class={cn(className, 'min-w-128')}> {#snippet content({ className })}
<div class="grid grid-cols-2 gap-4"> <div class={cn(className, 'min-w-128')}>
<div class="p-4 bg-background-40 rounded-lg"> <div class="grid grid-cols-2 gap-4">
<h3 class="font-semibold mb-2">Card 1</h3> <div class="p-4 bg-background-40 rounded-lg">
<p class="text-sm text-text-muted">Some content here</p> <h3 class="font-semibold mb-2">Card 1</h3>
</div> <p class="text-sm text-text-muted">Some content here</p>
<div class="p-4 bg-background-40 rounded-lg"> </div>
<h3 class="font-semibold mb-2">Card 2</h3> <div class="p-4 bg-background-40 rounded-lg">
<p class="text-sm text-text-muted">More content here</p> <h3 class="font-semibold mb-2">Card 2</h3>
<p class="text-sm text-text-muted">More content here</p>
</div>
</div> </div>
</div> </div>
</div> {/snippet}
{/snippet} </Section>
</Section> </div>
</div> {/snippet}
</Story> </Story>

View File

@@ -29,13 +29,15 @@ const { Story } = defineMeta({
animate: true, animate: true,
}} }}
> >
<div class="flex flex-col gap-4 p-4 w-full"> {#snippet template(args)}
<div class="flex flex-col gap-2 p-4 border rounded-xl border-border-subtle bg-background-40"> <div class="flex flex-col gap-4 p-4 w-full">
<div class="flex items-center justify-between mb-4"> <div class="flex flex-col gap-2 p-4 border rounded-xl border-border-subtle bg-background-40">
<Skeleton class="h-8 w-1/3" /> <div class="flex items-center justify-between mb-4">
<Skeleton class="h-8 w-8 rounded-full" /> <Skeleton class="h-8 w-1/3" {...args} />
<Skeleton class="h-8 w-8 rounded-full" {...args} />
</div>
<Skeleton class="h-32 w-full" {...args} />
</div> </div>
<Skeleton class="h-32 w-full" />
</div> </div>
</div> {/snippet}
</Story> </Story>

View File

@@ -44,13 +44,19 @@ let value = $state(50);
</script> </script>
<Story name="Horizontal" args={{ orientation: 'horizontal', min: 0, max: 100, step: 1, value }}> <Story name="Horizontal" args={{ orientation: 'horizontal', min: 0, max: 100, step: 1, value }}>
<Slider bind:value /> {#snippet template(args)}
<Slider bind:value {...args} />
{/snippet}
</Story> </Story>
<Story name="Vertical" args={{ orientation: 'vertical', min: 0, max: 100, step: 1, value }}> <Story name="Vertical" args={{ orientation: 'vertical', min: 0, max: 100, step: 1, value }}>
<Slider bind:value /> {#snippet template(args)}
<Slider bind:value {...args} />
{/snippet}
</Story> </Story>
<Story name="With Label" args={{ orientation: 'horizontal', min: 0, max: 100, step: 1, value, label: 'SIZE' }}> <Story name="With Label" args={{ orientation: 'horizontal', min: 0, max: 100, step: 1, value, label: 'SIZE' }}>
<Slider bind:value /> {#snippet template(args)}
<Slider bind:value {...args} />
{/snippet}
</Story> </Story>

View File

@@ -46,29 +46,35 @@ const emptyDataSet: string[] = [];
</script> </script>
<Story name="Small Dataset"> <Story name="Small Dataset">
<div class="h-[400px]"> {#snippet template(args)}
<VirtualList items={smallDataSet} itemHeight={40}> <div class="h-[400px]">
{#snippet children({ item })} <VirtualList items={smallDataSet} itemHeight={40} {...args}>
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div> {#snippet children({ item })}
{/snippet} <div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
</VirtualList> {/snippet}
</div> </VirtualList>
</div>
{/snippet}
</Story> </Story>
<Story name="Medium Dataset (200 items)"> <Story name="Medium Dataset (200 items)">
<div class="h-[400px]"> {#snippet template(args)}
<VirtualList items={mediumDataSet} itemHeight={40}> <div class="h-[400px]">
<VirtualList items={mediumDataSet} itemHeight={40} {...args}>
{#snippet children({ item })}
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
{/snippet}
</VirtualList>
</div>
{/snippet}
</Story>
<Story name="Empty Dataset">
{#snippet template(args)}
<VirtualList items={emptyDataSet} itemHeight={40} {...args}>
{#snippet children({ item })} {#snippet children({ item })}
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div> <div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
{/snippet} {/snippet}
</VirtualList> </VirtualList>
</div> {/snippet}
</Story>
<Story name="Empty Dataset">
<VirtualList items={emptyDataSet} itemHeight={40}>
{#snippet children({ item })}
<div class="p-2 m-0.5 rounded-sm hover:bg-accent">{item}</div>
{/snippet}
</VirtualList>
</Story> </Story>