Skip to content

Commit

Permalink
context trigger
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte committed May 14, 2024
1 parent 4ea3399 commit af44145
Show file tree
Hide file tree
Showing 15 changed files with 261 additions and 246 deletions.
Original file line number Diff line number Diff line change
@@ -1,104 +1,92 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { getCtx, updatePositioning } from "../ctx.js";
import { box } from "runed";
import type { ContentProps } from "../index.js";
import type { Transition } from "$lib/internal/types.js";
import { createDispatcher } from "$lib/internal/events.js";
import { useMenuContent } from "$lib/bits/menu/menu.svelte.js";
import { useId } from "$lib/internal/useId.svelte.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
import { noop } from "$lib/internal/callbacks.js";
import PopperLayer from "$lib/bits/utilities/popper-layer/popper-layer.svelte";
import { isElement } from "$lib/internal/is.js";
import type { InteractOutsideEvent } from "$lib/bits/utilities/dismissable-layer/types.js";
type T = $$Generic<Transition>;
type In = $$Generic<Transition>;
type Out = $$Generic<Transition>;
type $$Props = ContentProps<T, In, Out>;
let {
id = useId(),
asChild,
child,
children,
el = $bindable(),
loop = true,
onInteractOutside = noop,
onEscapeKeydown = noop,
forceMount = false,
...restProps
}: ContentProps = $props();
export let transition: $$Props["transition"] = undefined;
export let transitionConfig: $$Props["transitionConfig"] = undefined;
export let inTransition: $$Props["inTransition"] = undefined;
export let inTransitionConfig: $$Props["inTransitionConfig"] = undefined;
export let outTransition: $$Props["outTransition"] = undefined;
export let outTransitionConfig: $$Props["outTransitionConfig"] = undefined;
export let asChild: $$Props["asChild"] = false;
export let id: $$Props["id"] = undefined;
export let alignOffset: $$Props["alignOffset"] = 0;
export let collisionPadding: $$Props["collisionPadding"] = 8;
export let avoidCollisions: $$Props["avoidCollisions"] = true;
export let collisionBoundary: $$Props["collisionBoundary"] = undefined;
export let fitViewport: $$Props["fitViewport"] = false;
export let strategy: $$Props["strategy"] = "absolute";
export let overlap: $$Props["overlap"] = false;
export let el: $$Props["el"] = undefined;
const {
elements: { menu },
states: { open },
ids,
getAttrs,
} = getCtx();
const dispatch = createDispatcher();
const attrs = getAttrs("content");
const state = useMenuContent({
id: box.with(() => id),
loop: box.with(() => loop),
});
$: if (id) {
ids.menu.set(id);
function handleInteractOutsideStart(e: InteractOutsideEvent) {
if (!isElement(e.target)) return;
if (e.target.id === state.parentMenu.triggerId.value) {
e.preventDefault();
return;
}
if (e.target.closest(`#${state.parentMenu.triggerId.value}`)) {
e.preventDefault();
}
}
$: builder = $menu;
$: Object.assign(builder, attrs);
$: updatePositioning({
alignOffset,
collisionPadding,
avoidCollisions,
collisionBoundary,
fitViewport,
strategy,
overlap,
});
const mergedProps = $derived(
mergeProps(restProps, state.props, {
onInteractOutsideStart: handleInteractOutsideStart,
style: {
outline: "none",
"--bits-context-menu-content-transform-origin":
"var(--bits-floating-transform-origin)",
"--bits-context-menu-content-available-width":
"var(--bits-floating-available-width)",
"--bits-context-menu-content-available-height":
"var(--bits-floating-available-height)",
"--bits-context-menu-trigger-width": "var(--bits-floating-anchor-width)",
"--bits-context-menu-trigger-height": "var(--bits-floating-anchor-height)",
},
})
);
</script>

{#if asChild && $open}
<slot {builder} />
{:else if transition && $open}
<div
bind:this={el}
transition:transition={transitionConfig}
use:melt={builder}
{...$$restProps}
on:m-keydown={dispatch}
>
<slot {builder} />
</div>
{:else if inTransition && outTransition && $open}
<div
bind:this={el}
in:inTransition={inTransitionConfig}
out:outTransition={outTransitionConfig}
use:melt={builder}
{...$$restProps}
on:m-keydown={dispatch}
>
<slot {builder} />
</div>
{:else if inTransition && $open}
<div
bind:this={el}
in:inTransition={inTransitionConfig}
use:melt={builder}
{...$$restProps}
on:m-keydown={dispatch}
>
<slot {builder} />
</div>
{:else if outTransition && $open}
<div
bind:this={el}
out:outTransition={outTransitionConfig}
use:melt={builder}
{...$$restProps}
on:m-keydown={dispatch}
>
<slot {builder} />
</div>
{:else if $open}
<div bind:this={el} use:melt={builder} {...$$restProps} on:m-keydown={dispatch}>
<slot {builder} />
</div>
{/if}
<PopperLayer
{...mergedProps}
side="right"
sideOffset={2}
align="start"
present={state.parentMenu.open.value || forceMount}
onInteractOutsideStart={(e) => {
console.log("interactoutsidestart", e);
}}
onInteractOutside={(e) => {
console.log(e);
onInteractOutside(e);
if (e.defaultPrevented) return;
state.parentMenu.onClose();
}}
onEscapeKeydown={(e) => {
// TODO: users should be able to cancel this
onEscapeKeydown(e);
state.parentMenu.onClose();
}}
trapped
{loop}
>
{#snippet popper({ props })}
{@const finalProps = mergeProps(props, mergedProps)}
{#if asChild}
{@render child?.({ props: finalProps })}
{:else}
<div {...finalProps} bind:this={el}>
{@render children?.()}
</div>
{/if}
{/snippet}
</PopperLayer>
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { getCtx } from "../ctx.js";
import type { TriggerEvents, TriggerProps } from "../index.js";
import { createDispatcher } from "$lib/internal/events.js";
import { box } from "runed";
import type { TriggerProps } from "../index.js";
import { useMenuContextTrigger } from "$lib/bits/menu/menu.svelte.js";
import { useId } from "$lib/internal/useId.svelte.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
import { FloatingLayer } from "$lib/bits/utilities/floating-layer/index.js";
type $$Props = TriggerProps;
type $$Events = TriggerEvents;
let {
id = useId(),
el = $bindable(),
asChild,
child,
children,
disabled = false,
...restProps
}: TriggerProps = $props();
export let asChild: $$Props["asChild"] = false;
export let id: $$Props["id"] = undefined;
export let el: $$Props["el"] = undefined;
const state = useMenuContextTrigger({
id: box.with(() => id),
disabled: box.with(() => disabled),
});
const {
elements: { trigger },
ids,
getAttrs,
} = getCtx();
const dispatch = createDispatcher();
const attrs = getAttrs("trigger");
$: if (id) {
ids.trigger.set(id);
}
$: builder = $trigger;
$: Object.assign(builder, attrs);
const mergedProps = $derived(
mergeProps(restProps, state.props, { style: { pointerEvents: "auto" } })
);
</script>

{#if asChild}
<slot {builder} />
{:else}
<div
bind:this={el}
use:melt={builder}
{...$$restProps}
on:m-contextmenu={dispatch}
on:m-pointercancel={dispatch}
on:m-pointerdown={dispatch}
on:m-pointermove={dispatch}
on:m-pointerup={dispatch}
>
<slot {builder} />
</div>
{/if}
<FloatingLayer.Anchor {id} virtualEl={state.virtualElement}>
{#if asChild}
{@render child?.({ props: mergedProps })}
{:else}
<div {...mergedProps} bind:this={el}>
{@render children?.()}
</div>
{/if}
</FloatingLayer.Anchor>

This file was deleted.

5 changes: 3 additions & 2 deletions packages/bits-ui/src/lib/bits/context-menu/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as Root } from "./components/context-menu.svelte";
export { default as Root } from "$lib/bits/menu/components/menu.svelte";
export { default as Sub } from "$lib/bits/menu/components/menu-sub.svelte";
export { default as Item } from "$lib/bits/menu/components/menu-item.svelte";
export { default as Group } from "$lib/bits/menu/components/menu-group.svelte";
Expand All @@ -19,12 +19,13 @@ export type {
ContextMenuGroupProps as GroupProps,
ContextMenuItemProps as ItemProps,
ContextMenuLabelProps as LabelProps,
ContextMenuProps as Props,
ContextMenuRootProps as RootProps,
ContextMenuRadioGroupProps as RadioGroupProps,
ContextMenuRadioItemProps as RadioItemProps,
ContextMenuSeparatorProps as SeparatorProps,
ContextMenuSubContentProps as SubContentProps,
ContextMenuSubProps as SubProps,
ContextMenuSubTriggerProps as SubTriggerProps,
ContextMenuContentProps as ContentProps,
ContextMenuTriggerProps as TriggerProps,
} from "./types.js";
Loading

0 comments on commit af44145

Please sign in to comment.