Components
Dialogs
Dialogs in @wayvo-ai/core use the Popup component, which provides resizable and movable dialogs perfect for forms.
Popup Component
Popup is a resizable, movable dialog component:
import { Popup, showSuccess, showError } from '@wayvo-ai/core/ui';
<Popup
title="Edit Entity"
onClose={handleClose}
width={520}
height={430}
footer={
<>
<Button variant="outline" onClick={handleClose}>Cancel</Button>
<Button onClick={handleSave}>Save</Button>
</>
}
>
{/* Form content */}
</Popup>
Complete Dialog Pattern
'use client';
import { useCallback, useState } from 'react';
import { Popup, useCurrentRowSync, useIsStoreDirty, useIsStorePosting, showSuccess, showError } from '@wayvo-ai/core/ui';
import type { Entity } from '@/lib/common/ds/types/module/Entity';
import type { Store } from '@wayvo-ai/core/common';
import { TextInput } from '@wayvo-ai/core/ui';
interface Props {
open: boolean;
onOpenChange: (open: boolean) => void;
store: Store<Entity>;
editingRow?: Entity | null;
}
export function EntityDialog({ open, onOpenChange, store, editingRow }: Props) {
const row = useCurrentRowSync(store);
const isDirty = useIsStoreDirty(store);
const isPosting = useIsStorePosting(store);
const handleSave = async () => {
try {
const success = await store.save();
if (success) {
showSuccess(editingRow ? 'Entity updated' : 'Entity created');
onOpenChange(false);
}
} catch {
showError('Failed to save');
}
};
const handleClose = () => {
store.resetStore(); // Discard unsaved changes
onOpenChange(false);
};
if (!open) return null;
return (
<Popup
title={editingRow ? 'Edit Entity' : 'Add Entity'}
onClose={handleClose}
width={520}
height={430}
footer={
<>
<Button variant="outline" onClick={handleClose} disabled={isPosting}>
Cancel
</Button>
<Button onClick={handleSave} disabled={isPosting || !isDirty || !row}>
{isPosting && <Loader2 className="mr-2 size-4 animate-spin" />}
Save
</Button>
</>
}
>
{row && (
<div className="grid gap-4">
<TextInput
label="Name"
value={row.name || ''}
onChange={(value) => store.setValue('name', value)}
required
/>
<TextInput
label="Description"
value={row.description || ''}
onChange={(value) => store.setValue('description', value)}
/>
</div>
)}
</Popup>
);
}
Dialog Initialization
IMPORTANT: Initialize the store BEFORE opening the dialog, not in useEffect:
// ✅ Correct - Initialize in click handler
const handleAdd = useCallback(() => {
store.createNew({
partialRecord: { status: 'draft' },
});
setIsDialogOpen(true);
}, [store]);
// ❌ Wrong - Causes timing issues
useEffect(() => {
if (open) {
store.createNew({ partialRecord: { status: 'draft' } });
}
}, [open, store]);
Popup Sizing Guidelines
| Form Size | Dimensions | Use Case |
|---|---|---|
| Small (3-4 fields) | ~520x430 | Simple forms |
| Medium (5-6 fields) | ~520x530 | Standard forms |
| Large (7+ fields) | ~560x580+ | Complex forms |
| Settings dialogs | ~720x850 | Multi-section dialogs |
Popup Props
| Prop | Type | Description |
|---|---|---|
title | string | Dialog title (required) |
description | string | Optional subtitle |
onClose | () => void | Close handler (required) |
footer | ReactNode | Footer content (typically buttons) |
width | number | Initial width in pixels |
height | number | Initial height in pixels |
minWidth | number | Minimum resize width |
minHeight | number | Minimum resize height |
resizable | boolean | Enable resize handle (default: true) |
Usage in Parent Component
function ParentComponent() {
const store = useEntityDialogStore();
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [editingRow, setEditingRow] = useState<Entity | null>(null);
const handleAdd = useCallback(() => {
store.createNew({ partialRecord: { status: 'draft' } });
setEditingRow(null);
setIsDialogOpen(true);
}, [store]);
const handleEdit = useCallback((row: Entity) => {
store.setCurrentRow(row);
setEditingRow(row);
setIsDialogOpen(true);
}, [store]);
return (
<>
<Button onClick={handleAdd}>Add Entity</Button>
<EntityDialog
open={isDialogOpen}
onOpenChange={setIsDialogOpen}
store={store}
editingRow={editingRow}
/>
</>
);
}
Next Steps
- Forms - Form input components
- CRUD Operations - Complete CRUD patterns