From f348dabb58e10d0dad01acb35c817345223879f9 Mon Sep 17 00:00:00 2001 From: Hunter Johnston <64506580+huntabyte@users.noreply.github.com> Date: Fri, 20 Sep 2024 19:01:56 -0400 Subject: [PATCH] next: Chores & Prop Renames & More (#669) --- .../components/alert-dialog-content.svelte | 12 +- .../src/lib/bits/calendar/calendar.svelte.ts | 6 +- .../context-menu-content-static.svelte | 5 +- .../src/lib/bits/context-menu/types.ts | 2 +- .../date-picker-content-static.svelte | 6 +- .../components/date-picker-content.svelte | 4 +- .../dialog/components/dialog-content.svelte | 9 +- .../link-preview-content-static.svelte | 4 +- .../components/link-preview-content.svelte | 4 +- .../components/listbox-content-static.svelte | 4 +- .../listbox/components/listbox-content.svelte | 4 +- .../components/listbox-viewport.svelte | 10 + .../bits-ui/src/lib/bits/listbox/types.ts | 30 ++- .../menu/components/menu-sub-content.svelte | 10 +- .../bits-ui/src/lib/bits/menu/menu.svelte.ts | 2 +- packages/bits-ui/src/lib/bits/menu/types.ts | 23 +- .../components/menubar-content-static.svelte | 4 +- .../menubar/components/menubar-content.svelte | 4 +- .../src/lib/bits/menubar/menubar.svelte.ts | 4 +- .../navigation-menu/navigation-menu.svelte.ts | 6 +- .../components/popover-content-static.svelte | 11 +- .../popover/components/popover-content.svelte | 11 +- .../components/select-content-impl.svelte | 12 +- .../src/lib/bits/select/select.svelte.ts | 1 - packages/bits-ui/src/lib/bits/select/types.ts | 32 ++- .../components/tooltip-content-static.svelte | 4 +- .../tooltip/components/tooltip-content.svelte | 4 +- .../lib/bits/utilities/escape-layer/types.ts | 4 +- .../floating-layer/useFloatingLayer.svelte.ts | 2 +- .../utilities/focus-scope/focus-scope.svelte | 10 +- .../lib/bits/utilities/focus-scope/types.ts | 20 +- .../focus-scope/useFocusScope.svelte.ts | 28 +- .../popper-layer/popper-layer.svelte | 14 +- .../lib/bits/utilities/popper-layer/types.ts | 8 +- packages/bits-ui/src/lib/helpers/builders.ts | 57 ----- .../bits-ui/src/lib/helpers/event-handlers.ts | 4 - packages/bits-ui/src/lib/helpers/index.ts | 2 - packages/bits-ui/src/lib/index.ts | 2 - packages/bits-ui/src/lib/internal/attrs.ts | 42 --- packages/bits-ui/src/lib/internal/context.ts | 16 -- .../src/lib/internal/floating-svelte/arrow.ts | 52 ---- .../floating-svelte/floating-utils.svelte.ts | 4 +- .../src/lib/internal/useRovingFocus.svelte.ts | 5 +- .../shared/date/calendar-helpers.svelte.ts | 1 - .../src/lib/shared/date/field/helpers.ts | 6 +- .../src/lib/shared/date/field/types.ts | 11 - packages/bits-ui/src/lib/shared/date/types.ts | 6 - packages/bits-ui/src/lib/shared/date/utils.ts | 8 +- packages/bits-ui/src/lib/types.ts | 1 - ...=> accordion-multi-test-controlled.svelte} | 0 ...est.svelte => accordion-multi-test.svelte} | 0 ...> accordion-single-test-controlled.svelte} | 0 ...st.svelte => accordion-single-test.svelte} | 0 ....svelte => accordion-test-isolated.svelte} | 0 .../{Accordion.spec.ts => accordion.spec.ts} | 10 +- ...ogTest.svelte => alert-dialog-test.svelte} | 0 ...ertDialog.spec.ts => alert-dialog.spec.ts} | 2 +- .../{AvatarTest.svelte => avatar-test.svelte} | 0 .../avatar/{Avatar.spec.ts => avatar.spec.ts} | 2 +- ...Test.svelte => calendar-multi-test.svelte} | 0 ...lendarTest.svelte => calendar-test.svelte} | 0 .../{Calendar.spec.ts => calendar.spec.ts} | 4 +- ...eckboxTest.svelte => checkbox-test.svelte} | 0 .../{Checkbox.spec.ts => checkbox.spec.ts} | 2 +- ...bleTest.svelte => collapsible-test.svelte} | 0 ...ollapsible.spec.ts => collapsible.spec.ts} | 2 +- ...Test.svelte => combobox-multi-test.svelte} | 0 ...mboboxTest.svelte => combobox-test.svelte} | 0 .../{Combobox.spec.ts => combobox.spec.ts} | 8 +- ...nuTest.svelte => context-menu-test.svelte} | 0 ...ntextMenu.spec.ts => context-menu.spec.ts} | 4 +- ...ieldTest.svelte => date-field-test.svelte} | 0 .../{DateField.spec.ts => date-field.spec.ts} | 2 +- ...st.svelte => date-range-field-test.svelte} | 0 ...Field.spec.ts => date-range-field.spec.ts} | 2 +- .../{DialogTest.svelte => dialog-test.svelte} | 0 .../dialog/{Dialog.spec.ts => dialog.spec.ts} | 2 +- ...uTest.svelte => dropdown-menu-test.svelte} | 0 ...downMenu.spec.ts => dropdown-menu.spec.ts} | 8 +- .../{LabelTest.svelte => label-test.svelte} | 0 .../label/{Label.spec.ts => label.spec.ts} | 2 +- ...ewTest.svelte => link-preview-test.svelte} | 0 ...nkPreview.spec.ts => link-preview.spec.ts} | 2 +- ...iTest.svelte => listbox-multi-test.svelte} | 0 ...ListboxTest.svelte => listbox-test.svelte} | 0 .../{Listbox.spec.ts => listbox.spec.ts} | 8 +- ...arMenu.svelte => menubar-menu-test.svelte} | 0 ...MenubarTest.svelte => menubar-test.svelte} | 2 +- .../{Menubar.spec.ts => menubar.spec.ts} | 2 +- ...tionTest.svelte => pagination-test.svelte} | 0 ...{Pagination.spec.ts => pagination.spec.ts} | 2 +- ...InputTest.svelte => pin-input-test.svelte} | 0 .../{PinInput.spec.ts => pin-input.spec.ts} | 2 +- ...PopoverTest.svelte => popover-test.svelte} | 0 .../{Popover.spec.ts => popover.spec.ts} | 2 +- ...ogressTest.svelte => progress-test.svelte} | 0 .../{Progress.spec.ts => progress.spec.ts} | 2 +- ...oupTest.svelte => radio-group-test.svelte} | 0 ...RadioGroup.spec.ts => radio-group.spec.ts} | 4 +- ...Test.svelte => range-calendar-test.svelte} | 0 ...alendar.spec.ts => range-calendar.spec.ts} | 2 +- ...reaTest.svelte => scroll-area-test.svelte} | 0 ...ScrollArea.spec.ts => scroll-area.spec.ts} | 4 +- .../{SelectTest.svelte => select-test.svelte} | 0 .../select/{Select.spec.ts => select.spec.ts} | 4 +- ...ratorTest.svelte => separator-test.svelte} | 0 .../{Separator.spec.ts => separator.spec.ts} | 2 +- ...geTest.svelte => slider-range-test.svelte} | 0 .../{SliderTest.svelte => slider-test.svelte} | 0 .../slider/{Slider.spec.ts => slider.spec.ts} | 4 +- .../{SwitchTest.svelte => switch-test.svelte} | 0 .../switch/{Switch.spec.ts => switch.spec.ts} | 2 +- .../{TabsTest.svelte => tabs-test.svelte} | 0 .../tests/tabs/{Tabs.spec.ts => tabs.spec.ts} | 4 +- ....svelte => toggle-group-multi-test.svelte} | 0 ...upTest.svelte => toggle-group-test.svelte} | 0 ...ggleGroup.spec.ts => toggle-group.spec.ts} | 6 +- .../{ToggleTest.svelte => toggle-test.svelte} | 0 .../toggle/{Toggle.spec.ts => toggle.spec.ts} | 2 +- ...ToolbarTest.svelte => toolbar-test.svelte} | 0 .../{Toolbar.spec.ts => toolbar.spec.ts} | 4 +- ...TooltipTest.svelte => tooltip-test.svelte} | 0 .../{Tooltip.spec.ts => tooltip.spec.ts} | 2 +- sites/docs/content/components/alert-dialog.md | 12 +- sites/docs/content/components/dialog.md | 12 +- sites/docs/content/components/listbox.md | 241 +++++++++++++++++- sites/docs/content/components/select.md | 10 +- .../lib/components/demos/combobox-demo.svelte | 52 ++-- .../lib/components/demos/dialog-demo.svelte | 1 + .../src/lib/content/api-reference/combobox.ts | 42 ++- .../lib/content/api-reference/context-menu.ts | 24 ++ .../content/api-reference/dropdown-menu.ts | 8 + .../src/lib/content/api-reference/helpers.ts | 12 +- .../lib/content/api-reference/link-preview.ts | 62 +++-- .../src/lib/content/api-reference/listbox.ts | 47 +++- .../src/lib/content/api-reference/menu.ts | 28 ++ .../src/lib/content/api-reference/menubar.ts | 8 + .../src/lib/content/api-reference/popover.ts | 55 ++-- .../src/lib/content/api-reference/tooltip.ts | 46 +++- 139 files changed, 800 insertions(+), 503 deletions(-) delete mode 100644 packages/bits-ui/src/lib/helpers/builders.ts delete mode 100644 packages/bits-ui/src/lib/helpers/event-handlers.ts delete mode 100644 packages/bits-ui/src/lib/helpers/index.ts delete mode 100644 packages/bits-ui/src/lib/internal/context.ts delete mode 100644 packages/bits-ui/src/lib/internal/floating-svelte/arrow.ts rename packages/bits-ui/src/tests/accordion/{AccordionMultiTestControlled.svelte => accordion-multi-test-controlled.svelte} (100%) rename packages/bits-ui/src/tests/accordion/{AccordionMultiTest.svelte => accordion-multi-test.svelte} (100%) rename packages/bits-ui/src/tests/accordion/{AccordionSingleTestControlled.svelte => accordion-single-test-controlled.svelte} (100%) rename packages/bits-ui/src/tests/accordion/{AccordionSingleTest.svelte => accordion-single-test.svelte} (100%) rename packages/bits-ui/src/tests/accordion/{AccordionTestIsolated.svelte => accordion-test-isolated.svelte} (100%) rename packages/bits-ui/src/tests/accordion/{Accordion.spec.ts => accordion.spec.ts} (98%) rename packages/bits-ui/src/tests/alert-dialog/{AlertDialogTest.svelte => alert-dialog-test.svelte} (100%) rename packages/bits-ui/src/tests/alert-dialog/{AlertDialog.spec.ts => alert-dialog.spec.ts} (98%) rename packages/bits-ui/src/tests/avatar/{AvatarTest.svelte => avatar-test.svelte} (100%) rename packages/bits-ui/src/tests/avatar/{Avatar.spec.ts => avatar.spec.ts} (97%) rename packages/bits-ui/src/tests/calendar/{CalendarMultiTest.svelte => calendar-multi-test.svelte} (100%) rename packages/bits-ui/src/tests/calendar/{CalendarTest.svelte => calendar-test.svelte} (100%) rename packages/bits-ui/src/tests/calendar/{Calendar.spec.ts => calendar.spec.ts} (99%) rename packages/bits-ui/src/tests/checkbox/{CheckboxTest.svelte => checkbox-test.svelte} (100%) rename packages/bits-ui/src/tests/checkbox/{Checkbox.spec.ts => checkbox.spec.ts} (99%) rename packages/bits-ui/src/tests/collapsible/{CollapsibleTest.svelte => collapsible-test.svelte} (100%) rename packages/bits-ui/src/tests/collapsible/{Collapsible.spec.ts => collapsible.spec.ts} (97%) rename packages/bits-ui/src/tests/combobox/{ComboboxMultiTest.svelte => combobox-multi-test.svelte} (100%) rename packages/bits-ui/src/tests/combobox/{ComboboxTest.svelte => combobox-test.svelte} (100%) rename packages/bits-ui/src/tests/combobox/{Combobox.spec.ts => combobox.spec.ts} (98%) rename packages/bits-ui/src/tests/context-menu/{ContextMenuTest.svelte => context-menu-test.svelte} (100%) rename packages/bits-ui/src/tests/context-menu/{ContextMenu.spec.ts => context-menu.spec.ts} (98%) rename packages/bits-ui/src/tests/date-field/{DateFieldTest.svelte => date-field-test.svelte} (100%) rename packages/bits-ui/src/tests/date-field/{DateField.spec.ts => date-field.spec.ts} (99%) rename packages/bits-ui/src/tests/date-range-field/{DateRangeFieldTest.svelte => date-range-field-test.svelte} (100%) rename packages/bits-ui/src/tests/date-range-field/{DateRangeField.spec.ts => date-range-field.spec.ts} (99%) rename packages/bits-ui/src/tests/dialog/{DialogTest.svelte => dialog-test.svelte} (100%) rename packages/bits-ui/src/tests/dialog/{Dialog.spec.ts => dialog.spec.ts} (98%) rename packages/bits-ui/src/tests/dropdown-menu/{DropdownMenuTest.svelte => dropdown-menu-test.svelte} (100%) rename packages/bits-ui/src/tests/dropdown-menu/{DropdownMenu.spec.ts => dropdown-menu.spec.ts} (97%) rename packages/bits-ui/src/tests/label/{LabelTest.svelte => label-test.svelte} (100%) rename packages/bits-ui/src/tests/label/{Label.spec.ts => label.spec.ts} (95%) rename packages/bits-ui/src/tests/link-preview/{LinkPreviewTest.svelte => link-preview-test.svelte} (100%) rename packages/bits-ui/src/tests/link-preview/{LinkPreview.spec.ts => link-preview.spec.ts} (97%) rename packages/bits-ui/src/tests/listbox/{ListboxMultiTest.svelte => listbox-multi-test.svelte} (100%) rename packages/bits-ui/src/tests/listbox/{ListboxTest.svelte => listbox-test.svelte} (100%) rename packages/bits-ui/src/tests/listbox/{Listbox.spec.ts => listbox.spec.ts} (98%) rename packages/bits-ui/src/tests/menubar/{MenubarMenu.svelte => menubar-menu-test.svelte} (100%) rename packages/bits-ui/src/tests/menubar/{MenubarTest.svelte => menubar-test.svelte} (84%) rename packages/bits-ui/src/tests/menubar/{Menubar.spec.ts => menubar.spec.ts} (98%) rename packages/bits-ui/src/tests/pagination/{PaginationTest.svelte => pagination-test.svelte} (100%) rename packages/bits-ui/src/tests/pagination/{Pagination.spec.ts => pagination.spec.ts} (95%) rename packages/bits-ui/src/tests/pin-input/{PinInputTest.svelte => pin-input-test.svelte} (100%) rename packages/bits-ui/src/tests/pin-input/{PinInput.spec.ts => pin-input.spec.ts} (98%) rename packages/bits-ui/src/tests/popover/{PopoverTest.svelte => popover-test.svelte} (100%) rename packages/bits-ui/src/tests/popover/{Popover.spec.ts => popover.spec.ts} (98%) rename packages/bits-ui/src/tests/progress/{ProgressTest.svelte => progress-test.svelte} (100%) rename packages/bits-ui/src/tests/progress/{Progress.spec.ts => progress.spec.ts} (96%) rename packages/bits-ui/src/tests/radio-group/{RadioGroupTest.svelte => radio-group-test.svelte} (100%) rename packages/bits-ui/src/tests/radio-group/{RadioGroup.spec.ts => radio-group.spec.ts} (98%) rename packages/bits-ui/src/tests/range-calendar/{RangeCalendarTest.svelte => range-calendar-test.svelte} (100%) rename packages/bits-ui/src/tests/range-calendar/{RangeCalendar.spec.ts => range-calendar.spec.ts} (99%) rename packages/bits-ui/src/tests/scroll-area/{ScrollAreaTest.svelte => scroll-area-test.svelte} (100%) rename packages/bits-ui/src/tests/scroll-area/{ScrollArea.spec.ts => scroll-area.spec.ts} (90%) rename packages/bits-ui/src/tests/select/{SelectTest.svelte => select-test.svelte} (100%) rename packages/bits-ui/src/tests/select/{Select.spec.ts => select.spec.ts} (98%) rename packages/bits-ui/src/tests/separator/{SeparatorTest.svelte => separator-test.svelte} (100%) rename packages/bits-ui/src/tests/separator/{Separator.spec.ts => separator.spec.ts} (93%) rename packages/bits-ui/src/tests/slider/{SliderRangeTest.svelte => slider-range-test.svelte} (100%) rename packages/bits-ui/src/tests/slider/{SliderTest.svelte => slider-test.svelte} (100%) rename packages/bits-ui/src/tests/slider/{Slider.spec.ts => slider.spec.ts} (98%) rename packages/bits-ui/src/tests/switch/{SwitchTest.svelte => switch-test.svelte} (100%) rename packages/bits-ui/src/tests/switch/{Switch.spec.ts => switch.spec.ts} (98%) rename packages/bits-ui/src/tests/tabs/{TabsTest.svelte => tabs-test.svelte} (100%) rename packages/bits-ui/src/tests/tabs/{Tabs.spec.ts => tabs.spec.ts} (98%) rename packages/bits-ui/src/tests/toggle-group/{ToggleGroupMultipleTest.svelte => toggle-group-multi-test.svelte} (100%) rename packages/bits-ui/src/tests/toggle-group/{ToggleGroupTest.svelte => toggle-group-test.svelte} (100%) rename packages/bits-ui/src/tests/toggle-group/{ToggleGroup.spec.ts => toggle-group.spec.ts} (96%) rename packages/bits-ui/src/tests/toggle/{ToggleTest.svelte => toggle-test.svelte} (100%) rename packages/bits-ui/src/tests/toggle/{Toggle.spec.ts => toggle.spec.ts} (98%) rename packages/bits-ui/src/tests/toolbar/{ToolbarTest.svelte => toolbar-test.svelte} (100%) rename packages/bits-ui/src/tests/toolbar/{Toolbar.spec.ts => toolbar.spec.ts} (98%) rename packages/bits-ui/src/tests/tooltip/{TooltipTest.svelte => tooltip-test.svelte} (100%) rename packages/bits-ui/src/tests/tooltip/{Tooltip.spec.ts => tooltip.spec.ts} (98%) diff --git a/packages/bits-ui/src/lib/bits/alert-dialog/components/alert-dialog-content.svelte b/packages/bits-ui/src/lib/bits/alert-dialog/components/alert-dialog-content.svelte index 100e42fe6..522b10a1b 100644 --- a/packages/bits-ui/src/lib/bits/alert-dialog/components/alert-dialog-content.svelte +++ b/packages/bits-ui/src/lib/bits/alert-dialog/components/alert-dialog-content.svelte @@ -19,9 +19,9 @@ ref = $bindable(null), forceMount = false, interactOutsideBehavior = "ignore", - onDestroyAutoFocus = noop, + onCloseAutoFocus = noop, onEscapeKeydown = noop, - onMountAutoFocus = noop, + onOpenAutoFocus = noop, onInteractOutsideStart = noop, preventScroll = true, trapFocus = true, @@ -46,13 +46,13 @@ loop trapFocus={present.current && trapFocus} {...mergedProps} - onDestroyAutoFocus={(e) => { - onDestroyAutoFocus(e); + onCloseAutoFocus={(e) => { + onCloseAutoFocus(e); if (e.defaultPrevented) return; contentState.root.triggerNode?.focus(); }} - onMountAutoFocus={(e) => { - onMountAutoFocus(e); + onOpenAutoFocus={(e) => { + onOpenAutoFocus(e); if (e.defaultPrevented) return; e.preventDefault(); contentState.root.cancelNode?.focus(); diff --git a/packages/bits-ui/src/lib/bits/calendar/calendar.svelte.ts b/packages/bits-ui/src/lib/bits/calendar/calendar.svelte.ts index c893647f2..531f9abf5 100644 --- a/packages/bits-ui/src/lib/bits/calendar/calendar.svelte.ts +++ b/packages/bits-ui/src/lib/bits/calendar/calendar.svelte.ts @@ -21,11 +21,7 @@ import { getDataSelected, getDataUnavailable, } from "$lib/internal/attrs.js"; -import { - type ReadableBoxedValues, - type WritableBoxedValues, - watch, -} from "$lib/internal/box.svelte.js"; +import type { ReadableBoxedValues, WritableBoxedValues } from "$lib/internal/box.svelte.js"; import { createContext } from "$lib/internal/createContext.js"; import type { WithRefProps } from "$lib/internal/types.js"; import { useId } from "$lib/internal/useId.js"; diff --git a/packages/bits-ui/src/lib/bits/context-menu/components/context-menu-content-static.svelte b/packages/bits-ui/src/lib/bits/context-menu/components/context-menu-content-static.svelte index c30d0717f..1b0bf1118 100644 --- a/packages/bits-ui/src/lib/bits/context-menu/components/context-menu-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/context-menu/components/context-menu-content-static.svelte @@ -56,10 +56,8 @@ { @@ -83,7 +81,6 @@ } return false; }} - trapFocus {loop} > {#snippet popper({ props })} diff --git a/packages/bits-ui/src/lib/bits/context-menu/types.ts b/packages/bits-ui/src/lib/bits/context-menu/types.ts index c4ca51ee4..12686125e 100644 --- a/packages/bits-ui/src/lib/bits/context-menu/types.ts +++ b/packages/bits-ui/src/lib/bits/context-menu/types.ts @@ -9,7 +9,7 @@ export type ContextMenuContentPropsWithoutHTML = Omit< export type ContextMenuContentProps = Omit< MenuContentProps, - "side" | "onMountAutoFocus" | "sideOffset" | "align" + "side" | "onOpenAutoFocus" | "sideOffset" | "align" >; export type ContextMenuTriggerPropsWithoutHTML = WithChild<{ diff --git a/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content-static.svelte b/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content-static.svelte index a6cbd84f9..87479c22f 100644 --- a/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content-static.svelte @@ -4,11 +4,11 @@ import { pickerOpenFocus } from "$lib/shared/date/index.js"; import { mergeProps } from "$lib/internal/mergeProps.js"; - let { ref = $bindable(null), onMountAutoFocus, ...restProps }: ContentStaticProps = $props(); + let { ref = $bindable(null), onOpenAutoFocus, ...restProps }: ContentStaticProps = $props(); const mergedProps = $derived( - mergeProps({ onMountAutoFocus }, { onMountAutoFocus: pickerOpenFocus }) - ) as any; + mergeProps({ onOpenAutoFocus }, { onOpenAutoFocus: pickerOpenFocus }) + ); diff --git a/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content.svelte b/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content.svelte index 69cca883e..7390bfe5e 100644 --- a/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content.svelte +++ b/packages/bits-ui/src/lib/bits/date-picker/components/date-picker-content.svelte @@ -4,10 +4,10 @@ import { pickerOpenFocus } from "$lib/shared/date/index.js"; import { mergeProps } from "$lib/internal/mergeProps.js"; - let { ref = $bindable(null), onMountAutoFocus, ...restProps }: ContentProps = $props(); + let { ref = $bindable(null), onOpenAutoFocus, ...restProps }: ContentProps = $props(); const mergedProps = $derived( - mergeProps({ onMountAutoFocus }, { onMountAutoFocus: pickerOpenFocus }) + mergeProps({ onOpenAutoFocus }, { onOpenAutoFocus: pickerOpenFocus }) ) as any; diff --git a/packages/bits-ui/src/lib/bits/dialog/components/dialog-content.svelte b/packages/bits-ui/src/lib/bits/dialog/components/dialog-content.svelte index 2f7aac351..3c4added5 100644 --- a/packages/bits-ui/src/lib/bits/dialog/components/dialog-content.svelte +++ b/packages/bits-ui/src/lib/bits/dialog/components/dialog-content.svelte @@ -18,10 +18,11 @@ child, ref = $bindable(null), forceMount = false, - onDestroyAutoFocus = noop, + onCloseAutoFocus = noop, onEscapeKeydown = noop, onInteractOutside = noop, trapFocus = true, + preventScroll = true, ...restProps }: ContentProps = $props(); @@ -38,13 +39,13 @@ {#snippet presence({ present })} - + { - onDestroyAutoFocus(e); + onCloseAutoFocus={(e) => { + onCloseAutoFocus(e); if (e.defaultPrevented) return; contentState.root.triggerNode?.focus(); }} diff --git a/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content-static.svelte b/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content-static.svelte index 2e37fe8d9..aa01f8824 100644 --- a/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content-static.svelte @@ -43,8 +43,8 @@ if (e.defaultPrevented) return; contentState.root.immediateClose(); }} - onMountAutoFocus={(e) => e.preventDefault()} - onDestroyAutoFocus={(e) => e.preventDefault()} + onOpenAutoFocus={(e) => e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} trapFocus={false} loop={false} preventScroll={false} diff --git a/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content.svelte b/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content.svelte index 708ef2206..6136bd8b4 100644 --- a/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content.svelte +++ b/packages/bits-ui/src/lib/bits/link-preview/components/link-preview-content.svelte @@ -61,8 +61,8 @@ if (e.defaultPrevented) return; contentState.root.immediateClose(); }} - onMountAutoFocus={(e) => e.preventDefault()} - onDestroyAutoFocus={(e) => e.preventDefault()} + onOpenAutoFocus={(e) => e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} trapFocus={false} loop={false} preventScroll={false} diff --git a/packages/bits-ui/src/lib/bits/listbox/components/listbox-content-static.svelte b/packages/bits-ui/src/lib/bits/listbox/components/listbox-content-static.svelte index cc46e5b2c..6e239446b 100644 --- a/packages/bits-ui/src/lib/bits/listbox/components/listbox-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/listbox/components/listbox-content-static.svelte @@ -46,8 +46,8 @@ if (e.defaultPrevented) return; contentState.root.closeMenu(); }} - onMountAutoFocus={(e) => e.preventDefault()} - onDestroyAutoFocus={(e) => e.preventDefault()} + onOpenAutoFocus={(e) => e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} trapFocus={false} loop={false} preventScroll={false} diff --git a/packages/bits-ui/src/lib/bits/listbox/components/listbox-content.svelte b/packages/bits-ui/src/lib/bits/listbox/components/listbox-content.svelte index c21d8ed1c..d37cd03ea 100644 --- a/packages/bits-ui/src/lib/bits/listbox/components/listbox-content.svelte +++ b/packages/bits-ui/src/lib/bits/listbox/components/listbox-content.svelte @@ -47,8 +47,8 @@ if (e.defaultPrevented) return; contentState.root.closeMenu(); }} - onMountAutoFocus={(e) => e.preventDefault()} - onDestroyAutoFocus={(e) => e.preventDefault()} + onOpenAutoFocus={(e) => e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} trapFocus={false} loop={false} preventScroll={false} diff --git a/packages/bits-ui/src/lib/bits/listbox/components/listbox-viewport.svelte b/packages/bits-ui/src/lib/bits/listbox/components/listbox-viewport.svelte index bfeb625b5..5898dcace 100644 --- a/packages/bits-ui/src/lib/bits/listbox/components/listbox-viewport.svelte +++ b/packages/bits-ui/src/lib/bits/listbox/components/listbox-viewport.svelte @@ -39,6 +39,16 @@ -ms-overflow-style: none !important; -webkit-overflow-scrolling: touch !important; } + + :global([data-combobox-viewport]) { + scrollbar-width: none !important; + -ms-overflow-style: none !important; + -webkit-overflow-scrolling: touch !important; + } + + :global([data-combobox-viewport])::-webkit-scrollbar { + display: none !important; + } :global([data-listbox-viewport])::-webkit-scrollbar { display: none !important; } diff --git a/packages/bits-ui/src/lib/bits/listbox/types.ts b/packages/bits-ui/src/lib/bits/listbox/types.ts index 4ada99e89..b68ea27f5 100644 --- a/packages/bits-ui/src/lib/bits/listbox/types.ts +++ b/packages/bits-ui/src/lib/bits/listbox/types.ts @@ -1,11 +1,8 @@ +import type { Expand } from "svelte-toolbelt"; import type { PortalProps } from "../utilities/portal/types.js"; import type { PopperLayerProps, PopperLayerStaticProps } from "../utilities/popper-layer/types.js"; import type { ArrowProps, ArrowPropsWithoutHTML } from "../utilities/arrow/types.js"; -import type { - PrimitiveButtonAttributes, - PrimitiveDivAttributes, - PrimitiveInputAttributes, -} from "$lib/shared/attributes.js"; +import type { PrimitiveButtonAttributes, PrimitiveDivAttributes } from "$lib/shared/attributes.js"; import type { OnChangeFn, WithChild, WithChildren, Without } from "$lib/internal/types.js"; export type ListboxBaseRootPropsWithoutHTML = WithChildren<{ @@ -118,13 +115,30 @@ export type ListboxRootPropsWithoutHTML = ListboxBaseRootPropsWithoutHTML & export type ListboxRootProps = ListboxRootPropsWithoutHTML; -export type ListboxContentPropsWithoutHTML = WithChild>; +export type _SharedListboxContentProps = { + /** + * Whether or not to loop through the items when navigating with the keyboard. + * + * @defaultValue `false` + */ + loop?: boolean; +}; + +export type ListboxContentPropsWithoutHTML = Expand< + WithChild< + Omit & + _SharedListboxContentProps + > +>; export type ListboxContentProps = ListboxContentPropsWithoutHTML & Without; -export type ListboxContentStaticPropsWithoutHTML = WithChild< - Omit +export type ListboxContentStaticPropsWithoutHTML = Expand< + WithChild< + Omit & + _SharedListboxContentProps + > >; export type ListboxContentStaticProps = ListboxContentStaticPropsWithoutHTML & diff --git a/packages/bits-ui/src/lib/bits/menu/components/menu-sub-content.svelte b/packages/bits-ui/src/lib/bits/menu/components/menu-sub-content.svelte index c5b8d512a..f95500887 100644 --- a/packages/bits-ui/src/lib/bits/menu/components/menu-sub-content.svelte +++ b/packages/bits-ui/src/lib/bits/menu/components/menu-sub-content.svelte @@ -53,15 +53,15 @@ const mergedProps = $derived( mergeProps(restProps, subContentState.props, { - onMountAutoFocus, - onDestroyAutoFocus, + onOpenAutoFocus, + onCloseAutoFocus, side, onkeydown, "data-menu-sub-content": "", }) ); - function onMountAutoFocus(e: Event) { + function onOpenAutoFocus(e: Event) { afterTick(() => { e.preventDefault(); if (subContentState.parentMenu.root.isUsingKeyboard.current) { @@ -71,7 +71,7 @@ }); } - function onDestroyAutoFocus(e: Event) { + function onCloseAutoFocus(e: Event) { e.preventDefault(); } @@ -80,7 +80,7 @@ {...mergedProps} {interactOutsideBehavior} {escapeKeydownBehavior} - {onMountAutoFocus} + {onOpenAutoFocus} present={subContentState.parentMenu.open.current || forceMount} onInteractOutside={(e) => { onInteractOutside(e); diff --git a/packages/bits-ui/src/lib/bits/menu/menu.svelte.ts b/packages/bits-ui/src/lib/bits/menu/menu.svelte.ts index 9e8e9f838..7740b4745 100644 --- a/packages/bits-ui/src/lib/bits/menu/menu.svelte.ts +++ b/packages/bits-ui/src/lib/bits/menu/menu.svelte.ts @@ -344,7 +344,7 @@ class MenuContentState { return false; } - onMountAutoFocus(e: Event) { + onOpenAutoFocus(e: Event) { if (e.defaultPrevented) return; e.preventDefault(); const contentNode = this.parentMenu.contentNode; diff --git a/packages/bits-ui/src/lib/bits/menu/types.ts b/packages/bits-ui/src/lib/bits/menu/types.ts index 28808a487..ccdb6de74 100644 --- a/packages/bits-ui/src/lib/bits/menu/types.ts +++ b/packages/bits-ui/src/lib/bits/menu/types.ts @@ -1,3 +1,4 @@ +import type { Expand } from "svelte-toolbelt"; import type { PopperLayerProps, PopperLayerStaticProps } from "../utilities/popper-layer/types.js"; import type { ArrowProps, ArrowPropsWithoutHTML } from "../utilities/arrow/types.js"; import type { OnChangeFn, WithChild, WithChildren, Without } from "$lib/internal/types.js"; @@ -26,12 +27,25 @@ export type MenuRootPropsWithoutHTML = WithChildren<{ export type MenuRootProps = MenuRootPropsWithoutHTML; -export type MenuContentPropsWithoutHTML = WithChild>; +export type _SharedMenuContentProps = { + /** + * When `true`, the menu will loop through items when navigating with the keyboard. + * + * @defaultValue false + */ + loop?: boolean; +}; + +export type MenuContentPropsWithoutHTML = Expand< + WithChild & _SharedMenuContentProps> +>; export type MenuContentProps = MenuContentPropsWithoutHTML & Without; -export type MenuContentStaticPropsWithoutHTML = WithChild>; +export type MenuContentStaticPropsWithoutHTML = Expand< + WithChild & _SharedMenuContentProps> +>; export type MenuContentStaticProps = MenuContentStaticPropsWithoutHTML & Without; @@ -112,7 +126,10 @@ export type MenuSubPropsWithoutHTML = WithChildren<{ onOpenChange?: OnChangeFn; }>; -export type MenuSubContentPropsWithoutHTML = WithChild>; +export type MenuSubContentPropsWithoutHTML = Expand< + WithChild & _SharedMenuContentProps> +>; + export type MenuSubContentProps = MenuSubContentPropsWithoutHTML & Without; diff --git a/packages/bits-ui/src/lib/bits/menubar/components/menubar-content-static.svelte b/packages/bits-ui/src/lib/bits/menubar/components/menubar-content-static.svelte index 9a12b563f..5c0f268cc 100644 --- a/packages/bits-ui/src/lib/bits/menubar/components/menubar-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/menubar/components/menubar-content-static.svelte @@ -31,6 +31,6 @@ preventScroll={false} onInteractOutside={contentState.onInteractOutside} onFocusOutside={contentState.onFocusOutside} - onDestroyAutoFocus={contentState.onDestroyAutoFocus} - onMountAutoFocus={contentState.onMountAutoFocus} + onCloseAutoFocus={contentState.onCloseAutoFocus} + onOpenAutoFocus={contentState.onOpenAutoFocus} /> diff --git a/packages/bits-ui/src/lib/bits/menubar/components/menubar-content.svelte b/packages/bits-ui/src/lib/bits/menubar/components/menubar-content.svelte index bff910a9a..673eb1ef2 100644 --- a/packages/bits-ui/src/lib/bits/menubar/components/menubar-content.svelte +++ b/packages/bits-ui/src/lib/bits/menubar/components/menubar-content.svelte @@ -31,6 +31,6 @@ preventScroll={false} onInteractOutside={contentState.onInteractOutside} onFocusOutside={contentState.onFocusOutside} - onDestroyAutoFocus={contentState.onDestroyAutoFocus} - onMountAutoFocus={contentState.onMountAutoFocus} + onCloseAutoFocus={contentState.onCloseAutoFocus} + onOpenAutoFocus={contentState.onOpenAutoFocus} /> diff --git a/packages/bits-ui/src/lib/bits/menubar/menubar.svelte.ts b/packages/bits-ui/src/lib/bits/menubar/menubar.svelte.ts index bf9d5d21e..35fffbf6f 100644 --- a/packages/bits-ui/src/lib/bits/menubar/menubar.svelte.ts +++ b/packages/bits-ui/src/lib/bits/menubar/menubar.svelte.ts @@ -306,7 +306,7 @@ class MenubarContentState { }); } - onDestroyAutoFocus = (e: Event) => { + onCloseAutoFocus = (e: Event) => { const menubarOpen = Boolean(this.root.value.current); if (!menubarOpen && !this.hasInteractedOutside) { this.menu.getTriggerNode()?.focus(); @@ -328,7 +328,7 @@ class MenubarContentState { this.hasInteractedOutside = true; }; - onMountAutoFocus = () => { + onOpenAutoFocus = () => { afterTick(() => this.ref.current?.focus()); }; diff --git a/packages/bits-ui/src/lib/bits/navigation-menu/navigation-menu.svelte.ts b/packages/bits-ui/src/lib/bits/navigation-menu/navigation-menu.svelte.ts index a0535d112..1ad0b416b 100644 --- a/packages/bits-ui/src/lib/bits/navigation-menu/navigation-menu.svelte.ts +++ b/packages/bits-ui/src/lib/bits/navigation-menu/navigation-menu.svelte.ts @@ -2,11 +2,7 @@ import { untrack } from "svelte"; import { box } from "svelte-toolbelt"; import { Previous } from "runed"; import { getTabbableCandidates } from "$lib/internal/focus.js"; -import { - type ReadableBoxedValues, - type WritableBoxedValues, - watch, -} from "$lib/internal/box.svelte.js"; +import type { ReadableBoxedValues, WritableBoxedValues } from "$lib/internal/box.svelte.js"; import type { Direction, Orientation } from "$lib/shared/index.js"; import { getAriaExpanded, diff --git a/packages/bits-ui/src/lib/bits/popover/components/popover-content-static.svelte b/packages/bits-ui/src/lib/bits/popover/components/popover-content-static.svelte index 0d81a1972..76611e43c 100644 --- a/packages/bits-ui/src/lib/bits/popover/components/popover-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/popover/components/popover-content-static.svelte @@ -13,9 +13,11 @@ ref = $bindable(null), id = useId(), forceMount = false, - onDestroyAutoFocus = noop, + onCloseAutoFocus = noop, onEscapeKeydown = noop, onInteractOutside = noop, + preventScroll = false, + trapFocus = true, ...restProps }: ContentStaticProps = $props(); @@ -45,13 +47,14 @@ if (e.defaultPrevented) return; contentState.root.close(); }} - onDestroyAutoFocus={(e) => { - onDestroyAutoFocus(e); + onCloseAutoFocus={(e) => { + onCloseAutoFocus(e); if (e.defaultPrevented) return; e.preventDefault(); contentState.root.triggerNode?.focus(); }} - trapFocus + {trapFocus} + {preventScroll} loop {forceMount} > diff --git a/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte b/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte index 6c094fa7e..32cd3dc45 100644 --- a/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte +++ b/packages/bits-ui/src/lib/bits/popover/components/popover-content.svelte @@ -13,9 +13,11 @@ ref = $bindable(null), id = useId(), forceMount = false, - onDestroyAutoFocus = noop, + onCloseAutoFocus = noop, onEscapeKeydown = noop, onInteractOutside = noop, + trapFocus = true, + preventScroll = false, ...restProps }: ContentProps = $props(); @@ -44,13 +46,14 @@ if (e.defaultPrevented) return; contentState.root.close(); }} - onDestroyAutoFocus={(e) => { - onDestroyAutoFocus(e); + onCloseAutoFocus={(e) => { + onCloseAutoFocus(e); if (e.defaultPrevented) return; e.preventDefault(); contentState.root.triggerNode?.focus(); }} - trapFocus + {trapFocus} + {preventScroll} loop {forceMount} > diff --git a/packages/bits-ui/src/lib/bits/select/components/select-content-impl.svelte b/packages/bits-ui/src/lib/bits/select/components/select-content-impl.svelte index a29a05caa..095b245c9 100644 --- a/packages/bits-ui/src/lib/bits/select/components/select-content-impl.svelte +++ b/packages/bits-ui/src/lib/bits/select/components/select-content-impl.svelte @@ -14,8 +14,8 @@ let { ref: _ref = $bindable(null), id = useId(), - onMountAutoFocus = noop, - onDestroyAutoFocus = noop, + onOpenAutoFocus = noop, + onCloseAutoFocus = noop, present, position = "floating", context, @@ -32,12 +32,12 @@ { - onMountAutoFocus(e); + onOpenAutoFocus={(e) => { + onOpenAutoFocus(e); e.preventDefault(); }} - onDestroyAutoFocus={(e) => { - onDestroyAutoFocus(e); + onCloseAutoFocus={(e) => { + onCloseAutoFocus(e); }} > {#snippet focusScope({ props: focusScopeProps })} diff --git a/packages/bits-ui/src/lib/bits/select/select.svelte.ts b/packages/bits-ui/src/lib/bits/select/select.svelte.ts index ee7162a29..42ce2f252 100644 --- a/packages/bits-ui/src/lib/bits/select/select.svelte.ts +++ b/packages/bits-ui/src/lib/bits/select/select.svelte.ts @@ -7,7 +7,6 @@ import { type ReadableBox, type WritableBox, box } from "svelte-toolbelt"; import { SvelteMap } from "svelte/reactivity"; import { untrack } from "svelte"; import type { ReadableBoxedValues, WritableBoxedValues } from "$lib/internal/box.svelte.js"; -import { watch } from "$lib/internal/box.svelte.js"; import { useId } from "$lib/internal/useId.js"; import type { Direction } from "$lib/shared/index.js"; import { createContext } from "$lib/internal/createContext.js"; diff --git a/packages/bits-ui/src/lib/bits/select/types.ts b/packages/bits-ui/src/lib/bits/select/types.ts index c4239c2d7..c6c81b742 100644 --- a/packages/bits-ui/src/lib/bits/select/types.ts +++ b/packages/bits-ui/src/lib/bits/select/types.ts @@ -1,4 +1,5 @@ import type { HTMLSelectAttributes } from "svelte/elements"; +import type { Expand } from "svelte-toolbelt"; import type { PopperLayerProps } from "../utilities/popper-layer/types.js"; import type { ArrowProps, ArrowPropsWithoutHTML } from "../utilities/arrow/types.js"; import type { OnChangeFn, WithChild, WithChildren, Without } from "$lib/internal/types.js"; @@ -64,16 +65,27 @@ export type SelectRootPropsWithoutHTML = WithChildren<{ export type SelectRootProps = SelectRootPropsWithoutHTML; -export type SelectContentImplPropsWithoutHTML = WithChild< - Omit & { - /** - * The positioning mode to use - * - * `item-aligned` - behaves similarly to a native MacOS menu by positioning content relative to the active item.
- * `floating (default)` - positions content in the same way as our other primitives, for example `Popover` or `DropdownMenu`. - */ - position?: "item-aligned" | "floating"; - } +export type _SharedSelectContentProps = { + /** + * Whether or not to loop through the items when navigating with the keyboard. + * + * @defaultValue `false` + */ + loop?: boolean; +}; + +export type SelectContentImplPropsWithoutHTML = Expand< + WithChild< + Omit & { + /** + * The positioning mode to use + * + * `item-aligned` - behaves similarly to a native MacOS menu by positioning content relative to the active item.
+ * `floating (default)` - positions content in the same way as our other primitives, for example `Popover` or `DropdownMenu`. + */ + position?: "item-aligned" | "floating"; + } & _SharedSelectContentProps + > >; export type SelectContentImplProps = SelectContentImplPropsWithoutHTML & diff --git a/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content-static.svelte b/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content-static.svelte index ac120b0b0..e1861d89f 100644 --- a/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content-static.svelte +++ b/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content-static.svelte @@ -43,8 +43,8 @@ if (e.defaultPrevented) return; contentState.root.handleClose(); }} - onMountAutoFocus={(e) => e.preventDefault()} - onDestroyAutoFocus={(e) => e.preventDefault()} + onOpenAutoFocus={(e) => e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} trapFocus={false} loop={false} preventScroll={false} diff --git a/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content.svelte b/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content.svelte index 855240011..c016989e2 100644 --- a/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content.svelte +++ b/packages/bits-ui/src/lib/bits/tooltip/components/tooltip-content.svelte @@ -61,8 +61,8 @@ if (e.defaultPrevented) return; contentState.root.handleClose(); }} - onMountAutoFocus={(e) => e.preventDefault()} - onDestroyAutoFocus={(e) => e.preventDefault()} + onOpenAutoFocus={(e) => e.preventDefault()} + onCloseAutoFocus={(e) => e.preventDefault()} trapFocus={false} loop={false} preventScroll={false} 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 d9df34cea..f014d797b 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 @@ -15,7 +15,8 @@ export type EscapeLayerProps = { /** * Escape behavior type. * `close`: Closes the element immediately. - * `defer-otherwise-close`: Delegates the action to the parent element. If no parent is found, it closes the element. + * `defer-otherwise-close`: Delegates the action to its parent component that has an + * escape keydown handler. If no parent is found, it closes the element. * `defer-otherwise-ignore`: Delegates the action to the parent element. If no parent is found, nothing is done. * `ignore`: Prevents the element from closing and also blocks the parent element from closing in response to an escape key press. * @@ -31,6 +32,5 @@ export type EscapeLayerImplProps = { * `presence` returned from the `presence` layer. */ enabled: boolean; - children?: Snippet; } & EscapeLayerProps; diff --git a/packages/bits-ui/src/lib/bits/utilities/floating-layer/useFloatingLayer.svelte.ts b/packages/bits-ui/src/lib/bits/utilities/floating-layer/useFloatingLayer.svelte.ts index de52d8fe4..76831cc60 100644 --- a/packages/bits-ui/src/lib/bits/utilities/floating-layer/useFloatingLayer.svelte.ts +++ b/packages/bits-ui/src/lib/bits/utilities/floating-layer/useFloatingLayer.svelte.ts @@ -173,7 +173,7 @@ class FloatingContentState { this.#avoidCollisions && flip({ ...this.detectOverflowOptions }), size({ ...this.detectOverflowOptions, - apply: ({ elements, rects, availableWidth, availableHeight }) => { + apply: ({ rects, availableWidth, availableHeight }) => { const { width: anchorWidth, height: anchorHeight } = rects.reference; this.#availableWidth = availableWidth; this.#availableHeight = availableHeight; diff --git a/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte b/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte index 892218638..b3db908d3 100644 --- a/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte +++ b/packages/bits-ui/src/lib/bits/utilities/focus-scope/focus-scope.svelte @@ -8,16 +8,16 @@ id, trapFocus = false, loop = false, - onDestroyAutoFocus = noop, - onMountAutoFocus = noop, + onCloseAutoFocus = noop, + onOpenAutoFocus = noop, focusScope, }: FocusScopeImplProps = $props(); const focusScopeState = useFocusScope({ - trapFocus: box.with(() => trapFocus), + enabled: box.with(() => trapFocus), loop: box.with(() => loop), - onDestroyAutoFocus: box.with(() => onDestroyAutoFocus), - onMountAutoFocus: box.with(() => onMountAutoFocus), + onCloseAutoFocus: box.with(() => onCloseAutoFocus), + onOpenAutoFocus: box.with(() => onOpenAutoFocus), id: box.with(() => id), }); diff --git a/packages/bits-ui/src/lib/bits/utilities/focus-scope/types.ts b/packages/bits-ui/src/lib/bits/utilities/focus-scope/types.ts index 6aa53da0f..181511200 100644 --- a/packages/bits-ui/src/lib/bits/utilities/focus-scope/types.ts +++ b/packages/bits-ui/src/lib/bits/utilities/focus-scope/types.ts @@ -4,16 +4,16 @@ import type { EventCallback } from "$lib/internal/events.js"; export type FocusScopeProps = { /** - * Event handler called when auto-focusing onMount. + * Event handler called when auto-focusing on open. * Can be prevented. */ - onMountAutoFocus?: EventCallback; + onOpenAutoFocus?: EventCallback; /** - * Event handler called when auto-focusing onDestroy. + * Event handler called when auto-focusing on close. * Can be prevented. */ - onDestroyAutoFocus?: EventCallback; + onCloseAutoFocus?: EventCallback; /** * Whether focus is trapped within the focus scope. @@ -21,13 +21,6 @@ export type FocusScopeProps = { * @defaultValue false */ trapFocus?: boolean; - - /** - * When `true` will loop through the tabbable elements in the focus scope. - * - * @defaultValue false - */ - loop?: boolean; }; export type FocusScopeImplProps = { @@ -40,4 +33,9 @@ export type FocusScopeImplProps = { * The snippet to render the focus scope container with its props. */ focusScope?: Snippet<[{ props: FocusScopeContainerProps }]>; + + /** + * When `true` will loop through the tabbable elements in the focus scope. + */ + loop?: boolean; } & FocusScopeProps; diff --git a/packages/bits-ui/src/lib/bits/utilities/focus-scope/useFocusScope.svelte.ts b/packages/bits-ui/src/lib/bits/utilities/focus-scope/useFocusScope.svelte.ts index 8a1a25bed..baf31b135 100644 --- a/packages/bits-ui/src/lib/bits/utilities/focus-scope/useFocusScope.svelte.ts +++ b/packages/bits-ui/src/lib/bits/utilities/focus-scope/useFocusScope.svelte.ts @@ -35,19 +35,19 @@ type UseFocusScopeProps = ReadableBoxedValues<{ * * @defaultValue false */ - trapFocus: boolean; + enabled: boolean; /** * Event handler called when auto-focusing onMount. * Can be prevented. */ - onMountAutoFocus: EventCallback; + onOpenAutoFocus: EventCallback; /** * Event handler called when auto-focusing onDestroy. * Can be prevented. */ - onDestroyAutoFocus: EventCallback; + onCloseAutoFocus: EventCallback; }>; export type FocusScopeContainerProps = { @@ -59,23 +59,26 @@ export type FocusScopeContainerProps = { export function useFocusScope({ id, loop, - trapFocus, - onMountAutoFocus, - onDestroyAutoFocus, + enabled, + onOpenAutoFocus, + onCloseAutoFocus, }: UseFocusScopeProps) { const focusScopeStack = createFocusScopeStack(); const focusScope = createFocusScopeAPI(); const ref = box(null); + useRefById({ id, ref, + condition: () => enabled.current, }); + let lastFocusedElement = $state(null); $effect(() => { const container = ref.current; if (!container) return; - if (!trapFocus.current) return; + if (!enabled.current) return; function handleFocusIn(event: FocusEvent) { if (focusScope.paused || !container) return; @@ -153,7 +156,7 @@ export function useFocusScope({ const mountEvent = new CustomEvent(AUTOFOCUS_ON_MOUNT, EVENT_OPTIONS); container.addEventListener( AUTOFOCUS_ON_MOUNT, - untrack(() => onMountAutoFocus.current) + untrack(() => onOpenAutoFocus.current) ); container.dispatchEvent(mountEvent); @@ -174,13 +177,13 @@ export function useFocusScope({ if (!container) return; container.removeEventListener( AUTOFOCUS_ON_MOUNT, - untrack(() => onMountAutoFocus.current) + untrack(() => onOpenAutoFocus.current) ); const destroyEvent = new CustomEvent(AUTOFOCUS_ON_DESTROY, EVENT_OPTIONS); container.addEventListener( AUTOFOCUS_ON_DESTROY, - untrack(() => onDestroyAutoFocus.current) + untrack(() => onCloseAutoFocus.current) ); container.dispatchEvent(destroyEvent); @@ -189,7 +192,7 @@ export function useFocusScope({ focus(previouslyFocusedElement ?? document.body, { select: true }); } - container?.removeEventListener(AUTOFOCUS_ON_DESTROY, onDestroyAutoFocus.current); + container?.removeEventListener(AUTOFOCUS_ON_DESTROY, onCloseAutoFocus.current); focusScopeStack.remove(focusScope); }, 0); @@ -197,7 +200,8 @@ export function useFocusScope({ }); function handleKeydown(e: KeyboardEvent) { - if (!loop.current && !trapFocus.current) return; + if (!enabled.current) return; + if (!loop.current && !enabled.current) return; if (focusScope.paused) return; const isTabKey = e.key === kbd.TAB && !e.ctrlKey && !e.altKey && !e.metaKey; 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 ec8ffe491..0ecb7d37d 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 @@ -37,12 +37,12 @@ onPlaced, onInteractOutside, onInteractOutsideStart, - onDestroyAutoFocus, - onMountAutoFocus, + onCloseAutoFocus, + onOpenAutoFocus, onFocusOutside, interactOutsideBehavior = "close", loop, - trapFocus, + trapFocus = true, isValidEvent = () => false, customAnchor = null, isStatic = false, @@ -75,7 +75,13 @@ > {#snippet content({ props: floatingProps })} - + {#snippet focusScope({ props: focusScopeProps })} }]>; dir?: Direction; - preventScroll?: boolean; }; export type PopperLayerImplProps = Omit< diff --git a/packages/bits-ui/src/lib/helpers/builders.ts b/packages/bits-ui/src/lib/helpers/builders.ts deleted file mode 100644 index 37584acc9..000000000 --- a/packages/bits-ui/src/lib/helpers/builders.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Action, ActionReturn } from "svelte/action"; - -export type Builder< - // eslint-disable-next-line ts/no-explicit-any - Element = any, - // eslint-disable-next-line ts/no-explicit-any - Param = any, - // eslint-disable-next-line ts/no-explicit-any - Attributes extends Record = Record, - // eslint-disable-next-line ts/no-explicit-any -> = Record & { - action: Action; -}; - -type BuilderActionsParams = { - // eslint-disable-next-line ts/no-explicit-any - builders: Builder[]; -}; - -type BuilderActionsReturn = { - destroy: () => void; -}; - -export function builderActions( - node: HTMLElement, - params: BuilderActionsParams -): BuilderActionsReturn { - const unsubs: ActionReturn[] = []; - params.builders.forEach((builder) => { - const act = builder.action(node); - if (act) { - unsubs.push(act); - } - }); - return { - destroy: () => { - unsubs.forEach((unsub) => { - if (unsub.destroy) { - unsub.destroy(); - } - }); - }, - }; -} - -// eslint-disable-next-line ts/no-explicit-any -export function getAttrs(builders: Builder[]) { - const attrs: Record = {}; - builders.forEach((builder) => { - Object.keys(builder).forEach((key) => { - if (key !== "action") { - attrs[key] = builder[key]; - } - }); - }); - return attrs; -} diff --git a/packages/bits-ui/src/lib/helpers/event-handlers.ts b/packages/bits-ui/src/lib/helpers/event-handlers.ts deleted file mode 100644 index 4cbd6b06c..000000000 --- a/packages/bits-ui/src/lib/helpers/event-handlers.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type CustomEventHandler = CustomEvent<{ - currentTarget: EventTarget & M; - originalEvent: T; -}>; diff --git a/packages/bits-ui/src/lib/helpers/index.ts b/packages/bits-ui/src/lib/helpers/index.ts deleted file mode 100644 index 322b82e36..000000000 --- a/packages/bits-ui/src/lib/helpers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./builders.js"; -export * from "./event-handlers.js"; diff --git a/packages/bits-ui/src/lib/index.ts b/packages/bits-ui/src/lib/index.ts index 94eedbfe7..3be82e84f 100644 --- a/packages/bits-ui/src/lib/index.ts +++ b/packages/bits-ui/src/lib/index.ts @@ -1,7 +1,5 @@ export * from "./bits/index.js"; export type * from "./bits/index.js"; -export * from "./helpers/index.js"; -export type * from "./helpers/index.js"; export * from "./shared/index.js"; export type * from "./shared/index.js"; export * from "./types.js"; diff --git a/packages/bits-ui/src/lib/internal/attrs.ts b/packages/bits-ui/src/lib/internal/attrs.ts index 46b8035ac..7621d9224 100644 --- a/packages/bits-ui/src/lib/internal/attrs.ts +++ b/packages/bits-ui/src/lib/internal/attrs.ts @@ -1,41 +1,3 @@ -export const bits = [ - "accordion", - "alert-dialog", - "aspect-ratio", - "avatar", - "button", - "calendar", - "checkbox", - "collapsible", - "combobox", - "context-menu", - "date-field", - "date-picker", - "date-range-field", - "date-range-picker", - "dialog", - "dropdown-menu", - "label", - "link-preview", - "menubar", - "pagination", - "pin-input", - "popover", - "progress", - "radio-group", - "range-calendar", - "scroll-area", - "select", - "separator", - "slider", - "switch", - "tabs", - "toggle", - "toggle-group", - "toolbar", - "tooltip", -] as const; - export function getDataOpenClosed(condition: boolean): "open" | "closed" { return condition ? "open" : "closed"; } @@ -44,10 +6,6 @@ export function getDataChecked(condition: boolean): "checked" | "unchecked" { return condition ? "checked" : "unchecked"; } -export function dataDisabledAttrs(condition: boolean): "" | undefined { - return condition ? "" : undefined; -} - export function getAriaDisabled(condition: boolean): "true" | "false" { return condition ? "true" : "false"; } diff --git a/packages/bits-ui/src/lib/internal/context.ts b/packages/bits-ui/src/lib/internal/context.ts deleted file mode 100644 index 40eacc405..000000000 --- a/packages/bits-ui/src/lib/internal/context.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { hasContext } from "svelte"; -import { DEV } from "esm-env"; - -export function verifyContextDeps(...deps: Array) { - if (DEV) { - const missing: string[] = []; - for (const dep of deps) { - if (hasContext(dep)) continue; - const depLabel = typeof dep === "symbol" ? dep.description : dep; - missing.push(depLabel!); - } - if (missing.length) { - throw new Error(`Missing context dependencies: ${missing.join(", ")}`); - } - } -} diff --git a/packages/bits-ui/src/lib/internal/floating-svelte/arrow.ts b/packages/bits-ui/src/lib/internal/floating-svelte/arrow.ts deleted file mode 100644 index 19b3cfa26..000000000 --- a/packages/bits-ui/src/lib/internal/floating-svelte/arrow.ts +++ /dev/null @@ -1,52 +0,0 @@ -import type { Derivable, Middleware, Padding } from "@floating-ui/dom"; -import { arrow as arrowCore } from "@floating-ui/dom"; -import type { MiddlewareState } from "@floating-ui/core"; -import { type WritableBox, box } from "svelte-toolbelt"; -import { isElement } from "../is.js"; - -export type ArrowOptions = { - /** - * The arrow element to be positioned. - * @default undefined - */ - element: WritableBox | Element | null; - - /** - * The padding between the arrow element and the floating element edges. - * Useful when the floating element has rounded corners. - * - * @default 0 - */ - padding?: Padding; -}; - -/** - * Provides data to position an inner element of the floating element so that it - * appears centered to the reference element. - * This wraps the core `arrow` middleware to allow React refs as the element. - * @see https://floating-ui.com/docs/arrow - */ - -export function arrow(options: ArrowOptions | Derivable): Middleware { - return { - name: "arrow", - options, - fn(state: MiddlewareState) { - const { element, padding } = typeof options === "function" ? options(state) : options; - - if (element && box.isBox(element)) { - if (element.current !== null) { - return arrowCore({ element: element.current, padding }).fn(state); - } - - return {}; - } - - if (isElement(element)) { - return arrowCore({ element, padding }).fn(state); - } - - return {}; - }, - }; -} diff --git a/packages/bits-ui/src/lib/internal/floating-svelte/floating-utils.svelte.ts b/packages/bits-ui/src/lib/internal/floating-svelte/floating-utils.svelte.ts index 8ce2dcda2..078726db3 100644 --- a/packages/bits-ui/src/lib/internal/floating-svelte/floating-utils.svelte.ts +++ b/packages/bits-ui/src/lib/internal/floating-svelte/floating-utils.svelte.ts @@ -7,9 +7,7 @@ export function get(valueOrGetValue: MaybeGetter): T { } export function getDPR(element: Element): number { - if (typeof window === "undefined") { - return 1; - } + if (typeof window === "undefined") return 1; const win = element.ownerDocument.defaultView || window; return win.devicePixelRatio || 1; } diff --git a/packages/bits-ui/src/lib/internal/useRovingFocus.svelte.ts b/packages/bits-ui/src/lib/internal/useRovingFocus.svelte.ts index b57e26def..1c4c63926 100644 --- a/packages/bits-ui/src/lib/internal/useRovingFocus.svelte.ts +++ b/packages/bits-ui/src/lib/internal/useRovingFocus.svelte.ts @@ -55,9 +55,8 @@ export function useRovingFocus(props: UseRovingFocusProps) { function focusFirstCandidate() { const items = getCandidateNodes(); - if (items.length) { - items[0]?.focus(); - } + if (!items.length) return; + items[0]?.focus(); } function handleKeydown(node: HTMLElement | null | undefined, e: KeyboardEvent) { diff --git a/packages/bits-ui/src/lib/shared/date/calendar-helpers.svelte.ts b/packages/bits-ui/src/lib/shared/date/calendar-helpers.svelte.ts index f0a770f2c..ceee75a9a 100644 --- a/packages/bits-ui/src/lib/shared/date/calendar-helpers.svelte.ts +++ b/packages/bits-ui/src/lib/shared/date/calendar-helpers.svelte.ts @@ -20,7 +20,6 @@ import { chunk, isValidIndex } from "$lib/internal/arrays.js"; import { isHTMLElement } from "$lib/internal/is.js"; import { kbd } from "$lib/internal/kbd.js"; import { styleToString } from "$lib/internal/style.js"; -import { watch } from "$lib/internal/box.svelte.js"; /** * Checks if a given node is a calendar cell element. * diff --git a/packages/bits-ui/src/lib/shared/date/field/helpers.ts b/packages/bits-ui/src/lib/shared/date/field/helpers.ts index 6ae47bae8..dee0de62c 100644 --- a/packages/bits-ui/src/lib/shared/date/field/helpers.ts +++ b/packages/bits-ui/src/lib/shared/date/field/helpers.ts @@ -2,6 +2,7 @@ import type { DateValue } from "@internationalized/date"; import { type Formatter, type Granularity, + type HourCycle, getPlaceholder, getSegments, hasTime, @@ -11,7 +12,6 @@ import type { DateAndTimeSegmentObj, DateSegmentPart, EditableSegmentPart, - HourCycle, SegmentContentObj, SegmentPart, SegmentStateMap, @@ -53,7 +53,7 @@ type SharedContentProps = { dateRef: DateValue; formatter: Formatter; hideTimeZone: boolean; - hourCycle: HourCycle; + hourCycle: HourCycle | undefined; }; type CreateContentObjProps = SharedContentProps & { @@ -216,7 +216,7 @@ export function createContent(props: CreateContentProps) { }; } -function getOptsByGranularity(granularity: Granularity, hourCycle: HourCycle) { +function getOptsByGranularity(granularity: Granularity, hourCycle: HourCycle | undefined) { const opts: Intl.DateTimeFormatOptions = { year: "numeric", month: "2-digit", diff --git a/packages/bits-ui/src/lib/shared/date/field/types.ts b/packages/bits-ui/src/lib/shared/date/field/types.ts index 56ef05450..e37ba4482 100644 --- a/packages/bits-ui/src/lib/shared/date/field/types.ts +++ b/packages/bits-ui/src/lib/shared/date/field/types.ts @@ -1,4 +1,3 @@ -import type { DateValue } from "@internationalized/date"; import type { DATE_SEGMENT_PARTS, EDITABLE_SEGMENT_PARTS, @@ -34,13 +33,3 @@ export type SegmentState = { export type SegmentStateMap = { [K in EditableSegmentPart]: SegmentState; }; - -export type SegmentAttrProps = { - segmentValues: SegmentValueObj; - hourCycle: 12 | 24 | undefined; - placeholder: DateValue; -}; - -export type SegmentAttrFn = (props: SegmentAttrProps) => Record; - -export type HourCycle = 12 | 24 | undefined; diff --git a/packages/bits-ui/src/lib/shared/date/types.ts b/packages/bits-ui/src/lib/shared/date/types.ts index fdffa7a68..3d1581611 100644 --- a/packages/bits-ui/src/lib/shared/date/types.ts +++ b/packages/bits-ui/src/lib/shared/date/types.ts @@ -4,12 +4,6 @@ export type Granularity = "day" | "hour" | "minute" | "second"; export type HourCycle = 12 | 24; export type WeekStartsOn = 0 | 1 | 2 | 3 | 4 | 5 | 6; -// Days of the week, starting with Sunday -const daysOfWeek = [0, 1, 2, 3, 4, 5, 6] as const; -export type DayOfWeek = { - daysOfWeek: (typeof daysOfWeek)[number][]; -}; - export type DateMatcher = (date: DateValue) => boolean; export type DateRange = { start: DateValue | undefined; diff --git a/packages/bits-ui/src/lib/shared/date/utils.ts b/packages/bits-ui/src/lib/shared/date/utils.ts index 6a9d7aed4..3a451f216 100644 --- a/packages/bits-ui/src/lib/shared/date/utils.ts +++ b/packages/bits-ui/src/lib/shared/date/utils.ts @@ -97,7 +97,7 @@ export function toDate(dateValue: DateValue, tz: string = getLocalTimeZone()) { } } -export function isCalendarDateTime(dateValue: DateValue): dateValue is CalendarDateTime { +function isCalendarDateTime(dateValue: DateValue): dateValue is CalendarDateTime { return dateValue instanceof CalendarDateTime; } @@ -157,7 +157,7 @@ export function isAfter(dateToCompare: DateValue, referenceDate: DateValue) { * * @see {@link isBefore} for non-inclusive */ -export function isBeforeOrSame(dateToCompare: DateValue, referenceDate: DateValue) { +function isBeforeOrSame(dateToCompare: DateValue, referenceDate: DateValue) { return dateToCompare.compare(referenceDate) <= 0; } @@ -169,7 +169,7 @@ export function isBeforeOrSame(dateToCompare: DateValue, referenceDate: DateValu * * @see {@link isAfter} for non-inclusive */ -export function isAfterOrSame(dateToCompare: DateValue, referenceDate: DateValue) { +function isAfterOrSame(dateToCompare: DateValue, referenceDate: DateValue) { return dateToCompare.compare(referenceDate) >= 0; } @@ -195,7 +195,7 @@ export function isBetweenInclusive(date: DateValue, start: DateValue, end: DateV * * @see {@link isBetweenInclusive} for inclusive */ -export function isBetween(date: DateValue, start: DateValue, end: DateValue) { +function isBetween(date: DateValue, start: DateValue, end: DateValue) { return isAfter(date, start) && isBefore(date, end); } diff --git a/packages/bits-ui/src/lib/types.ts b/packages/bits-ui/src/lib/types.ts index f1fee33fb..9e378f8a6 100644 --- a/packages/bits-ui/src/lib/types.ts +++ b/packages/bits-ui/src/lib/types.ts @@ -1,4 +1,3 @@ -// Prop/Event types prefixed with the component name for convenience export type * from "$lib/bits/accordion/types.js"; export type * from "$lib/bits/alert-dialog/types.js"; export type * from "$lib/bits/aspect-ratio/types.js"; diff --git a/packages/bits-ui/src/tests/accordion/AccordionMultiTestControlled.svelte b/packages/bits-ui/src/tests/accordion/accordion-multi-test-controlled.svelte similarity index 100% rename from packages/bits-ui/src/tests/accordion/AccordionMultiTestControlled.svelte rename to packages/bits-ui/src/tests/accordion/accordion-multi-test-controlled.svelte diff --git a/packages/bits-ui/src/tests/accordion/AccordionMultiTest.svelte b/packages/bits-ui/src/tests/accordion/accordion-multi-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/accordion/AccordionMultiTest.svelte rename to packages/bits-ui/src/tests/accordion/accordion-multi-test.svelte diff --git a/packages/bits-ui/src/tests/accordion/AccordionSingleTestControlled.svelte b/packages/bits-ui/src/tests/accordion/accordion-single-test-controlled.svelte similarity index 100% rename from packages/bits-ui/src/tests/accordion/AccordionSingleTestControlled.svelte rename to packages/bits-ui/src/tests/accordion/accordion-single-test-controlled.svelte diff --git a/packages/bits-ui/src/tests/accordion/AccordionSingleTest.svelte b/packages/bits-ui/src/tests/accordion/accordion-single-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/accordion/AccordionSingleTest.svelte rename to packages/bits-ui/src/tests/accordion/accordion-single-test.svelte diff --git a/packages/bits-ui/src/tests/accordion/AccordionTestIsolated.svelte b/packages/bits-ui/src/tests/accordion/accordion-test-isolated.svelte similarity index 100% rename from packages/bits-ui/src/tests/accordion/AccordionTestIsolated.svelte rename to packages/bits-ui/src/tests/accordion/accordion-test-isolated.svelte diff --git a/packages/bits-ui/src/tests/accordion/Accordion.spec.ts b/packages/bits-ui/src/tests/accordion/accordion.spec.ts similarity index 98% rename from packages/bits-ui/src/tests/accordion/Accordion.spec.ts rename to packages/bits-ui/src/tests/accordion/accordion.spec.ts index 0e33e36b6..bdb0db049 100644 --- a/packages/bits-ui/src/tests/accordion/Accordion.spec.ts +++ b/packages/bits-ui/src/tests/accordion/accordion.spec.ts @@ -4,11 +4,11 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { tick } from "svelte"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import AccordionSingleTest from "./AccordionSingleTest.svelte"; -import AccordionMultiTest from "./AccordionMultiTest.svelte"; -import AccordionTestIsolated from "./AccordionTestIsolated.svelte"; -import AccordionSingleTestControlledSvelte from "./AccordionSingleTestControlled.svelte"; -import AccordionMultiTestControlled from "./AccordionMultiTestControlled.svelte"; +import AccordionSingleTest from "./accordion-single-test.svelte"; +import AccordionMultiTest from "./accordion-multi-test.svelte"; +import AccordionTestIsolated from "./accordion-test-isolated.svelte"; +import AccordionSingleTestControlledSvelte from "./accordion-single-test-controlled.svelte"; +import AccordionMultiTestControlled from "./accordion-multi-test-controlled.svelte"; import { sleep } from "$lib/internal/sleep.js"; export type Item = { diff --git a/packages/bits-ui/src/tests/alert-dialog/AlertDialogTest.svelte b/packages/bits-ui/src/tests/alert-dialog/alert-dialog-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/alert-dialog/AlertDialogTest.svelte rename to packages/bits-ui/src/tests/alert-dialog/alert-dialog-test.svelte diff --git a/packages/bits-ui/src/tests/alert-dialog/AlertDialog.spec.ts b/packages/bits-ui/src/tests/alert-dialog/alert-dialog.spec.ts similarity index 98% rename from packages/bits-ui/src/tests/alert-dialog/AlertDialog.spec.ts rename to packages/bits-ui/src/tests/alert-dialog/alert-dialog.spec.ts index bf6f89095..d5f776797 100644 --- a/packages/bits-ui/src/tests/alert-dialog/AlertDialog.spec.ts +++ b/packages/bits-ui/src/tests/alert-dialog/alert-dialog.spec.ts @@ -9,7 +9,7 @@ import { userEvent } from "@testing-library/user-event"; import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { getTestKbd } from "../utils.js"; -import AlertDialogTest, { type AlertDialogTestProps } from "./AlertDialogTest.svelte"; +import AlertDialogTest, { type AlertDialogTestProps } from "./alert-dialog-test.svelte"; import { sleep } from "$lib/internal/sleep.js"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/avatar/AvatarTest.svelte b/packages/bits-ui/src/tests/avatar/avatar-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/avatar/AvatarTest.svelte rename to packages/bits-ui/src/tests/avatar/avatar-test.svelte diff --git a/packages/bits-ui/src/tests/avatar/Avatar.spec.ts b/packages/bits-ui/src/tests/avatar/avatar.spec.ts similarity index 97% rename from packages/bits-ui/src/tests/avatar/Avatar.spec.ts rename to packages/bits-ui/src/tests/avatar/avatar.spec.ts index 85bfdad7e..06a04773b 100644 --- a/packages/bits-ui/src/tests/avatar/Avatar.spec.ts +++ b/packages/bits-ui/src/tests/avatar/avatar.spec.ts @@ -2,7 +2,7 @@ import { render } from "@testing-library/svelte/svelte5"; import { userEvent } from "@testing-library/user-event"; import { axe } from "jest-axe"; import { describe, it } from "vitest"; -import AvatarTest from "./AvatarTest.svelte"; +import AvatarTest from "./avatar-test.svelte"; const src = "https://github.com/huntabyte.png"; diff --git a/packages/bits-ui/src/tests/calendar/CalendarMultiTest.svelte b/packages/bits-ui/src/tests/calendar/calendar-multi-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/calendar/CalendarMultiTest.svelte rename to packages/bits-ui/src/tests/calendar/calendar-multi-test.svelte diff --git a/packages/bits-ui/src/tests/calendar/CalendarTest.svelte b/packages/bits-ui/src/tests/calendar/calendar-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/calendar/CalendarTest.svelte rename to packages/bits-ui/src/tests/calendar/calendar-test.svelte diff --git a/packages/bits-ui/src/tests/calendar/Calendar.spec.ts b/packages/bits-ui/src/tests/calendar/calendar.spec.ts similarity index 99% rename from packages/bits-ui/src/tests/calendar/Calendar.spec.ts rename to packages/bits-ui/src/tests/calendar/calendar.spec.ts index 84b9a992d..f3c5cf3be 100644 --- a/packages/bits-ui/src/tests/calendar/Calendar.spec.ts +++ b/packages/bits-ui/src/tests/calendar/calendar.spec.ts @@ -5,8 +5,8 @@ import { describe, it } from "vitest"; import { CalendarDate, CalendarDateTime, toZoned } from "@internationalized/date"; import { getTestKbd } from "../utils.js"; import { getSelectedDay, getSelectedDays } from "../helpers/calendar.js"; -import CalendarTest, { type CalendarSingleTestProps } from "./CalendarTest.svelte"; -import CalendarMultiTest, { type CalendarMultiTestProps } from "./CalendarMultiTest.svelte"; +import CalendarTest, { type CalendarSingleTestProps } from "./calendar-test.svelte"; +import CalendarMultiTest, { type CalendarMultiTestProps } from "./calendar-multi-test.svelte"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/checkbox/CheckboxTest.svelte b/packages/bits-ui/src/tests/checkbox/checkbox-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/checkbox/CheckboxTest.svelte rename to packages/bits-ui/src/tests/checkbox/checkbox-test.svelte diff --git a/packages/bits-ui/src/tests/checkbox/Checkbox.spec.ts b/packages/bits-ui/src/tests/checkbox/checkbox.spec.ts similarity index 99% rename from packages/bits-ui/src/tests/checkbox/Checkbox.spec.ts rename to packages/bits-ui/src/tests/checkbox/checkbox.spec.ts index 8d1d23ad6..11638ee91 100644 --- a/packages/bits-ui/src/tests/checkbox/Checkbox.spec.ts +++ b/packages/bits-ui/src/tests/checkbox/checkbox.spec.ts @@ -3,7 +3,7 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { tick } from "svelte"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import CheckboxTest from "./CheckboxTest.svelte"; +import CheckboxTest from "./checkbox-test.svelte"; import type { Checkbox } from "$lib/index.js"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/collapsible/CollapsibleTest.svelte b/packages/bits-ui/src/tests/collapsible/collapsible-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/collapsible/CollapsibleTest.svelte rename to packages/bits-ui/src/tests/collapsible/collapsible-test.svelte diff --git a/packages/bits-ui/src/tests/collapsible/Collapsible.spec.ts b/packages/bits-ui/src/tests/collapsible/collapsible.spec.ts similarity index 97% rename from packages/bits-ui/src/tests/collapsible/Collapsible.spec.ts rename to packages/bits-ui/src/tests/collapsible/collapsible.spec.ts index 947c9bf0c..24478e09a 100644 --- a/packages/bits-ui/src/tests/collapsible/Collapsible.spec.ts +++ b/packages/bits-ui/src/tests/collapsible/collapsible.spec.ts @@ -2,7 +2,7 @@ import { render } from "@testing-library/svelte/svelte5"; import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { setupUserEvents } from "../utils.js"; -import CollapsibleTest from "./CollapsibleTest.svelte"; +import CollapsibleTest from "./collapsible-test.svelte"; import type { Collapsible } from "$lib/index.js"; function setup(props?: Collapsible.RootProps) { diff --git a/packages/bits-ui/src/tests/combobox/ComboboxMultiTest.svelte b/packages/bits-ui/src/tests/combobox/combobox-multi-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/combobox/ComboboxMultiTest.svelte rename to packages/bits-ui/src/tests/combobox/combobox-multi-test.svelte diff --git a/packages/bits-ui/src/tests/combobox/ComboboxTest.svelte b/packages/bits-ui/src/tests/combobox/combobox-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/combobox/ComboboxTest.svelte rename to packages/bits-ui/src/tests/combobox/combobox-test.svelte diff --git a/packages/bits-ui/src/tests/combobox/Combobox.spec.ts b/packages/bits-ui/src/tests/combobox/combobox.spec.ts similarity index 98% rename from packages/bits-ui/src/tests/combobox/Combobox.spec.ts rename to packages/bits-ui/src/tests/combobox/combobox.spec.ts index 8f1bcd0fb..40a54309e 100644 --- a/packages/bits-ui/src/tests/combobox/Combobox.spec.ts +++ b/packages/bits-ui/src/tests/combobox/combobox.spec.ts @@ -3,10 +3,10 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { tick } from "svelte"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import ComboboxTest from "./ComboboxTest.svelte"; -import type { ComboboxSingleTestProps, Item } from "./ComboboxTest.svelte"; -import type { ComboboxMultipleTestProps } from "./ComboboxMultiTest.svelte"; -import ComboboxMultiTest from "./ComboboxMultiTest.svelte"; +import ComboboxTest from "./combobox-test.svelte"; +import type { ComboboxSingleTestProps, Item } from "./combobox-test.svelte"; +import type { ComboboxMultipleTestProps } from "./combobox-multi-test.svelte"; +import ComboboxMultiTest from "./combobox-multi-test.svelte"; import { sleep } from "$lib/internal/sleep.js"; import type { AnyFn } from "$lib/internal/types.js"; diff --git a/packages/bits-ui/src/tests/context-menu/ContextMenuTest.svelte b/packages/bits-ui/src/tests/context-menu/context-menu-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/context-menu/ContextMenuTest.svelte rename to packages/bits-ui/src/tests/context-menu/context-menu-test.svelte diff --git a/packages/bits-ui/src/tests/context-menu/ContextMenu.spec.ts b/packages/bits-ui/src/tests/context-menu/context-menu.spec.ts similarity index 98% rename from packages/bits-ui/src/tests/context-menu/ContextMenu.spec.ts rename to packages/bits-ui/src/tests/context-menu/context-menu.spec.ts index 0dab9ac9d..114e574cf 100644 --- a/packages/bits-ui/src/tests/context-menu/ContextMenu.spec.ts +++ b/packages/bits-ui/src/tests/context-menu/context-menu.spec.ts @@ -2,8 +2,8 @@ import { cleanup, render, screen, waitFor } from "@testing-library/svelte/svelte import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import ContextMenuTest from "./ContextMenuTest.svelte"; -import type { ContextMenuTestProps } from "./ContextMenuTest.svelte"; +import ContextMenuTest from "./context-menu-test.svelte"; +import type { ContextMenuTestProps } from "./context-menu-test.svelte"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/date-field/DateFieldTest.svelte b/packages/bits-ui/src/tests/date-field/date-field-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/date-field/DateFieldTest.svelte rename to packages/bits-ui/src/tests/date-field/date-field-test.svelte diff --git a/packages/bits-ui/src/tests/date-field/DateField.spec.ts b/packages/bits-ui/src/tests/date-field/date-field.spec.ts similarity index 99% rename from packages/bits-ui/src/tests/date-field/DateField.spec.ts rename to packages/bits-ui/src/tests/date-field/date-field.spec.ts index 1cff87793..04cfcf13c 100644 --- a/packages/bits-ui/src/tests/date-field/DateField.spec.ts +++ b/packages/bits-ui/src/tests/date-field/date-field.spec.ts @@ -11,7 +11,7 @@ import { toZoned, } from "@internationalized/date"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import DateFieldTest, { type DateFieldTestProps } from "./DateFieldTest.svelte"; +import DateFieldTest, { type DateFieldTestProps } from "./date-field-test.svelte"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/date-range-field/DateRangeFieldTest.svelte b/packages/bits-ui/src/tests/date-range-field/date-range-field-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/date-range-field/DateRangeFieldTest.svelte rename to packages/bits-ui/src/tests/date-range-field/date-range-field-test.svelte diff --git a/packages/bits-ui/src/tests/date-range-field/DateRangeField.spec.ts b/packages/bits-ui/src/tests/date-range-field/date-range-field.spec.ts similarity index 99% rename from packages/bits-ui/src/tests/date-range-field/DateRangeField.spec.ts rename to packages/bits-ui/src/tests/date-range-field/date-range-field.spec.ts index dbd86b6a4..1a9a94d03 100644 --- a/packages/bits-ui/src/tests/date-range-field/DateRangeField.spec.ts +++ b/packages/bits-ui/src/tests/date-range-field/date-range-field.spec.ts @@ -4,7 +4,7 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { CalendarDate, CalendarDateTime, toZoned } from "@internationalized/date"; import { getTestKbd } from "../utils.js"; -import DateRangeFieldTest, { type DateRangeFieldTestProps } from "./DateRangeFieldTest.svelte"; +import DateRangeFieldTest, { type DateRangeFieldTestProps } from "./date-range-field-test.svelte"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/dialog/DialogTest.svelte b/packages/bits-ui/src/tests/dialog/dialog-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/dialog/DialogTest.svelte rename to packages/bits-ui/src/tests/dialog/dialog-test.svelte diff --git a/packages/bits-ui/src/tests/dialog/Dialog.spec.ts b/packages/bits-ui/src/tests/dialog/dialog.spec.ts similarity index 98% rename from packages/bits-ui/src/tests/dialog/Dialog.spec.ts rename to packages/bits-ui/src/tests/dialog/dialog.spec.ts index d82ae7947..efbb4ebcf 100644 --- a/packages/bits-ui/src/tests/dialog/Dialog.spec.ts +++ b/packages/bits-ui/src/tests/dialog/dialog.spec.ts @@ -10,7 +10,7 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { tick } from "svelte"; import { getTestKbd } from "../utils.js"; -import DialogTest, { type DialogTestProps } from "./DialogTest.svelte"; +import DialogTest, { type DialogTestProps } from "./dialog-test.svelte"; import { sleep } from "$lib/internal/sleep.js"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/dropdown-menu/DropdownMenuTest.svelte b/packages/bits-ui/src/tests/dropdown-menu/dropdown-menu-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/dropdown-menu/DropdownMenuTest.svelte rename to packages/bits-ui/src/tests/dropdown-menu/dropdown-menu-test.svelte diff --git a/packages/bits-ui/src/tests/dropdown-menu/DropdownMenu.spec.ts b/packages/bits-ui/src/tests/dropdown-menu/dropdown-menu.spec.ts similarity index 97% rename from packages/bits-ui/src/tests/dropdown-menu/DropdownMenu.spec.ts rename to packages/bits-ui/src/tests/dropdown-menu/dropdown-menu.spec.ts index c461f721d..0d2cdf786 100644 --- a/packages/bits-ui/src/tests/dropdown-menu/DropdownMenu.spec.ts +++ b/packages/bits-ui/src/tests/dropdown-menu/dropdown-menu.spec.ts @@ -4,8 +4,8 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { tick } from "svelte"; import { getTestKbd } from "../utils.js"; -import DropdownMenuTest from "./DropdownMenuTest.svelte"; -import type { DropdownMenuTestProps } from "./DropdownMenuTest.svelte"; +import DropdownMenuTest from "./dropdown-menu-test.svelte"; +import type { DropdownMenuTestProps } from "./dropdown-menu-test.svelte"; import { sleep } from "$lib/internal/sleep.js"; const kbd = getTestKbd(); @@ -327,10 +327,10 @@ describe("dropdown menu", () => { expect(contentWrapper?.parentElement).toEqual(ogContainer); }); - it("should allow preventing autofocusing first item with `onMountAutoFocus` prop", async () => { + it("should allow preventing autofocusing first item with `onOpenAutoFocus` prop", async () => { const { getByTestId } = await openWithKbd({ contentProps: { - onMountAutoFocus: (e) => { + onOpenAutoFocus: (e) => { e.preventDefault(); }, }, diff --git a/packages/bits-ui/src/tests/label/LabelTest.svelte b/packages/bits-ui/src/tests/label/label-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/label/LabelTest.svelte rename to packages/bits-ui/src/tests/label/label-test.svelte diff --git a/packages/bits-ui/src/tests/label/Label.spec.ts b/packages/bits-ui/src/tests/label/label.spec.ts similarity index 95% rename from packages/bits-ui/src/tests/label/Label.spec.ts rename to packages/bits-ui/src/tests/label/label.spec.ts index cd36885fa..19ea53612 100644 --- a/packages/bits-ui/src/tests/label/Label.spec.ts +++ b/packages/bits-ui/src/tests/label/label.spec.ts @@ -2,7 +2,7 @@ import { render } from "@testing-library/svelte"; import { userEvent } from "@testing-library/user-event"; import { axe } from "jest-axe"; import { describe, it } from "vitest"; -import LabelTest from "./LabelTest.svelte"; +import LabelTest from "./label-test.svelte"; describe("label", () => { it("should have no accessibility violations", async () => { diff --git a/packages/bits-ui/src/tests/link-preview/LinkPreviewTest.svelte b/packages/bits-ui/src/tests/link-preview/link-preview-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/link-preview/LinkPreviewTest.svelte rename to packages/bits-ui/src/tests/link-preview/link-preview-test.svelte diff --git a/packages/bits-ui/src/tests/link-preview/LinkPreview.spec.ts b/packages/bits-ui/src/tests/link-preview/link-preview.spec.ts similarity index 97% rename from packages/bits-ui/src/tests/link-preview/LinkPreview.spec.ts rename to packages/bits-ui/src/tests/link-preview/link-preview.spec.ts index f4b31c560..c7b56e1ad 100644 --- a/packages/bits-ui/src/tests/link-preview/LinkPreview.spec.ts +++ b/packages/bits-ui/src/tests/link-preview/link-preview.spec.ts @@ -2,7 +2,7 @@ import { render, waitFor } from "@testing-library/svelte"; import { axe } from "jest-axe"; import { describe, it, vi } from "vitest"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import LinkPreviewTest, { type LinkPreviewTestProps } from "./LinkPreviewTest.svelte"; +import LinkPreviewTest, { type LinkPreviewTestProps } from "./link-preview-test.svelte"; const kbd = getTestKbd(); diff --git a/packages/bits-ui/src/tests/listbox/ListboxMultiTest.svelte b/packages/bits-ui/src/tests/listbox/listbox-multi-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/listbox/ListboxMultiTest.svelte rename to packages/bits-ui/src/tests/listbox/listbox-multi-test.svelte diff --git a/packages/bits-ui/src/tests/listbox/ListboxTest.svelte b/packages/bits-ui/src/tests/listbox/listbox-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/listbox/ListboxTest.svelte rename to packages/bits-ui/src/tests/listbox/listbox-test.svelte diff --git a/packages/bits-ui/src/tests/listbox/Listbox.spec.ts b/packages/bits-ui/src/tests/listbox/listbox.spec.ts similarity index 98% rename from packages/bits-ui/src/tests/listbox/Listbox.spec.ts rename to packages/bits-ui/src/tests/listbox/listbox.spec.ts index 06803e8bf..f8fd8431b 100644 --- a/packages/bits-ui/src/tests/listbox/Listbox.spec.ts +++ b/packages/bits-ui/src/tests/listbox/listbox.spec.ts @@ -3,10 +3,10 @@ import { axe } from "jest-axe"; import { describe, it } from "vitest"; import { tick } from "svelte"; import { getTestKbd, setupUserEvents } from "../utils.js"; -import ListboxTest from "./ListboxTest.svelte"; -import type { Item, ListboxSingleTestProps } from "./ListboxTest.svelte"; -import type { ListboxMultipleTestProps } from "./ListboxMultiTest.svelte"; -import ListboxMultiTest from "./ListboxMultiTest.svelte"; +import ListboxTest from "./listbox-test.svelte"; +import type { Item, ListboxSingleTestProps } from "./listbox-test.svelte"; +import type { ListboxMultipleTestProps } from "./listbox-multi-test.svelte"; +import ListboxMultiTest from "./listbox-multi-test.svelte"; import { sleep } from "$lib/internal/sleep.js"; import type { AnyFn } from "$lib/internal/types.js"; diff --git a/packages/bits-ui/src/tests/menubar/MenubarMenu.svelte b/packages/bits-ui/src/tests/menubar/menubar-menu-test.svelte similarity index 100% rename from packages/bits-ui/src/tests/menubar/MenubarMenu.svelte rename to packages/bits-ui/src/tests/menubar/menubar-menu-test.svelte diff --git a/packages/bits-ui/src/tests/menubar/MenubarTest.svelte b/packages/bits-ui/src/tests/menubar/menubar-test.svelte similarity index 84% rename from packages/bits-ui/src/tests/menubar/MenubarTest.svelte rename to packages/bits-ui/src/tests/menubar/menubar-test.svelte index 15fbf36cb..6856ab95c 100644 --- a/packages/bits-ui/src/tests/menubar/MenubarTest.svelte +++ b/packages/bits-ui/src/tests/menubar/menubar-test.svelte @@ -1,5 +1,5 @@ - - - - - - - + + + + + + + + + + + + + + ``` +## Reusable Components + +As you can see from the structure above, there are a number of pieces that make up the `Listbox` component. These pieces are provided to give you maximum flexibility and customization options, but can be a burden to write out everywhere you need to use a listbox in your application. + +To ease this burden, it's recommended to create your own reusable listbox component that wraps the primitives and provides a more convenient API for your use cases. + +Here's an example of how you might create a reusable `MyListbox` component that receives a list of options and renders each of them as an item. + +```svelte title="MyListbox.svelte" + + + + + {#if selectedLabel} + + {selectedLabel} + + {:else} + + {/if} + + + + up + + {#each items as { value, label, disabled } (value)} + + {#snippet children({ selected })} + {selected ? "✅" : ""} + + {item.label} + + {/snippet} + + {/each} + + down + + + +``` + +You can then use the `MyListbox` component throughout your application like so: + +```svelte + + + +``` + +## Managing Value State + +The `value` represents the currently selected item/option within the listbox. Bits UI provides flexible options for controlling and synchronizing the Listbox's value state. + +### Two-Way Binding + +Use the `bind:value` directive for effortless two-way synchronization between your local state and the Listbox's internal state. + +```svelte {3,6,8} + + + + + + + +``` + +This setup enables toggling the value via the custom button and ensures the local `myValue` state updates when the Listbox's value changes through any internal means (e.g., clicking on an item's button). + +### Change Handler + +You can also use the `onValueChange` prop to update local state when the Listbox's `value` state changes. This is useful when you don't want two-way binding for one reason or another, or you want to perform additional logic when the Select changes. + +```svelte {3,7-11} + + + { + myValue = value; + // additional logic here. + }} +> + + +``` + +## Managing Open State + +The `open` state represents whether or not the listbox content is open. Bits UI provides flexible options for controlling and synchronizing the Listbox's open state. + +### Two-Way Binding + +Use the `bind:open` directive for effortless two-way synchronization between your local state and the Listbox's internal state. + +```svelte {3,6,8} + + + + + + + +``` + +This setup enables toggling the Listbox via the custom button and ensures the local `isOpen` state updates when the Listbox's open state changes through any internal means e.g. clicking on the trigger or outside the content. + +### Change Handler + +You can also use the `onOpenChange` prop to update local state when the Listbox's `open` state changes. This is useful when you don't want two-way binding for one reason or another, or you want to perform additional logic when the Listbox's open state changes. + +```svelte {3,7-11} + + + { + isOpen = open; + // additional logic here. + }} +> + + +``` + +## Opt-out of Floating UI + +When you use the `Listbox.Content` component, Bits UI uses [Floating UI](https://floating-ui.com/) to position the content relative to the trigger, similar to other popover-like components. + +You can opt-out of this behavior by instead using the `Listbox.ContentStatic` component. + +```svelte {4,14} + + + + + + + + + + + + + + + + +``` + +When using this component, you'll need to handle the positioning of the content yourself. Keep in mind that using `Listbox.Portal` alongside `Listbox.ContentStatic` may result in some unexpected positioning behavior, feel free to not use the portal or work around it. + +## What is the Viewport? + +The `Listbox.Viewport` component is used to determine the size of the content in order to determine whether or not the scroll up and down buttons should be rendered. + +If you wish to set a minimum/maximum height for the select content, you should apply it to the `Listbox.Viewport` component. + +## Scroll Up/Down Buttons + +The `Listbox.ScrollUpButton` and `Listbox.ScrollDownButton` components are used to render the scroll up and down buttons when the select content is larger than the viewport. + +You must use the `Listbox.Viewport` component when using the scroll buttons. + +## Native Scrolling/Overflow + +If you don't want to use the scroll buttons and prefer to use the standard scrollbar/overflow behavior, you can omit the `Listbox.Scroll[Up|Down]Button` components and the `Listbox.Viewport` component. + +You'll need to set a height on the `Listbox.Content` component and appropriate `overflow` styles to enable scrolling. + +## Scroll Lock + +By default, when a user opens the listbox, scrolling outside the content will be disabled. You can override this behavior by setting the `preventScroll` prop to `false`. + +```svelte /preventScroll={false}/ + + + +``` + diff --git a/sites/docs/content/components/select.md b/sites/docs/content/components/select.md index c57c9097e..dd7925f3e 100644 --- a/sites/docs/content/components/select.md +++ b/sites/docs/content/components/select.md @@ -145,7 +145,7 @@ Use the `bind:value` directive for effortless two-way synchronization between yo ``` -This setup enables toggling the Select via the custom button and ensures the local `myValue` state updates when the Select changes through any internal means (e.g., clicking on an item's button). +This setup enables toggling the value via the custom button and ensures the local `myValue` state updates when the Select's value changes through any internal means (e.g., clicking on an item's button). ### Change Handler @@ -170,7 +170,7 @@ You can also use the `onValueChange` prop to update local state when the Select' ## Managing Open State -The `open` state represents whether or not the select menu is open. Bits UI provides flexible options for controlling and synchronizing the Select's open state. +The `open` state represents whether or not the select content is open. Bits UI provides flexible options for controlling and synchronizing the Select's open state. ### Two-Way Binding @@ -189,7 +189,7 @@ Use the `bind:open` directive for effortless two-way synchronization between you ``` -This setup enables toggling the Select via the custom button and ensures the local `isOpen` state updates when the Select changes through any internal means e.g. clicking on the trigger or outside the content. +This setup enables toggling the Select via the custom button and ensures the local `isOpen` state updates when the Select's open state changes through any internal means e.g. clicking on the trigger or outside the content. ### Change Handler @@ -290,9 +290,9 @@ By default, when a user opens the select, scrolling outside the content will be ``` -## Viewport +## What is the Viewport? -The `Select.Viewport` component is used to determine the size of the select menu in order to determine whether or not the scroll up and down buttons should be rendered. If you wish to set a minimum/maxmimum height for the select content, you should apply it to the `Select.Viewport` component. +The `Select.Viewport` component is used to determine the size of the select content in order to determine whether or not the scroll up and down buttons should be rendered. If you wish to set a minimum/maximum height for the select content, you should apply it to the `Select.Viewport` component. ## Scroll Up/Down Buttons diff --git a/sites/docs/src/lib/components/demos/combobox-demo.svelte b/sites/docs/src/lib/components/demos/combobox-demo.svelte index 520abd9f6..c7d0f1568 100644 --- a/sites/docs/src/lib/components/demos/combobox-demo.svelte +++ b/sites/docs/src/lib/components/demos/combobox-demo.svelte @@ -3,6 +3,8 @@ import CaretUpDown from "phosphor-svelte/lib/CaretUpDown"; import Check from "phosphor-svelte/lib/Check"; import OrangeSlice from "phosphor-svelte/lib/OrangeSlice"; + import CaretDoubleUp from "phosphor-svelte/lib/CaretDoubleUp"; + import CaretDoubleDown from "phosphor-svelte/lib/CaretDoubleDown"; const fruits = [ { value: "mango", label: "Mango" }, @@ -59,29 +61,37 @@ - {#each filteredFruits as fruit, i (i + fruit.value)} - - {#snippet children({ selected })} - {fruit.label} - {#if selected} -
- -
- {/if} - {/snippet} -
- {:else} - - No results found, try again. - - {/each} + + + + + {#each filteredFruits as fruit, i (i + fruit.value)} + + {#snippet children({ selected })} + {fruit.label} + {#if selected} +
+ +
+ {/if} + {/snippet} +
+ {:else} + + No results found, try again. + + {/each} +
+ + +
diff --git a/sites/docs/src/lib/components/demos/dialog-demo.svelte b/sites/docs/src/lib/components/demos/dialog-demo.svelte index 7f087519f..537018a7d 100644 --- a/sites/docs/src/lib/components/demos/dialog-demo.svelte +++ b/sites/docs/src/lib/components/demos/dialog-demo.svelte @@ -17,6 +17,7 @@ class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0" /> ({ ...floatingProps(), ...escapeLayerProps, ...dismissableLayerProps, - ...focusScopeProps, + onCloseAutoFocus: onCloseAutoFocusProp, preventOverflowTextSelection: preventOverflowTextSelectionProp, dir: dirProp, loop: createBooleanProp({ @@ -123,6 +126,10 @@ export const content = createApiSchema({ "Whether or not the combobox should loop through items when reaching the end.", }), forceMount: forceMountProp, + preventScroll: { + ...preventScrollProp, + default: C.FALSE, + }, ...withChildProps({ elType: "HTMLDivElement", childrenDef: OpenChildrenSnippetProps, @@ -160,6 +167,37 @@ export const content = createApiSchema({ ], }); +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: "The element which contains the combobox's items. (Static/No Floating UI)", + props: { + ...escapeLayerProps, + ...dismissableLayerProps, + ...focusScopeProps, + preventScroll: preventScrollProp, + preventOverflowTextSelection: preventOverflowTextSelectionProp, + dir: dirProp, + loop: createBooleanProp({ + default: C.FALSE, + description: + "Whether or not the combobox should loop through items when reaching the end.", + }), + forceMount: forceMountProp, + ...withChildProps({ + elType: "HTMLDivElement", + childrenDef: OpenChildrenSnippetProps, + childDef: OpenChildSnippetProps, + }), + }, + dataAttributes: [ + stateDataAttr, + createDataAttrSchema({ + name: "combobox-content", + description: "Present on the content element.", + }), + ], +}); + export const item = createApiSchema({ title: "Item", description: "A combobox item, which must be a child of the `Combobox.Content` component.", @@ -299,4 +337,4 @@ export const arrow = createApiSchema({ ], }); -export const combobox = [root, trigger, content, item, input, groupLabel, arrow]; +export const combobox = [root, trigger, content, contentStatic, item, input, groupLabel, arrow]; diff --git a/sites/docs/src/lib/content/api-reference/context-menu.ts b/sites/docs/src/lib/content/api-reference/context-menu.ts index 950fab547..ccb7187e5 100644 --- a/sites/docs/src/lib/content/api-reference/context-menu.ts +++ b/sites/docs/src/lib/content/api-reference/context-menu.ts @@ -2,6 +2,7 @@ import type { ContextMenuArrowPropsWithoutHTML, ContextMenuCheckboxItemPropsWithoutHTML, ContextMenuContentPropsWithoutHTML, + ContextMenuContentStaticPropsWithoutHTML, ContextMenuGroupPropsWithoutHTML, ContextMenuItemPropsWithoutHTML, ContextMenuLabelPropsWithoutHTML, @@ -26,6 +27,7 @@ import { focusScopeProps, forceMountProp, preventOverflowTextSelectionProp, + preventScrollProp, refProp, withChildProps, } from "./helpers.js"; @@ -99,6 +101,27 @@ export const content = createApiSchema({ ], }); +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: "The content displayed when the context menu is open. (Static/No Floating UI)", + props: { + ...escapeLayerProps, + ...dismissableLayerProps, + ...focusScopeProps, + preventScroll: preventScrollProp, + preventOverflowTextSelection: preventOverflowTextSelectionProp, + dir: dirProp, + forceMount: forceMountProp, + loop: createBooleanProp({ + default: C.FALSE, + description: + "Whether or not the context menu should loop through items when reaching the end.", + }), + ...withChildProps({ elType: "HTMLDivElement" }), + }, + dataAttributes: menu.content.dataAttributes, +}); + export const item = createApiSchema({ title: "Item", description: "A menu item within the context menu.", @@ -174,6 +197,7 @@ export const contextMenu = [ trigger, portal, content, + contentStatic, item, checkboxItem, radioGroup, diff --git a/sites/docs/src/lib/content/api-reference/dropdown-menu.ts b/sites/docs/src/lib/content/api-reference/dropdown-menu.ts index 75d9b81ea..1ee4926f0 100644 --- a/sites/docs/src/lib/content/api-reference/dropdown-menu.ts +++ b/sites/docs/src/lib/content/api-reference/dropdown-menu.ts @@ -2,6 +2,7 @@ import type { DropdownMenuArrowPropsWithoutHTML, DropdownMenuCheckboxItemPropsWithoutHTML, DropdownMenuContentPropsWithoutHTML, + DropdownMenuContentStaticPropsWithoutHTML, DropdownMenuGroupPropsWithoutHTML, DropdownMenuItemPropsWithoutHTML, DropdownMenuLabelPropsWithoutHTML, @@ -43,6 +44,12 @@ export const content = createApiSchema({ ...menu.content, }); +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: "The content displayed when the dropdown menu is open. (Static/No Floating UI)", + ...menu.contentStatic, +}); + export const item = createApiSchema({ title: "Item", description: "A menu item within the dropdown menu.", @@ -118,6 +125,7 @@ export const dropdownMenu = [ trigger, portal, content, + contentStatic, item, checkboxItem, radioGroup, diff --git a/sites/docs/src/lib/content/api-reference/helpers.ts b/sites/docs/src/lib/content/api-reference/helpers.ts index 8a06dc0ce..7f11e40d0 100644 --- a/sites/docs/src/lib/content/api-reference/helpers.ts +++ b/sites/docs/src/lib/content/api-reference/helpers.ts @@ -508,16 +508,16 @@ export const forceMountProp = createBooleanProp({ default: C.FALSE, }); -export const onMountAutoFocusProp = createFunctionProp({ +export const onOpenAutoFocusProp = createFunctionProp({ definition: OnFocusOutsideProp, description: - "Event handler called when auto-focusing the content as it is mounted. Can be prevented.", + "Event handler called when auto-focusing the content as it is opened. Can be prevented.", }); -export const onDestroyAutoFocusProp = createFunctionProp({ +export const onCloseAutoFocusProp = createFunctionProp({ definition: OnFocusOutsideProp, description: - "Event handler called when auto-focusing the content as it is destroyed. Can be prevented.", + "Event handler called when auto-focusing the content as it is closed. Can be prevented.", }); export const trapFocusProp = createBooleanProp({ @@ -526,8 +526,8 @@ export const trapFocusProp = createBooleanProp({ }); export const focusScopeProps = { - onMountAutoFocus: onMountAutoFocusProp, - onDestroyAutoFocus: onDestroyAutoFocusProp, + onOpenAutoFocus: onOpenAutoFocusProp, + onCloseAutoFocus: onCloseAutoFocusProp, trapFocus: trapFocusProp, } as const; diff --git a/sites/docs/src/lib/content/api-reference/link-preview.ts b/sites/docs/src/lib/content/api-reference/link-preview.ts index 0946ca4bb..2e81b1700 100644 --- a/sites/docs/src/lib/content/api-reference/link-preview.ts +++ b/sites/docs/src/lib/content/api-reference/link-preview.ts @@ -1,6 +1,7 @@ import type { LinkPreviewArrowPropsWithoutHTML, LinkPreviewContentPropsWithoutHTML, + LinkPreviewContentStaticPropsWithoutHTML, LinkPreviewRootPropsWithoutHTML, LinkPreviewTriggerPropsWithoutHTML, } from "bits-ui"; @@ -9,6 +10,8 @@ import { childrenSnippet, createApiSchema, createBooleanProp, + createDataAttrSchema, + createEnumDataAttr, createFunctionProp, createNumberProp, dirProp, @@ -22,6 +25,12 @@ import { } from "$lib/content/api-reference/helpers.js"; import * as C from "$lib/content/constants.js"; +const openClosedDataAttr = createEnumDataAttr({ + name: "state", + description: "Whether the accordion item is open or closed.", + options: ["open", "closed"], +}); + export const root = createApiSchema({ title: "Root", description: "The root component used to manage the state of the state of the link preview.", @@ -63,16 +72,11 @@ export const trigger = createApiSchema({ "A component which triggers the opening and closing of the link preview on hover or focus.", props: withChildProps({ elType: "HTMLAnchorElement" }), dataAttributes: [ - { - name: "state", - value: enums("open", "closed"), - description: "The open state of the link preview.", - isEnum: true, - }, - { + openClosedDataAttr, + createDataAttrSchema({ name: "link-preview-trigger", description: "Present on the trigger element.", - }, + }), ], }); @@ -89,16 +93,32 @@ export const content = createApiSchema({ ...withChildProps({ elType: "HTMLDivElement" }), }, dataAttributes: [ - { - name: "state", - value: enums("open", "closed"), - description: "The open state of the link preview.", - isEnum: true, - }, - { + openClosedDataAttr, + createDataAttrSchema({ + name: "link-preview-content", + description: "Present on the content element.", + }), + ], +}); + +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: + "The contents of the link preview which are displayed when the preview is open. (Static/No Floating UI)", + props: { + ...dismissableLayerProps, + ...escapeLayerProps, + ...focusScopeProps, + dir: dirProp, + forceMount: forceMountProp, + ...withChildProps({ elType: "HTMLDivElement" }), + }, + dataAttributes: [ + openClosedDataAttr, + createDataAttrSchema({ name: "link-preview-content", description: "Present on the content element.", - }, + }), ], }); @@ -107,15 +127,11 @@ export const arrow = createApiSchema({ description: "An optional arrow element which points to the trigger when the preview is open.", props: arrowProps, dataAttributes: [ - { - name: "arrow", - description: "Present on the arrow element.", - }, - { + createDataAttrSchema({ name: "link-preview-arrow", description: "Present on the arrow element.", - }, + }), ], }); -export const linkPreview = [root, trigger, content, arrow]; +export const linkPreview = [root, trigger, content, contentStatic, arrow]; diff --git a/sites/docs/src/lib/content/api-reference/listbox.ts b/sites/docs/src/lib/content/api-reference/listbox.ts index 55ec5327b..3c1020b27 100644 --- a/sites/docs/src/lib/content/api-reference/listbox.ts +++ b/sites/docs/src/lib/content/api-reference/listbox.ts @@ -1,6 +1,7 @@ import type { ListboxArrowPropsWithoutHTML, ListboxContentPropsWithoutHTML, + ListboxContentStaticPropsWithoutHTML, ListboxGroupLabelPropsWithoutHTML, ListboxGroupPropsWithoutHTML, ListboxItemPropsWithoutHTML, @@ -39,7 +40,9 @@ import { floatingProps, focusScopeProps, forceMountProp, + onCloseAutoFocusProp, preventOverflowTextSelectionProp, + preventScrollProp, withChildProps, } from "$lib/content/api-reference/helpers.js"; import * as C from "$lib/content/constants.js"; @@ -116,7 +119,7 @@ export const content = createApiSchema({ ...floatingProps(), ...escapeLayerProps, ...dismissableLayerProps, - ...focusScopeProps, + onCloseAutoFocus: onCloseAutoFocusProp, preventOverflowTextSelection: preventOverflowTextSelectionProp, dir: dirProp, loop: createBooleanProp({ @@ -125,6 +128,10 @@ export const content = createApiSchema({ "Whether or not the listbox should loop through items when reaching the end.", }), forceMount: forceMountProp, + preventScroll: { + ...preventScrollProp, + default: C.FALSE, + }, ...withChildProps({ elType: "HTMLDivElement", childrenDef: OpenChildrenSnippetProps, @@ -162,6 +169,37 @@ export const content = createApiSchema({ ], }); +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: "The element which contains the listbox's items. (Static/No Floating UI)", + props: { + ...escapeLayerProps, + ...dismissableLayerProps, + ...focusScopeProps, + preventScroll: preventScrollProp, + preventOverflowTextSelection: preventOverflowTextSelectionProp, + dir: dirProp, + loop: createBooleanProp({ + default: C.FALSE, + description: + "Whether or not the listbox should loop through items when reaching the end.", + }), + forceMount: forceMountProp, + ...withChildProps({ + elType: "HTMLDivElement", + childrenDef: OpenChildrenSnippetProps, + childDef: OpenChildSnippetProps, + }), + }, + dataAttributes: [ + stateDataAttr, + createDataAttrSchema({ + name: "listbox-content", + description: "Present on the content element.", + }), + ], +}); + export const item = createApiSchema({ title: "Item", description: "A listbox item, which must be a child of the `Listbox.Content` component.", @@ -225,11 +263,7 @@ export const trigger = createApiSchema({ description: "A button which toggles the listbox's open state.", props: withChildProps({ elType: "HTMLButtonElement" }), dataAttributes: [ - createEnumDataAttr({ - name: "state", - options: ["open", "closed"], - description: "The listbox's open state.", - }), + stateDataAttr, createDataAttrSchema({ name: "disabled", description: "Present when the listbox is disabled.", @@ -321,6 +355,7 @@ export const listbox = [ root, trigger, content, + contentStatic, item, viewport, scrollUpButton, diff --git a/sites/docs/src/lib/content/api-reference/menu.ts b/sites/docs/src/lib/content/api-reference/menu.ts index aecf69dc1..dccd0b258 100644 --- a/sites/docs/src/lib/content/api-reference/menu.ts +++ b/sites/docs/src/lib/content/api-reference/menu.ts @@ -1,6 +1,7 @@ import type { DropdownMenuCheckboxItemPropsWithoutHTML, DropdownMenuContentPropsWithoutHTML, + DropdownMenuContentStaticPropsWithoutHTML, DropdownMenuGroupPropsWithoutHTML, DropdownMenuItemPropsWithoutHTML, DropdownMenuLabelPropsWithoutHTML, @@ -28,6 +29,7 @@ import { forceMountProp, portalProps, preventOverflowTextSelectionProp, + preventScrollProp, withChildProps, } from "./helpers.js"; import { @@ -107,6 +109,26 @@ const contentProps = { }), } satisfies PropObj; +const contentStaticProps = { + ...escapeLayerProps, + ...dismissableLayerProps, + ...focusScopeProps, + preventScroll: preventScrollProp, + forceMount: forceMountProp, + preventOverflowTextSelection: preventOverflowTextSelectionProp, + dir: dirProp, + loop: createBooleanProp({ + default: C.FALSE, + description: + "Whether or not to loop through the menu items in when navigating with the keyboard.", + }), + ...withChildProps({ + elType: "HTMLDivElement", + childrenDef: OpenChildrenSnippetProps, + childDef: OpenChildSnippetProps, + }), +} satisfies PropObj; + const subContentProps = contentProps satisfies PropObj< Omit >; @@ -349,6 +371,11 @@ export const content = { dataAttributes: contentAttrs, }; +export const contentStatic = { + props: contentStaticProps, + dataAttributes: contentAttrs, +}; + export const arrow = { props: arrowProps, dataAttributes: arrowAttrs, @@ -415,6 +442,7 @@ export const menu = { root, trigger, content, + contentStatic, item, checkboxItem, radioGroup, diff --git a/sites/docs/src/lib/content/api-reference/menubar.ts b/sites/docs/src/lib/content/api-reference/menubar.ts index 999455e85..1c147865b 100644 --- a/sites/docs/src/lib/content/api-reference/menubar.ts +++ b/sites/docs/src/lib/content/api-reference/menubar.ts @@ -2,6 +2,7 @@ import type { MenubarArrowPropsWithoutHTML, MenubarCheckboxItemPropsWithoutHTML, MenubarContentPropsWithoutHTML, + MenubarContentStaticPropsWithoutHTML, MenubarGroupLabelPropsWithoutHTML, MenubarGroupPropsWithoutHTML, MenubarItemPropsWithoutHTML, @@ -82,6 +83,12 @@ export const content = createApiSchema({ ...m.content, }); +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: "The content displayed when the dropdown menu is open. (Static/No Floating UI)", + ...m.contentStatic, +}); + export const item = createApiSchema({ title: "Item", description: "A menu item within the dropdown menu.", @@ -132,6 +139,7 @@ export const subTrigger = createApiSchema({ ...m.subTrigger, }); +// TODO: add subContentStatic export const subContent = createApiSchema({ title: "SubContent", description: "The submenu content displayed when the parent submenu is open.", diff --git a/sites/docs/src/lib/content/api-reference/popover.ts b/sites/docs/src/lib/content/api-reference/popover.ts index 2e57498ca..e203facf9 100644 --- a/sites/docs/src/lib/content/api-reference/popover.ts +++ b/sites/docs/src/lib/content/api-reference/popover.ts @@ -2,19 +2,19 @@ import type { PopoverArrowPropsWithoutHTML, PopoverClosePropsWithoutHTML, PopoverContentPropsWithoutHTML, + PopoverContentStaticPropsWithoutHTML, PopoverRootPropsWithoutHTML, PopoverTriggerPropsWithoutHTML, } from "bits-ui"; -import { OpenClosedProp } from "./extended-types/shared/index.js"; import { arrowProps, childrenSnippet, createApiSchema, createBooleanProp, + createEnumDataAttr, createFunctionProp, dirProp, dismissableLayerProps, - enums, escapeLayerProps, floatingProps, focusScopeProps, @@ -25,6 +25,12 @@ import { } from "$lib/content/api-reference/helpers.js"; import * as C from "$lib/content/constants.js"; +const openClosedDataAttr = createEnumDataAttr({ + name: "state", + description: "Whether the popover is open or closed.", + options: ["open", "closed"], +}); + export const root = createApiSchema({ title: "Root", description: "The root component used to manage the state of the state of the popover.", @@ -47,13 +53,7 @@ export const trigger = createApiSchema({ description: "A component which toggles the opening and closing of the popover on press.", props: withChildProps({ elType: "HTMLButtonElement" }), dataAttributes: [ - { - name: "state", - value: enums("open", "closed"), - description: "The open state of the link preview.", - isEnum: true, - definition: OpenClosedProp, - }, + openClosedDataAttr, { name: "popover-trigger", description: "Present on the trigger element.", @@ -70,19 +70,42 @@ export const content = createApiSchema({ ...escapeLayerProps, ...focusScopeProps, preventOverflowTextSelection: preventOverflowTextSelectionProp, - preventScroll: preventScrollProp, + preventScroll: { + ...preventScrollProp, + default: C.FALSE, + }, forceMount: forceMountProp, dir: dirProp, ...withChildProps({ elType: "HTMLDivElement" }), }, dataAttributes: [ + openClosedDataAttr, { - name: "state", - value: enums("open", "closed"), - description: "The open state of the popover.", - isEnum: true, - definition: OpenClosedProp, + name: "popover-content", + description: "Present on the content element.", + }, + ], +}); + +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: + "The contents of the popover which are displayed when the popover is open. (Static/No Floating UI)", + props: { + ...dismissableLayerProps, + ...escapeLayerProps, + ...focusScopeProps, + preventOverflowTextSelection: preventOverflowTextSelectionProp, + preventScroll: { + ...preventScrollProp, + default: C.FALSE, }, + forceMount: forceMountProp, + dir: dirProp, + ...withChildProps({ elType: "HTMLDivElement" }), + }, + dataAttributes: [ + openClosedDataAttr, { name: "popover-content", description: "Present on the content element.", @@ -119,4 +142,4 @@ export const arrow = createApiSchema({ ], }); -export const popover = [root, trigger, content, close, arrow]; +export const popover = [root, trigger, content, contentStatic, close, arrow]; diff --git a/sites/docs/src/lib/content/api-reference/tooltip.ts b/sites/docs/src/lib/content/api-reference/tooltip.ts index f8396475a..b300a9e1a 100644 --- a/sites/docs/src/lib/content/api-reference/tooltip.ts +++ b/sites/docs/src/lib/content/api-reference/tooltip.ts @@ -1,6 +1,7 @@ import type { TooltipArrowPropsWithoutHTML, TooltipContentPropsWithoutHTML, + TooltipContentStaticPropsWithoutHTML, TooltipProviderPropsWithoutHTML, TooltipRootPropsWithoutHTML, TooltipTriggerPropsWithoutHTML, @@ -9,18 +10,17 @@ import { OnOpenChangeProp, OpenChildSnippetProps, OpenChildrenSnippetProps, - OpenClosedProp, } from "./extended-types/shared/index.js"; import { arrowProps, childrenSnippet, createApiSchema, createBooleanProp, + createEnumDataAttr, createFunctionProp, createNumberProp, dirProp, dismissableLayerProps, - enums, escapeLayerProps, floatingProps, forceMountProp, @@ -29,6 +29,12 @@ import { import * as C from "$lib/content/constants.js"; import type { APISchema } from "$lib/types/index.js"; +const openClosedDataAttr = createEnumDataAttr({ + name: "state", + description: "Whether the tooltip is open or closed.", + options: ["open", "closed"], +}); + const delayDuration = createNumberProp({ default: "700", description: @@ -114,13 +120,7 @@ export const trigger = createApiSchema({ ...withChildProps({ elType: "HTMLButtonElement" }), }, dataAttributes: [ - { - name: "state", - description: "The open state of the tooltip.", - value: enums("open", "closed"), - isEnum: true, - definition: OpenClosedProp, - }, + openClosedDataAttr, { name: "tooltip-trigger", description: "Present on the trigger element.", @@ -144,13 +144,31 @@ export const content = createApiSchema({ }), }, dataAttributes: [ + openClosedDataAttr, { - name: "state", - value: enums("open", "closed"), - description: "The open state of the tooltip.", - isEnum: true, - definition: OpenClosedProp, + name: "tooltip-content", + description: "Present on the content element.", }, + ], +}); + +export const contentStatic = createApiSchema({ + title: "ContentStatic", + description: + "The contents of the tooltip which are displayed when the tooltip is open. (Static/No Floating UI)", + props: { + ...dismissableLayerProps, + ...escapeLayerProps, + forceMount: forceMountProp, + dir: dirProp, + ...withChildProps({ + elType: "HTMLDivElement", + childrenDef: OpenChildrenSnippetProps, + childDef: OpenChildSnippetProps, + }), + }, + dataAttributes: [ + openClosedDataAttr, { name: "tooltip-content", description: "Present on the content element.",