317 lines
7.6 KiB
Markdown
317 lines
7.6 KiB
Markdown
# Oranguru Integration Guide
|
|
|
|
## Overview
|
|
|
|
Oranguru is a React component library that provides enhanced Mantine-based components with advanced features and state management capabilities. For WhatsHooked, we'll use it to build data grids and forms for the admin interface.
|
|
|
|
## Installation
|
|
|
|
```bash
|
|
npm install @warkypublic/oranguru
|
|
```
|
|
|
|
### Peer Dependencies
|
|
|
|
```bash
|
|
npm install react zustand @mantine/core @mantine/hooks @warkypublic/artemis-kit @warkypublic/zustandsyncstore use-sync-external-store
|
|
```
|
|
|
|
## Core Concepts
|
|
|
|
### Enhanced Context Menus
|
|
Oranguru provides better menu positioning and visibility control than standard Mantine menus.
|
|
|
|
### Custom Rendering
|
|
Support for custom menu item renderers and complete menu rendering.
|
|
|
|
### State Management
|
|
Uses Zustand for component state management with sync capabilities.
|
|
|
|
## Basic Setup
|
|
|
|
```tsx
|
|
import { MantineBetterMenusProvider } from '@warkypublic/oranguru';
|
|
import { MantineProvider } from '@mantine/core';
|
|
|
|
function App() {
|
|
return (
|
|
<MantineProvider>
|
|
<MantineBetterMenusProvider>
|
|
{/* Your app content */}
|
|
</MantineBetterMenusProvider>
|
|
</MantineProvider>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Using Context Menus
|
|
|
|
```tsx
|
|
import { useMantineBetterMenus } from '@warkypublic/oranguru';
|
|
|
|
function DataGrid() {
|
|
const { show, hide } = useMantineBetterMenus();
|
|
|
|
const handleContextMenu = (e: React.MouseEvent, record: any) => {
|
|
e.preventDefault();
|
|
show('record-menu', {
|
|
x: e.clientX,
|
|
y: e.clientY,
|
|
items: [
|
|
{
|
|
label: 'Edit',
|
|
onClick: () => handleEdit(record)
|
|
},
|
|
{
|
|
label: 'Delete',
|
|
onClick: () => handleDelete(record)
|
|
},
|
|
{
|
|
isDivider: true
|
|
},
|
|
{
|
|
label: 'View Details',
|
|
onClick: () => handleViewDetails(record)
|
|
}
|
|
]
|
|
});
|
|
};
|
|
|
|
return (
|
|
<table>
|
|
{records.map(record => (
|
|
<tr key={record.id} onContextMenu={(e) => handleContextMenu(e, record)}>
|
|
<td>{record.name}</td>
|
|
</tr>
|
|
))}
|
|
</table>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Async Actions
|
|
|
|
```tsx
|
|
const asyncMenuItem = {
|
|
label: 'Sync Data',
|
|
onClickAsync: async () => {
|
|
await fetch('/api/sync', { method: 'POST' });
|
|
// Shows loading state automatically
|
|
}
|
|
};
|
|
```
|
|
|
|
## Custom Menu Items
|
|
|
|
```tsx
|
|
const customItem = {
|
|
renderer: ({ loading }: any) => (
|
|
<div style={{ padding: '8px 12px' }}>
|
|
{loading ? (
|
|
<Loader size="xs" />
|
|
) : (
|
|
<Group>
|
|
<IconCheck size={16} />
|
|
<Text>Custom Action</Text>
|
|
</Group>
|
|
)}
|
|
</div>
|
|
),
|
|
onClickAsync: async () => {
|
|
await performAction();
|
|
}
|
|
};
|
|
```
|
|
|
|
## Integration with Data Grids
|
|
|
|
While Oranguru doesn't provide a built-in data grid component yet, it works excellently with Mantine's DataTable or custom table implementations:
|
|
|
|
```tsx
|
|
import { useMantineBetterMenus } from '@warkypublic/oranguru';
|
|
import { DataTable } from '@mantine/datatable';
|
|
|
|
function UserGrid() {
|
|
const { show } = useMantineBetterMenus();
|
|
const [users, setUsers] = useState([]);
|
|
|
|
const columns = [
|
|
{ accessor: 'name', title: 'Name' },
|
|
{ accessor: 'email', title: 'Email' },
|
|
{ accessor: 'status', title: 'Status' }
|
|
];
|
|
|
|
const handleRowContextMenu = (e: React.MouseEvent, user: User) => {
|
|
e.preventDefault();
|
|
show('user-menu', {
|
|
x: e.clientX,
|
|
y: e.clientY,
|
|
items: [
|
|
{
|
|
label: 'Edit User',
|
|
onClick: () => navigate(`/users/${user.id}/edit`)
|
|
},
|
|
{
|
|
label: 'Deactivate',
|
|
onClickAsync: async () => {
|
|
await fetch(`/api/users/${user.id}/deactivate`, { method: 'POST' });
|
|
await refreshUsers();
|
|
}
|
|
},
|
|
{
|
|
isDivider: true
|
|
},
|
|
{
|
|
label: 'Delete',
|
|
onClick: () => handleDelete(user.id)
|
|
}
|
|
]
|
|
});
|
|
};
|
|
|
|
return (
|
|
<DataTable
|
|
columns={columns}
|
|
records={users}
|
|
onRowContextMenu={({ event, record }) => handleRowContextMenu(event, record)}
|
|
/>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Form Integration
|
|
|
|
For forms, use Mantine's form components with Oranguru's menu system for enhanced UX:
|
|
|
|
```tsx
|
|
import { useForm } from '@mantine/form';
|
|
import { TextInput, Select, Button } from '@mantine/core';
|
|
|
|
function UserForm() {
|
|
const form = useForm({
|
|
initialValues: {
|
|
name: '',
|
|
email: '',
|
|
role: ''
|
|
},
|
|
validate: {
|
|
email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'),
|
|
name: (value) => (value.length < 2 ? 'Name too short' : null)
|
|
}
|
|
});
|
|
|
|
const handleSubmit = async (values: typeof form.values) => {
|
|
try {
|
|
await fetch('/api/users', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(values)
|
|
});
|
|
notifications.show({ message: 'User created successfully' });
|
|
} catch (error) {
|
|
notifications.show({
|
|
message: 'Failed to create user',
|
|
color: 'red'
|
|
});
|
|
}
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={form.onSubmit(handleSubmit)}>
|
|
<TextInput
|
|
label="Name"
|
|
placeholder="John Doe"
|
|
{...form.getInputProps('name')}
|
|
/>
|
|
<TextInput
|
|
label="Email"
|
|
placeholder="john@example.com"
|
|
{...form.getInputProps('email')}
|
|
/>
|
|
<Select
|
|
label="Role"
|
|
data={['Admin', 'User', 'Viewer']}
|
|
{...form.getInputProps('role')}
|
|
/>
|
|
<Button type="submit">Create User</Button>
|
|
</form>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Provider Placement**: Place MantineBetterMenusProvider at the root of your app
|
|
2. **Menu IDs**: Use descriptive, unique IDs for each menu type
|
|
3. **Context Menus**: Always prevent default on right-click events
|
|
4. **Loading States**: Use onClickAsync for async operations to get automatic loading states
|
|
5. **Custom Renderers**: Use custom renderers for complex menu items with icons, badges, etc.
|
|
6. **Portal Rendering**: Oranguru uses portals for proper z-index handling
|
|
7. **State Management**: Leverage Zustand store for complex menu state
|
|
8. **Accessibility**: Ensure keyboard navigation works with context menus
|
|
|
|
## Common Patterns
|
|
|
|
### Multi-Select Context Menu
|
|
```tsx
|
|
const handleBulkContextMenu = (e: React.MouseEvent, selectedIds: string[]) => {
|
|
e.preventDefault();
|
|
show('bulk-menu', {
|
|
x: e.clientX,
|
|
y: e.clientY,
|
|
items: [
|
|
{
|
|
label: `Delete ${selectedIds.length} items`,
|
|
onClickAsync: async () => {
|
|
await bulkDelete(selectedIds);
|
|
}
|
|
},
|
|
{
|
|
label: 'Export selection',
|
|
onClick: () => exportItems(selectedIds)
|
|
}
|
|
]
|
|
});
|
|
};
|
|
```
|
|
|
|
### Conditional Menu Items
|
|
```tsx
|
|
const menuItems = [
|
|
{
|
|
label: 'Edit',
|
|
onClick: () => handleEdit(record)
|
|
},
|
|
canDelete(record) && {
|
|
label: 'Delete',
|
|
onClick: () => handleDelete(record)
|
|
},
|
|
{
|
|
isDivider: true
|
|
},
|
|
isAdmin && {
|
|
label: 'Admin Actions',
|
|
onClick: () => showAdminActions(record)
|
|
}
|
|
].filter(Boolean); // Remove falsy items
|
|
```
|
|
|
|
### Nested Menus (Future Feature)
|
|
While not yet supported, Oranguru is designed to support nested menus in future versions.
|
|
|
|
## Integration with WhatsHooked
|
|
|
|
For WhatsHooked's admin interface:
|
|
|
|
1. **User Management Grid**: Use DataTable with context menus for user actions
|
|
2. **Hook Configuration**: Form with validation and async submission
|
|
3. **WhatsApp Account Management**: Grid with QR code display and pairing actions
|
|
4. **API Key Management**: Grid with copy-to-clipboard and revoke actions
|
|
5. **Event Logs**: Read-only grid with filtering and export
|
|
|
|
## References
|
|
|
|
- Official Repository: https://git.warky.dev/wdevs/oranguru
|
|
- Mantine Documentation: https://mantine.dev
|
|
- Mantine DataTable: https://icflorescu.github.io/mantine-datatable/
|