huntabyte / bits-ui

The headless components for Svelte.
https://bits-ui.com
MIT License
1.1k stars 81 forks source link

Enable Svelte Action #611

Open julien-blanchon opened 1 month ago

julien-blanchon commented 1 month ago

Describe the feature in detail (code, mocks, or screenshots encouraged)

I would like to use custom made Svelte Motion transition (https://svelte-motion.gradientdescent.de/animation). I would be super conveniant to be able to pass the use:action to component directly without delegation.

Something like the use:useAction of svelte-material-ui would be great.

For reference:

What type of pull request would this be?

New Feature

Provide relevant links or additional information.

No response

julien-blanchon commented 1 month ago

Typically this lead to such ugly code :{

<script lang="ts">
    import { Button } from '$lib/components/external/shadcn-ui/button/index.js';
    import { Input } from '$lib/components/external/shadcn-ui/input/index.js';
    import { Label } from '$lib/components/external/shadcn-ui/label/index.js';
    import * as Popover from '$lib/components/external/shadcn-ui/popover/index.js';
    import { Motion, AnimatePresence, type Variants, type Transition } from 'svelte-motion';
    import { Popover as PopoverPrimitive } from 'bits-ui';

    let open: boolean = false;

    const transition = {
        duration: 1,
        ease: [0.32, 0.72, 0, 1]
    } as Transition;

    const variants = {
        open: {
            opacity: 1,
            scale: 1,
            filter: 'blur(0px)',
            height: 'auto'
        },
        closed: {
            opacity: 0.6,
            scale: 0,
            filter: 'blur(2px)',
            height: 0
        }
    } as Variants;
</script>

<Popover.Root portal={null} bind:open>
    <Popover.Trigger asChild let:builder>
        <Button builders={[builder]} variant="outline">Open</Button>
    </Popover.Trigger>
    <PopoverPrimitive.Content asChild let:builder>
        <AnimatePresence show={open}>
            <Motion
                layoutId="popover"
                initial="closed"
                animate={open ? 'open' : 'closed'}
                {variants}
                {transition}
                style={{
                    transformOrigin: 'top'
                }}
                let:motion
            >
                <div
                    use:motion
                    use:builder.action
                    {...builder}
                    class="z-50 w-80 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none"
                >
                    <div class="grid gap-4">
                        <div class="space-y-2">
                            <h4 class="font-medium leading-none">Dimensions</h4>
                            <p class="text-sm text-muted-foreground">Set the dimensions for the layer.</p>
                        </div>
                        <div class="grid gap-2">
                            <div class="grid grid-cols-3 items-center gap-4">
                                <Label for="width">Width</Label>
                                <Input id="width" value="100%" class="col-span-2 h-8" />
                            </div>
                            <div class="grid grid-cols-3 items-center gap-4">
                                <Label for="maxWidth">Max. width</Label>
                                <Input id="maxWidth" value="300px" class="col-span-2 h-8" />
                            </div>
                            <div class="grid grid-cols-3 items-center gap-4">
                                <Label for="height">Height</Label>
                                <Input id="height" value="25px" class="col-span-2 h-8" />
                            </div>
                            <div class="grid grid-cols-3 items-center gap-4">
                                <Label for="maxHeight">Max. height</Label>
                                <Input id="maxHeight" value="none" class="col-span-2 h-8" />
                            </div>
                        </div>
                    </div>
                </div>
            </Motion>
        </AnimatePresence>
    </PopoverPrimitive.Content>
</Popover.Root>