Chapter 4: Display

Formatting Components

SmartCommon provides components to display data in a formatted and consistent way.

String

Display short text.

import { String } from '@cap-rel/smartcommon';

<String value="John Doe" />

<String value={user.name} fallback="Not specified" />

// With truncation
<String value={longText} maxLength={50} />

Text

Display long text with line breaks preserved.

import { Text } from '@cap-rel/smartcommon';

<Text value={product.description} />

// With line limit
<Text value={description} maxLines={3} />

Number

Display formatted number according to locale.

import { Number } from '@cap-rel/smartcommon';

// Simple number
<Number value={1234.56} />
// Displays: 1,234.56

// With fixed decimals
<Number value={99.9} decimals={2} />
// Displays: 99.90

// Price
<Number value={29.99} suffix=" €" />
// Displays: 29.99 €

// Percentage
<Number value={0.156} format="percent" />
// Displays: 15.6%

Datetime

Display formatted date and time.

import { Datetime } from '@cap-rel/smartcommon';

// Full date
<Datetime value="2024-03-15T14:30:00" />
// Displays: March 15, 2024 at 2:30 PM

// Date only
<Datetime value="2024-03-15" format="date" />
// Displays: March 15, 2024

// Time only
<Datetime value="2024-03-15T14:30:00" format="time" />
// Displays: 2:30 PM

// Short format
<Datetime value="2024-03-15" format="short" />
// Displays: 03/15/2024

// Relative format
<Datetime value="2024-03-15T14:30:00" format="relative" />
// Displays: 2 days ago

Duration

Display duration.

import { Duration } from '@cap-rel/smartcommon';

// Duration in seconds
<Duration value={3661} />
// Displays: 1h 1min 1s

// Duration in minutes
<Duration value={90} unit="minutes" />
// Displays: 1h 30min

// Compact format
<Duration value={3600} format="compact" />
// Displays: 1:00:00

Email

Display email with clickable link.

import { Email } from '@cap-rel/smartcommon';

<Email value="contact@example.com" />
// Creates a mailto link

<Email value={user.email} showIcon />

Url

Display URL with clickable link.

import { Url } from '@cap-rel/smartcommon';

<Url value="https://example.com" />

<Url value={product.website} label="View website" />

<Url value={link} openInNewTab />

PhoneNumber

Display clickable phone number.

import { PhoneNumber } from '@cap-rel/smartcommon';

<PhoneNumber value="+33612345678" />
// Displays: +33 6 12 34 56 78 (formatted)
// Creates a tel: link

<PhoneNumber value={contact.phone} showIcon />

Address

Display formatted address.

import { Address } from '@cap-rel/smartcommon';

<Address
    value={{
        street: '15 rue de la Paix',
        city: 'Paris',
        zip: '75002',
        country: 'France'
    }}
/>

// With Google Maps link
<Address value={customer.address} showMap />

Coordinates

Display GPS coordinates.

import { Coordinates } from '@cap-rel/smartcommon';

<Coordinates value={{ lat: 48.8566, lng: 2.3522 }} />

// With map link
<Coordinates value={location} showMap />

Color

Display a color.

import { Color } from '@cap-rel/smartcommon';

<Color value="#FF5733" />
// Displays color square + code

<Color value={product.color} showCode={false} />
// Displays only the square

Icon

Display icon.

import { Icon } from '@cap-rel/smartcommon';

<Icon name="home" />

<Icon name="check" size={24} color="green" />

// With react-icons
import { FiCheck, FiX } from 'react-icons/fi';

<Icon component={FiCheck} color="green" />
<Icon component={FiX} color="red" />

Tags

Display multiple tags.

import { Tags } from '@cap-rel/smartcommon';

<Tags value={['React', 'JavaScript', 'TypeScript']} />

<Tags
    value={product.categories}
    color="blue"
/>

Tag

Single tag.

import { Tag } from '@cap-rel/smartcommon';

<Tag>New</Tag>

<Tag color="green">Active</Tag>

<Tag color="red">Urgent</Tag>

// With icon
import { FiCheck } from 'react-icons/fi';
<Tag icon={FiCheck} color="green">Validated</Tag>

Files

Display files with download.

import { Files } from '@cap-rel/smartcommon';

<Files
    value={[
        { name: 'document.pdf', url: '/files/doc.pdf', size: 1024000 },
        { name: 'image.jpg', url: '/files/img.jpg', size: 512000 }
    ]}
/>

Signature

Display a signature.

import { Signature } from '@cap-rel/smartcommon';

<Signature value={contract.signature} />

