Components and widgets

The Ui library ships the common building blocks a page needs: tables, cards, tabs, navigation, paging bars and status messages. You compose them the same way as everything else, so a list or a table looks the same in every module and you never rebuild one by hand.

Use the ready-made block, never rebuild it

Before writing markup for a table, a list of cards, or a paging bar, reach for the component that already exists. Each one is a small set of static methods you compose with closures, and each carries the framework's styling, so a component you drop into a new page matches every other page without extra CSS. Reinventing one by hand produces unstyled markup that has to be maintained forever, which is exactly what these blocks exist to avoid.

Tables

UiTable::Header takes the column labels and a closure for the body. Inside it, UiTable::Row opens a row and the typed cell methods fill it: UiTable::CellText, UiTable::CellInteger, UiTable::CellNumber, UiTable::CellBoolean, UiTable::CellCode and UiTable::CellButton. The typed cells format their value for you, so a boolean shows as Yes or No and a number is formatted consistently. If the body adds no rows, the table renders a single "(none)" row on its own:

UiTable::Header(['Name', 'Price', 'In stock', ''], function() use($products) {
    CollectionArray::Each($products, function($product) {
        UiTable::Row(function() use($product) {
            UiTable::CellText($product->name);
            UiTable::CellNumber($product->price);
            UiTable::CellBoolean($product->inStock);
            UiTable::CellButton($product->editUrl, 'Edit');
        });
    });
});

Cards

UiCard::List opens a flexible row of cards, and UiCard::Card renders one from a title, a line of information, a link, and an optional image URL. The heading level is chosen from the section depth so cards nest correctly in a page. For a card whose body you want to build yourself, UiCard::RawCard takes a closure:

UiCard::List(function() use($products) {
    CollectionArray::Each($products, function($product) {
        UiCard::Card($product->name, $product->summary, $product->url);
    });
});

Navigation and tabs

UiNavigation::Menu holds a menu and UiNavigation::Item adds a navigation item that takes a label, whether it is selected, and its URL. For tabbed surfaces, UiTabs::Horizontal and UiTabs::Vertical lay out the tab strip and UiTabs::Tab declares each tab with its label, URL and a closure for its panel. A tab reads the current URL to know whether it is the selected one, so you never track selection by hand.

Paging

UiPaging::Bar renders a full paging bar from the current page, the total number of items, the number per page, and a base URL. It works out the page count, shows Prev and Next, and collapses a long run of pages with ellipses; if there is only one page it renders nothing. Because the component owns all of that, a listing page only has to hand it the counts:

UiPaging::Bar($currentPage, $totalItems, $itemsPerPage, '/shop/products');

Status messages

After a form redirects with a success or error message, StatusUi::Message renders that message at the top of the page. It reads the outcome from the redirect and shows a success, error or info banner, and it renders only once per page. Place it near the top of a page that receives form submissions. How that message is set is covered in Forms and input.

Where widgets live

These blocks are part of the Ui library because a component used by more than one module belongs in one shared place, never copied into a leaf module. A dedicated Widget module exists for larger self-contained widgets built the same way, for example WidgetSearchUi, WidgetPagingUi and WidgetTileUi; the rule is identical, compose the provided methods and extend the library rather than hand-rolling a private copy.

Next: Forms and input.