Patterns
Action Bar
AppActionBar — the top toolbar for list pages: two house dividers wrapping a space-between Row of small Create / Refresh / Delete buttons, identical across every module.
AppActionBar
ComponentList-page toolbar — two house dividers wrapping a Create / Refresh / Delete row.
src/components/common/RequiresantdLive example
The house toolbar: two dividers frame a space-between Row. Create and Refresh group on the left, Delete sits on the right. Buttons are always size="small".
// React — Template List Page (rate-fe). Place after AppBreadcrumb, before AppSearchBar.
<Row className="w-100">
<AppBreadcrumb items={TEMPLATE_BREADCRUMB_ITEMS.management} translate={t} />
<AppActionBar>
<Button size="small" onClick={() => navigate(PATH_ROUTER.TEMPLATE_CREATE)} className="btn-none-border d-flex align-items-center gap-2 font-size-13">
<i className="fa-solid fa-plus fs-5" />
{t('template.actions.create')}
</Button>
<Button size="small" onClick={handleDeleteSelected} disabled={selectedRowKeys.length === 0} className="btn-none-border d-flex align-items-center gap-2 font-size-13">
<i className="fa-regular fa-trash-can fs-6" />
{t('template.actions.delete')}
</Button>
</AppActionBar>Component
One tiny component ships identically in every React MFE (rate-fe, reference-fe, racar-fe, report-fe) and as a Vue equivalent in administrator-fe. The two Dividers and the Row are the whole thing — callers only supply buttons.
import { Divider, Row } from 'antd';
import type { ReactNode } from 'react';
type RowJustify = 'start' | 'end' | 'center' | 'space-around' | 'space-between' | 'space-evenly';
type AppActionBarProps = {
children: ReactNode;
/** Row justify. Defaults to the house 'space-between'. */
justify?: RowJustify;
/** Horizontal gap between the buttons in the bar. */
gap?: number | string;
};
const AppActionBar = ({ children, justify = 'space-between', gap = '10px' }: AppActionBarProps) => (
<>
<Divider className="mt-0 mb-0" />
<Row justify={justify} style={{ gap }}>
{children}
</Row>
<Divider className="mt-0 mb-2" />
</>
);
export default AppActionBar;Props
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | React: the buttons / action elements to display. In the Vue version this is the default <slot />. |
justify | RowJustify | 'space-between' | AntD Row justify. Keep the house 'space-between', use 'start' to anchor all buttons left, or wrap groups in Space/div for a left/right split. |
gap | number | string | '10px' | Horizontal spacing between buttons, set on the Row style. React takes a number or string; the Vue version takes a number and appends px automatically (default 10). |
Usage
Place AppActionBar immediately after AppBreadcrumb and before AppSearchBar on a list page. For a left/right split, wrap each button group in its own Space/div — the space-between justify does the rest. To anchor everything left instead, pass justify="start".
// Left/right split — wrap each group; space-between pushes them apart.
<AppActionBar>
<Space size={10}>
<Button size="small" icon={<PlusOutlined />}>Create</Button>
<Button size="small" icon={<ReloadOutlined />}>Refresh</Button>
</Space>
<Button size="small" danger icon={<DeleteOutlined />}>Delete</Button>
</AppActionBar>
// All buttons anchored left — pass justify="start".
<AppActionBar justify="start">
<Button size="small" icon={<PlusOutlined />}>Create</Button>
<Button size="small" icon={<ReloadOutlined />}>Refresh</Button>
</AppActionBar>Rules
AppActionBar already renders a Divider above and below its button row (mt-0 mb-0 on top, mt-0 mb-2 on the bottom). Never add another Divider right before or after it, and never override those margin classes — they are the spacing contract.size="small" with btn-none-border d-flex align-items-center gap-2 font-size-13, so the bar reads the same everywhere. Use danger for Delete.gap prop only sets the distance between buttons. To create a left/right split, group the buttons — don’t reach for gap. Never set custom margin or padding on the Row or Dividers.