@pzerelles/headlessui-svelte 2.1.2-next.23 → 2.1.2-next.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/button/Button.svelte.d.ts +7 -4
  3. package/dist/checkbox/Checkbox.svelte +173 -120
  4. package/dist/checkbox/Checkbox.svelte.d.ts +7 -4
  5. package/dist/close-button/CloseButton.svelte +12 -6
  6. package/dist/close-button/CloseButton.svelte.d.ts +13 -10
  7. package/dist/combobox/Combobox.svelte +50 -3
  8. package/dist/data-interactive/DataInteractive.svelte +55 -29
  9. package/dist/data-interactive/DataInteractive.svelte.d.ts +7 -5
  10. package/dist/description/Description.svelte +31 -21
  11. package/dist/description/Description.svelte.d.ts +7 -4
  12. package/dist/dialog/Dialog.svelte +358 -38
  13. package/dist/dialog/Dialog.svelte.d.ts +10 -7
  14. package/dist/dialog/DialogBackdrop.svelte +30 -13
  15. package/dist/dialog/DialogBackdrop.svelte.d.ts +7 -4
  16. package/dist/dialog/DialogPanel.svelte +49 -26
  17. package/dist/dialog/DialogPanel.svelte.d.ts +7 -4
  18. package/dist/dialog/DialogTitle.svelte +38 -23
  19. package/dist/dialog/DialogTitle.svelte.d.ts +7 -4
  20. package/dist/field/Field.svelte +50 -28
  21. package/dist/field/Field.svelte.d.ts +7 -4
  22. package/dist/fieldset/Fieldset.svelte +50 -29
  23. package/dist/fieldset/Fieldset.svelte.d.ts +7 -4
  24. package/dist/focus-trap/FocusTrap.svelte +419 -283
  25. package/dist/focus-trap/FocusTrap.svelte.d.ts +7 -4
  26. package/dist/input/Input.svelte +84 -53
  27. package/dist/input/Input.svelte.d.ts +7 -4
  28. package/dist/internal/FloatingProvider.svelte +14 -9
  29. package/dist/internal/FocusSentinel.svelte +16 -8
  30. package/dist/internal/ForcePortalRoot.svelte +7 -3
  31. package/dist/internal/FormFields.svelte +47 -34
  32. package/dist/internal/FormFieldsProvider.svelte +9 -5
  33. package/dist/internal/FormResolver.svelte +20 -15
  34. package/dist/internal/Hidden.svelte +50 -29
  35. package/dist/internal/Hidden.svelte.d.ts +7 -4
  36. package/dist/internal/MainTreeProvider.svelte +89 -36
  37. package/dist/internal/Portal.svelte +18 -14
  38. package/dist/internal/floating-provider.svelte.js +1 -1
  39. package/dist/internal/floating.svelte.d.ts +5 -5
  40. package/dist/internal/floating.svelte.js +17 -17
  41. package/dist/label/Label.svelte +93 -58
  42. package/dist/label/Label.svelte.d.ts +7 -4
  43. package/dist/legend/Legend.svelte +12 -3
  44. package/dist/listbox/Listbox.svelte +525 -387
  45. package/dist/listbox/Listbox.svelte.d.ts +7 -5
  46. package/dist/listbox/ListboxButton.svelte +173 -127
  47. package/dist/listbox/ListboxButton.svelte.d.ts +7 -5
  48. package/dist/listbox/ListboxOption.svelte +170 -129
  49. package/dist/listbox/ListboxOption.svelte.d.ts +7 -5
  50. package/dist/listbox/ListboxOptions.svelte +400 -304
  51. package/dist/listbox/ListboxOptions.svelte.d.ts +7 -5
  52. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  53. package/dist/listbox/ListboxSelectedOption.svelte.d.ts +7 -4
  54. package/dist/listbox/index.d.ts +4 -4
  55. package/dist/listbox/index.js +1 -1
  56. package/dist/menu/Menu.svelte +78 -51
  57. package/dist/menu/Menu.svelte.d.ts +7 -5
  58. package/dist/menu/MenuButton.svelte +157 -117
  59. package/dist/menu/MenuButton.svelte.d.ts +7 -5
  60. package/dist/menu/MenuHeading.svelte +32 -14
  61. package/dist/menu/MenuHeading.svelte.d.ts +7 -5
  62. package/dist/menu/MenuItem.svelte +142 -107
  63. package/dist/menu/MenuItem.svelte.d.ts +8 -8
  64. package/dist/menu/MenuItems.svelte +301 -229
  65. package/dist/menu/MenuItems.svelte.d.ts +7 -5
  66. package/dist/menu/MenuSection.svelte +24 -9
  67. package/dist/menu/MenuSection.svelte.d.ts +7 -5
  68. package/dist/menu/MenuSeparator.svelte +17 -4
  69. package/dist/menu/MenuSeparator.svelte.d.ts +7 -6
  70. package/dist/menu/index.d.ts +7 -7
  71. package/dist/popover/Popover.svelte +216 -150
  72. package/dist/popover/Popover.svelte.d.ts +7 -4
  73. package/dist/popover/PopoverBackdrop.svelte +67 -41
  74. package/dist/popover/PopoverBackdrop.svelte.d.ts +7 -4
  75. package/dist/popover/PopoverButton.svelte +292 -212
  76. package/dist/popover/PopoverButton.svelte.d.ts +7 -4
  77. package/dist/popover/PopoverGroup.svelte +62 -35
  78. package/dist/popover/PopoverGroup.svelte.d.ts +7 -4
  79. package/dist/popover/PopoverPanel.svelte +311 -229
  80. package/dist/popover/PopoverPanel.svelte.d.ts +7 -4
  81. package/dist/portal/InternalPortal.svelte +141 -85
  82. package/dist/portal/InternalPortal.svelte.d.ts +7 -4
  83. package/dist/portal/Portal.svelte +5 -2
  84. package/dist/portal/PortalGroup.svelte +30 -9
  85. package/dist/portal/PortalGroup.svelte.d.ts +7 -4
  86. package/dist/select/Select.svelte +98 -68
  87. package/dist/select/Select.svelte.d.ts +7 -4
  88. package/dist/switch/Switch.svelte +179 -132
  89. package/dist/switch/Switch.svelte.d.ts +7 -4
  90. package/dist/switch/SwitchGroup.svelte +44 -31
  91. package/dist/switch/SwitchGroup.svelte.d.ts +7 -4
  92. package/dist/tabs/Tab.svelte +194 -143
  93. package/dist/tabs/Tab.svelte.d.ts +7 -4
  94. package/dist/tabs/TabGroup.svelte +81 -214
  95. package/dist/tabs/TabGroup.svelte.d.ts +7 -24
  96. package/dist/tabs/TabList.svelte +31 -11
  97. package/dist/tabs/TabList.svelte.d.ts +7 -4
  98. package/dist/tabs/TabPanel.svelte +67 -43
  99. package/dist/tabs/TabPanel.svelte.d.ts +7 -4
  100. package/dist/tabs/TabPanels.svelte +18 -7
  101. package/dist/tabs/TabPanels.svelte.d.ts +7 -4
  102. package/dist/tabs/context.svelte.d.ts +31 -0
  103. package/dist/tabs/context.svelte.js +134 -0
  104. package/dist/textarea/Textarea.svelte +84 -53
  105. package/dist/textarea/Textarea.svelte.d.ts +7 -4
  106. package/dist/transition/InternalTransitionChild.svelte +259 -170
  107. package/dist/transition/InternalTransitionChild.svelte.d.ts +7 -4
  108. package/dist/transition/Transition.svelte +96 -66
  109. package/dist/transition/Transition.svelte.d.ts +7 -4
  110. package/dist/transition/TransitionChild.svelte +31 -11
  111. package/dist/transition/TransitionChild.svelte.d.ts +7 -4
  112. package/dist/utils/ElementOrComponent.svelte +43 -23
  113. package/dist/utils/ElementOrComponent.svelte.d.ts +10 -4
  114. package/dist/utils/Generic.svelte +36 -22
  115. package/dist/utils/Generic.svelte.d.ts +7 -4
  116. package/dist/utils/StableCollection.svelte +54 -36
  117. package/dist/utils/floating-ui/svelte/components/FloatingNode.svelte +27 -12
  118. package/dist/utils/floating-ui/svelte/components/FloatingTree.svelte +88 -44
  119. package/dist/utils/floating-ui/svelte/hooks/useFloating.svelte.js +7 -7
  120. package/dist/utils/floating-ui/svelte/hooks/useFloatingRootContext.svelte.js +1 -1
  121. package/dist/utils/floating-ui/svelte/types.d.ts +4 -4
  122. package/dist/utils/floating-ui/svelte-dom/types.d.ts +2 -2
  123. package/dist/utils/floating-ui/svelte-dom/useFloating.svelte.js +6 -6
  124. package/dist/utils/types.d.ts +11 -4
  125. package/package.json +1 -1
  126. package/dist/dialog/InternalDialog.svelte +0 -233
  127. package/dist/dialog/InternalDialog.svelte.d.ts +0 -42
