diff --git a/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts b/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts index dbd5c1025..7e2b383cd 100644 --- a/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts +++ b/packages/bits-ui/src/lib/bits/popover/popover.svelte.ts @@ -30,7 +30,6 @@ class PopoverRootState { } close = () => { - console.log("closing!"); if (!this.open.value) return; this.open.value = false; }; diff --git a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte index 169ce27e0..eb90a5948 100644 --- a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte @@ -9,6 +9,7 @@ onInteractOutsideStart = noop, id, children, + present, }: DismissableLayerProps = $props(); useDismissableLayer({ @@ -16,6 +17,7 @@ behaviorType: readonlyBox(() => behaviorType), onInteractOutside: readonlyBox(() => onInteractOutside), onInteractOutsideStart: readonlyBox(() => onInteractOutsideStart), + present: readonlyBox(() => present), }); diff --git a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte.ts b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte.ts index d828975bd..b75cc8946 100644 --- a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte.ts +++ b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/dismissable-layer.svelte.ts @@ -1,3 +1,4 @@ +import { onDestroy, onMount, untrack } from "svelte"; import type { DismissableLayerProps, InteractOutsideBehaviorType, @@ -16,6 +17,7 @@ import { getOwnerDocument, isElement, isOrContainsTarget, + noop, useNodeById, } from "$lib/internal/index.js"; @@ -54,31 +56,58 @@ export class DismissableLayerState { #isResponsibleLayer = false; node: Box; #documentObj = undefined as unknown as Document; + #present: ReadonlyBox; constructor(props: DismissableLayerStateProps) { this.node = useNodeById(props.id); this.#behaviorType = props.behaviorType; this.#interactOutsideStartProp = props.onInteractOutsideStart; this.#interactOutsideProp = props.onInteractOutside; + this.#present = props.present; - layers.set(this, this.#behaviorType); + $effect(() => { + console.log("present", this.#present.value); + }); $effect(() => { this.#documentObj = getOwnerDocument(this.node.value); + }); - const unsubEvents = this.#addEventListeners(); + let unsubEvents = noop; + $effect(() => { + if (this.#present.value) { + layers.set( + this, + untrack(() => this.#behaviorType) + ); + unsubEvents = this.#addEventListeners(); + } return () => { - unsubEvents(); - this.#resetState.destroy(); + this.#resetState(); + layers.delete(this); this.#onInteractOutsideStart.destroy(); this.#onInteractOutside.destroy(); - layers.delete(this); + unsubEvents(); + }; + }); + + $effect(() => { + return () => { + // onDestroy, cleanup anything leftover + untrack(() => { + this.#resetState.destroy(); + layers.delete(this); + this.#onInteractOutsideStart.destroy(); + this.#onInteractOutside.destroy(); + unsubEvents(); + }); }; }); } #addEventListeners() { + console.log("adding event listeners"); return executeCallbacks( /** * CAPTURE INTERACTION START @@ -131,8 +160,13 @@ export class DismissableLayerState { } #onInteractOutsideStart = debounce((e: InteractOutsideEvent) => { - const node = this.node.value!; - if (!this.#isResponsibleLayer || this.#isAnyEventIntercepted() || !isValidEvent(e, node)) + console.log("onInteractOutsideStart"); + if (!this.node.value) return; + if ( + !this.#isResponsibleLayer || + this.#isAnyEventIntercepted() || + !isValidEvent(e, this.node.value) + ) return; this.#interactOutsideStartProp.value(e); if (e.defaultPrevented) return; @@ -140,6 +174,7 @@ export class DismissableLayerState { }, 10); #onInteractOutside = debounce((e: InteractOutsideEvent) => { + console.log("onInteractOutside"); if (!this.node.value) return; const behaviorType = this.#behaviorType.value; @@ -156,19 +191,23 @@ export class DismissableLayerState { }, 10); #markInterceptedEvent = (e: HTMLElementEventMap[InteractOutsideInterceptEventType]) => { + console.log("markInterceptedEvent", e.type); this.#interceptedEvents[e.type as InteractOutsideInterceptEventType] = true; }; #markNonInterceptedEvent = (e: HTMLElementEventMap[InteractOutsideInterceptEventType]) => { + console.log("markNonInterceptedEvent", e.type); this.#interceptedEvents[e.type as InteractOutsideInterceptEventType] = false; }; #markResponsibleLayer = () => { + console.log("markResponsibleLayer"); if (!this.node.value) return; this.#isResponsibleLayer = isResponsibleLayer(this.node.value); }; #resetState = debounce(() => { + console.log("resetState"); for (const eventType in this.#interceptedEvents) { this.#interceptedEvents[eventType as InteractOutsideInterceptEventType] = false; } @@ -177,6 +216,7 @@ export class DismissableLayerState { }, 20); #isAnyEventIntercepted() { + console.log("isAnyEventIntercepted"); return Object.values(this.#interceptedEvents).some(Boolean); } } @@ -186,7 +226,7 @@ export function useDismissableLayer(props: DismissableLayerStateProps) { } function isResponsibleLayer(node: HTMLElement): boolean { - console.log(layers); + console.log("isResponsibleLayer"); const layersArr = [...layers]; /** * We first check if we can find a top layer with `close` or `ignore`. @@ -203,6 +243,7 @@ function isResponsibleLayer(node: HTMLElement): boolean { } function isValidEvent(e: InteractOutsideEvent, node: HTMLElement): boolean { + console.log("isValidEvent"); if ("button" in e && e.button > 0) return false; const target = e.target; if (!isElement(target)) return false; diff --git a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/types.ts index f245451a8..044e3fca4 100644 --- a/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/dismissable-layer/types.ts @@ -45,6 +45,12 @@ export type DismissableLayerProps = { * @defaultValue `close` */ behaviorType?: InteractOutsideBehaviorType; + + /** + * Whether the layer is active. Currently, we determine this with the + * `presence` returned from the `presence` layer. + */ + present: boolean; }; export type InteractOutsideInterceptEventType = diff --git a/packages/bits-ui/src/lib/bits/utilities/escape-layer/escape-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/escape-layer/escape-layer.svelte index 2d8e25c99..dbb13614c 100644 --- a/packages/bits-ui/src/lib/bits/utilities/escape-layer/escape-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/escape-layer/escape-layer.svelte @@ -3,11 +3,12 @@ import { useEscapeLayer } from "./escape-layer.svelte.js"; import { noop, readonlyBox } from "$lib/internal/index.js"; - let { behaviorType = "close", onEscape = noop, children }: EscapeLayerProps = $props(); + let { behaviorType = "close", onEscape = noop, children, present }: EscapeLayerProps = $props(); useEscapeLayer({ behaviorType: readonlyBox(() => behaviorType), onEscape: readonlyBox(() => onEscape), + present: readonlyBox(() => present), }); diff --git a/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts index 59f21dc40..f0dd2e5b4 100644 --- a/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/escape-layer/types.ts @@ -24,4 +24,10 @@ export type EscapeLayerProps = { * @defaultValue `close` */ behaviorType?: EscapeBehaviorType; + + /** + * Whether the layer is enabled. Currently, we determine this with the + * `presence` returned from the `presence` layer. + */ + present: boolean; }; diff --git a/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte index 52249a3f3..89569bf4f 100644 --- a/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/popper-layer/popper-layer.svelte @@ -15,9 +15,12 @@ {#snippet presence({ present })} {#snippet content({ props })} - - - + + + {@render popper?.({ props: { ...props, hidden: present.value ? undefined : true }, })} diff --git a/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/prevent-text-selection-overflow-layer.svelte b/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/prevent-text-selection-overflow-layer.svelte index 824fc4852..e48686f99 100644 --- a/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/prevent-text-selection-overflow-layer.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/prevent-text-selection-overflow-layer.svelte @@ -9,6 +9,7 @@ onPointerUp = noop, id, children, + present, }: PreventTextSelectionOverflowLayerProps = $props(); usePreventTextSelectionOverflowLayer({ @@ -16,6 +17,7 @@ enabled: readonlyBox(() => enabled), onPointerDown: readonlyBox(() => onPointerDown), onPointerUp: readonlyBox(() => onPointerUp), + present: readonlyBox(() => present), }); diff --git a/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/types.ts b/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/types.ts index 0fbac8849..fd78c7051 100644 --- a/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/prevent-text-selection-overflow-layer/types.ts @@ -27,4 +27,10 @@ export type PreventTextSelectionOverflowLayerProps = { * @defaultValue `true` */ enabled?: boolean; + + /** + * Whether the layer is enabled. Currently, we determine this with the + * `presence` returned from the `presence` layer. + */ + present: boolean; }; diff --git a/packages/bits-ui/src/lib/internal/debounce.ts b/packages/bits-ui/src/lib/internal/debounce.ts index 5d837cce6..718f703c0 100644 --- a/packages/bits-ui/src/lib/internal/debounce.ts +++ b/packages/bits-ui/src/lib/internal/debounce.ts @@ -8,6 +8,8 @@ export function debounce any>(fn: T, wait = 500) { timeout = setTimeout(later, wait); }; - debounced.destroy = () => clearTimeout(timeout); + debounced.destroy = () => { + clearTimeout(timeout); + }; return debounced; } diff --git a/packages/bits-ui/src/lib/internal/events.ts b/packages/bits-ui/src/lib/internal/events.ts index 37939412c..8de5a309d 100644 --- a/packages/bits-ui/src/lib/internal/events.ts +++ b/packages/bits-ui/src/lib/internal/events.ts @@ -97,6 +97,7 @@ export function addEventListener( // Return a function that removes the event listener from the target element(s). return () => { + console.log("removing event listeners"); events.forEach((_event) => target.removeEventListener(_event, handler, options)); }; } diff --git a/sites/docs/src/lib/components/api-ref/data-attr-value-content.svelte b/sites/docs/src/lib/components/api-ref/data-attr-value-content.svelte index 9cd4308ab..4a5bcded2 100644 --- a/sites/docs/src/lib/components/api-ref/data-attr-value-content.svelte +++ b/sites/docs/src/lib/components/api-ref/data-attr-value-content.svelte @@ -3,7 +3,6 @@ import { Info } from "$icons/index.js"; import { Code } from "$lib/components/index.js"; import type { DataAttrSchema } from "$lib/types/index.js"; - import { flyAndScale } from "$lib/utils/index.js"; export let attr: DataAttrSchema; @@ -23,8 +22,6 @@ diff --git a/sites/docs/src/lib/components/api-ref/prop-copy.svelte b/sites/docs/src/lib/components/api-ref/prop-copy.svelte index 6507c771b..2cffa29fc 100644 --- a/sites/docs/src/lib/components/api-ref/prop-copy.svelte +++ b/sites/docs/src/lib/components/api-ref/prop-copy.svelte @@ -1,7 +1,6 @@ @@ -16,8 +15,6 @@ diff --git a/sites/docs/src/lib/components/api-ref/prop-type-content.svelte b/sites/docs/src/lib/components/api-ref/prop-type-content.svelte index 112264292..c25792df2 100644 --- a/sites/docs/src/lib/components/api-ref/prop-type-content.svelte +++ b/sites/docs/src/lib/components/api-ref/prop-type-content.svelte @@ -3,7 +3,7 @@ import { Info } from "$icons/index.js"; import { Code } from "$lib/components/index.js"; import type { PropType } from "$lib/types/index.js"; - import { flyAndScale, parseTypeDef } from "$lib/utils/index.js"; + import { parseTypeDef } from "$lib/utils/index.js"; export let type: PropType | string; @@ -23,8 +23,6 @@ diff --git a/sites/docs/src/routes/sink/+page.svelte b/sites/docs/src/routes/sink/+page.svelte new file mode 100644 index 000000000..b570287b9 --- /dev/null +++ b/sites/docs/src/routes/sink/+page.svelte @@ -0,0 +1,7 @@ + + +
+ +