Skip to content

Commit

Permalink
next: Select (#564)
Browse files Browse the repository at this point in the history
  • Loading branch information
huntabyte authored Jun 10, 2024
1 parent d589233 commit ba7ebfa
Show file tree
Hide file tree
Showing 81 changed files with 9,361 additions and 6,239 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"prettier": "^3.2.5",
"prettier-plugin-svelte": "^3.2.2",
"prettier-plugin-tailwindcss": "0.5.13",
"svelte": "5.0.0-next.148",
"svelte": "5.0.0-next.143",
"svelte-eslint-parser": "^0.34.1",
"wrangler": "^3.44.0"
},
Expand Down
2 changes: 2 additions & 0 deletions packages/bits-ui/other/setupTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,5 @@ vi.mock("$app/stores", (): typeof stores => {
// eslint-disable-next-line ts/no-require-imports
globalThis.ResizeObserver = require("resize-observer-polyfill");
Element.prototype.scrollIntoView = () => {};
Element.prototype.hasPointerCapture = (() => {}) as any

2 changes: 1 addition & 1 deletion packages/bits-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"jsdom": "^24.0.0",
"publint": "^0.2.7",
"resize-observer-polyfill": "^1.5.1",
"svelte": "5.0.0-next.148",
"svelte": "5.0.0-next.143",
"svelte-check": "^3.6.9",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,22 @@
{#snippet focusScope({ props: focusScopeProps })}
<EscapeLayer
{...mergedProps}
present={present.value}
enabled={present.value}
onEscapeKeydown={(e) => {
onEscapeKeydown(e);
state.root.closeDialog();
}}
>
<DismissableLayer
{...mergedProps}
present={present.value}
enabled={present.value}
onInteractOutside={(e) => {
onInteractOutside(e);
if (e.defaultPrevented) return;
state.root.closeDialog();
}}
>
<TextSelectionLayer {...mergedProps} present={present.value}>
<TextSelectionLayer {...mergedProps} enabled={present.value}>
{#if asChild}
{@render child?.({
props: mergeProps(mergedProps, focusScopeProps, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
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";
import { isElementOrSVGElement } from "$lib/internal/is.js";
let {
id = useId(),
Expand All @@ -22,25 +21,13 @@
...restProps
}: ContentProps = $props();
const state = useMenuContent({
const contentState = useMenuContent({
id: box.with(() => id),
loop: box.with(() => loop),
});
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();
}
}
const mergedProps = $derived(
mergeProps(restProps, state.props, {
onInteractOutsideStart: handleInteractOutsideStart,
mergeProps(restProps, contentState.props, {
style: {
outline: "none",
"--bits-dropdown-menu-content-transform-origin":
Expand All @@ -58,16 +45,44 @@

<PopperLayer
{...mergedProps}
present={state.parentMenu.open.value || forceMount}
present={contentState.parentMenu.open.value || forceMount}
enabled={contentState.parentMenu.open.value || forceMount}
onInteractOutsideStart={(e) => {
if (!isElementOrSVGElement(e.target)) return;
if (e.target.id === contentState.parentMenu.triggerId.value) {
console.log("start: is trigger, should not be closing");
e.preventDefault();
return;
}
if (e.target.closest(`#${contentState.parentMenu.triggerId.value}`)) {
e.preventDefault();
console.log("start: is within trigger, should not be closing");
}
}}
onInteractOutside={(e) => {
onInteractOutside(e);
if (e.defaultPrevented) return;
state.parentMenu.onClose();

if (!isElementOrSVGElement(e.target)) return;
if (e.target.id === contentState.parentMenu.triggerId.value) {
console.log("is trigger, should not be closing");
e.preventDefault();
return;
}
if (e.target.closest(`#${contentState.parentMenu.triggerId.value}`)) {
e.preventDefault();
console.log("is within trigger, should not be closing");
return;
}

console.log("target", e.target);

console.log("it should not make it here!", e);
contentState.parentMenu.onClose();
}}
onEscapeKeydown={(e) => {
// TODO: users should be able to cancel this
onEscapeKeydown(e);
state.parentMenu.onClose();
contentState.parentMenu.onClose();
}}
trapped
{loop}
Expand Down
4 changes: 4 additions & 0 deletions packages/bits-ui/src/lib/bits/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { writable } from "svelte/store";

export * as Accordion from "./accordion/index.js";
export * as AlertDialog from "./alert-dialog/index.js";
export * as AspectRatio from "./aspect-ratio/index.js";
Expand Down Expand Up @@ -33,3 +35,5 @@ export * as Toggle from "./toggle/index.js";
export * as ToggleGroup from "./toggle-group/index.js";
export * as Toolbar from "./toolbar/index.js";
export * as Tooltip from "./tooltip/index.js";

export const eventLogs = writable([]);
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

<PopperLayer
{...mergedProps}
enabled={state.parentMenu.open.value || forceMount}
present={state.parentMenu.open.value || forceMount}
onInteractOutside={(e) => {
onInteractOutside(e);
Expand Down
7 changes: 4 additions & 3 deletions packages/bits-ui/src/lib/bits/menu/menu.svelte.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,17 @@ class MenuMenuState {
}

toggleOpen() {
console.log("calling toggle open");
this.open.value = !this.open.value;
}

onOpen() {
console.log("calling onOpen");
this.open.value = true;
}

onClose() {
console.log("calling onClose");
this.open.value = false;
}

Expand Down Expand Up @@ -779,9 +782,7 @@ class DropdownMenuTriggerState {
this.#parentMenu.toggleOpen();
// prevent trigger focusing when opening to allow
// the content to be given focus without competition
afterTick(() => {
if (!this.#parentMenu.open.value) e.preventDefault();
});
if (!this.#parentMenu.open.value) e.preventDefault();
}
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
<script lang="ts">
import { melt } from "@melt-ui/svelte";
import { setArrow } from "../ctx.js";
import type { ArrowProps } from "../index.js";
import { useSelectArrow } from "../select.svelte.js";
import { FloatingLayer } from "$lib/bits/utilities/floating-layer/index.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
type $$Props = ArrowProps;
let { el = $bindable(), ...restProps }: ArrowProps = $props();
export let asChild: $$Props["asChild"] = false;
export let el: $$Props["el"] = undefined;
export let size = 8;
const state = useSelectArrow();
const {
elements: { arrow },
getAttrs,
} = setArrow(size);
const attrs = getAttrs("arrow");
$: builder = $arrow;
$: Object.assign(builder, attrs);
const mergedProps = $derived(mergeProps(restProps, state.props as any));
</script>

{#if asChild}
<slot {builder} />
{:else}
<div bind:this={el} use:melt={builder} {...$$restProps} />
{/if}
<FloatingLayer.Arrow bind:el {...mergedProps} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script lang="ts">
import { CONTENT_MARGIN, useSelectFloatingPosition } from "../select.svelte.js";
import type { ContentProps } from "../index.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
import type { WithoutChildren } from "$lib/shared/index.js";
import type { WithAsChild } from "$lib/internal/types.js";
import type { PopperLayerImplProps } from "$lib/bits/utilities/popper-layer/types.js";
import { FloatingLayer } from "$lib/bits/utilities/floating-layer/index.js";
let {
children,
asChild,
child,
align = "start",
collisionPadding = CONTENT_MARGIN,
el = $bindable(),
enabled = false,
...restProps
}: WithoutChildren<PopperLayerImplProps> & WithAsChild<ContentProps> = $props();
const state = useSelectFloatingPosition();
const mergedProps = $derived(mergeProps(restProps, state.content.props, state.props));
</script>

<FloatingLayer.Content {...restProps} {enabled} {align} {collisionPadding}>
{#snippet content({ props })}
{@const finalProps = mergeProps(props, mergedProps)}
{#if asChild}
{@render child?.({ props: finalProps })}
{:else}
<div {...finalProps} bind:this={el}>
{@render children?.()}
</div>
{/if}
{/snippet}
</FloatingLayer.Content>
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<script lang="ts">
import type { ContentImplProps } from "../index.js";
import { SelectContentState } from "../select.svelte.js";
import SelectContentFloating from "./select-content-floating.svelte";
import SelectContentItemAligned from "./select-content-item-aligned.svelte";
import FocusScope from "$lib/bits/utilities/focus-scope/focus-scope.svelte";
import { noop } from "$lib/internal/callbacks.js";
import { useId } from "$lib/internal/useId.svelte.js";
import EscapeLayer from "$lib/bits/utilities/escape-layer/escape-layer.svelte";
import DismissableLayer from "$lib/bits/utilities/dismissable-layer/dismissable-layer.svelte";
import TextSelectionLayer from "$lib/bits/utilities/text-selection-layer/text-selection-layer.svelte";
import { mergeProps } from "$lib/internal/mergeProps.js";
let {
el = $bindable(),
id = useId(),
onMountAutoFocus = noop,
onDestroyAutoFocus = noop,
present,
position = "floating",
context,
onEscapeKeydown = noop,
onInteractOutside = noop,
...restProps
}: ContentImplProps & { present: boolean; context: SelectContentState } = $props();
const state = context;
// eslint-disable-next-line unused-imports/no-unused-vars, ts/no-unused-vars
const { children, child, asChild, ...restWithoutChildren } = restProps;
</script>

<FocusScope
{id}
trapped={present}
onMountAutoFocus={(e) => {
onMountAutoFocus(e);
e.preventDefault();
}}
onDestroyAutoFocus={(e) => {
onDestroyAutoFocus(e);
}}
>
{#snippet focusScope({ props: focusScopeProps })}
<EscapeLayer
{...restWithoutChildren}
enabled={present}
onEscapeKeydown={(e) => {
// TODO: users should be able to cancel this
onEscapeKeydown(e);
state.root.handleClose();
}}
>
<DismissableLayer
{...restWithoutChildren}
{id}
enabled={present}
onInteractOutside={(e) => {
onInteractOutside(e);
if (e.defaultPrevented) return;
state.root.handleClose();
}}
>
{#snippet children({ props: dismissableProps })}
<TextSelectionLayer {...restWithoutChildren} {id} enabled={present}>
{@const mergedProps = mergeProps(
restWithoutChildren,
dismissableProps,
focusScopeProps,
state.props,
{ style: { pointerEvents: "auto" } }
) as any}
{#if position === "floating"}
<SelectContentFloating
{...restProps}
{...mergedProps}
bind:el
onPlaced={() => (state.isPositioned.value = true)}
/>
{:else}
<SelectContentItemAligned
{...restProps}
{...mergedProps}
bind:el
onPlaced={() => (state.isPositioned.value = true)}
/>
{/if}
</TextSelectionLayer>
{/snippet}
</DismissableLayer>
</EscapeLayer>
{/snippet}
</FocusScope>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<script lang="ts">
import { box } from "svelte-toolbelt";
import { useSelectItemAlignedPosition } from "../select.svelte.js";
import type { PrimitiveDivAttributes, WithAsChild } from "$lib/internal/types.js";
import { mergeProps } from "$lib/internal/mergeProps.js";
import { useBodyScrollLock } from "$lib/internal/useBodyScrollLock.svelte.js";
let {
child,
children,
asChild,
el = $bindable(),
preventScroll = true,
onPlaced,
...restProps
}: WithAsChild<PrimitiveDivAttributes> & {
preventScroll?: boolean;
onPlaced: () => void;
} = $props();
const state = useSelectItemAlignedPosition({
onPlaced: box.with(() => onPlaced),
});
const mergedProps = $derived(mergeProps(restProps, state.props));
const mergedWrapperProps = $derived(mergeProps(state.wrapperProps, {}));
useBodyScrollLock(preventScroll);
</script>

<div {...mergedWrapperProps}>
{#if asChild}
{@render child?.({ props: mergedProps })}
{:else}
<div bind:this={el} {...mergedProps}>
{@render children?.()}
</div>
{/if}
</div>
Loading

0 comments on commit ba7ebfa

Please sign in to comment.