@@ -1,34 +1,60 @@
1
- <script lang="ts" module>import { useFocusRing } from "../hooks/use-focus-ring.svelte.js";
2
- import { useActivePress } from "../hooks/use-active-press.svelte.js";
3
- import { useHover } from "../hooks/use-hover.svelte.js";
4
- import { mergeProps } from "../utils/render.js";
5
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
6
- const DEFAULT_DATA_INTERACTIVE_TAG = "svelte:fragment";
1
+ <script lang="ts" module>
2
+ import type { Props, ElementType } from "../utils/types.js"
3
+ import { useFocusRing } from "../hooks/use-focus-ring.svelte.js"
4
+ import { useActivePress } from "../hooks/use-active-press.svelte.js"
5
+ import type { Snippet } from "svelte"
6
+ import { useHover } from "../hooks/use-hover.svelte.js"
7
+ import { mergeProps } from "../utils/render.js"
8
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
9
+
10
+ const DEFAULT_DATA_INTERACTIVE_TAG = "svelte:fragment" as const
11
+
12
+ type DataInteractiveRenderPropArg = {
13
+ hover: boolean
14
+ focus: boolean
15
+ active: boolean
16
+ }
17
+ type DataInteractivePropsWeControl = never
18
+
19
+ export type DataInteractiveProps<TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG> = Props<
20
+ TTag,
21
+ DataInteractiveRenderPropArg,
22
+ DataInteractivePropsWeControl,
23
+ {}
24
+ >
7
25
  </script>
