Chapter 1: Layout
Page
The Page component is the main container for each screen. It manages:
- Page title
- Transition animations
- Scroll
Basic Syntax
import { Page } from '@cap-rel/smartcommon';
function HomePage() {
return (
<Page title="Home">
<p>Page content</p>
</Page>
);
}
Main Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | - | Title displayed in the navbar |
| subtitle | string | - | Optional subtitle |
| className | string | - | Additional CSS classes |
| onRefresh | function | - | Pull-to-refresh callback |
Page with Refresh
function ItemsPage() {
const [items, setItems] = useState([]);
const handleRefresh = async () => {
const data = await api.private.get('items').json();
setItems(data);
};
return (
<Page title="My Items" onRefresh={handleRefresh}>
{items.map(item => (
<div key={item.id}>{item.label}</div>
))}
</Page>
);
}
Block
The Block component structures content into distinct visual sections.
Basic Syntax
import { Page, Block } from '@cap-rel/smartcommon';
function ProfilePage() {
return (
<Page title="Profile">
<Block>
<h2>Personal Information</h2>
<p>Name: John Doe</p>
</Block>
<Block>
<h2>Preferences</h2>
<p>Language: French</p>
</Block>
</Page>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | - | Block title |
| className | string | - | Additional CSS classes |
| padding | boolean | true | Add internal padding |
Block with Title
<Block title="Information">
<p>Block content</p>
</Block>
Panel
The Panel component displays a sliding panel from the side of the screen.
Syntax
import { useState } from 'react';
import { Page, Block, Button, Panel } from '@cap-rel/smartcommon';
function MyPage() {
const [isPanelOpen, setIsPanelOpen] = useState(false);
return (
<Page title="My Page">
<Block>
<Button onClick={() => setIsPanelOpen(true)}>
Open Panel
</Button>
</Block>
<Panel
isOpen={isPanelOpen}
onClose={() => setIsPanelOpen(false)}
position="right"
>
<h2>Panel Content</h2>
<p>Additional information</p>
</Panel>
</Page>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | false | Controls display |
| onClose | function | - | Close callback |
| position | string | 'right' | 'left', 'right', 'top', 'bottom' |
| title | string | - | Panel title |
Popup
The Popup component displays a centered modal window.
Syntax
import { useState } from 'react';
import { Page, Block, Button, Popup } from '@cap-rel/smartcommon';
function MyPage() {
const [isPopupOpen, setIsPopupOpen] = useState(false);
return (
<Page title="My Page">
<Block>
<Button onClick={() => setIsPopupOpen(true)}>
Delete
</Button>
</Block>
<Popup
isOpen={isPopupOpen}
onClose={() => setIsPopupOpen(false)}
title="Confirmation"
>
<p>Are you sure you want to delete this item?</p>
<div className="flex gap-2 mt-4">
<Button onClick={() => setIsPopupOpen(false)}>
Cancel
</Button>
<Button variant="danger" onClick={handleDelete}>
Delete
</Button>
</div>
</Popup>
</Page>
);
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| isOpen | boolean | false | Controls display |
| onClose | function | - | Close callback |
| title | string | - | Popup title |
| closeOnOverlay | boolean | true | Close on overlay click |
Complete Example
import { useState } from 'react';
import {
Page,
Block,
Panel,
Popup,
Button,
List,
ListItem
} from '@cap-rel/smartcommon';
export const ProductsPage = () => {
const [selectedProduct, setSelectedProduct] = useState(null);
const [showDeletePopup, setShowDeletePopup] = useState(false);
const [showFilters, setShowFilters] = useState(false);
const products = [
{ id: 1, label: 'Product A', price: 29.99 },
{ id: 2, label: 'Product B', price: 49.99 },
{ id: 3, label: 'Product C', price: 19.99 }
];
const handleDelete = async () => {
// Delete product
console.log('Deleting', selectedProduct);
setShowDeletePopup(false);
setSelectedProduct(null);
};
return (
<Page title="Products">
{/* Actions */}
<Block>
<Button onClick={() => setShowFilters(true)}>
Filters
</Button>
</Block>
{/* List */}
<Block title="Catalog">
<List>
{products.map(product => (
<ListItem
key={product.id}
title={product.label}
subtitle={`${product.price} €`}
onClick={() => setSelectedProduct(product)}
actions={
<Button
size="sm"
variant="danger"
onClick={(e) => {
e.stopPropagation();
setSelectedProduct(product);
setShowDeletePopup(true);
}}
>
Delete
</Button>
}
/>
))}
</List>
</Block>
{/* Filter Panel */}
<Panel
isOpen={showFilters}
onClose={() => setShowFilters(false)}
title="Filters"
position="right"
>
<p>Filter options here</p>
</Panel>
{/* Confirmation Popup */}
<Popup
isOpen={showDeletePopup}
onClose={() => setShowDeletePopup(false)}
title="Confirm Deletion"
>
<p>
Delete "{selectedProduct?.label}"?
</p>
<div className="flex gap-2 mt-4">
<Button onClick={() => setShowDeletePopup(false)}>
Cancel
</Button>
<Button variant="danger" onClick={handleDelete}>
Delete
</Button>
</div>
</Popup>
</Page>
);
};
Best Practices
- One Page per route component
- Use Block to group content logically
- Panel for secondary actions (filters, details)
- Popup for confirmations and critical actions
Key Points to Remember
- Page = main container with title and animations
- Block = content section with styling
- Panel = sliding side panel
- Popup = centered modal for confirmations