@dialogkit/core
v0.0.2
Published
Core std ui components
Readme
@dialogkit/core
Core utilities for building overlay-based UI components like dialogs, toasts, side sheets, and more.
Installation
pnpm add @dialogkit/coreFeatures
- 🎯 Simple API - Create overlay components with a single function call
- 🔒 Type-safe - Full TypeScript support with generic props and results
- 🎨 Customizable - Configure backdrop, positioning, and behavior
- ⚡ Promise-based - Async/await support for user interactions
- 🔧 Flexible - Build any overlay-based component (dialogs, toasts, sheets, etc.)
- 🚫 Body Scroll Lock - Automatic scroll locking when overlays are open
- 🎯 Outside Click Handling - Configurable outside-click-to-close behavior
Core API
createOverlay<TProps, TResult>(config)
Creates a reusable overlay hook with typed props and results.
Parameters
interface CreateOverlayConfig<TProps, TResult> {
/** Render function that receives controller props */
render: (controller: OverlayController<TProps, TResult>) => React.ReactNode;
/** Backdrop configuration. Set to false to render without backdrop (optional) */
backdrop?: BackdropConfig | false;
/** Close overlay when clicking outside (default: true, ignored if backdrop is false) */
closeOnOutsideClick?: boolean;
/** Lock body scroll when overlay is open (default: true) */
lockBodyScroll?: boolean;
}Returns
A React hook that returns:
interface UseOverlayHook<TProps, TResult> {
/** Opens the overlay with the given props and returns a promise */
open: (props: TProps) => Promise<OverlayResult<TResult>>;
/** Closes the overlay (cancels) */
close: () => void;
/** Whether the overlay is currently open */
isOpen: boolean;
}Examples
Basic Confirm Dialog
import { createOverlay } from '@dialogkit/core';
const useConfirmDialog = createOverlay<{ message: string }, boolean>({
render: ({ props, submit, cancel }) => (
<div
style={{
background: 'white',
padding: '20px',
borderRadius: '8px',
}}
>
<h2>Confirm</h2>
<p>{props.message}</p>
<button onClick={() => submit(true)}>Yes</button>
<button onClick={() => cancel()}>No</button>
</div>
),
});
// Usage in component:
function MyComponent() {
const { open } = useConfirmDialog();
const handleDelete = async () => {
const result = await open({ message: 'Are you sure?' });
if (result.success && result.data) {
// User confirmed
console.log('Deleting...');
}
};
return <button onClick={handleDelete}>Delete</button>;
}Toast Notification
import { createOverlay } from '@dialogkit/core';
const useToast = createOverlay<{ message: string; type: 'success' | 'error' }, void>({
render: ({ props, cancel }) => {
// Auto-close after 3 seconds
setTimeout(cancel, 3000);
return (
<div
style={{
position: 'fixed',
top: '20px',
left: '50%',
transform: 'translateX(-50%)',
zIndex: 1000,
}}
>
<div
style={{
background: props.type === 'success' ? '#4caf50' : '#f44336',
color: 'white',
padding: '16px',
borderRadius: '4px',
}}
>
{props.message}
</div>
</div>
);
},
backdrop: false, // No backdrop needed for toasts
lockBodyScroll: false,
});
// Usage:
function MyComponent() {
const { open } = useToast();
const showSuccess = () => {
open({ message: 'Success!', type: 'success' });
};
return <button onClick={showSuccess}>Save</button>;
}Side Sheet
import { createOverlay } from '@dialogkit/core';
const useSideSheet = createOverlay<{ title: string }, void>({
render: ({ props, cancel }) => (
<div
style={{
background: 'white',
height: '100%',
width: '400px',
marginLeft: 'auto',
padding: '20px',
boxShadow: '-2px 0 8px rgba(0,0,0,0.1)',
}}
>
<h2>{props.title}</h2>
<button onClick={cancel}>Close</button>
{/* Sheet content */}
</div>
),
backdrop: {
backgroundColor: 'rgba(0, 0, 0, 0.3)',
alignItems: 'stretch',
justifyContent: 'flex-end',
},
});
// Usage:
function MyComponent() {
const { open } = useSideSheet();
return <button onClick={() => open({ title: 'Settings' })}>Open Settings</button>;
}Modal with Form
import { createOverlay } from '@dialogkit/core';
import { useState } from 'react';
interface FormData {
name: string;
email: string;
}
const useFormModal = createOverlay<{ title: string }, FormData>({
render: ({ props, submit, cancel }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
submit({ name, email });
};
return (
<div
style={{
background: 'white',
padding: '24px',
borderRadius: '8px',
minWidth: '400px',
}}
>
<h2>{props.title}</h2>
<form onSubmit={handleSubmit}>
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" />
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
type="email"
/>
<button type="submit">Submit</button>
<button type="button" onClick={cancel}>
Cancel
</button>
</form>
</div>
);
},
});
// Usage:
function MyComponent() {
const { open } = useFormModal();
const handleOpenForm = async () => {
const result = await open({ title: 'User Information' });
if (result.success && result.data) {
console.log('Form data:', result.data);
}
};
return <button onClick={handleOpenForm}>Add User</button>;
}Backdrop Configuration
Customize the overlay backdrop appearance and behavior:
interface BackdropConfig {
/** Background color (default: rgba(0, 0, 0, 0.1)) */
backgroundColor?: string;
/** Backdrop filter (default: blur(1px)) */
backdropFilter?: string;
/** Z-index (default: 1000) */
zIndex?: number;
/** Display positioning (default: flex) */
display?: 'flex' | 'block';
/** Justify content (default: center) */
justifyContent?: 'center' | 'flex-start' | 'flex-end' | 'space-between';
/** Align items (default: center) */
alignItems?: 'center' | 'flex-start' | 'flex-end' | 'stretch';
/** Custom styles to override defaults */
customStyles?: React.CSSProperties;
}TypeScript Support
The library is written in TypeScript and provides full type safety:
// Define your prop and result types
interface MyDialogProps {
title: string;
message: string;
}
interface MyDialogResult {
action: 'confirm' | 'cancel';
data?: string;
}
// Create typed hook
const useMyDialog = createOverlay<MyDialogProps, MyDialogResult>({
render: ({ props, submit, cancel }) => {
// props is typed as MyDialogProps
// submit expects MyDialogResult
// cancel returns void
return <div>{/* ... */}</div>;
},
});
// Usage with type inference
const { open } = useMyDialog();
const result = await open({ title: 'Hello', message: 'World' });
// result is typed as OverlayResult<MyDialogResult>License
MIT