List and ListItem

Display lists.

import { List, ListItem } from '@cap-rel/smartcommon';
import { useNavigation } from '@cap-rel/smartcommon';

function ProductsList({ products }) {
    const nav = useNavigation();

    return (
        <List>
            {products.map(product => (
                <ListItem
                    key={product.id}
                    title={product.label}
                    subtitle={`${product.price} €`}
                    image={product.thumbnail}
                    onClick={() => nav.navigate(`/products/${product.id}`)}
                    chevron
                />
            ))}
        </List>
    );
}

ListItem Props

Prop Type Description
title string Main title
subtitle string Subtitle
image string Image URL
icon Component Icon on the left
onClick function Click action
chevron boolean Show right arrow
actions ReactNode Actions on the right

Button

Action button.

import { Button } from '@cap-rel/smartcommon';

// Variants
<Button>Default</Button>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="danger">Danger</Button>
<Button variant="ghost">Ghost</Button>

// Sizes
<Button size="sm">Small</Button>
<Button size="md">Normal</Button>
<Button size="lg">Large</Button>

// States
<Button loading>Loading</Button>
<Button disabled>Disabled</Button>

// With icon
import { FiPlus } from 'react-icons/fi';
<Button icon={FiPlus}>Add</Button>

Spinner

Loading indicator.

import { Spinner } from '@cap-rel/smartcommon';

<Spinner />

<Spinner size="sm" />
<Spinner size="lg" />

// Loading page
function LoadingPage() {
    return (
        <div className="flex items-center justify-center h-screen">
            <Spinner size="lg" />
        </div>
    );
}

Complete Example: Product Detail

import {
    Page,
    Block,
    String,
    Text,
    Number,
    Datetime,
    Tags,
    Tag,
    Files,
    Button,
    Carousel,
    CarouselItem
} from '@cap-rel/smartcommon';
import { useNavigation } from '@cap-rel/smartcommon';

export const ProductDetailPage = ({ product }) => {
    const nav = useNavigation();

    return (
        <Page title={product.label}>
            {/* Photos */}
            {product.photos?.length > 0 && (
                <Carousel>
                    {product.photos.map((photo, index) => (
                        <CarouselItem key={index}>
                            <img src={photo.url} alt={`Photo ${index + 1}`} />
                        </CarouselItem>
                    ))}
                </Carousel>
            )}

            {/* Main Information */}
            <Block>
                <div className="flex justify-between items-start">
                    <div>
                        <h1 className="text-2xl font-bold">
                            <String value={product.label} />
                        </h1>
                        <Tags value={product.categories} />
                    </div>
                    <Tag color={product.isActive ? 'green' : 'gray'}>
                        {product.isActive ? 'Active' : 'Inactive'}
                    </Tag>
                </div>

                <div className="mt-4 text-3xl font-bold text-primary">
                    <Number value={product.price} suffix=" €" />
                </div>
            </Block>

            {/* Description */}
            <Block title="Description">
                <Text value={product.description} />
            </Block>

            {/* Details */}
            <Block title="Details">
                <dl className="space-y-2">
                    <div className="flex justify-between">
                        <dt className="text-gray-500">Reference</dt>
                        <dd><String value={product.ref} /></dd>
                    </div>
                    <div className="flex justify-between">
                        <dt className="text-gray-500">Stock</dt>
                        <dd><Number value={product.stock} /></dd>
                    </div>
                    <div className="flex justify-between">
                        <dt className="text-gray-500">Created on</dt>
                        <dd><Datetime value={product.createdAt} format="date" /></dd>
                    </div>
                    <div className="flex justify-between">
                        <dt className="text-gray-500">Modified on</dt>
                        <dd><Datetime value={product.updatedAt} format="relative" /></dd>
                    </div>
                </dl>
            </Block>

            {/* Documents */}
            {product.documents?.length > 0 && (
                <Block title="Documents">
                    <Files value={product.documents} />
                </Block>
            )}

            {/* Actions */}
            <Block>
                <div className="flex gap-2">
                    <Button
                        variant="outline"
                        onClick={() => nav.navigate(`/products/${product.id}/edit`)}
                    >
                        Edit
                    </Button>
                    <Button variant="primary">
                        Order
                    </Button>
                </div>
            </Block>
        </Page>
    );
};

Key Points to Remember

  1. Formatting components for consistent display
  2. Number with automatic French locale
  3. Datetime with practical relative formats
  4. List/ListItem for navigable lists
  5. Button with variants and states

Previous Chapter | Back to Module | Next Module: SmartCommon Hooks ->