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

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 (59) 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 +436 -290
  15. package/dist/input/Input.svelte +85 -53
  16. package/dist/internal/FocusSentinel.svelte +16 -8
  17. package/dist/internal/ForcePortalRoot.svelte +7 -3
  18. package/dist/internal/FormFields.svelte +31 -20
  19. package/dist/internal/FormResolver.svelte +20 -15
  20. package/dist/internal/Hidden.svelte +44 -27
  21. package/dist/internal/Hidden.svelte.d.ts +2 -5
  22. package/dist/internal/HiddenFeatures.d.ts +5 -0
  23. package/dist/internal/HiddenFeatures.js +9 -0
  24. package/dist/internal/HoistFormFields.svelte +7 -4
  25. package/dist/internal/MainTreeProvider.svelte +89 -36
  26. package/dist/internal/Portal.svelte +18 -14
  27. package/dist/label/Label.svelte +91 -57
  28. package/dist/legend/Legend.svelte +18 -3
  29. package/dist/listbox/Listbox.svelte +600 -409
  30. package/dist/listbox/ListboxButton.svelte +176 -127
  31. package/dist/listbox/ListboxOption.svelte +166 -125
  32. package/dist/listbox/ListboxOptions.svelte +340 -244
  33. package/dist/listbox/ListboxSelectedOption.svelte +38 -15
  34. package/dist/menu/Menu.svelte +307 -218
  35. package/dist/menu/MenuButton.svelte +157 -115
  36. package/dist/menu/MenuHeading.svelte +34 -14
  37. package/dist/menu/MenuItem.svelte +145 -107
  38. package/dist/menu/MenuItems.svelte +298 -224
  39. package/dist/menu/MenuSection.svelte +26 -9
  40. package/dist/menu/MenuSeparator.svelte +20 -4
  41. package/dist/portal/InternalPortal.svelte +141 -85
  42. package/dist/portal/Portal.svelte +5 -2
  43. package/dist/portal/PortalGroup.svelte +30 -9
  44. package/dist/switch/Switch.svelte +179 -122
  45. package/dist/switch/Switch.svelte.d.ts +4 -4
  46. package/dist/switch/SwitchGroup.svelte +44 -31
  47. package/dist/tabs/Tab.svelte +195 -143
  48. package/dist/tabs/TabGroup.svelte +292 -205
  49. package/dist/tabs/TabList.svelte +31 -11
  50. package/dist/tabs/TabPanel.svelte +68 -43
  51. package/dist/tabs/TabPanels.svelte +18 -7
  52. package/dist/textarea/Textarea.svelte +83 -53
  53. package/dist/transition/InternalTransitionChild.svelte +259 -170
  54. package/dist/transition/Transition.svelte +96 -66
  55. package/dist/transition/TransitionChild.svelte +31 -11
  56. package/dist/utils/ElementOrComponent.svelte +44 -23
  57. package/dist/utils/Generic.svelte +29 -17
  58. package/dist/utils/StableCollection.svelte +54 -36
  59. package/package.json +10 -10
