@pzerelles/headlessui-svelte 2.1.2-next.3 → 2.1.2-next.5

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 (65) hide show
  1. package/dist/button/Button.svelte +84 -54
  2. package/dist/checkbox/Checkbox.svelte +174 -120
  3. package/dist/close-button/CloseButton.svelte +12 -6
  4. package/dist/combobox/Combobox.svelte +50 -3
  5. package/dist/data-interactive/DataInteractive.svelte +57 -29
  6. package/dist/description/Description.svelte +32 -21
  7. package/dist/dialog/Dialog.svelte +69 -34
  8. package/dist/dialog/DialogBackdrop.svelte +29 -12
  9. package/dist/dialog/DialogPanel.svelte +49 -26
  10. package/dist/dialog/DialogTitle.svelte +38 -23
  11. package/dist/dialog/InternalDialog.svelte +263 -202
  12. package/dist/field/Field.svelte +49 -26
  13. package/dist/fieldset/Fieldset.svelte +50 -29
  14. package/dist/focus-trap/FocusTrap.svelte +419 -290
  15. package/dist/focus-trap/FocusTrap.svelte.d.ts +2 -14
  16. package/dist/focus-trap/FocusTrapFeatures.d.ts +14 -0
  17. package/dist/focus-trap/FocusTrapFeatures.js +15 -0
  18. package/dist/input/Input.svelte +85 -53
  19. package/dist/internal/FocusSentinel.svelte +16 -8
  20. package/dist/internal/ForcePortalRoot.svelte +7 -3
  21. package/dist/internal/FormFields.svelte +31 -20
  22. package/dist/internal/FormResolver.svelte +20 -15
  23. package/dist/internal/Hidden.svelte +44 -27
  24. package/dist/internal/Hidden.svelte.d.ts +2 -5
  25. package/dist/internal/HiddenFeatures.d.ts +5 -0
  26. package/dist/internal/HiddenFeatures.js +9 -0
  27. package/dist/internal/HoistFormFields.svelte +7 -4
  28. package/dist/internal/MainTreeProvider.svelte +89 -36
  29. package/dist/internal/Portal.svelte +18 -14
  30. package/dist/label/Label.svelte +91 -57
  31. package/dist/legend/Legend.svelte +18 -3
  32. package/dist/listbox/Listbox.svelte +588 -409
  33. package/dist/listbox/Listbox.svelte.d.ts +2 -12
  34. package/dist/listbox/ListboxButton.svelte +176 -127
  35. package/dist/listbox/ListboxOption.svelte +166 -125
  36. package/dist/listbox/ListboxOptions.svelte +340 -244
  37. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  38. package/dist/listbox/ListboxStates.d.ts +12 -0
  39. package/dist/listbox/ListboxStates.js +15 -0
  40. package/dist/menu/Menu.svelte +307 -218
  41. package/dist/menu/MenuButton.svelte +157 -115
  42. package/dist/menu/MenuHeading.svelte +34 -14
  43. package/dist/menu/MenuItem.svelte +145 -107
  44. package/dist/menu/MenuItems.svelte +298 -224
  45. package/dist/menu/MenuSection.svelte +26 -9
  46. package/dist/menu/MenuSeparator.svelte +20 -4
  47. package/dist/portal/InternalPortal.svelte +141 -85
  48. package/dist/portal/Portal.svelte +5 -2
  49. package/dist/portal/PortalGroup.svelte +30 -9
  50. package/dist/switch/Switch.svelte +179 -122
  51. package/dist/switch/Switch.svelte.d.ts +4 -4
  52. package/dist/switch/SwitchGroup.svelte +44 -31
  53. package/dist/tabs/Tab.svelte +195 -143
  54. package/dist/tabs/TabGroup.svelte +292 -205
  55. package/dist/tabs/TabList.svelte +31 -11
  56. package/dist/tabs/TabPanel.svelte +68 -43
  57. package/dist/tabs/TabPanels.svelte +18 -7
  58. package/dist/textarea/Textarea.svelte +83 -53
  59. package/dist/transition/InternalTransitionChild.svelte +259 -170
  60. package/dist/transition/Transition.svelte +96 -66
  61. package/dist/transition/TransitionChild.svelte +31 -11
  62. package/dist/utils/ElementOrComponent.svelte +44 -23
  63. package/dist/utils/Generic.svelte +29 -17
  64. package/dist/utils/StableCollection.svelte +54 -36
  65. package/package.json +10 -10