8
26
 
9
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG">let { ref = $bindable(), ...theirProps } = $props();
10
- const disabled = false;
11
- const { isHovered: hover, hoverProps } = $derived(
12
- useHover({
13
- get disabled() {
14
- return disabled;
15
- }
16
- })
17
- );
18
- const { pressed: active, pressProps } = $derived(
19
- useActivePress({
20
- get disabled() {
21
- return disabled;
22
- }
23
- })
24
- );
25
- const { isFocusVisible: focus, focusProps } = $derived(useFocusRing());
26
- const slot = $derived({
27
- hover,
28
- focus,
29
- active
30
- });
31
- const ourProps = $derived(mergeProps(focusProps, hoverProps, pressProps));
27
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG">
28
+ let { ref = $bindable(), ...theirProps }: { as?: TTag } & DataInteractiveProps<TTag> = $props()
29
+
30
+ // Ideally we can use a `disabled` prop, but that would depend on the props of the child element
31
+ // and we don't have access to that in this component.
32
+
33
+ const disabled = false
34
+
35
+ const { isHovered: hover, hoverProps } = $derived(
36
+ useHover({
37
+ get disabled() {
38
+ return disabled
39
+ },
40
+ })
41
+ )
42
+ const { pressed: active, pressProps } = $derived(
43
+ useActivePress({
44
+ get disabled() {
45
+ return disabled
46
+ },
47
+ })
48
+ )
49
+ const { isFocusVisible: focus, focusProps } = $derived(useFocusRing())
50
+
51
+ const slot = $derived({
52
+ hover,
53
+ focus,
54
+ active,
55
+ } satisfies DataInteractiveRenderPropArg)
56
+
57
+ const ourProps = $derived(mergeProps(focusProps, hoverProps, pressProps))
32
58
  </script>
33
59
 
34
60
  <ElementOrComponent
@@ -8,15 +8,17 @@ type DataInteractiveRenderPropArg = {
8
8
  };
9
9
  type DataInteractivePropsWeControl = never;
10
10
  export type DataInteractiveProps<TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG> = Props<TTag, DataInteractiveRenderPropArg, DataInteractivePropsWeControl, {}>;
11
- export type DataInteractiveChildren = Snippet<[DataInteractiveRenderPropArg]>;
12
11
  declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_DATA_INTERACTIVE_TAG> {
13
12
  props(): {
14
13
  as?: TTag | undefined;
15
- } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, "as" | "children" | "refName" | "class"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
16
- children?: Snippet<[DataInteractiveRenderPropArg, Record<string, any>]> | undefined;
14
+ } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, "as" | "children" | "class"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
15
+ children?: Snippet<[{
16
+ slot: DataInteractiveRenderPropArg;
17
+ props: Record<string, any>;
18
+ }]> | undefined;
17
19
  ref?: HTMLElement;