@@ -1,209 +1,270 @@
1
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DIALOG_TAG">import { useId } from "../hooks/use-id.js";
2
- import { useMainTreeNode, useRootContainers } from "../hooks/use-root-containers.svelte.js";
3
- import { clearOpenClosedContext, State, useOpenClosed } from "../internal/open-closed.js";
4
- import { useNestedPortals } from "../portal/InternalPortal.svelte";
5
- import { getOwnerDocument } from "../utils/owner.js";
6
- import { DEFAULT_DIALOG_TAG, DialogRenderFeatures } from "./Dialog.svelte";
7
- import { useInertOthers } from "../hooks/use-inert-others.svelte.js";
8
- import { useOutsideClick } from "../hooks/use-outside-click.svelte.js";
9
- import { useEscape } from "../hooks/use-escape.svelte.js";
10
- import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js";
11
- import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js";
12
- import { setContext } from "svelte";
13
- import { useIsTouchDevice } from "../hooks/use-is-touch-device.svelte.js";
14
- import FocusTrap, { FocusTrapFeatures } from "../focus-trap/FocusTrap.svelte";
15
- import Portal from "../portal/Portal.svelte";
16
- import PortalGroup from "../portal/PortalGroup.svelte";
17
- import ForcePortalRoot from "../internal/ForcePortalRoot.svelte";
18
- import { createCloseContext } from "../internal/close-provider.js";
19
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
20
- import { DialogStates } from "./context.svelte.js";
21
- import { useDescriptions } from "../description/context.svelte.js";
22
- const internalId = useId();
23
- let {
24
- ref = $bindable(),
25
- id = `headlessui-dialog-${internalId}`,
26
- open: theirOpen,
27
- onclose,
28
- initialFocus,
29
- role: theirRole = "dialog",
30
- autofocus = true,
31
- __demoMode = false,
32
- unmount = false,
33
- ...theirProps
34
- } = $props();
35
- let didWarnOnRole = $state(false);
36
- const role = $derived.by(() => {
37
- if (theirRole === "dialog" || theirRole === "alertdialog") {
38
- return theirRole;
39
- }
40
- if (!didWarnOnRole) {
41
- didWarnOnRole = true;
42
- console.warn(
43
- `Invalid role [${theirRole}] passed to <Dialog />. Only \`dialog\` and and \`alertdialog\` are supported. Using \`dialog\` instead.`
44
- );
45
- }
46
- return "dialog";
47
- });
48
- const usesOpenClosedState = useOpenClosed();
49
- const open = $derived(
50
- theirOpen === void 0 && usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Open) === State.Open : theirOpen
51
- );
52
- const ownerDocument = $derived(getOwnerDocument(ref));
53
- const dialogState = $derived(open ? DialogStates.Open : DialogStates.Closed);
54
- let _state = $state({
55
- titleId: null,
56
- panelRef: null
57
- });
58
- const close = $derived(() => onclose(false));
59
- const setTitleId = (id2) => _state.titleId = id2;
60
- const enabled = $derived(dialogState === DialogStates.Open);
61
- const nestedPortals = useNestedPortals();
62
- const { portals } = $derived(nestedPortals);
63
- const defaultContainer = {
64
- get current() {
65
- return _state.panelRef ?? ref;
66
- }
67
- };
68
- const mainTreeNode = useMainTreeNode();
69
- let { resolvedContainers: resolvedRootContainers } = $derived(
70
- useRootContainers({
71
- get mainTreeNode() {
72
- return mainTreeNode.node;
73
- },
74
- get portals() {
75
- return portals;
76
- },
77
- get defaultContainers() {
78
- return defaultContainer.current ? [defaultContainer.current] : [];
79
- }
80
- })
81
- );
82
- const isClosing = $derived(
83
- usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Closing) === State.Closing : false
84
- );
85
- const inertOthersEnabled = $derived(__demoMode ? false : isClosing ? false : enabled);
86
- useInertOthers({
87
- get enabled() {
88
- return inertOthersEnabled;
89
- },
90
- elements: {
91
- get allowed() {
92
- return [
93
- // Allow the headlessui-portal of the Dialog to be interactive. This
94
- // contains the current dialog and the necessary focus guard elements.
95
- ref?.closest("[data-headlessui-portal]") ?? null
96
- ];
97
- },
98
- get disallowed() {
99
- return [
100
- // Disallow the "main" tree root node
101
- mainTreeNode.node?.closest("body > *:not(#headlessui-portal-root)") ?? null
102
- ];
1
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_DIALOG_TAG">
2
+ import { useId } from "../hooks/use-id.js"
3
+ import { useMainTreeNode, useRootContainers } from "../hooks/use-root-containers.svelte.js"
4
+ import { clearOpenClosedContext, State, useOpenClosed } from "../internal/open-closed.js"
5
+ import { useNestedPortals } from "../portal/InternalPortal.svelte"
6
+ import { getOwnerDocument } from "../utils/owner.js"
7
+ import { DEFAULT_DIALOG_TAG, DialogRenderFeatures, type DialogProps, type DialogRenderPropArg } from "./Dialog.svelte"
8
+ import { useInertOthers } from "../hooks/use-inert-others.svelte.js"
9
+ import { useOutsideClick } from "../hooks/use-outside-click.svelte.js"
10
+ import { useEscape } from "../hooks/use-escape.svelte.js"
11
+ import { useScrollLock } from "../hooks/use-scroll-lock.svelte.js"
12
+ import { useOnDisappear } from "../hooks/use-on-disappear.svelte.js"
13
+ import { setContext } from "svelte"
14
+ import { useIsTouchDevice } from "../hooks/use-is-touch-device.svelte.js"
15
+ import FocusTrap, { FocusTrapFeatures } from "../focus-trap/FocusTrap.svelte"
16
+ import Portal from "../portal/Portal.svelte"
17
+ import PortalGroup from "../portal/PortalGroup.svelte"
18
+ import ForcePortalRoot from "../internal/ForcePortalRoot.svelte"
19
+ import { createCloseContext } from "../internal/close-provider.js"
20
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
21
+ import type { ElementType } from "../utils/types.js"
22
+ import { DialogStates, type DialogContext, type StateDefinition } from "./context.svelte.js"
23
+ import { useDescriptions } from "../description/context.svelte.js"
24
+
25
+ const internalId = useId()
26
+ let {
27
+ ref = $bindable(),
28
+ id = `headlessui-dialog-${internalId}`,
29
+ open: theirOpen,
30
+ onclose,
31
+ initialFocus,
32
+ role: theirRole = "dialog",
33
+ autofocus = true,
34
+ __demoMode = false,
35
+ unmount = false,
36
+ ...theirProps
37
+ }: { as?: TTag } & DialogProps<TTag> = $props()
38
+
39
+ let didWarnOnRole = $state(false)
40
+
41
+ const role = $derived.by(() => {
42
+ if (theirRole === "dialog" || theirRole === "alertdialog") {
43
+ return theirRole
103
44
  }
104
- }
105
- });
106
- useOutsideClick({
107
- get enabled() {
108
- return enabled;
109
- },
110
- get containers() {
111
- return resolvedRootContainers;
112
- },
113
- cb(event) {
114
- event.preventDefault();
115
- close();
116
- }
117
- });
118
- useEscape({
119
- get enabled() {
120
- return enabled;
121
- },
122
- get view() {
123
- return ownerDocument?.defaultView ?? null;
124
- },
125
- cb(event) {
126
- event.preventDefault();
127
- event.stopPropagation();
128
- if (document.activeElement && "blur" in document.activeElement && typeof document.activeElement.blur === "function") {
129
- document.activeElement.blur();
45
+
46
+ if (!didWarnOnRole) {
47
+ didWarnOnRole = true
48
+ console.warn(
49
+ `Invalid role [${theirRole}] passed to <Dialog />. Only \`dialog\` and and \`alertdialog\` are supported. Using \`dialog\` instead.`
50
+ )
130
51
  }
131
- close();
132
- }
133
- });
134
- const scrollLockEnabled = $derived(__demoMode ? false : isClosing ? false : enabled);
135
- useScrollLock({
136
- get enabled() {
137
- return scrollLockEnabled;
138
- },
139
- get ownerDocument() {
140
- return ownerDocument;
141
- },
142
- resolveAllowedContainers() {
143
- return resolvedRootContainers;
144
- }
145
- });
146
- useOnDisappear({
147
- get enabled() {
148
- return enabled;
149
- },
150
- get ref() {
151
- return ref;
152
- },
153
- get ondisappear() {
154
- return close;
52
+
53
+ return "dialog"
54
+ })
55
+
56
+ const usesOpenClosedState = useOpenClosed()
57
+ // Update the `open` prop based on the open closed state
58
+ const open = $derived(
59
+ theirOpen === undefined && usesOpenClosedState !== null
60
+ ? (usesOpenClosedState.value & State.Open) === State.Open
61
+ : theirOpen
62
+ )
63
+
64
+ const ownerDocument = $derived(getOwnerDocument(ref))
65
+
66
+ const dialogState = $derived(open ? DialogStates.Open : DialogStates.Closed)
67
+
68
+ let _state = $state({
69
+ titleId: null,
70
+ panelRef: null,
71
+ } as StateDefinition)
72
+
73
+ const close = $derived(() => onclose(false))
74
+
75
+ const setTitleId = (id: string | null) => (_state.titleId = id)
76
+
77
+ const enabled = $derived(dialogState === DialogStates.Open)
78
+ const nestedPortals = useNestedPortals()
79
+ const { portals } = $derived(nestedPortals)
80
+
81
+ // We use this because reading these values during initial render(s)
82
+ // can result in `null` rather then the actual elements
83
+ // This doesn't happen when using certain components like a
84
+ // `<Dialog.Title>` because they cause the parent to re-render
85
+ const defaultContainer: { readonly current: HTMLElement | undefined } = {
86
+ get current() {
87
+ return _state.panelRef ?? ref
88
+ },
155
89
  }
156
- });
157
- const describedby = useDescriptions();
158
- setContext("DialogContext", {
159
- get titleId() {
160
- return _state.titleId;
161
- },
162
- get panelRef() {
163
- return _state.panelRef;
164
- },
165
- get dialogState() {
166
- return dialogState;
167
- },
168
- get close() {
169
- return close;
170
- },
171
- get unmount() {
172
- return unmount;
173
- },
174
- setTitleId
175
- });
176
- const slot = $derived({ open: dialogState === DialogStates.Open });
177
- const ourProps = $derived({
178
- id,
179
- role,
180
- tabIndex: -1,
181
- "aria-modal": __demoMode ? void 0 : dialogState === DialogStates.Open ? true : void 0,
182
- "aria-labelledby": _state.titleId,
183
- "aria-describedby": describedby.value,
184
- unmount
185
- });
186
- const shouldMoveFocusInside = !useIsTouchDevice().value;
187
- const focusTrapFeatures = $derived.by(() => {
188
- let focusTrapFeatures2 = FocusTrapFeatures.None;
189
- if (enabled && !__demoMode) {
190
- focusTrapFeatures2 |= FocusTrapFeatures.RestoreFocus;
191
- focusTrapFeatures2 |= FocusTrapFeatures.TabLock;
192
- if (autofocus) {
193
- focusTrapFeatures2 |= FocusTrapFeatures.AutoFocus;
194
- }
195
- if (shouldMoveFocusInside) {
196
- focusTrapFeatures2 |= FocusTrapFeatures.InitialFocus;
90
+
91
+ const mainTreeNode = useMainTreeNode()
92
+ let { resolvedContainers: resolvedRootContainers } = $derived(
93
+ useRootContainers({
94
+ get mainTreeNode() {
95
+ return mainTreeNode.node
96
+ },
97
+ get portals() {
98
+ return portals
99
+ },
100
+ get defaultContainers() {
101
+ return defaultContainer.current ? [defaultContainer.current] : []
102
+ },
103
+ })
104
+ )
105
+
106
+ // When the `Dialog` is wrapped in a `Transition` (or another Headless UI component that exposes
107
+ // the OpenClosed state) then we get some information via context about its state. When the
108
+ // `Transition` is about to close, then the `State.Closing` state will be exposed. This allows us
109
+ // to enable/disable certain functionality in the `Dialog` upfront instead of waiting until the
110
+ // `Transition` is done transitioning.
111
+ const isClosing = $derived(
112
+ usesOpenClosedState !== null ? (usesOpenClosedState.value & State.Closing) === State.Closing : false
113
+ )
114
+
115
+ // Ensure other elements can't be interacted with
116
+ const inertOthersEnabled = $derived(__demoMode ? false : isClosing ? false : enabled)
117
+ useInertOthers({
118
+ get enabled() {
119
+ return inertOthersEnabled
120
+ },
121
+ elements: {
122
+ get allowed() {
123
+ return [
124
+ // Allow the headlessui-portal of the Dialog to be interactive. This
125
+ // contains the current dialog and the necessary focus guard elements.
126
+ ref?.closest<HTMLElement>("[data-headlessui-portal]") ?? null,
127
+ ]
128
+ },
129
+ get disallowed() {
130
+ return [
131
+ // Disallow the "main" tree root node
132
+ mainTreeNode.node?.closest<HTMLElement>("body > *:not(#headlessui-portal-root)") ?? null,
133
+ ]
134
+ },
135
+ },
136
+ })
137
+
138
+ // Close Dialog on outside click
139
+ useOutsideClick({
140
+ get enabled() {
141
+ return enabled
142
+ },
143
+ get containers() {
144
+ return resolvedRootContainers
145
+ },
146
+ cb(event) {
147
+ event.preventDefault()
148
+ close()
149
+ },
150
+ })
151
+
152
+ // Handle `Escape` to close
153
+ useEscape({
154
+ get enabled() {
155
+ return enabled
156
+ },
157
+ get view() {
158
+ return ownerDocument?.defaultView ?? null
159
+ },
160
+ cb(event) {
161
+ event.preventDefault()
162
+ event.stopPropagation()
163
+
164
+ // Ensure that we blur the current activeElement to prevent maintaining
165
+ // focus and potentially scrolling the page to the end (because the Dialog
166
+ // is rendered in a Portal at the end of the document.body and the browser
167
+ // tries to keep the focused element in view)
168
+ //
169
+ // Typically only happens in Safari.
170
+ if (
171
+ document.activeElement &&
172
+ "blur" in document.activeElement &&
173
+ typeof document.activeElement.blur === "function"
174
+ ) {
175
+ document.activeElement.blur()
176
+ }
177
+
178
+ close()
179
+ },
180
+ })
181
+
182
+ // Scroll lock
183
+ const scrollLockEnabled = $derived(__demoMode ? false : isClosing ? false : enabled)
184
+ useScrollLock({
185
+ get enabled() {
186
+ return scrollLockEnabled
187
+ },
188
+ get ownerDocument() {
189
+ return ownerDocument
190
+ },
191
+ resolveAllowedContainers() {
192
+ return resolvedRootContainers
193
+ },
194
+ })
195
+
196
+ // Ensure we close the dialog as soon as the dialog itself becomes hidden
197
+ useOnDisappear({
198
+ get enabled() {
199
+ return enabled
200
+ },
201
+ get ref() {
202
+ return ref
203
+ },
204
+ get ondisappear() {
205
+ return close
206
+ },
207
+ })
208
+
209
+ const describedby = useDescriptions()
210
+
211
+ setContext<DialogContext>("DialogContext", {
212
+ get titleId() {
213
+ return _state.titleId
214
+ },
215
+ get panelRef() {
216
+ return _state.panelRef
217
+ },
218
+ get dialogState() {
219
+ return dialogState
220
+ },
221
+ get close() {
222
+ return close
223
+ },
224
+ get unmount() {
225
+ return unmount
226
+ },
227
+ setTitleId,
228
+ })
229
+
230
+ const slot = $derived({ open: dialogState === DialogStates.Open } satisfies DialogRenderPropArg)
231
+
232
+ const ourProps = $derived({
233
+ id,
234
+ role,
235
+ tabIndex: -1,
236
+ "aria-modal": __demoMode ? undefined : dialogState === DialogStates.Open ? true : undefined,
237
+ "aria-labelledby": _state.titleId,
238
+ "aria-describedby": describedby.value,
239
+ unmount,
240
+ })
241
+
242
+ const shouldMoveFocusInside = !useIsTouchDevice().value
243
+ const focusTrapFeatures = $derived.by(() => {
244
+ let focusTrapFeatures = FocusTrapFeatures.None
245
+
246
+ if (enabled && !__demoMode) {
247
+ focusTrapFeatures |= FocusTrapFeatures.RestoreFocus
248
+ focusTrapFeatures |= FocusTrapFeatures.TabLock
249
+
250
+ if (autofocus) {
251
+ focusTrapFeatures |= FocusTrapFeatures.AutoFocus
252
+ }
253
+
254
+ if (shouldMoveFocusInside) {
255
+ focusTrapFeatures |= FocusTrapFeatures.InitialFocus
256
+ }
197
257
  }
198
- }
199
- return focusTrapFeatures2;
200
- });
201
- clearOpenClosedContext();
202
- createCloseContext({
203
- get close() {
204
- return close;
205
- }
206
- });
258
+
259
+ return focusTrapFeatures
260
+ })
261
+
262
+ clearOpenClosedContext()
263
+ createCloseContext({
264
+ get close() {
265
+ return close
266
+ },
267
+ })
207
268
  </script>
208
269
 
209
270
  <ForcePortalRoot force={true}>
@@ -1,31 +1,54 @@
1
- <script lang="ts" module>let DEFAULT_FIELD_TAG = "div";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+
4
+ let DEFAULT_FIELD_TAG = "div" as const
5
+
6
+ type FieldRenderPropArg = {}
7
+ type FieldPropsWeControl = never
8
+
9
+ export type FieldProps<TTag extends ElementType = typeof DEFAULT_FIELD_TAG> = Props<
10
+ TTag,
11
+ FieldRenderPropArg,
12
+ FieldPropsWeControl,
13
+ {
14
+ disabled?: boolean
15
+ }
16
+ >
2
17
  </script>
3
18
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_FIELD_TAG">import { useDisabled } from "../hooks/use-disabled.js";
5
- import { createIdContext } from "../utils/id.js";
6
- import { stateFromSlot } from "../utils/state.js";
7
- import { nanoid } from "nanoid";
8
- import { setContext } from "svelte";
9
- import { useLabels } from "../label/context.svelte.js";
10
- import { useDescriptions } from "../description/context.svelte.js";
11
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
12
- let { ref = $bindable(), disabled: ownDisabled = false, ...theirProps } = $props();
13
- const inputId = `headlessui-control-${nanoid(8)}`;
14
- createIdContext(inputId);
15
- useLabels();
16
- useDescriptions();
17
- const providedDisabled = useDisabled();
18
- const disabled = $derived(providedDisabled.value || ownDisabled);
19
- setContext("DisabledContext", {
20
- get value() {
21
- return disabled;
22
- }
23
- });
24
- const slot = $derived({ disabled });
25
- const ourProps = $derived({
26
- disabled: disabled || void 0,
27
- "aria-disabled": disabled || void 0
28
- });
19
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_FIELD_TAG">
20
+ import { useDisabled } from "../hooks/use-disabled.js"
21
+ import { createIdContext } from "../utils/id.js"
22
+ import { stateFromSlot } from "../utils/state.js"
23
+ import { nanoid } from "nanoid"
24
+ import { setContext, type Snippet } from "svelte"
25
+ import { useLabels } from "../label/context.svelte.js"
26
+ import { useDescriptions } from "../description/context.svelte.js"
27
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
28
+
29
+ let { ref = $bindable(), disabled: ownDisabled = false, ...theirProps }: { as?: TTag } & FieldProps<TTag> = $props()
30
+
31
+ const inputId = `headlessui-control-${nanoid(8)}`
32
+ createIdContext(inputId)
33
+
34
+ useLabels()
35
+ useDescriptions()
36
+
37
+ const providedDisabled = useDisabled()
38
+ const disabled = $derived(providedDisabled.value || ownDisabled)
39
+
40
+ setContext("DisabledContext", {
41
+ get value() {
42
+ return disabled
43
+ },
44
+ })
45
+
46
+ const slot = $derived({ disabled })
47
+
48
+ const ourProps = $derived({
49
+ disabled: disabled || undefined,
50
+ "aria-disabled": disabled || undefined,
51
+ })
29
52
  </script>
30
53
 
31
54
  <ElementOrComponent {ourProps} {theirProps} {slot} defaultTag={DEFAULT_FIELD_TAG} name="Field" bind:ref />
@@ -1,34 +1,55 @@
1
- <script lang="ts" module>let DEFAULT_FIELDSET_TAG = "fieldset";
1
+ <script lang="ts" module>
2
+ import type { ElementType, Props } from "../utils/types.js"
3
+ let DEFAULT_FIELDSET_TAG = "fieldset" as const
4
+
5
+ type FieldsetRenderPropArg = {}
6
+ type FieldsetPropsWeControl = "aria-labelledby" | "aria-disabled" | "role"
7
+
8
+ export type FieldsetProps<TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG> = Props<
9
+ TTag,
10
+ FieldsetRenderPropArg,
11
+ FieldsetPropsWeControl,
12
+ {
13
+ disabled?: boolean
14
+ }
15
+ >
2
16
  </script>
3
17
 
4
- <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG">import { setContext } from "svelte";
5
- import { useDisabled } from "../hooks/use-disabled.js";
6
- import { useLabels } from "../label/context.svelte.js";
7
- import ElementOrComponent from "../utils/ElementOrComponent.svelte";
8
- let {
9
- ref = $bindable(),
10
- disabled: ownDisabled = false,
11
- ...theirProps
12
- } = $props();
13
- const providedDisabled = useDisabled();
14
- const disabled = $derived(providedDisabled.value || ownDisabled);
15
- setContext("DisabledContext", {
16
- get value() {
17
- return disabled;
18
- }
19
- });
20
- const labelledBy = useLabels();
21
- const slot = $derived({ disabled });
22
- const ourProps = $derived(
23
- (theirProps.as ?? DEFAULT_FIELDSET_TAG) === "fieldset" ? {
24
- "aria-labelledby": labelledBy.value,
25
- disabled: disabled || void 0
26
- } : {
27
- role: "group",
28
- "aria-labelledby": labelledBy.value,
29
- "aria-disabled": disabled || void 0
30
- }
31
- );
18
+ <script lang="ts" generics="TTag extends ElementType = typeof DEFAULT_FIELDSET_TAG">
19
+ import { setContext } from "svelte"
20
+ import { useDisabled } from "../hooks/use-disabled.js"
21
+ import { useLabels } from "../label/context.svelte.js"
22
+ import ElementOrComponent from "../utils/ElementOrComponent.svelte"
23
+
24
+ let {
25
+ ref = $bindable(),
26
+ disabled: ownDisabled = false,
27
+ ...theirProps
28
+ }: { as?: TTag } & FieldsetProps<TTag> = $props()
29
+
30
+ const providedDisabled = useDisabled()
31
+ const disabled = $derived(providedDisabled.value || ownDisabled)
32
+
33
+ setContext("DisabledContext", {
34
+ get value() {
35
+ return disabled
36
+ },
37
+ })
38
+
39
+ const labelledBy = useLabels()
40
+ const slot = $derived({ disabled })
41
+ const ourProps = $derived(
42
+ (theirProps.as ?? DEFAULT_FIELDSET_TAG) === "fieldset"
43
+ ? {
44
+ "aria-labelledby": labelledBy.value,
45
+ disabled: disabled || undefined,
46
+ }
47
+ : {
48
+ role: "group",
49
+ "aria-labelledby": labelledBy.value,
50
+ "aria-disabled": disabled || undefined,
51
+ }
52
+ )
32
53
  </script>
33
54
 
34
55
  <ElementOrComponent {ourProps} {theirProps} {slot} defaultTag={DEFAULT_FIELDSET_TAG} name="Fieldset" bind:ref />