118 lines
4.1 KiB
Svelte
118 lines
4.1 KiB
Svelte
|
|
<script module>
|
||
|
|
import { defineMeta } from '@storybook/addon-svelte-csf';
|
||
|
|
import Popover from './Popover.svelte';
|
||
|
|
|
||
|
|
const { Story } = defineMeta({
|
||
|
|
title: 'Shared/Popover',
|
||
|
|
component: Popover,
|
||
|
|
tags: ['autodocs'],
|
||
|
|
parameters: {
|
||
|
|
docs: {
|
||
|
|
description: {
|
||
|
|
component:
|
||
|
|
'Anchored popover on the native Popover API (top-layer, light-dismiss, ESC, focus return). Hand-rolled side/align/offset positioning with flip + shift.',
|
||
|
|
},
|
||
|
|
story: { inline: false }, // Render stories in iframe for state isolation
|
||
|
|
},
|
||
|
|
},
|
||
|
|
argTypes: {
|
||
|
|
side: {
|
||
|
|
control: 'select',
|
||
|
|
options: ['top', 'bottom', 'left', 'right'],
|
||
|
|
description: 'Preferred side',
|
||
|
|
},
|
||
|
|
align: {
|
||
|
|
control: 'select',
|
||
|
|
options: ['start', 'center', 'end'],
|
||
|
|
description: 'Cross-axis alignment',
|
||
|
|
},
|
||
|
|
sideOffset: {
|
||
|
|
control: 'number',
|
||
|
|
description: 'Gap between trigger and content (px)',
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<script lang="ts">
|
||
|
|
import { Slider } from '$shared/ui';
|
||
|
|
|
||
|
|
let open = $state(false);
|
||
|
|
let value = $state(50);
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<Story name="Bottom">
|
||
|
|
{#snippet template()}
|
||
|
|
<div class="p-32 flex-center min-h-screen">
|
||
|
|
<Popover bind:open side="bottom" align="center" sideOffset={8}>
|
||
|
|
{#snippet trigger(props)}
|
||
|
|
<button {...props} class="surface-card-elevated px-4 py-2">Open popover</button>
|
||
|
|
{/snippet}
|
||
|
|
{#snippet children()}
|
||
|
|
<div class="surface-popover p-4 w-56">Popover content</div>
|
||
|
|
{/snippet}
|
||
|
|
</Popover>
|
||
|
|
</div>
|
||
|
|
{/snippet}
|
||
|
|
</Story>
|
||
|
|
|
||
|
|
<Story name="Top">
|
||
|
|
{#snippet template()}
|
||
|
|
<div class="p-32 flex-center min-h-screen">
|
||
|
|
<Popover bind:open side="top" align="center" sideOffset={8}>
|
||
|
|
{#snippet trigger(props)}
|
||
|
|
<button {...props} class="surface-card-elevated px-4 py-2">Open popover</button>
|
||
|
|
{/snippet}
|
||
|
|
{#snippet children()}
|
||
|
|
<div class="surface-popover p-4 w-56">Popover content</div>
|
||
|
|
{/snippet}
|
||
|
|
</Popover>
|
||
|
|
</div>
|
||
|
|
{/snippet}
|
||
|
|
</Story>
|
||
|
|
|
||
|
|
<!--
|
||
|
|
Mirrors TypographyMenu: top/end placement with a programmatic Close button
|
||
|
|
wired to the `close()` param of the children snippet.
|
||
|
|
-->
|
||
|
|
<Story name="AlignedEnd">
|
||
|
|
{#snippet template()}
|
||
|
|
<div class="p-32 flex-center min-h-screen">
|
||
|
|
<Popover bind:open side="top" align="end" sideOffset={8}>
|
||
|
|
{#snippet trigger(props)}
|
||
|
|
<button {...props} class="surface-card-elevated px-4 py-2">Open menu</button>
|
||
|
|
{/snippet}
|
||
|
|
{#snippet children({ close })}
|
||
|
|
<div class="surface-popover p-4 w-72">
|
||
|
|
<h3 class="text-sm font-medium mb-3">Menu header</h3>
|
||
|
|
<p class="text-sm text-muted-foreground mb-4">
|
||
|
|
Aligned to the trigger's end edge.
|
||
|
|
</p>
|
||
|
|
<button class="surface-card-elevated px-3 py-1.5 text-sm" onclick={close}>
|
||
|
|
Close
|
||
|
|
</button>
|
||
|
|
</div>
|
||
|
|
{/snippet}
|
||
|
|
</Popover>
|
||
|
|
</div>
|
||
|
|
{/snippet}
|
||
|
|
</Story>
|
||
|
|
|
||
|
|
<!-- Mirrors ComboControl: a vertical Slider lives inside the popover content. -->
|
||
|
|
<Story name="WithSlider">
|
||
|
|
{#snippet template()}
|
||
|
|
<div class="p-32 flex-center min-h-screen">
|
||
|
|
<Popover bind:open side="top" align="center" sideOffset={8}>
|
||
|
|
{#snippet trigger(props)}
|
||
|
|
<button {...props} class="surface-card-elevated px-4 py-2">Adjust value</button>
|
||
|
|
{/snippet}
|
||
|
|
{#snippet children()}
|
||
|
|
<div class="surface-card-elevated p-3 h-64 flex-center">
|
||
|
|
<Slider orientation="vertical" min={0} max={100} bind:value />
|
||
|
|
</div>
|
||
|
|
{/snippet}
|
||
|
|
</Popover>
|
||
|
|
</div>
|
||
|
|
{/snippet}
|
||
|
|
</Story>
|