Closed jjones315 closed 5 days ago
Can you provide an example with the code of what you're trying to do? Happy to help.
I'm not really able to understand exactly what you're trying to do.
Do you mean something like this? @jjones315
<script lang="ts">
import { Button } from '$lib/components/ui/button'
import { Tooltip } from 'bits-ui'
</script>
<Tooltip.Root>
<Tooltip.Trigger>
{#snippet child({ props })}
<Button variant="ghost" {...props}>
Hover to open
</Button>
{/snippet}
</Tooltip.Trigger>
</Tooltip.Root>
Sorry, i meant to add an example. brain isn't working today 😅.
yes that is effectively what i have. Button is an internal based of next-ui styles, but shadcn works fine as a substitute, its using tallwind-variants to create classes from variants.
Toolstip.svelte
<script lang="ts">
import { cn } from "$ui/utils";
import { Tooltip } from "bits-ui";
import type { TooltipProps } from ".";
let {
delayDuration = 300,
skipDelayDuration,
disableHoverableContent,
disableCloseOnTriggerClick,
disabled,
ignoreNonKeyboardFocus,
// root
open = $bindable(false),
onOpenChange,
portal = "body",
// content
content,
contentProps,
side = "bottom",
sideOffset = 10,
transition,
// trigger
triggerRef = $bindable(),
class: className,
classes,
...otherProps
}: TooltipProps = $props();
</script>
<Tooltip.Provider
{delayDuration}
{disableCloseOnTriggerClick}
{disableHoverableContent}
{disabled}
{ignoreNonKeyboardFocus}
{skipDelayDuration}
>
<Tooltip.Root
{delayDuration}
{disableCloseOnTriggerClick}
{disableHoverableContent}
{disabled}
{ignoreNonKeyboardFocus}
{onOpenChange}
bind:open
>
<Tooltip.Trigger class={cn("outline-none", className, classes?.trigger)} tabindex={-1} {...otherProps} />
{#if content}
<Tooltip.Portal to={portal}>
<Tooltip.Content
style="transform: translate({open ? 0 : 8}px);"
class={cn(
"z-[100000] rounded-lg border bg-content1 p-2 text-sm text-foreground shadow-md transition-all",
classes?.content,
{
"scale-80 opacity-0": !open,
"translate-x-4": !open && side === "left",
"-translate-x-4": !open && side === "right",
"translate-y-4": !open && side === "top",
"-translate-y-4": !open && side === "bottom",
}
)}
{...contentProps}
forceMount
{side}
{sideOffset}
>
{#if typeof content === "string"}
{content}
{:else}
{@render content()}
{/if}
</Tooltip.Content>
</Tooltip.Portal>
{/if}
</Tooltip.Root>
</Tooltip.Provider>
example usage
<!-- works fine, but has type errors from button variants on Tooltip -->
<Tooltip
class="text-neutral-500"
color="default"
content="Edit Email"
isIconOnly
onclick={handleEditEmail}
radius="full"
size="sm"
variant="light"
>
{#snippet child({ props })}
<Button {...props}>
<Icon name="material-symbols/edit" class="text-xl" />
</Button>
{/snippet}
</Tooltip>
<!-- works fine, but have to modify every delegated component type to use mergeProps Internally -->
<Tooltip
class="text-neutral-500"
content={showPasswordText ? "Hide Password" : "Show Password"}
>
{#snippet child({ props })}
<Button
class="text-neutral-500"
as="span"
delegateProps={props}
isIconOnly
onclick={() => (showPasswordText = !showPasswordText)}
radius="full"
size="sm"
variant="light"
>
{#if showPasswordText}
<Icon
name="material-symbols/visibility-off"
class="pointer-events-none text-xl"
/>
{:else}
<Icon name="material-symbols/visibility" class="pointer-events-none text-xl" />
{/if}
</Button>
{/snippet}
</Tooltip>
<!-- this was the initial conversion i had after updating bits-ui. onclick and class are obviously broken. -->
<Tooltip
class="text-neutral-500"
content={showPasswordText ? "Hide Password" : "Show Password"}
>
{#snippet child({ props })}
<Button
class="text-neutral-500"
as="span"
isIconOnly
onclick={() => (showPasswordText = !showPasswordText)}
radius="full"
size="sm"
variant="light"
{...props}
>
{#if showPasswordText}
<Icon
name="material-symbols/visibility-off"
class="pointer-events-none text-xl"
/>
{:else}
<Icon name="material-symbols/visibility" class="pointer-events-none text-xl" />
{/if}
</Button>
{/snippet}
</Tooltip>
One other approach would be to put attributers on the Trigger
and props on the Button
, but having to explain that to every developer and onboard is going to be cumbersome and bug-ridden long term.
thanks for any advice
Oh, here you just need to modify the type you're accepting at the top level:
<script lang="ts">
import { cn } from "$ui/utils";
import { Tooltip } from "bits-ui";
import type { TooltipProps } from ".";
import type { ButtonVariants } from '$ui/somewhere'
type MyTooltipProps = TooltipProps & {
variant?: ButtonVariants;
// any other props you want to be typed...
}
// or you could do something like this
type MyTooltipProps = TooltipProps & ButtonProps
let {
// all your props
...otherProps
}: MyTooltipProps = $props();
</script>
You're combining several different components into one. Why not just inside the Toolstip.svelte
handle the delegation there instead of making consumers handle it outside each time? You could always handle it for them by default as the fallback, and if children
is passed, then the custom whatever will render.
If you can link me a repo with some minimal code for this I'm happy to help make it click.
Yes, we use a lot of different components as triggers. Buttons, chips, styled links, avatars, etc… if we always used a button I would just embed it as you mentioned. I can try to work out a minimal repo, but not sure how beneficial it will be. Since it effectively the code about with button replaces with several other things.
I know you mentioned the fact I bundled the tooltip in to a component, not sure that changes the paradigm here? The same issues would arise with them separate? Not sure if I’m missing something there
I could make my tooltip generic and to get type safe props at the tooltip level. Any thought about adding generics at a library level or do you think it’s best to leave it to user land?
Can you elaborate on what mean by generics at the library level? What would these generics be applied to?
Change Type
Addition
Proposed Changes
While testing the bits-ui@next i ran in to some speed bumps i wanted to clarify, and also possibly improve guidance in docs. When using the tooltip with the child snippet for example, i want to have a pre-styled button as the trigger. for example a shadcn button where variants create a set of classes and they are not directly passed. but the child props are overriding the styling since they are spreading. My 2 solutions are to put the variants on the Tooltip Trigger and loose types since the render delegation is not generic, or modify my button component to receive "delegateProps" and use the mergeProps utility internally. i dont think either of those soltutions are ideal, since on one hand i have to modify all of my possible delegates, or lose my variant types. was wondering if you had guidance or plan to address situtions like these.
Thanks for any guidance (and this library) :)