import type { Meta, StoryObj } from '@storybook/vue3-vite' import { ref } from 'vue' import CpBottomSheet from '@/components/CpBottomSheet.vue' const meta = { title: 'Organisms/CpBottomSheet', component: CpBottomSheet, globals: { viewport: { value: 'mobile1', isRotated: false }, }, parameters: { layout: 'fullscreen', }, argTypes: { animationDuration: { control: 'number', description: 'Duration of the spring used to present the sheet, in milliseconds. Leaving reuses a third of it on a dry curve.', }, canBackdropClose: { control: 'boolean', description: 'Allow closing by tapping the backdrop', }, canSwipeClose: { control: 'boolean', description: 'Enable swipe-to-close gesture', }, expandOnContentDrag: { control: 'boolean', description: 'Let a drag started on the body move the sheet instead of scrolling it', }, preventClose: { control: 'boolean', description: 'Prevent the sheet from emitting close when dismissed', }, snapPoints: { control: 'object', description: 'Heights the sheet snaps to, in pixels or in percentage of the viewport height', }, swipeCloseThreshold: { control: 'text', description: 'Translation threshold after which the sheet closes (px or %)', }, teleportTo: { control: 'text', description: 'Selector the sheet teleports to. Defaults to Nuxt SSR-safe `#teleports` container, falling back to `body` when the host does not provide it.', }, onClose: { action: 'close' }, onOpen: { action: 'open' }, }, } satisfies Meta export default meta type Story = StoryObj /** * Default bottom sheet featuring header, body and footer slots. * Use the controls to experiment with each prop in isolation. */ export const Default: Story = { args: { animationDuration: 600, canBackdropClose: true, canSwipeClose: true, swipeCloseThreshold: '20%', }, render: (args) => ({ setup() { const isVisible = ref(false) return { args, isVisible } }, template: ` Open Bottom Sheet

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

This is the default slot content. You can put any content here.

`, }), } /** * Disable backdrop tap to close — the sheet can only be dismissed * programmatically or via swipe (when enabled). */ export const NoBackdropClose: Story = { args: { canBackdropClose: false, }, render: (args) => ({ setup() { const isVisible = ref(false) return { args, isVisible } }, template: ` Open Bottom Sheet

Tap the backdrop — the sheet stays open. Use the button below or swipe down to dismiss.

`, }), } /** * With `expandOnContentDrag`, a drag started on the body of the sheet moves the sheet * itself instead of scrolling it. The sheet opens on its smallest snap point (50%): * drag the list upwards to expand it to full height, then drag it back down — once the * list is scrolled to the top — to collapse it and eventually close it. Anywhere else in * the scroll, the body scrolls as usual. * * Turn the `expandOnContentDrag` control off to compare: the body then only ever scrolls, * and the sheet can only be resized from its header or its footer. */ export const ExpandOnContentDrag: Story = { args: { expandOnContentDrag: true, snapPoints: ['50%', '100%'], }, render: (args) => ({ setup() { const isVisible = ref(false) const destinations = [ 'Paris', 'London', 'Berlin', 'Madrid', 'Rome', 'Lisbon', 'Amsterdam', 'Vienna', 'Prague', 'Copenhagen', 'Stockholm', 'Oslo', 'Helsinki', 'Dublin', 'Brussels', 'Zurich', 'Warsaw', 'Budapest', 'Athens', 'Porto', 'Paris', 'London', 'Berlin', 'Madrid', 'Rome', 'Lisbon', 'Amsterdam', 'Vienna', 'Prague', 'Copenhagen', 'Stockholm', 'Oslo', 'Helsinki', 'Dublin', 'Brussels', 'Zurich', 'Warsaw', 'Budapest', 'Athens', 'Porto', 'Paris', 'London', 'Berlin', 'Madrid', 'Rome', 'Lisbon', 'Amsterdam', 'Vienna', 'Prague', 'Copenhagen', 'Stockholm', 'Oslo', 'Helsinki', 'Dublin', 'Brussels', 'Zurich', 'Warsaw', 'Budapest', 'Athens', 'Porto', ] return { args, isVisible, destinations } }, template: ` Open Bottom Sheet

{{ destination }}

`, }), } /** * Sheets can be nested by declaring one inside the slots of another. Every sheet teleports * to the same container, so the child is inserted after its parent and stacks on top of it * with its own backdrop — no `z-index` juggling required. * * Each level keeps its own props, height and gestures: dismiss the child by tapping its * backdrop, swiping it down or using its actions, and the parent is revealed untouched. * Closing the parent unmounts the whole chain. * * The nested levels declare no snap points, so each one measures its own header, body and * footer and shows them in full. Only the parent is given a height, wide enough to stay * visible above its children. */ export const Nested: Story = { args: { canBackdropClose: true, canSwipeClose: true, snapPoints: ['70%'], }, render: (args) => ({ setup() { const isVisible = ref(false) const isPassengerVisible = ref(false) const isSeatVisible = ref(false) const passengers = ['Ada Lovelace', 'Grace Hopper', 'Alan Turing'] const selectedPassenger = ref(passengers[0]) return { args, isVisible, isPassengerVisible, isSeatVisible, passengers, selectedPassenger } }, template: ` Open Bottom Sheet

This is the parent sheet. Open the next level to stack a second sheet on top of it.

Traveller: {{ selectedPassenger }}

Pick a traveller, or go one level deeper.

Third level. Dismiss it to fall back on level 2, still open underneath.

`, }), } /** * Disable swipe-to-close — the sheet can only be dismissed * by tapping the backdrop or using a close action. */ export const NoSwipeClose: Story = { args: { canSwipeClose: false, }, render: (args) => ({ setup() { const isVisible = ref(false) return { args, isVisible } }, template: ` Open Bottom Sheet

Swipe down will not dismiss this sheet. Use the backdrop or the button below.

`, }), }