feat: add ActionTools component documentation and update navigation links
This commit is contained in:
@@ -52,6 +52,7 @@ const config = withMermaid(
|
|||||||
items: [
|
items: [
|
||||||
{ text: 'Overview', link: '/packages/ui/' },
|
{ text: 'Overview', link: '/packages/ui/' },
|
||||||
{ text: 'App Layout', link: '/packages/ui/CORE-APP-SHELL' },
|
{ text: 'App Layout', link: '/packages/ui/CORE-APP-SHELL' },
|
||||||
|
{ text: 'Action Tools', link: '/packages/ui/ACTION-TOOLS' },
|
||||||
{ text: 'Form Primitives', link: '/packages/ui/FORM-COMPONENTS' },
|
{ text: 'Form Primitives', link: '/packages/ui/FORM-COMPONENTS' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -114,10 +114,42 @@ apps/web/src/apps/modules/booking/
|
|||||||
└── en/booking.json
|
└── en/booking.json
|
||||||
```
|
```
|
||||||
|
|
||||||
### Lazy Loading & Type Safety
|
### Namespace Registration & Context
|
||||||
Register the namespace when the component mounts. To get native TypeScript autocomplete for nested keys (e.g., `header.title`), augment the global `react-i18next` types.
|
|
||||||
|
To prevent unnecessary React re-renders and ensure dictionaries are loaded before the first paint, translations are registered at the module scope using `registerModuleNamespace`.
|
||||||
|
|
||||||
|
**1. Register the Namespace in the Module Factory:**
|
||||||
|
|
||||||
|
Call `registerModuleNamespace` at the top level of your module factory (e.g., `index.tsx`). This function is idempotent and safe to call outside the React render cycle. The namespace provided must match the `translationNamespace` defined in your module configuration.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// apps/web/src/apps/modules/booking/presentation/factory/index.tsx
|
||||||
|
import { EnterpriseModuleProvider } from '@repo/ui/foundations';
|
||||||
|
import { registerModuleNamespace } from '@repo/core-i18n';
|
||||||
|
import { BookingModuleConfig } from '../../domain/constants';
|
||||||
|
|
||||||
|
import bookingId from '../locales/id/booking.json';
|
||||||
|
import bookingEn from '../locales/en/booking.json';
|
||||||
|
|
||||||
|
// Called once at import time — safe, idempotent, outside React render cycle.
|
||||||
|
registerModuleNamespace(BookingModuleConfig.translationNamespace, {
|
||||||
|
id: bookingId,
|
||||||
|
en: bookingEn,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function BookingModule() {
|
||||||
|
return (
|
||||||
|
<EnterpriseModuleProvider config={BookingModuleConfig}>
|
||||||
|
{/* Routes and Pages */}
|
||||||
|
</EnterpriseModuleProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Augment Types for Autocomplete:**
|
||||||
|
|
||||||
|
To get native TypeScript autocomplete for nested keys (e.g., `header.title`), augment the global `react-i18next` types.
|
||||||
|
|
||||||
**1. Augment Types:**
|
|
||||||
```ts
|
```ts
|
||||||
// apps/web/src/types/i18next.d.ts
|
// apps/web/src/types/i18next.d.ts
|
||||||
import 'react-i18next';
|
import 'react-i18next';
|
||||||
@@ -132,26 +164,31 @@ declare module 'react-i18next' {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Use in Component:**
|
**3. Consume in Components:**
|
||||||
|
|
||||||
|
Inside your pages and components, use `useEnterpriseModuleTranslationContext()` instead of the raw `useTranslation` hook. The context automatically scopes the `t` function to your module's namespace and the global `common` namespace.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> **Best Practice:** Do not hardcode the module namespace prefix (e.g., `t('booking:header.title')`) when using `useEnterpriseModuleTranslationContext`. The provider already scopes it. Simply use `t('header.title')`. You can still access global keys using the common prefix: `t('common:edit')`.
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
import { useEffect } from 'react';
|
// apps/web/src/apps/modules/booking/presentation/pages/booking.page.index.tsx
|
||||||
import { i18n, useTranslation } from '@repo/core-i18n';
|
import { Title } from '@repo/ui/components';
|
||||||
import bookingId from '../locales/id/booking.json';
|
import { useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
|
||||||
import bookingEn from '../locales/en/booking.json';
|
|
||||||
|
|
||||||
export default function BookingFeature() {
|
export default function BookingPageIndex() {
|
||||||
const { t } = useTranslation(['common', 'booking']);
|
const { t } = useEnterpriseModuleTranslationContext();
|
||||||
|
|
||||||
useEffect(() => {
|
return (
|
||||||
i18n.addResourceBundle('id', 'booking', bookingId, true, false);
|
<div>
|
||||||
i18n.addResourceBundle('en', 'booking', bookingEn, true, false);
|
<Title>{t('title')}</Title> {/* Automatically resolves to booking:title */}
|
||||||
}, []);
|
<button>{t('common:edit')}</button> {/* Fallback to global common vocabulary */}
|
||||||
|
</div>
|
||||||
return <h1>{t('booking:header.title')}</h1>; // Autocomplete works!
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**3. Dynamic Variables (Interpolation):**
|
**4. Dynamic Variables (Interpolation):**
|
||||||
```json
|
```json
|
||||||
// booking.json
|
// booking.json
|
||||||
{
|
{
|
||||||
@@ -162,7 +199,7 @@ export default function BookingFeature() {
|
|||||||
```
|
```
|
||||||
```tsx
|
```tsx
|
||||||
// Inside component
|
// Inside component
|
||||||
<h1>{t('booking:messages.welcome', { name: 'Firman', count: 5 })}</h1>
|
<h1>{t('messages.welcome', { name: 'Firman', count: 5 })}</h1>
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -0,0 +1,183 @@
|
|||||||
|
# ActionTools Component
|
||||||
|
|
||||||
|
The `ActionTools` suite provides flexible, responsive, and semantic action menus and toolbars for standardizing interactions across the application. It consists of two main presentational components: `PageActions` and `RowActions`.
|
||||||
|
|
||||||
|
These components automatically adapt to screen sizes, handle tooltip generation, and construct dropdown menus for nested actions.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
- **`PageActions`**: A responsive toolbar for page-level actions. On desktop, it renders a horizontal button group. On mobile, it collapses into a single "More" dropdown menu. Best used in page headers, toolbars, or detailed forms.
|
||||||
|
- **`RowActions`**: A lightweight component optimized for dense areas like data grid rows or list items. It renders standalone icon buttons or kebab menus for nested actions. Uses `React.memo` for zero overhead inside large lists.
|
||||||
|
|
||||||
|
## Import Statement
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import {
|
||||||
|
PageActions,
|
||||||
|
RowActions,
|
||||||
|
type PageAction,
|
||||||
|
type RowAction
|
||||||
|
} from '@repo/ui/components';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### 1. Page Actions (Toolbars & Headers)
|
||||||
|
|
||||||
|
`PageActions` requires a text `label` (unless the type is `divider`). You can customize the look using Mantine's button variants and map specific intents for semantic colors.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { PageActions, type PageAction } from '@repo/ui/components';
|
||||||
|
import { Save, Printer, Trash, FileText, CheckCircle } from 'lucide-react';
|
||||||
|
|
||||||
|
function PageHeader() {
|
||||||
|
const actions: PageAction[] = [
|
||||||
|
{
|
||||||
|
key: 'save',
|
||||||
|
label: 'Save Changes',
|
||||||
|
icon: <Save size={16} />,
|
||||||
|
intent: 'primary',
|
||||||
|
onClick: (key) => console.log('Clicked', key),
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{
|
||||||
|
key: 'print',
|
||||||
|
label: 'Print',
|
||||||
|
icon: <Printer size={16} />,
|
||||||
|
// Nested actions render as a dropdown menu below the main button
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'print-original',
|
||||||
|
label: 'Print Original',
|
||||||
|
icon: <FileText size={16} />,
|
||||||
|
onClick: (k) => console.log(k),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'print-copy',
|
||||||
|
label: 'Print Copy',
|
||||||
|
icon: <FileText size={16} />,
|
||||||
|
onClick: (k) => console.log(k)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: 'Delete',
|
||||||
|
icon: <Trash size={16} />,
|
||||||
|
intent: 'destructive',
|
||||||
|
onClick: (key) => console.log('Clicked', key),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return <PageActions actions={actions} onClose={() => console.log('closed')} />;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Row Actions (Data Grids & Lists)
|
||||||
|
|
||||||
|
`RowActions` are optimized for density. Text labels are optional and primarily shown inside nested dropdowns. Hover tooltips are supported.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { RowActions, type RowAction } from '@repo/ui/components';
|
||||||
|
import { Edit, CheckCircle, MoreVertical, Trash } from 'lucide-react';
|
||||||
|
import { Table } from '@repo/ui/components';
|
||||||
|
|
||||||
|
function DataTable() {
|
||||||
|
const rowActions: RowAction[] = [
|
||||||
|
{
|
||||||
|
key: 'edit',
|
||||||
|
tooltip: 'Edit Record',
|
||||||
|
icon: <Edit size={16} />,
|
||||||
|
onClick: (key) => console.log('Clicked', key),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'approve',
|
||||||
|
tooltip: 'Approve',
|
||||||
|
icon: <CheckCircle size={16} />,
|
||||||
|
intent: 'success',
|
||||||
|
onClick: (key) => console.log('Clicked', key),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'more',
|
||||||
|
label: 'More Options',
|
||||||
|
icon: <MoreVertical size={16} />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
label: 'Delete Record',
|
||||||
|
icon: <Trash size={16} />,
|
||||||
|
intent: 'destructive',
|
||||||
|
onClick: (k) => console.log(k),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table>
|
||||||
|
<Table.Tbody>
|
||||||
|
<Table.Tr>
|
||||||
|
<Table.Td>Invoice #001</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<RowActions actions={rowActions} />
|
||||||
|
</Table.Td>
|
||||||
|
</Table.Tr>
|
||||||
|
</Table.Tbody>
|
||||||
|
</Table>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Props API Reference
|
||||||
|
|
||||||
|
### PageActions Props
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `actions` | `PageAction[]` | Required | Array of configured page-level actions. |
|
||||||
|
| `onClose` | `() => void` | `undefined` | Optional callback triggered when the close (X) button is clicked. |
|
||||||
|
|
||||||
|
### RowActions Props
|
||||||
|
|
||||||
|
| Prop | Type | Default | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `actions` | `RowAction[]` | `[]` | Array of configured row-level actions. |
|
||||||
|
| `showLabels` | `boolean` | `false` | If true, renders the text label alongside the icon for top-level buttons. |
|
||||||
|
|
||||||
|
### Action Definitions
|
||||||
|
|
||||||
|
Both `PageAction` and `RowAction` share a common base interface.
|
||||||
|
|
||||||
|
**Base Action Properties (`BaseAction`)**
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `key` | `string` | Unique identifier. Required for 'action', optional for 'divider'. |
|
||||||
|
| `type` | `'action'` \| `'divider'` | Type of action. Defaults to 'action'. |
|
||||||
|
| `icon` | `ReactNode` | Visual representation of the action. |
|
||||||
|
| `disabled` | `boolean` | Disables interaction if set to true. |
|
||||||
|
| `intent` | `'default'` \| `'success'` \| `'warning'` \| `'destructive'` \| `'primary'` | Semantic context to determine visual emphasis (color mapping). |
|
||||||
|
| `onClick` | `(key: string) => void` | Callback triggered upon execution. |
|
||||||
|
|
||||||
|
**`PageAction` Specific Properties**
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `label` | `string` | Text label displayed on the button. Required for 'action' type. |
|
||||||
|
| `variant` | `'filled'` \| `'light'` \| `'outline'` \| `'default'` \| `'subtle'` \| `'transparent'` | Specifies the Mantine button variant. Defaults to 'transparent' internally. |
|
||||||
|
| `children` | `PageAction[]` | Nested actions rendered as a dropdown menu below the main button. |
|
||||||
|
|
||||||
|
**`RowAction` Specific Properties**
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `label` | `string` | Text primarily used when rendered inside a nested menu item. |
|
||||||
|
| `tooltip` | `string` | Optional text displayed on hover over the standalone icon. |
|
||||||
|
| `children` | `RowAction[]` | Nested actions that will be rendered inside a dropdown menu. |
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- **Semantic Intents:** Always map your actions to a specific `intent` (e.g., `intent: 'destructive'` for deletions). The components will automatically map these to the appropriate theme colors.
|
||||||
|
- **Nested Actions (Dropdowns):** For actions that trigger sub-actions (like multiple print options), use the `children` array property. The component automatically manages the dropdown positioning and presentation.
|
||||||
|
- **Density in Rows:** Inside lists and tables, favor `RowActions` over `PageActions` and provide a `tooltip` instead of forcing a full `label`. This keeps the UI clean and performs efficiently, especially over many rows. Keep `showLabels` as `false` (default) for a tighter grid layout unless specifically required.
|
||||||
|
- **Dividers:** Use `{ type: 'divider' }` within your actions array to visually group related buttons together. The component handles both horizontal and vertical divider logic depending on the screen size.
|
||||||
@@ -68,6 +68,12 @@ function UserForm() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🛠️ ActionTools Component
|
||||||
|
|
||||||
|
> **Full Documentation**: [ACTION-TOOLS.md](./ACTION-TOOLS.md)
|
||||||
|
|
||||||
|
The `ActionTools` suite provides flexible, responsive, and semantic action menus and toolbars (`PageActions` and `RowActions`).
|
||||||
|
|
||||||
## Scripts
|
## Scripts
|
||||||
|
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|
|||||||
Reference in New Issue
Block a user