@@ -1,37 +1,50 @@
1
- <script lang="ts" module>const DEFAULT_GROUP_TAG = "svelte:fragment";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+
4
+ const DEFAULT_GROUP_TAG = "svelte:fragment"
5
+
6
+ export type SwitchGroupProps<TTag extends ElementType = typeof DEFAULT_GROUP_TAG> = Props<TTag>
7
+
8
+ export type GroupContext = {
9
+ switchElement: HTMLElement | null
10
+ }
2
11
  </script>
3
12
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_GROUP_TAG">import { setContext } from "svelte";
5
- import { useLabels } from "../label/context.svelte.js";
6
- import { useDescriptions } from "../description/context.svelte.js";
7
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
8
- let switchElement = $state(null);
9
- useLabels({
10
- name: "SwitchGroup",
11
- props: {
12
- get htmlFor() {
13
- return switchElement?.id;
13
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_GROUP_TAG">
14
+ import { setContext } from "svelte"
15
+ import { useLabels } from "../label/context.svelte.js"
16
+ import { useDescriptions } from "../description/context.svelte.js"
17
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
18
+
19
+ let switchElement = $state<HTMLElement | null>(null)
20
+ useLabels({
21
+ name: "SwitchGroup",
22
+ props: {
23
+ get htmlFor() {
24
+ return switchElement?.id
25
+ },
26
+ onclick: (event: MouseEvent) => {
27
+ if (!switchElement) return
28
+ if (event.currentTarget instanceof HTMLLabelElement) {
29
+ event.preventDefault()
30
+ }
31
+ switchElement.click()
32
+ switchElement.focus({ preventScroll: true })
33
+ },
14
34
  },
15
- onclick: (event) => {
16
- if (!switchElement) return;
17
- if (event.currentTarget instanceof HTMLLabelElement) {
18
- event.preventDefault();
19
- }
20
- switchElement.click();
21
- switchElement.focus({ preventScroll: true });
22
- }
23
- }
24
- });
25
- useDescriptions();
26
- setContext("GroupContext", {
27
- get switchElement() {
28
- return switchElement;
29
- },
30
- set switchElement(element) {
31
- switchElement = element;
32
- }
33
- });
34
- let { ref = $bindable(), ...theirProps } = $props();
35
+ })
36
+ useDescriptions()
37
+
38
+ setContext<GroupContext>("GroupContext", {
39
+ get switchElement() {
40
+ return switchElement
41
+ },
42
+ set switchElement(element) {
43
+ switchElement = element
44
+ },
45
+ })
46
+
47
+ let { ref = $bindable(), ...theirProps }: { as?: TTag } & SwitchGroupProps<TTag> = $props()
35
48
  </script>
36
49
 
37
50
  <ElementOrComponent {theirProps} defaultTag={DEFAULT_GROUP_TAG} name="SwitchGroup" bind:ref />
@@ -1,156 +1,208 @@
1
- <script lang="ts" module>const DEFAULT_TAB_TAG = "button";
2
- </script>
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
3
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_TAB_TAG">import { useId } from "../hooks/use-id.js";
5
- import { useActions, useData } from "./TabGroup.svelte";
6
- import { useStableCollectionIndex } from "../utils/StableCollection.svelte";
7
- import { Focus, focusIn, FocusResult } from "../utils/focus-management.js";
8
- import { getOwnerDocument } from "../utils/owner.js";
9
- import { match } from "../utils/match.js";
10
- import { microTask } from "../utils/microTask.js";
11
- import { useActivePress } from "../hooks/use-active-press.svelte.js";
12
- import { useFocusRing } from "../hooks/use-focus-ring.svelte.js";
13
- import { useResolveButtonType } from "../hooks/use-resolve-button-type.svelte.js";
14
- import { onMount } from "svelte";
15
- import { useHover } from "../hooks/use-hover.svelte.js";
16
- import { mergeProps } from "../utils/render.js";
17
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
18
- const internalId = useId();
19
- let {
20
- ref = $bindable(),
21
- id = `headlessui-tabs-tab-${internalId}`,
22
- disabled = false,
23
- autofocus = false,
24
- ...theirProps
25
- } = $props();
26
- const data = useData("Tab");
27
- const { orientation, activation, selectedIndex, tabs, panels } = $derived(data);
28
- const actions = useActions("Tab");
29
- const tabRef = $derived({ current: ref });
30
- onMount(() => actions.registerTab(tabRef));
31
- const mySSRIndex = useStableCollectionIndex("tabs");
32
- const myIndex = $derived.by(() => {
33
- const index = tabs.findIndex((tab) => tab === tabRef);
34
- return index === -1 ? mySSRIndex : index;
35
- });
36
- const selected = $derived(myIndex === selectedIndex);
37
- const activateUsing = $derived((cb) => {
38
- let result = cb();
39
- if (result === FocusResult.Success && activation === "auto") {
40
- let newTab = getOwnerDocument(ref)?.activeElement;
41
- let idx = data.tabs.findIndex((tab) => tab.current === newTab);
42
- if (idx !== -1) actions.change(idx);
43
- }
44
- return result;
45
- });
46
- const handleKeyDown = (event) => {
47
- let list = tabs.map((tab) => tab.current).filter(Boolean);
48
- if (event.key === " " || event.key === "Enter") {
49
- event.preventDefault();
50
- event.stopPropagation();
51
- actions.change(myIndex);
52
- return;
53
- }
54
- switch (event.key) {
55
- case "Home":
56
- case "PageUp":
57
- event.preventDefault();
58
- event.stopPropagation();
59
- return activateUsing(() => focusIn(list, Focus.First));
60
- case "End":
61
- case "PageDown":
62
- event.preventDefault();
63
- event.stopPropagation();
64
- return activateUsing(() => focusIn(list, Focus.Last));
4
+ const DEFAULT_TAB_TAG = "button" as const
5
+ type TabRenderPropArg = {
6
+ hover: boolean
7
+ focus: boolean
8
+ active: boolean
9
+ autofocus: boolean
10
+ selected: boolean
11
+ disabled: boolean
65
12
  }
66
- let result = activateUsing(() => {
67
- return match(orientation, {
68
- vertical() {
69
- if (event.key === "ArrowUp") return focusIn(list, Focus.Previous | Focus.WrapAround);
70
- if (event.key === "ArrowDown") return focusIn(list, Focus.Next | Focus.WrapAround);
71
- return FocusResult.Error;
72
- },
73
- horizontal() {
74
- if (event.key === "ArrowLeft") return focusIn(list, Focus.Previous | Focus.WrapAround);
75
- if (event.key === "ArrowRight") return focusIn(list, Focus.Next | Focus.WrapAround);
76
- return FocusResult.Error;
77
- }
78
- });
79
- });
80
- if (result === FocusResult.Success) {
81
- return event.preventDefault();
82
- }
83
- };
84
- let ready = $state(false);
85
- const handleSelection = () => {
86
- if (ready) return;
87
- ready = true;
88
- ref?.focus({ preventScroll: true });
89
- actions.change(myIndex);
90
- microTask(() => {
91
- ready = false;
92
- });
93
- };
94
- const handleMouseDown = (event) => {
95
- event.preventDefault();
96
- };
97
- const { isHovered: hover, hoverProps } = $derived(
98
- useHover({
99
- get disabled() {
100
- return disabled;
13
+ type TabPropsWeControl = "aria-controls" | "aria-selected" | "role" | "tabIndex"
14
+
15
+ export type TabProps<TTag extends ElementType = typeof DEFAULT_TAB_TAG> = Props<
16
+ TTag,
17
+ TabRenderPropArg,
18
+ TabPropsWeControl,
19
+ {
20
+ id?: string
21
+ autofocus?: boolean
22
+ disabled?: boolean
101
23
  }
24
+ >
25
+ </script>
26
+
27
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_TAB_TAG">
28
+ import { useId } from "../hooks/use-id.js"
29
+ import { useActions, useData } from "./TabGroup.svelte"
30
+ import { useStableCollectionIndex } from "../utils/StableCollection.svelte"
31
+ import { Focus, focusIn, FocusResult } from "../utils/focus-management.js"
32
+ import { getOwnerDocument } from "../utils/owner.js"
33
+ import { match } from "../utils/match.js"
34
+ import { microTask } from "../utils/microTask.js"
35
+ import { useActivePress } from "../hooks/use-active-press.svelte.js"
36
+ import { useFocusRing } from "../hooks/use-focus-ring.svelte.js"
37
+ import { useResolveButtonType } from "../hooks/use-resolve-button-type.svelte.js"
38
+ import type { MutableRefObject } from "../utils/ref.svelte.js"
39
+ import { onMount } from "svelte"
40
+ import { useHover } from "../hooks/use-hover.svelte.js"
41
+ import { mergeProps } from "../utils/render.js"
42
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
43
+
44
+ const internalId = useId()
45
+ let {
46
+ ref = $bindable(),
47
+ id = `headlessui-tabs-tab-${internalId}`,
48
+ disabled = false,
49
+ autofocus = false,
50
+ ...theirProps
51
+ }: { as?: TTag } & TabProps<TTag> = $props()
52
+
53
+ const data = useData("Tab")
54
+ const { orientation, activation, selectedIndex, tabs, panels } = $derived(data)
55
+ const actions = useActions("Tab")
56
+
57
+ const tabRef = $derived<MutableRefObject<HTMLElement | undefined>>({ current: ref })
58
+
59
+ onMount(() => actions.registerTab(tabRef))
60
+
61
+ const mySSRIndex = useStableCollectionIndex("tabs")
62
+
63
+ const myIndex = $derived.by(() => {
64
+ const index = tabs.findIndex((tab) => tab === tabRef)
65
+ return index === -1 ? mySSRIndex : index
102
66
  })
103
- );
104
- const { pressed: active, pressProps } = $derived(
105
- useActivePress({
106
- get disabled() {
107
- return disabled;
67
+ const selected = $derived(myIndex === selectedIndex)
68
+
69
+ const activateUsing = $derived((cb: () => FocusResult) => {
70
+ let result = cb()
71
+ if (result === FocusResult.Success && activation === "auto") {
72
+ let newTab = getOwnerDocument(ref)?.activeElement
73
+ let idx = data.tabs.findIndex((tab) => tab.current === newTab)
74
+ if (idx !== -1) actions.change(idx)
108
75
  }
76
+ return result
109
77
  })
110
- );
111
- const { isFocusVisible: focus, focusProps } = $derived(
112
- useFocusRing({
113
- get autofocus() {
114
- return autofocus;
78
+
79
+ const handleKeyDown = (event: KeyboardEvent) => {
80
+ let list = tabs.map((tab) => tab.current).filter(Boolean) as HTMLElement[]
81
+
82
+ if (event.key === " " || event.key === "Enter") {
83
+ event.preventDefault()
84
+ event.stopPropagation()
85
+
86
+ actions.change(myIndex)
87
+ return
88
+ }
89
+
90
+ switch (event.key) {
91
+ case "Home":
92
+ case "PageUp":
93
+ event.preventDefault()
94
+ event.stopPropagation()
95
+
96
+ return activateUsing(() => focusIn(list, Focus.First))
97
+
98
+ case "End":
99
+ case "PageDown":
100
+ event.preventDefault()
101
+ event.stopPropagation()
102
+
103
+ return activateUsing(() => focusIn(list, Focus.Last))
104
+ }
105
+
106
+ let result = activateUsing(() => {
107
+ return match(orientation, {
108
+ vertical() {
109
+ if (event.key === "ArrowUp") return focusIn(list, Focus.Previous | Focus.WrapAround)
110
+ if (event.key === "ArrowDown") return focusIn(list, Focus.Next | Focus.WrapAround)
111
+ return FocusResult.Error
112
+ },
113
+ horizontal() {
114
+ if (event.key === "ArrowLeft") return focusIn(list, Focus.Previous | Focus.WrapAround)
115
+ if (event.key === "ArrowRight") return focusIn(list, Focus.Next | Focus.WrapAround)
116
+ return FocusResult.Error
117
+ },
118
+ })
119
+ })
120
+
121
+ if (result === FocusResult.Success) {
122
+ return event.preventDefault()
115
123
  }
116
- })
117
- );
118
- const slot = $derived({
119
- selected,
120
- hover,
121
- active,
122
- focus,
123
- autofocus,
124
- disabled
125
- });
126
- const resolvedType = useResolveButtonType({
127
- get props() {
128
- return { type: theirProps.type, as: theirProps.as };
129
- },
130
- get ref() {
131
- return tabRef;
132
124
  }
133
- });
134
- const ourProps = $derived(
135
- mergeProps(
136
- {
137
- onkeydown: handleKeyDown,
138
- onmousedown: handleMouseDown,
139
- onclick: handleSelection,
140
- id,
141
- role: "tab",
142
- type: resolvedType.type,
143
- "aria-controls": panels[myIndex]?.current?.id,
144
- "aria-selected": selected,
145
- tabIndex: selected ? 0 : -1,
146
- disabled: disabled || void 0,
147
- autofocus
125
+
126
+ let ready = $state(false)
127
+ const handleSelection = () => {
128
+ if (ready) return
129
+ ready = true
130
+
131
+ ref?.focus({ preventScroll: true })
132
+ actions.change(myIndex)
133
+
134
+ microTask(() => {
135
+ ready = false
136
+ })
137
+ }
138
+
139
+ // This is important because we want to only focus the tab when it gets focus
140
+ // OR it finished the click event (mouseup). However, if you perform a `click`,
141
+ // then you will first get the `focus` and then get the `click` event.
142
+ const handleMouseDown = (event: MouseEvent) => {
143
+ event.preventDefault()
144
+ }
145
+
146
+ const { isHovered: hover, hoverProps } = $derived(
147
+ useHover({
148
+ get disabled() {
149
+ return disabled
150
+ },
151
+ })
152
+ )
153
+ const { pressed: active, pressProps } = $derived(
154
+ useActivePress({
155
+ get disabled() {
156
+ return disabled
157
+ },
158
+ })
159
+ )
160
+ const { isFocusVisible: focus, focusProps } = $derived(
161
+ useFocusRing({
162
+ get autofocus() {
163
+ return autofocus
164
+ },
165
+ })
166
+ )
167
+
168
+ const slot = $derived({
169
+ selected,
170
+ hover,
171
+ active,
172
+ focus,
173
+ autofocus,
174
+ disabled,
175
+ } satisfies TabRenderPropArg)
176
+
177
+ const resolvedType = useResolveButtonType({
178
+ get props() {
179
+ return { type: theirProps.type, as: theirProps.as }
180
+ },
181
+ get ref() {
182
+ return tabRef
148
183
  },
149
- focusProps,
150
- hoverProps,
151
- pressProps
184
+ })
185
+
186
+ const ourProps = $derived(
187
+ mergeProps(
188
+ {
189
+ onkeydown: handleKeyDown,
190
+ onmousedown: handleMouseDown,
191
+ onclick: handleSelection,
192
+ id,
193
+ role: "tab",
194
+ type: resolvedType.type,
195
+ "aria-controls": panels[myIndex]?.current?.id,
196
+ "aria-selected": selected,
197
+ tabIndex: selected ? 0 : -1,
198
+ disabled: disabled || undefined,
199
+ autofocus,
200
+ },
201
+ focusProps,
202
+ hoverProps,
203
+ pressProps
204
+ )
152
205
  )
153
- );
154
206
  </script>
155
207
 
156
208
  <ElementOrComponent {ourProps} {theirProps} {slot} defaultTag={DEFAULT_TAB_TAG} name="Tab" bind:ref />