@alankrit2/ui 0.1.0
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.
- package/README.md +194 -0
- package/dist/index.d.mts +211 -0
- package/dist/index.d.ts +211 -0
- package/dist/index.js +404 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +373 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +52 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React8 = require('react');
|
|
4
|
+
var ReactDOM = require('react-dom');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
function _interopNamespace(e) {
|
|
8
|
+
if (e && e.__esModule) return e;
|
|
9
|
+
var n = Object.create(null);
|
|
10
|
+
if (e) {
|
|
11
|
+
Object.keys(e).forEach(function (k) {
|
|
12
|
+
if (k !== 'default') {
|
|
13
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: function () { return e[k]; }
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var React8__namespace = /*#__PURE__*/_interopNamespace(React8);
|
|
26
|
+
var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
|
|
27
|
+
|
|
28
|
+
// src/components/dialog/dialog.tsx
|
|
29
|
+
function useControlled({
|
|
30
|
+
value: valueProp,
|
|
31
|
+
defaultValue,
|
|
32
|
+
onChange
|
|
33
|
+
}) {
|
|
34
|
+
const [valueState, setValueState] = React8__namespace.useState(defaultValue);
|
|
35
|
+
const isControlled = valueProp !== void 0;
|
|
36
|
+
const value = isControlled ? valueProp : valueState;
|
|
37
|
+
const setValue = React8__namespace.useCallback(
|
|
38
|
+
(newValue) => {
|
|
39
|
+
if (!isControlled) {
|
|
40
|
+
setValueState(newValue);
|
|
41
|
+
}
|
|
42
|
+
onChange?.(newValue);
|
|
43
|
+
},
|
|
44
|
+
[isControlled, onChange]
|
|
45
|
+
);
|
|
46
|
+
return [value, setValue];
|
|
47
|
+
}
|
|
48
|
+
var FOCUSABLE_SELECTOR = [
|
|
49
|
+
"a[href]",
|
|
50
|
+
"button:not([disabled])",
|
|
51
|
+
"textarea:not([disabled])",
|
|
52
|
+
"input:not([disabled])",
|
|
53
|
+
"select:not([disabled])",
|
|
54
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
55
|
+
].join(",");
|
|
56
|
+
function useFocusTrap(containerRef, enabled = true, autoFocus = true) {
|
|
57
|
+
const previouslyFocusedElement = React8__namespace.useRef(null);
|
|
58
|
+
React8__namespace.useEffect(() => {
|
|
59
|
+
if (!enabled || !containerRef.current) return;
|
|
60
|
+
const container = containerRef.current;
|
|
61
|
+
previouslyFocusedElement.current = document.activeElement;
|
|
62
|
+
const getFocusableElements = () => {
|
|
63
|
+
return Array.from(
|
|
64
|
+
container.querySelectorAll(FOCUSABLE_SELECTOR)
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
if (autoFocus) {
|
|
68
|
+
const focusableElements = getFocusableElements();
|
|
69
|
+
if (focusableElements.length > 0) {
|
|
70
|
+
focusableElements[0].focus();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const handleKeyDown = (event) => {
|
|
74
|
+
if (event.key !== "Tab") return;
|
|
75
|
+
const focusableElements = getFocusableElements();
|
|
76
|
+
if (focusableElements.length === 0) return;
|
|
77
|
+
const firstElement = focusableElements[0];
|
|
78
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
79
|
+
if (event.shiftKey) {
|
|
80
|
+
if (document.activeElement === firstElement) {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
lastElement.focus();
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
if (document.activeElement === lastElement) {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
firstElement.focus();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
container.addEventListener("keydown", handleKeyDown);
|
|
92
|
+
return () => {
|
|
93
|
+
container.removeEventListener("keydown", handleKeyDown);
|
|
94
|
+
if (previouslyFocusedElement.current) {
|
|
95
|
+
previouslyFocusedElement.current.focus();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}, [enabled, autoFocus, containerRef]);
|
|
99
|
+
}
|
|
100
|
+
function useEscapeKeydown(handler, enabled = true) {
|
|
101
|
+
React8__namespace.useEffect(() => {
|
|
102
|
+
if (!enabled) return;
|
|
103
|
+
const handleKeyDown = (event) => {
|
|
104
|
+
if (event.key === "Escape") {
|
|
105
|
+
handler(event);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
109
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
110
|
+
}, [handler, enabled]);
|
|
111
|
+
}
|
|
112
|
+
function useBodyScrollLock(enabled = true) {
|
|
113
|
+
React8__namespace.useEffect(() => {
|
|
114
|
+
if (!enabled) return;
|
|
115
|
+
const originalOverflow = document.body.style.overflow;
|
|
116
|
+
const originalPaddingRight = document.body.style.paddingRight;
|
|
117
|
+
document.body.style.overflow = "hidden";
|
|
118
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
119
|
+
if (scrollbarWidth > 0) {
|
|
120
|
+
document.body.style.paddingRight = `${scrollbarWidth}px`;
|
|
121
|
+
}
|
|
122
|
+
return () => {
|
|
123
|
+
document.body.style.overflow = originalOverflow;
|
|
124
|
+
document.body.style.paddingRight = originalPaddingRight;
|
|
125
|
+
};
|
|
126
|
+
}, [enabled]);
|
|
127
|
+
}
|
|
128
|
+
var count = 0;
|
|
129
|
+
function generateId() {
|
|
130
|
+
return `headless-ui-${++count}`;
|
|
131
|
+
}
|
|
132
|
+
function useStableId(idProp) {
|
|
133
|
+
const [id] = React8__namespace.useState(() => idProp || generateId());
|
|
134
|
+
return idProp || id;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// src/utils/compose-refs.ts
|
|
138
|
+
function composeRefs(...refs) {
|
|
139
|
+
return (node) => {
|
|
140
|
+
refs.forEach((ref) => {
|
|
141
|
+
if (!ref) return;
|
|
142
|
+
if (typeof ref === "function") {
|
|
143
|
+
ref(node);
|
|
144
|
+
} else {
|
|
145
|
+
ref.current = node;
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/primitives/slot.tsx
|
|
152
|
+
var Slot = React8__namespace.forwardRef(
|
|
153
|
+
(props, forwardedRef) => {
|
|
154
|
+
const { children, ...slotProps } = props;
|
|
155
|
+
if (!React8__namespace.isValidElement(children)) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
return React8__namespace.cloneElement(children, {
|
|
159
|
+
...mergeProps(slotProps, children.props),
|
|
160
|
+
ref: forwardedRef ? composeRefs(forwardedRef, children.ref) : children.ref
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
Slot.displayName = "Slot";
|
|
165
|
+
function mergeProps(slotProps, childProps) {
|
|
166
|
+
const merged = { ...childProps };
|
|
167
|
+
for (const key in childProps) {
|
|
168
|
+
const slotValue = slotProps[key];
|
|
169
|
+
const childValue = childProps[key];
|
|
170
|
+
if (/^on[A-Z]/.test(key)) {
|
|
171
|
+
if (slotValue && childValue) {
|
|
172
|
+
merged[key] = (...args) => {
|
|
173
|
+
childValue(...args);
|
|
174
|
+
slotValue(...args);
|
|
175
|
+
};
|
|
176
|
+
} else if (slotValue) {
|
|
177
|
+
merged[key] = slotValue;
|
|
178
|
+
}
|
|
179
|
+
} else if (key === "style") {
|
|
180
|
+
merged[key] = { ...slotValue, ...childValue };
|
|
181
|
+
} else if (key === "className") {
|
|
182
|
+
merged[key] = [slotValue, childValue].filter(Boolean).join(" ");
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
for (const key in slotProps) {
|
|
186
|
+
if (!(key in childProps)) {
|
|
187
|
+
merged[key] = slotProps[key];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return merged;
|
|
191
|
+
}
|
|
192
|
+
var DialogContext = React8__namespace.createContext(
|
|
193
|
+
null
|
|
194
|
+
);
|
|
195
|
+
function useDialogContext(componentName) {
|
|
196
|
+
const context = React8__namespace.useContext(DialogContext);
|
|
197
|
+
if (!context) {
|
|
198
|
+
throw new Error(
|
|
199
|
+
`<${componentName}> must be used within a <Dialog> component.`
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
return context;
|
|
203
|
+
}
|
|
204
|
+
function Dialog({
|
|
205
|
+
open: openProp,
|
|
206
|
+
defaultOpen = false,
|
|
207
|
+
onOpenChange,
|
|
208
|
+
children
|
|
209
|
+
}) {
|
|
210
|
+
const [open, setOpen] = useControlled({
|
|
211
|
+
value: openProp,
|
|
212
|
+
defaultValue: defaultOpen,
|
|
213
|
+
onChange: onOpenChange
|
|
214
|
+
});
|
|
215
|
+
const triggerRef = React8__namespace.useRef(null);
|
|
216
|
+
const contentRef = React8__namespace.useRef(null);
|
|
217
|
+
const titleId = useStableId();
|
|
218
|
+
const descriptionId = useStableId();
|
|
219
|
+
const contextValue = React8__namespace.useMemo(
|
|
220
|
+
() => ({
|
|
221
|
+
open,
|
|
222
|
+
onOpenChange: setOpen,
|
|
223
|
+
triggerRef,
|
|
224
|
+
contentRef,
|
|
225
|
+
titleId,
|
|
226
|
+
descriptionId
|
|
227
|
+
}),
|
|
228
|
+
[open, setOpen, titleId, descriptionId]
|
|
229
|
+
);
|
|
230
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DialogContext.Provider, { value: contextValue, children });
|
|
231
|
+
}
|
|
232
|
+
var DialogTrigger = React8__namespace.forwardRef(
|
|
233
|
+
({ asChild = false, children, onClick, ...props }, forwardedRef) => {
|
|
234
|
+
const { onOpenChange, triggerRef } = useDialogContext("Dialog.Trigger");
|
|
235
|
+
const handleClick = (event) => {
|
|
236
|
+
onClick?.(event);
|
|
237
|
+
onOpenChange(true);
|
|
238
|
+
};
|
|
239
|
+
const Comp = asChild ? Slot : "button";
|
|
240
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
241
|
+
Comp,
|
|
242
|
+
{
|
|
243
|
+
ref: (node) => {
|
|
244
|
+
if (forwardedRef) {
|
|
245
|
+
if (typeof forwardedRef === "function") {
|
|
246
|
+
forwardedRef(node);
|
|
247
|
+
} else {
|
|
248
|
+
forwardedRef.current = node;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
triggerRef.current = node;
|
|
252
|
+
},
|
|
253
|
+
type: asChild ? void 0 : "button",
|
|
254
|
+
onClick: handleClick,
|
|
255
|
+
...props,
|
|
256
|
+
children
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
);
|
|
261
|
+
DialogTrigger.displayName = "Dialog.Trigger";
|
|
262
|
+
function DialogPortal({ container, children }) {
|
|
263
|
+
const { open } = useDialogContext("Dialog.Portal");
|
|
264
|
+
const [mounted, setMounted] = React8__namespace.useState(false);
|
|
265
|
+
React8__namespace.useEffect(() => {
|
|
266
|
+
setMounted(true);
|
|
267
|
+
}, []);
|
|
268
|
+
if (!open || !mounted) return null;
|
|
269
|
+
return ReactDOM__namespace.createPortal(
|
|
270
|
+
children,
|
|
271
|
+
container || document.body
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
var DialogOverlay = React8__namespace.forwardRef(
|
|
275
|
+
({ onClick, ...props }, forwardedRef) => {
|
|
276
|
+
const { onOpenChange } = useDialogContext("Dialog.Overlay");
|
|
277
|
+
const handleClick = (event) => {
|
|
278
|
+
onClick?.(event);
|
|
279
|
+
onOpenChange(false);
|
|
280
|
+
};
|
|
281
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
282
|
+
"div",
|
|
283
|
+
{
|
|
284
|
+
ref: forwardedRef,
|
|
285
|
+
onClick: handleClick,
|
|
286
|
+
style: {
|
|
287
|
+
position: "fixed",
|
|
288
|
+
inset: 0,
|
|
289
|
+
zIndex: 9998
|
|
290
|
+
},
|
|
291
|
+
...props
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
DialogOverlay.displayName = "Dialog.Overlay";
|
|
297
|
+
var DialogContent = React8__namespace.forwardRef(
|
|
298
|
+
({ onEscapeKeyDown, onOverlayClick, children, ...props }, forwardedRef) => {
|
|
299
|
+
const { open, onOpenChange, contentRef, titleId, descriptionId } = useDialogContext("Dialog.Content");
|
|
300
|
+
useFocusTrap(contentRef, open, true);
|
|
301
|
+
useBodyScrollLock(open);
|
|
302
|
+
useEscapeKeydown(
|
|
303
|
+
(event) => {
|
|
304
|
+
onEscapeKeyDown?.(event);
|
|
305
|
+
onOpenChange(false);
|
|
306
|
+
},
|
|
307
|
+
open
|
|
308
|
+
);
|
|
309
|
+
const handleContentClick = (event) => {
|
|
310
|
+
event.stopPropagation();
|
|
311
|
+
};
|
|
312
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
313
|
+
"div",
|
|
314
|
+
{
|
|
315
|
+
ref: (node) => {
|
|
316
|
+
if (forwardedRef) {
|
|
317
|
+
if (typeof forwardedRef === "function") {
|
|
318
|
+
forwardedRef(node);
|
|
319
|
+
} else {
|
|
320
|
+
forwardedRef.current = node;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
contentRef.current = node;
|
|
324
|
+
},
|
|
325
|
+
role: "dialog",
|
|
326
|
+
"aria-modal": "true",
|
|
327
|
+
"aria-labelledby": titleId,
|
|
328
|
+
"aria-describedby": descriptionId,
|
|
329
|
+
onClick: handleContentClick,
|
|
330
|
+
style: {
|
|
331
|
+
position: "fixed",
|
|
332
|
+
top: "50%",
|
|
333
|
+
left: "50%",
|
|
334
|
+
transform: "translate(-50%, -50%)",
|
|
335
|
+
zIndex: 9999
|
|
336
|
+
},
|
|
337
|
+
...props,
|
|
338
|
+
children
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
DialogContent.displayName = "Dialog.Content";
|
|
344
|
+
var DialogTitle = React8__namespace.forwardRef(
|
|
345
|
+
({ children, ...props }, forwardedRef) => {
|
|
346
|
+
const { titleId } = useDialogContext("Dialog.Title");
|
|
347
|
+
return /* @__PURE__ */ jsxRuntime.jsx("h2", { ref: forwardedRef, id: titleId, ...props, children });
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
DialogTitle.displayName = "Dialog.Title";
|
|
351
|
+
var DialogDescription = React8__namespace.forwardRef(({ children, ...props }, forwardedRef) => {
|
|
352
|
+
const { descriptionId } = useDialogContext("Dialog.Description");
|
|
353
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { ref: forwardedRef, id: descriptionId, ...props, children });
|
|
354
|
+
});
|
|
355
|
+
DialogDescription.displayName = "Dialog.Description";
|
|
356
|
+
var DialogClose = React8__namespace.forwardRef(
|
|
357
|
+
({ asChild = false, children, onClick, ...props }, forwardedRef) => {
|
|
358
|
+
const { onOpenChange } = useDialogContext("Dialog.Close");
|
|
359
|
+
const handleClick = (event) => {
|
|
360
|
+
onClick?.(event);
|
|
361
|
+
onOpenChange(false);
|
|
362
|
+
};
|
|
363
|
+
const Comp = asChild ? Slot : "button";
|
|
364
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
365
|
+
Comp,
|
|
366
|
+
{
|
|
367
|
+
ref: forwardedRef,
|
|
368
|
+
type: asChild ? void 0 : "button",
|
|
369
|
+
onClick: handleClick,
|
|
370
|
+
...props,
|
|
371
|
+
children
|
|
372
|
+
}
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
DialogClose.displayName = "Dialog.Close";
|
|
377
|
+
Dialog.Trigger = DialogTrigger;
|
|
378
|
+
Dialog.Portal = DialogPortal;
|
|
379
|
+
Dialog.Overlay = DialogOverlay;
|
|
380
|
+
Dialog.Content = DialogContent;
|
|
381
|
+
Dialog.Title = DialogTitle;
|
|
382
|
+
Dialog.Description = DialogDescription;
|
|
383
|
+
Dialog.Close = DialogClose;
|
|
384
|
+
var dialog_default = Dialog;
|
|
385
|
+
var Button = React8__namespace.forwardRef(
|
|
386
|
+
({ asChild = false, ...props }, ref) => {
|
|
387
|
+
const Comp = asChild ? Slot : "button";
|
|
388
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Comp, { ref, ...props });
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
Button.displayName = "Button";
|
|
392
|
+
var button_default = Button;
|
|
393
|
+
|
|
394
|
+
exports.Button = button_default;
|
|
395
|
+
exports.Dialog = dialog_default;
|
|
396
|
+
exports.Slot = Slot;
|
|
397
|
+
exports.composeRefs = composeRefs;
|
|
398
|
+
exports.useBodyScrollLock = useBodyScrollLock;
|
|
399
|
+
exports.useControlled = useControlled;
|
|
400
|
+
exports.useEscapeKeydown = useEscapeKeydown;
|
|
401
|
+
exports.useFocusTrap = useFocusTrap;
|
|
402
|
+
exports.useStableId = useStableId;
|
|
403
|
+
//# sourceMappingURL=index.js.map
|
|
404
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-controlled.ts","../src/hooks/use-focus-trap.ts","../src/hooks/use-escape-keydown.ts","../src/hooks/use-body-scroll-lock.ts","../src/hooks/use-id.ts","../src/utils/compose-refs.ts","../src/primitives/slot.tsx","../src/components/dialog/dialog-context.ts","../src/components/dialog/dialog.tsx","../src/components/button/button.tsx"],"names":["React","React2","React3","React4","React5","React6","React7","React8","jsx","ReactDOM","React9"],"mappings":"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","file":"index.js","sourcesContent":["import * as React from 'react';\n\ninterface UseControlledOptions<T> {\n /** Controlled value */\n value?: T;\n /** Default value for uncontrolled mode */\n defaultValue?: T;\n /** Change handler */\n onChange?: (value: T) => void;\n}\n\n/**\n * Hook for managing controlled/uncontrolled state pattern.\n * Supports both controlled (value + onChange) and uncontrolled (defaultValue) modes.\n * \n * @example\n * // Controlled\n * const [value, setValue] = useControlled({ value: externalValue, onChange: setExternalValue });\n * \n * // Uncontrolled\n * const [value, setValue] = useControlled({ defaultValue: false });\n */\nexport function useControlled<T>({\n value: valueProp,\n defaultValue,\n onChange,\n}: UseControlledOptions<T>): [T, (newValue: T) => void] {\n const [valueState, setValueState] = React.useState<T>(defaultValue as T);\n const isControlled = valueProp !== undefined;\n\n const value = isControlled ? valueProp : valueState;\n\n const setValue = React.useCallback(\n (newValue: T) => {\n if (!isControlled) {\n setValueState(newValue);\n }\n onChange?.(newValue);\n },\n [isControlled, onChange]\n );\n\n return [value as T, setValue];\n}\n","import * as React from 'react';\n\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(',');\n\n/**\n * Hook for trapping focus within a container.\n * Handles Tab/Shift+Tab cycling and prevents focus escape.\n * \n * @param containerRef - Ref to the container element\n * @param enabled - Whether the focus trap is active\n * @param autoFocus - Whether to auto-focus the first element on mount\n */\nexport function useFocusTrap(\n containerRef: React.RefObject<HTMLElement | null>,\n enabled = true,\n autoFocus = true\n) {\n const previouslyFocusedElement = React.useRef<HTMLElement | null>(null);\n\n React.useEffect(() => {\n if (!enabled || !containerRef.current) return;\n\n const container = containerRef.current;\n\n // Store the previously focused element\n previouslyFocusedElement.current = document.activeElement as HTMLElement;\n\n // Get all focusable elements\n const getFocusableElements = (): HTMLElement[] => {\n return Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)\n );\n };\n\n // Auto-focus first element if enabled\n if (autoFocus) {\n const focusableElements = getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n }\n }\n\n // Handle Tab key to cycle focus\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Tab') return;\n\n const focusableElements = getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (event.shiftKey) {\n // Shift + Tab: move focus to last element if on first\n if (document.activeElement === firstElement) {\n event.preventDefault();\n lastElement.focus();\n }\n } else {\n // Tab: move focus to first element if on last\n if (document.activeElement === lastElement) {\n event.preventDefault();\n firstElement.focus();\n }\n }\n };\n\n container.addEventListener('keydown', handleKeyDown);\n\n return () => {\n container.removeEventListener('keydown', handleKeyDown);\n\n // Restore focus to previously focused element\n if (previouslyFocusedElement.current) {\n previouslyFocusedElement.current.focus();\n }\n };\n }, [enabled, autoFocus, containerRef]);\n}\n","import * as React from 'react';\n\n/**\n * Hook for handling Escape key press events.\n * Automatically cleans up event listener on unmount.\n */\nexport function useEscapeKeydown(\n handler: (event: KeyboardEvent) => void,\n enabled = true\n) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n handler(event);\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [handler, enabled]);\n}\n","import * as React from 'react';\n\n/**\n * Hook for preventing body scroll when a modal/dialog is open.\n * This is an accessibility requirement to prevent background scrolling.\n */\nexport function useBodyScrollLock(enabled = true) {\n React.useEffect(() => {\n if (!enabled) return;\n\n const originalOverflow = document.body.style.overflow;\n const originalPaddingRight = document.body.style.paddingRight;\n\n // Prevent scroll\n document.body.style.overflow = 'hidden';\n\n // Prevent layout shift by adding padding to compensate for scrollbar\n const scrollbarWidth =\n window.innerWidth - document.documentElement.clientWidth;\n if (scrollbarWidth > 0) {\n document.body.style.paddingRight = `${scrollbarWidth}px`;\n }\n\n return () => {\n document.body.style.overflow = originalOverflow;\n document.body.style.paddingRight = originalPaddingRight;\n };\n }, [enabled]);\n}\n","import * as React from 'react';\n\nlet count = 0;\nfunction generateId() {\n return `headless-ui-${++count}`;\n}\n\n/**\n * Hook for generating stable, unique IDs.\n * Uses React's useId when available, with a fallback for older versions.\n */\nexport function useStableId(idProp?: string): string {\n const [id] = React.useState(() => idProp || generateId());\n return idProp || id;\n}\n","/**\n * Composes multiple refs into a single ref callback.\n * Useful when you need to forward a ref while also using it internally.\n */\nexport function composeRefs<T>(...refs: Array<React.Ref<T> | undefined>) {\n return (node: T | null) => {\n refs.forEach((ref) => {\n if (!ref) return;\n\n if (typeof ref === 'function') {\n ref(node);\n } else {\n (ref as React.MutableRefObject<T | null>).current = node;\n }\n });\n };\n}\n","import * as React from 'react';\nimport { composeRefs } from '../utils/compose-refs';\n\ninterface SlotProps extends React.HTMLAttributes<HTMLElement> {\n children?: React.ReactNode;\n}\n\n/**\n * Slot component for the asChild pattern.\n * When asChild is true, merges props with the child element instead of rendering a wrapper.\n */\nexport const Slot = React.forwardRef<HTMLElement, SlotProps>(\n (props, forwardedRef) => {\n const { children, ...slotProps } = props;\n\n if (!React.isValidElement(children)) {\n return null;\n }\n\n return React.cloneElement(children, {\n ...mergeProps(slotProps, children.props),\n ref: forwardedRef\n ? composeRefs(forwardedRef, (children as any).ref)\n : (children as any).ref,\n });\n }\n);\n\nSlot.displayName = 'Slot';\n\n/**\n * Merges props from the Slot with props from the child element.\n * Event handlers are composed, other props from child take precedence.\n */\nfunction mergeProps(slotProps: any, childProps: any) {\n const merged = { ...childProps };\n\n for (const key in childProps) {\n const slotValue = slotProps[key];\n const childValue = childProps[key];\n\n // Compose event handlers\n if (/^on[A-Z]/.test(key)) {\n if (slotValue && childValue) {\n merged[key] = (...args: any[]) => {\n childValue(...args);\n slotValue(...args);\n };\n } else if (slotValue) {\n merged[key] = slotValue;\n }\n }\n // Child props take precedence for non-event handlers\n else if (key === 'style') {\n merged[key] = { ...slotValue, ...childValue };\n } else if (key === 'className') {\n merged[key] = [slotValue, childValue].filter(Boolean).join(' ');\n }\n }\n\n // Add slot props that don't exist in child\n for (const key in slotProps) {\n if (!(key in childProps)) {\n merged[key] = slotProps[key];\n }\n }\n\n return merged;\n}\n","import * as React from 'react';\n\nexport interface DialogContextValue {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n triggerRef: React.RefObject<HTMLElement | null>;\n contentRef: React.RefObject<HTMLElement | null>;\n titleId: string;\n descriptionId: string;\n}\n\nexport const DialogContext = React.createContext<DialogContextValue | null>(\n null\n);\n\n/**\n * Hook to access Dialog context.\n * Throws an error if used outside of a Dialog component.\n */\nexport function useDialogContext(componentName: string): DialogContextValue {\n const context = React.useContext(DialogContext);\n\n if (!context) {\n throw new Error(\n `<${componentName}> must be used within a <Dialog> component.`\n );\n }\n\n return context;\n}\n","import * as React from 'react';\nimport * as ReactDOM from 'react-dom';\nimport { useControlled } from '../../hooks/use-controlled';\nimport { useFocusTrap } from '../../hooks/use-focus-trap';\nimport { useEscapeKeydown } from '../../hooks/use-escape-keydown';\nimport { useBodyScrollLock } from '../../hooks/use-body-scroll-lock';\nimport { useStableId } from '../../hooks/use-id';\nimport { Slot } from '../../primitives/slot';\nimport { DialogContext, useDialogContext } from './dialog-context';\nimport type {\n DialogProps,\n DialogTriggerProps,\n DialogPortalProps,\n DialogOverlayProps,\n DialogContentProps,\n DialogTitleProps,\n DialogDescriptionProps,\n DialogCloseProps,\n} from './types';\n\n/**\n * Dialog Root Component\n * \n * Provides context for all Dialog sub-components.\n * Supports both controlled and uncontrolled state.\n * \n * @example\n * // Controlled\n * <Dialog open={open} onOpenChange={setOpen}>\n * ...\n * </Dialog>\n * \n * // Uncontrolled\n * <Dialog defaultOpen={false}>\n * ...\n * </Dialog>\n */\nexport function Dialog({\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n children,\n}: DialogProps) {\n const [open, setOpen] = useControlled({\n value: openProp,\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n });\n\n const triggerRef = React.useRef<HTMLElement>(null);\n const contentRef = React.useRef<HTMLElement>(null);\n const titleId = useStableId();\n const descriptionId = useStableId();\n\n const contextValue = React.useMemo(\n () => ({\n open,\n onOpenChange: setOpen,\n triggerRef,\n contentRef,\n titleId,\n descriptionId,\n }),\n [open, setOpen, titleId, descriptionId]\n );\n\n return (\n <DialogContext.Provider value={contextValue}>\n {children}\n </DialogContext.Provider>\n );\n}\n\n/**\n * Dialog Trigger Component\n * \n * Button that opens the dialog when clicked.\n * Supports asChild pattern for custom elements.\n * \n * @example\n * <Dialog.Trigger>Open Dialog</Dialog.Trigger>\n * \n * // With asChild\n * <Dialog.Trigger asChild>\n * <button className=\"custom-button\">Open</button>\n * </Dialog.Trigger>\n */\nconst DialogTrigger = React.forwardRef<HTMLButtonElement, DialogTriggerProps>(\n ({ asChild = false, children, onClick, ...props }, forwardedRef) => {\n const { onOpenChange, triggerRef } = useDialogContext('Dialog.Trigger');\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n onOpenChange(true);\n };\n\n const Comp = asChild ? Slot : 'button';\n\n return (\n <Comp\n ref={(node: any) => {\n if (forwardedRef) {\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else {\n forwardedRef.current = node;\n }\n }\n (triggerRef as React.MutableRefObject<HTMLElement | null>).current =\n node;\n }}\n type={asChild ? undefined : 'button'}\n onClick={handleClick}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n);\n\nDialogTrigger.displayName = 'Dialog.Trigger';\n\n/**\n * Dialog Portal Component\n * \n * Renders children in a portal outside the DOM hierarchy.\n * Only renders when dialog is open.\n * \n * @example\n * <Dialog.Portal>\n * <Dialog.Overlay />\n * <Dialog.Content>...</Dialog.Content>\n * </Dialog.Portal>\n */\nfunction DialogPortal({ container, children }: DialogPortalProps) {\n const { open } = useDialogContext('Dialog.Portal');\n const [mounted, setMounted] = React.useState(false);\n\n React.useEffect(() => {\n setMounted(true);\n }, []);\n\n if (!open || !mounted) return null;\n\n return ReactDOM.createPortal(\n children,\n container || document.body\n );\n}\n\n/**\n * Dialog Overlay Component\n * \n * Semi-transparent backdrop behind the dialog.\n * Clicking it closes the dialog.\n * \n * @example\n * <Dialog.Overlay style={{ backgroundColor: 'rgba(0, 0, 0, 0.5)' }} />\n */\nconst DialogOverlay = React.forwardRef<HTMLDivElement, DialogOverlayProps>(\n ({ onClick, ...props }, forwardedRef) => {\n const { onOpenChange } = useDialogContext('Dialog.Overlay');\n\n const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {\n onClick?.(event);\n onOpenChange(false);\n };\n\n return (\n <div\n ref={forwardedRef}\n onClick={handleClick}\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 9998,\n }}\n {...props}\n />\n );\n }\n);\n\nDialogOverlay.displayName = 'Dialog.Overlay';\n\n/**\n * Dialog Content Component\n * \n * Main container for dialog content.\n * Implements focus trap, keyboard handling, and ARIA attributes.\n * \n * @example\n * <Dialog.Content>\n * <Dialog.Title>Title</Dialog.Title>\n * <Dialog.Description>Description</Dialog.Description>\n * <Dialog.Close>Close</Dialog.Close>\n * </Dialog.Content>\n */\nconst DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(\n (\n { onEscapeKeyDown, onOverlayClick, children, ...props },\n forwardedRef\n ) => {\n const { open, onOpenChange, contentRef, titleId, descriptionId } =\n useDialogContext('Dialog.Content');\n\n // Focus trap\n useFocusTrap(contentRef, open, true);\n\n // Body scroll lock\n useBodyScrollLock(open);\n\n // Escape key handling\n useEscapeKeydown(\n (event) => {\n onEscapeKeyDown?.(event);\n onOpenChange(false);\n },\n open\n );\n\n // Prevent clicks inside content from closing dialog\n const handleContentClick = (event: React.MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n };\n\n return (\n <div\n ref={(node) => {\n if (forwardedRef) {\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else {\n forwardedRef.current = node;\n }\n }\n (contentRef as React.MutableRefObject<HTMLDivElement | null>).current =\n node;\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={descriptionId}\n onClick={handleContentClick}\n style={{\n position: 'fixed',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n zIndex: 9999,\n }}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nDialogContent.displayName = 'Dialog.Content';\n\n/**\n * Dialog Title Component\n * \n * Title for the dialog, linked via aria-labelledby.\n * \n * @example\n * <Dialog.Title>Confirm Action</Dialog.Title>\n */\nconst DialogTitle = React.forwardRef<HTMLHeadingElement, DialogTitleProps>(\n ({ children, ...props }, forwardedRef) => {\n const { titleId } = useDialogContext('Dialog.Title');\n\n return (\n <h2 ref={forwardedRef} id={titleId} {...props}>\n {children}\n </h2>\n );\n }\n);\n\nDialogTitle.displayName = 'Dialog.Title';\n\n/**\n * Dialog Description Component\n * \n * Description for the dialog, linked via aria-describedby.\n * \n * @example\n * <Dialog.Description>This action cannot be undone.</Dialog.Description>\n */\nconst DialogDescription = React.forwardRef<\n HTMLParagraphElement,\n DialogDescriptionProps\n>(({ children, ...props }, forwardedRef) => {\n const { descriptionId } = useDialogContext('Dialog.Description');\n\n return (\n <p ref={forwardedRef} id={descriptionId} {...props}>\n {children}\n </p>\n );\n});\n\nDialogDescription.displayName = 'Dialog.Description';\n\n/**\n * Dialog Close Component\n * \n * Button that closes the dialog when clicked.\n * Supports asChild pattern for custom elements.\n * \n * @example\n * <Dialog.Close>Cancel</Dialog.Close>\n * \n * // With asChild\n * <Dialog.Close asChild>\n * <button className=\"custom-button\">Close</button>\n * </Dialog.Close>\n */\nconst DialogClose = React.forwardRef<HTMLButtonElement, DialogCloseProps>(\n ({ asChild = false, children, onClick, ...props }, forwardedRef) => {\n const { onOpenChange } = useDialogContext('Dialog.Close');\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n onOpenChange(false);\n };\n\n const Comp = asChild ? Slot : 'button';\n\n return (\n <Comp\n ref={forwardedRef}\n type={asChild ? undefined : 'button'}\n onClick={handleClick}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n);\n\nDialogClose.displayName = 'Dialog.Close';\n\n// Attach sub-components to Dialog\nDialog.Trigger = DialogTrigger;\nDialog.Portal = DialogPortal;\nDialog.Overlay = DialogOverlay;\nDialog.Content = DialogContent;\nDialog.Title = DialogTitle;\nDialog.Description = DialogDescription;\nDialog.Close = DialogClose;\n\n// Type augmentation for compound components\nexport interface DialogComponent extends React.FC<DialogProps> {\n Trigger: typeof DialogTrigger;\n Portal: typeof DialogPortal;\n Overlay: typeof DialogOverlay;\n Content: typeof DialogContent;\n Title: typeof DialogTitle;\n Description: typeof DialogDescription;\n Close: typeof DialogClose;\n}\n\nexport default Dialog as DialogComponent;\n","import * as React from 'react';\nimport { Slot } from '../../primitives/slot';\nimport { ButtonProps } from './types';\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : 'button';\n return <Comp ref={ref} {...props} />;\n }\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n"]}
|