18
- } & (true extends (import("../utils/types.js").PropsOf<TTag> extends infer T_1 ? T_1 extends import("../utils/types.js").PropsOf<TTag> ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
19
- class?: import("../utils/types.js").PropsOf<TTag>["class"] | ((bag: DataInteractiveRenderPropArg) => string) | undefined;
20
+ } & (true extends (import("svelte/elements").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
21
+ class?: string | ((bag: DataInteractiveRenderPropArg) => string) | null | undefined;
20
22
  } : {});
21
23
  events(): {} & {
22
24
  [evt: string]: CustomEvent<any>;
@@ -1,26 +1,36 @@
1
- <script lang="ts" module>let DEFAULT_DESCRIPTION_TAG = "p";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props, PropsOf } from "../utils/types.js"
3
+
4
+ let DEFAULT_DESCRIPTION_TAG = "p" as const
5
+
6
+ export type DescriptionProps<TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG> = Props<TTag>
2
7
  </script>
3
8
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG">import { useId } from "../hooks/use-id.js";
5
- import { useDisabled } from "../hooks/use-disabled.js";
6
- import { useDescriptionContext } from "./context.svelte.js";
7
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
8
- import { untrack } from "svelte";
9
- const internalId = useId();
10
- const providedDisabled = useDisabled();
11
- let {
12
- ref = $bindable(),
13
- id = `headlessui-description-${internalId}`,
14
- ...theirProps
15
- } = $props();
16
- const { register } = useDescriptionContext();
17
- $effect(() => {
18
- id;
19
- return untrack(() => register(id));
20
- });
21
- const disabled = $derived(providedDisabled.current || false);
22
- const slot = $derived({ disabled });
23
- const ourProps = $derived({ id });
9
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG">
10
+ import { useId } from "../hooks/use-id.js"
11
+ import { useDisabled } from "../hooks/use-disabled.js"
12
+ import { useDescriptionContext } from "./context.svelte.js"
13
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
14
+ import { untrack } from "svelte"
15
+
16
+ const internalId = useId()
17
+ const providedDisabled = useDisabled()
18
+
19
+ let {
20
+ ref = $bindable(),
21
+ id = `headlessui-description-${internalId}` as PropsOf<TTag>["id"],
22
+ ...theirProps
23
+ }: { as?: TTag } & DescriptionProps<TTag> = $props()
24
+
25
+ const { register } = useDescriptionContext()
26
+ $effect(() => {
27
+ id
28
+ return untrack(() => register(id))
29
+ })
30
+
31
+ const disabled = $derived(providedDisabled.current || false)
32
+ const slot = $derived({ disabled })
33
+ const ourProps = $derived({ id })
24
34
  </script>
25
35
 
26
36
  <ElementOrComponent
@@ -4,11 +4,14 @@ export type DescriptionProps<TTag extends ElementType = typeof DEFAULT_DESCRIPTI
4
4
  declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_DESCRIPTION_TAG> {
5
5
  props(): {
6
6
  as?: TTag | undefined;
7
- } & (Exclude<keyof PropsOf<TTag>, "as" | "children" | "refName" | "class"> extends infer T extends keyof PropsOf<TTag> ? { [P in T]: PropsOf<TTag>[P]; } : never) & {
8
- children?: import("svelte").Snippet<[{}, Record<string, any>]> | undefined;
7
+ } & (Exclude<keyof PropsOf<TTag>, "as" | "children" | "class"> extends infer T extends keyof PropsOf<TTag> ? { [P in T]: PropsOf<TTag>[P]; } : never) & {
8
+ children?: import("svelte").Snippet<[{
9
+ slot: {};
10
+ props: Record<string, any>;
11
+ }]> | undefined;
9
12
  ref?: HTMLElement;
10
- } & (true extends (PropsOf<TTag> extends infer T_1 ? T_1 extends PropsOf<TTag> ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
11
- class?: PropsOf<TTag>["class"] | ((bag: {}) => string) | undefined;
13
+ } & (true extends (import("svelte/elements.js").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements.js").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
14
+ class?: string | ((bag: {}) => string) | null | undefined;
12
15
  } : {});
13
16
  events(): {} & {
14
17
  [evt: string]: CustomEvent<any>;
@@ -1,51 +1,371 @@
1
- <script lang="ts" module>import { MainTreeProvider } from "../hooks/use-root-containers.svelte.js";
2
- import { useOpenClosed } from "../internal/open-closed.js";
3
- import { RenderFeatures } from "../utils/render.js";
4
- import { getContext } from "svelte";
5
- import InternalDialog from "./InternalDialog.svelte";
6
- import Transition from "../transition/Transition.svelte";
7
- export const DEFAULT_DIALOG_TAG = "div";
8
- export const DialogRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static;
9
- </script>
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+ import { RenderFeatures, type PropsForFeatures } from "../utils/render.js"
10
4
 
11
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DIALOG_TAG">let { ref = $bindable(), transition = false, open, ...rest } = $props();
12
- const usesOpenClosedState = useOpenClosed();
13
- const hasOpen = $derived(open !== void 0 || usesOpenClosedState);
14
- const hasOnClose = $derived(rest.hasOwnProperty("onclose"));
15
- $effect(() => {
16
- if (!hasOpen && !hasOnClose) {
17
- throw new Error(`You have to provide an \`open\` and an \`onclose\` prop to the \`Dialog\` component.`);
18
- }
19
- if (!hasOpen) {
20
- throw new Error(`You provided an \`onclose\` prop to the \`Dialog\`, but forgot an \`open\` prop.`);
21
- }
22
- if (!hasOnClose) {
23
- throw new Error(`You provided an \`open\` prop to the \`Dialog\`, but forgot an \`onclose\` prop.`);
5
+ export const DEFAULT_DIALOG_TAG = "div" as const
6
+ export type DialogRenderPropArg = {
7
+ open: boolean
24
8
  }
25
- if (!usesOpenClosedState && typeof open !== "boolean") {
26
- throw new Error(
27
- `You provided an \`open\` prop to the \`Dialog\`, but the value is not a boolean. Received: ${open}`
28
- );
29
- }
30
- if (typeof rest.onclose !== "function") {
31
- throw new Error(
32
- `You provided an \`onclose\` prop to the \`Dialog\`, but the value is not a function. Received: ${rest.onclose}`
33
- );
9
+ type DialogPropsWeControl = "aria-describedby" | "aria-labelledby" | "aria-modal"
10
+
11
+ export const DialogRenderFeatures = RenderFeatures.RenderStrategy | RenderFeatures.Static
12
+
13
+ export type DialogProps<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG> = Props<
14
+ TTag,
15
+ DialogRenderPropArg,
16
+ DialogPropsWeControl,
17
+ PropsForFeatures<typeof DialogRenderFeatures> & {
18
+ as?: TTag
19
+ id?: string
20
+ open?: boolean
21
+ onclose(value: boolean): void
22
+ initialFocus?: HTMLElement
23
+ role?: "dialog" | "alertdialog"
24
+ autofocus?: boolean
25
+ transition?: boolean
26
+ __demoMode?: boolean
27
+ }
28
+ >
29
+ </script>
30
+
31
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DIALOG_TAG">
32
+ import { useId } from "../hooks/use-id.js"
33
+ import { useMainTreeNode, useRootContainers } from "../hooks/use-root-containers.svelte.js"
34
+ import { clearOpenClosedContext, State, useOpenClosed } from "../internal/open-closed.js"
35
+ import { useNestedPortals } from "../portal/InternalPortal.svelte"
36
+ import { getOwnerDocument } from "../utils/owner.js"
37
+ import { useInertOthers } from "../hooks/use-inert-others.svelte.js"
38
+ import { useOutsideClick } from "../hooks/use-outside-click.svelte.js"
39
+ import { useEscape } from "../hooks/use-escape.svelte.js"
40
+ import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js"
41
+ import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js"
42
+ import { setContext } from "svelte"
43
+ import { useIsTouchDevice } from "../hooks/use-is-touch-device.svelte.js"
44
+ import FocusTrap, { FocusTrapFeatures } from "../focus-trap/FocusTrap.svelte"
45
+ import Portal from "../portal/Portal.svelte"
46
+ import PortalGroup from "../portal/PortalGroup.svelte"
47
+ import ForcePortalRoot from "../internal/ForcePortalRoot.svelte"
48
+ import { createCloseContext } from "../internal/close-provider.js"
49
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
50
+ import { DialogStates, type DialogContext, type StateDefinition } from "./context.svelte.js"
51
+ import { useDescriptions } from "../description/context.svelte.js"
52
+ import MainTreeProvider from "../internal/MainTreeProvider.svelte"
53
+ import Transition from "../transition/Transition.svelte"
54
+
55
+ const internalId = useId()
56
+ let {
57
+ ref = $bindable(),
58
+ id = `headlessui-dialog-${internalId}`,
59
+ open: theirOpen,
60
+ onclose,
61
+ initialFocus,
62
+ role: theirRole = "dialog",
63
+ autofocus = true,
64
+ __demoMode = false,
65
+ unmount = false,
66
+ transition = false,
67
+ ...theirProps
68
+ }: { as?: TTag } & DialogProps<TTag> = $props()
69
+
70
+ // Validations
71
+ const usesOpenClosedState = useOpenClosed()
72
+ const hasOpen = $derived(theirOpen !== undefined || usesOpenClosedState)
73
+ const hasOnClose = $derived(theirProps.hasOwnProperty("onclose"))
74
+
75
+ $effect(() => {
76
+ if (!hasOpen && !hasOnClose) {
77
+ throw new Error(`You have to provide an \`open\` and an \`onclose\` prop to the \`Dialog\` component.`)
78
+ }
79
+
80
+ if (!hasOpen) {
81
+ throw new Error(`You provided an \`onclose\` prop to the \`Dialog\`, but forgot an \`open\` prop.`)
82
+ }
83
+
84
+ if (!hasOnClose) {
85
+ throw new Error(`You provided an \`open\` prop to the \`Dialog\`, but forgot an \`onclose\` prop.`)
86
+ }
87
+
88
+ if (!usesOpenClosedState && typeof open !== "boolean") {
89
+ throw new Error(
90
+ `You provided an \`open\` prop to the \`Dialog\`, but the value is not a boolean. Received: ${open}`
91
+ )
92
+ }
93
+
94
+ if (typeof theirProps.onclose !== "function") {
95
+ throw new Error(
96
+ `You provided an \`onclose\` prop to the \`Dialog\`, but the value is not a function. Received: ${theirProps.onclose}`
97
+ )
98
+ }
99
+ })
100
+
101
+ let didWarnOnRole = $state(false)
102
+
103
+ const role = $derived.by(() => {
104
+ if (theirRole === "dialog" || theirRole === "alertdialog") {
105
+ return theirRole
106
+ }
107
+
108
+ if (!didWarnOnRole) {
109
+ didWarnOnRole = true
110
+ console.warn(
111
+ `Invalid role [${theirRole}] passed to <Dialog />. Only \`dialog\` and and \`alertdialog\` are supported. Using \`dialog\` instead.`
112
+ )
113
+ }
114
+
115
+ return "dialog"
116
+ })
117
+
118
+ // Update the `open` prop based on the open closed state
119
+ const open = $derived(
120
+ theirOpen === undefined && usesOpenClosedState !== null
121
+ ? (usesOpenClosedState.value & State.Open) === State.Open
122
+ : theirOpen
123
+ )
124
+
125
+ const ownerDocument = $derived(getOwnerDocument(ref))
126
+
127
+ const dialogState = $derived(open ? DialogStates.Open : DialogStates.Closed)
128
+
129
+ let _state = $state({
130
+ titleId: null,
131
+ panelRef: null,
132
+ } as StateDefinition)
133
+
134
+ const close = $derived(() => onclose(false))
135
+
136
+ const setTitleId = (id: string | null) => (_state.titleId = id)
137
+
138
+ const enabled = $derived(dialogState === DialogStates.Open)
139
+ const nestedPortals = useNestedPortals()
140
+ const { portals } = $derived(nestedPortals)
141
+
142
+ // We use this because reading these values during initial render(s)
143
+ // can result in `null` rather then the actual elements
144
+ // This doesn't happen when using certain components like a
145
+ // `<Dialog.Title>` because they cause the parent to re-render
146
+ const defaultContainer: { readonly current: HTMLElement | undefined } = {
147
+ get current() {
148
+ return _state.panelRef ?? ref
149
+ },
34
150
  }
35
- });
36
- const DialogComponent = InternalDialog;
151
+
152
+ const mainTreeNode = useMainTreeNode()
153
+ let { resolvedContainers: resolvedRootContainers } = $derived(
154
+ useRootContainers({
155
+ get mainTreeNode() {
156
+ return mainTreeNode.node
157
+ },
158
+ get portals() {
159
+ return portals
160
+ },
161
+ get defaultContainers() {
162
+ return defaultContainer.current ? [defaultContainer.current] : []
163
+ },
164
+ })
165
+ )
166
+
167
+ // When the `Dialog` is wrapped in a `Transition` (or another Headless UI component that exposes
168
+ // the OpenClosed state) then we get some information via context about its state. When the
169
+ // `Transition` is about to close, then the `State.Closing` state will be exposed. This allows us
170
+ // to enable/disable certain functionality in the `Dialog` upfront instead of waiting until the
171
+ // `Transition` is done transitioning.
172
+ const isClosing = $derived(
173
+ usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Closing) === State.Closing : false
174
+ )
175
+
176
+ // Ensure other elements can't be interacted with
177
+ const inertOthersEnabled = $derived(__demoMode ? false : isClosing ? false : enabled)
178
+ useInertOthers({
179
+ get enabled() {
180
+ return inertOthersEnabled
181
+ },
182
+ elements: {
183
+ get allowed() {
184
+ return [
185
+ // Allow the headlessui-portal of the Dialog to be interactive. This
186
+ // contains the current dialog and the necessary focus guard elements.
187
+ ref?.closest<HTMLElement>("[data-headlessui-portal]") ?? null,
188
+ ]
189
+ },
190
+ get disallowed() {
191
+ return [
192
+ // Disallow the "main" tree root node
193
+ mainTreeNode.node?.closest<HTMLElement>("body > *:not(#headlessui-portal-root)") ?? null,
194
+ ]
195
+ },
196
+ },
197
+ })
198
+
199
+ // Close Dialog on outside click
200
+ useOutsideClick({
201
+ get enabled() {
202
+ return enabled
203
+ },
204
+ get containers() {
205
+ return resolvedRootContainers
206
+ },
207
+ cb(event) {
208
+ event.preventDefault()
209
+ close()
210
+ },
211
+ })
212
+
213
+ // Handle `Escape` to close
214
+ useEscape({
215
+ get enabled() {
216
+ return enabled
217
+ },
218
+ get view() {
219
+ return ownerDocument?.defaultView ?? null
220
+ },
221
+ cb(event) {
222
+ event.preventDefault()
223
+ event.stopPropagation()
224
+
225
+ // Ensure that we blur the current activeElement to prevent maintaining
226
+ // focus and potentially scrolling the page to the end (because the Dialog
227
+ // is rendered in a Portal at the end of the document.body and the browser
228
+ // tries to keep the focused element in view)
229
+ //
230
+ // Typically only happens in Safari.
231
+ if (
232
+ document.activeElement &&
233
+ "blur" in document.activeElement &&
234
+ typeof document.activeElement.blur === "function"
235
+ ) {
236
+ document.activeElement.blur()
237
+ }
238
+
239
+ close()
240
+ },
241
+ })
242
+
243
+ // Scroll lock
244
+ const scrollLockEnabled = $derived(__demoMode ? false : isClosing ? false : enabled)
245
+ useScrollLock({
246
+ get enabled() {
247
+ return scrollLockEnabled
248
+ },
249
+ get ownerDocument() {
250
+ return ownerDocument
251
+ },
252
+ resolveAllowedContainers() {
253
+ return resolvedRootContainers
254
+ },
255
+ })
256
+
257
+ // Ensure we close the dialog as soon as the dialog itself becomes hidden
258
+ useOnDisappear({
259
+ get enabled() {
260
+ return enabled
261
+ },
262
+ get ref() {
263
+ return ref
264
+ },
265
+ get ondisappear() {
266
+ return close
267
+ },
268
+ })
269
+
270
+ const describedby = useDescriptions()
271
+
272
+ setContext<DialogContext>("DialogContext", {
273
+ get titleId() {
274
+ return _state.titleId
275
+ },
276
+ get panelRef() {
277
+ return _state.panelRef
278
+ },
279
+ get dialogState() {
280
+ return dialogState
281
+ },
282
+ get close() {
283
+ return close
284
+ },
285
+ get unmount() {
286
+ return unmount
287
+ },
288
+ setTitleId,
289
+ })
290
+
291
+ const slot = $derived({ open: dialogState === DialogStates.Open } satisfies DialogRenderPropArg)
292
+
293
+ const ourProps = $derived({
294
+ id,
295
+ role,
296
+ tabIndex: -1,
297
+ "aria-modal": __demoMode ? undefined : dialogState === DialogStates.Open ? true : undefined,
298
+ "aria-labelledby": _state.titleId,
299
+ "aria-describedby": describedby.value,
300
+ unmount,
301
+ })
302
+
303
+ const shouldMoveFocusInside = !useIsTouchDevice().value
304
+ const focusTrapFeatures = $derived.by(() => {
305
+ let focusTrapFeatures = FocusTrapFeatures.None
306
+
307
+ if (enabled && !__demoMode) {
308
+ focusTrapFeatures |= FocusTrapFeatures.RestoreFocus
309
+ focusTrapFeatures |= FocusTrapFeatures.TabLock
310
+
311
+ if (autofocus) {
312
+ focusTrapFeatures |= FocusTrapFeatures.AutoFocus
313
+ }
314
+
315
+ if (shouldMoveFocusInside) {
316
+ focusTrapFeatures |= FocusTrapFeatures.InitialFocus
317
+ }
318
+ }
319
+
320
+ return focusTrapFeatures
321
+ })
322
+
323
+ clearOpenClosedContext()
324
+ createCloseContext({
325
+ get close() {
326
+ return close
327
+ },
328
+ })
37
329
  </script>
38
330
 
39
- {#if (open !== undefined || transition) && !rest.static}
331
+ {#snippet internal(transitionProps?: Record<string, any>)}
332
+ <ForcePortalRoot force={true}>
333
+ <Portal>
334
+ <PortalGroup target={ref ?? null}>
335
+ <ForcePortalRoot force={false}>
336
+ <FocusTrap
337
+ {initialFocus}
338
+ initialFocusFallback={ref}
339
+ containers={resolvedRootContainers}
340
+ features={focusTrapFeatures}
341
+ >
342
+ <ElementOrComponent
343
+ {ourProps}
344
+ theirProps={{ ...theirProps, ...transitionProps }}
345
+ slots={slot}
346
+ defaultTag={DEFAULT_DIALOG_TAG}
347
+ features={DialogRenderFeatures}
348
+ visible={dialogState === DialogStates.Open}
349
+ name="Dialog"
350
+ bind:ref
351
+ />
352
+ </FocusTrap>
353
+ </ForcePortalRoot>
354
+ </PortalGroup>
355
+ </Portal>
356
+ </ForcePortalRoot>
357
+ {/snippet}
358
+
359
+ {#if (open !== undefined || transition) && !theirProps.static}
40
360
  <MainTreeProvider>
41
- <Transition show={open} {transition} unmount={rest.unmount} {ref}>
42
- {#snippet children(slot, props)}
43
- <DialogComponent bind:ref {...props} {...rest} />
361
+ <Transition show={open} {transition} unmount={theirProps.unmount} {ref}>
362
+ {#snippet children({ props })}
363
+ {@render internal(props)}
44
364
  {/snippet}
45
365
  </Transition>
46
366
  </MainTreeProvider>
47
367
  {:else}
48
368
  <MainTreeProvider>
49
- <DialogComponent bind:ref {open} {...rest} />
369
+ {@render internal()}
50
370
  </MainTreeProvider>
51
371
  {/if}
@@ -1,6 +1,5 @@
1
- import { type PropsForFeatures } from "../utils/render.js";
2
1
  import type { ElementType, Props } from "../utils/types.js";
3
- import { type Snippet } from "svelte";
2
+ import { type PropsForFeatures } from "../utils/render.js";
4
3
  export declare const DEFAULT_DIALOG_TAG: "div";
5
4
  export type DialogRenderPropArg = {
6
5
  open: boolean;
@@ -8,6 +7,7 @@ export type DialogRenderPropArg = {
8
7
  type DialogPropsWeControl = "aria-describedby" | "aria-labelledby" | "aria-modal";
9
8
  export declare const DialogRenderFeatures: number;
10
9
  export type DialogProps<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG> = Props<TTag, DialogRenderPropArg, DialogPropsWeControl, PropsForFeatures<typeof DialogRenderFeatures> & {
10
+ as?: TTag;
11
11
  id?: string;
12
12
  open?: boolean;
13
13
  onclose(value: boolean): void;
@@ -17,19 +17,22 @@ export type DialogProps<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG> =
17
17
  transition?: boolean;
18
18
  __demoMode?: boolean;
19
19
  }>;
20
- export type DialogChildren = Snippet<[DialogRenderPropArg]>;
21
20
  declare class __sveltets_Render<TTag extends ElementType = typeof DEFAULT_DIALOG_TAG> {
22
21
  props(): {
23
22
  as?: TTag | undefined;
24
- } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "refName" | "class") | "autofocus" | "unmount" | "static" | "id" | "role" | "onclose" | "initialFocus" | DialogPropsWeControl | "open" | "transition" | "__demoMode"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
25
- children?: Snippet<[DialogRenderPropArg, Record<string, any>]> | undefined;
23
+ } & (Exclude<keyof import("../utils/types.js").PropsOf<TTag>, ("as" | "children" | "class") | "autofocus" | "unmount" | "static" | "id" | "role" | "onclose" | "initialFocus" | "transition" | DialogPropsWeControl | "open" | "__demoMode"> extends infer T extends keyof import("../utils/types.js").PropsOf<TTag> ? { [P in T]: import("../utils/types.js").PropsOf<TTag>[P]; } : never) & {
24
+ children?: import("svelte").Snippet<[{
25
+ slot: DialogRenderPropArg;
26
+ props: Record<string, any>;
27
+ }]> | undefined;
26
28
  ref?: HTMLElement;
27
- } & (true extends (import("../utils/types.js").PropsOf<TTag> extends infer T_1 ? T_1 extends import("../utils/types.js").PropsOf<TTag> ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
28
- class?: import("../utils/types.js").PropsOf<TTag>["class"] | ((bag: DialogRenderPropArg) => string) | undefined;
29
+ } & (true extends (import("svelte/elements.js").SvelteHTMLElements[TTag] extends infer T_1 ? T_1 extends import("svelte/elements.js").SvelteHTMLElements[TTag] ? T_1 extends never ? never : "class" extends infer T_2 ? T_2 extends "class" ? T_2 extends keyof T_1 ? true : never : never : never : never : never) ? {
30
+ class?: string | ((bag: DialogRenderPropArg) => string) | null | undefined;
29
31
  } : {}) & {
30
32
  static?: boolean | undefined;
31
33
  unmount?: boolean | undefined;
32
34
  } & {
35
+ as?: TTag | undefined;
33
36
  id?: string;
34
37
  open?: boolean;
35
38
  onclose(value: boolean): void;