Search by

teknoo / east-ux

frenchcomp

Symfony bundle, following the #East programming philosophy, build on Teknoo/East-Foundation and Teknoo/East-Common, providing the application-level UI pieces of an East application: Symfony UX Twig and Live Components (windows, forms, lists, menu bar, ...), their Stimulus controllers and stylesheet,

Package info

github.com/TeknooSoftware/east-ux

Homepage

Type:symfony-bundle

pkg:composer/teknoo/east-ux

Fund package maintenance!

Patreon

TeknooSoftware

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

dev-main / 1.0.x-dev 2026-10-21 15:07 UTC

This package is auto-updated.

Last update: 2026-10-22 12:59:55 UTC


README

Latest Stable Version Latest Unstable Version Total Downloads License PHPStan

Symfony bundle, following the #East programming philosophy, built on Teknoo/East-Foundation and Teknoo/East-Common, providing the application-level UI pieces of an East application:

  • Symfony UX Twig and Live Components, with their templates: windows (Window, FormWindow, ListWindow, LoginWindow), ObjectList, Pagination, ConfirmModal, Card and MenuBar;
  • their Stimulus controllers and a structural stylesheet, served by AssetMapper without build step (or consumed with npm);
  • a component renderer endpoint, to load a FormWindow dynamically, validating what a request may render;
  • a menu service, fed by the menu generators of the application.

The Live Components are re-rendered by the East recipe of their page: East Foundation's router maps a POST /_components/<Name> back to the route of the component's originalPath and runs its recipe again, and East Common's LiveComponentBuilder copies the view parameters of the recipe into the component. East UX only uses this bridge: the router and LiveTrait stay in East Foundation, the LiveComponentBuilder and the recipe steps in East Common.

Documentation

  • Components: props, blocks and behaviour of each component.
  • Live Components and the East bridge: re-renders through the recipes, the component renderer, security, limitations.
  • Stimulus controllers: values of each controller and contract with the page.
  • Menu: menu generators, sections, items and the menu service.
  • Theming: stylesheet, form theme, template overrides and translations.

Installation & Requirements

This library requires:

* PHP 8.4+
* A PHP autoloader (Composer is recommended)
* Teknoo/East-Foundation 9.3+
* Teknoo/East-Common 4.6+
* Symfony 7.4+ or 8.1+ (FrameworkBundle, Form, Security HTTP), with SecurityBundle registered in the application
* Symfony UX TwigComponent 3 and LiveComponent 3
* Twig 3.24+
* Optional: Symfony AssetMapper and StimulusBundle, to serve the controllers and the stylesheet without build step

Before the release on Packagist

teknoo/east-ux is not published on Packagist yet: an application requires it from its Git repository. Add the repositories entry and the requirement together, in the application's composer.json:

{
    "repositories": [
        {
            "type": "vcs",
            "url": "git@gitlab.teknoo.software:teknoosoftware/east-ux.git"
        }
    ],
    "require": {
        "teknoo/east-ux": "^1.0@dev"
    },
    "config": {
        "gitlab-domains": [
            "gitlab.teknoo.software"
        ]
    }
}
  • The main branch is aliased 1.0.x-dev: ^1.0@dev resolves to it today and to the 1.0.0 tag once it exists (with "minimum-stability": "stable" and "prefer-stable": true, the tag wins). The constraint never changes; only the repositories entry is removed once the package is on Packagist.
  • With a GitLab token (the COMPOSER_AUTH variable, or auth.json: {"gitlab-token": {"gitlab.teknoo.software": "<token>"}}), Composer reaches the GitLab API and downloads the dist archive: the usual composer install --prefer-dist works. Use a project access token in the CI.
  • Composer 2.10 no longer installs a package from source when the download of its dist archive fails (its source-fallback option is false by default, and deprecated). Before the library is pushed to GitLab, or for a developer with an SSH access to the repository but without GitLab API token, install with composer install --prefer-install=auto (or composer update --prefer-install=auto): dev versions, such as 1.0.x-dev, are installed from source with git, every other package from its dist archive.
  • Before the library is pushed, the SSH URL can point at a local checkout of the library with a git rewrite scoped to the Composer process, never written to a configuration file: GIT_CONFIG_COUNT=1, GIT_CONFIG_KEY_0=url.<path of the local checkout>.insteadOf and GIT_CONFIG_VALUE_0=git@gitlab.teknoo.software:teknoosoftware/east-ux.git, with --no-interaction and --prefer-install=auto. composer.json and composer.lock keep the GitLab URL and the commit hashes.

Once released

composer require teknoo/east-ux

Symfony configuration

Bundles

// config/bundles.php
return [
    // ...
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
    Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
    Teknoo\DI\SymfonyBridge\DIBridgeBundle::class => ['all' => true],
    Teknoo\East\FoundationBundle\EastFoundationBundle::class => ['all' => true],
    Teknoo\East\CommonBundle\TeknooEastCommonBundle::class => ['all' => true],
    Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
    Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
    Symfony\UX\LiveComponent\LiveComponentBundle::class => ['all' => true],
    Teknoo\East\UXBundle\TeknooEastUXBundle::class => ['all' => true],
];

East Foundation and East Common are configured as their own documentation explains (di_bridge.definitions, ...). East UX has no PHP-DI definition today: its infrastructures/symfony/config/di.php and twig.php are empty, listing them in di_bridge.definitions is optional.

The bundle prepends:

  • twig_component.defaults with Teknoo\East\UXBundle\Components\ mapped to @TeknooEastUX/components/: the components keep their short names (<twig:FormWindow>, component('ListWindow', {...})). The application still configures twig_component (anonymous_template_directory and its own defaults, both required by TwigComponentBundle);
  • when AssetMapper is available, framework.asset_mapper.paths with the package's assets/dist directory, as the namespace @teknoo/east-ux.

Bundle configuration

# config/packages/teknoo_east_ux.yaml
teknoo_east_ux:
    home_route: 'app_home'                       # brand link of the MenuBar
    form_theme: '@App/form/fields.html.twig'     # form theme of the FormWindow
    renderable_components: ['FormWindow']        # components the component renderer may render
    renderable_forms:                            # form types a request may name as FormWindow's formName
        - 'App\Form\Type\ItemType'
Key Default Meaning
home_route null Route of the brand link of MenuBar; without it, the brand links to /
form_theme null Default of the formTheme prop of FormWindow; without it, no theme is set on the form and the global form themes apply
renderable_components ['FormWindow'] Components the component renderer may render (only those with an entry in ComponentRenderer::ALLOWED_PROPS, today FormWindow)
renderable_forms [] Form types a request to the component renderer may name as formName

A configured value can not be an empty string. The lists of every configuration are merged (and deduplicated): an extension whose lists open a FormWindow adds its form types from its bundle's prepend():

public function prepend(ContainerBuilder $container): void
{
    $container->prependExtensionConfig(
        'teknoo_east_ux',
        ['renderable_forms' => [CustomerType::class]],
    );
}

The default ['FormWindow'] of renderable_components applies only when no configuration sets this key: a configuration that sets it lists FormWindow itself.

The values are available as the parameters teknoo_east_ux.home_route, teknoo_east_ux.form_theme, teknoo_east_ux.renderable_components and teknoo_east_ux.renderable_forms.

Routes and access control

# config/routes/east_ux.yaml: the component renderer answers on `POST /component/render`
east_ux:
    resource: '@TeknooEastUXBundle/config/routes.yaml'
    prefix: '/component'

# config/routes/ux_live_component.yaml: the Live Component endpoint, re-routed by East Foundation
live_component:
    resource: '@LiveComponentBundle/config/routes.php'
    prefix: '/_components'
# config/packages/security.yaml
security:
    access_control:
        # ...
        - { path: '^/component', roles: [IS_AUTHENTICATED_FULLY], requires_channel: https }
        - { path: '^/_components', roles: [IS_AUTHENTICATED_FULLY], requires_channel: https }

The component renderer is reached only by an authenticated user, and checks what it renders: the component, the props (a request never sets objectInstance or originalPath), the form type, and a formPath that the access map must grant to the user. East Foundation re-runs, for a /_components request, the recipe of the route named by the signed originalPath, after checking the access rules of that route with the firewall (East Foundation 9.4): requiring authentication on ^/_components and signing only paths the user may reach keep a user away from the recipes their roles do not reach. The details are in documentation/live-components.md.

Components

Component Kind Purpose
Window Twig Generic window (title bar, separator, pane), base of the other windows' templates
FormWindow Live Window holding a Symfony form, as a modal loaded dynamically or as a full page
ListWindow Live Paginated list of objects with an optional search form and a "New" button
LoginWindow Twig Login form of Symfony's form_login, error in #login-error
MenuBar Twig Navigation bar built from the menu generators
ObjectList Twig Table of objects, one item component per row
Pagination Twig Page buttons bound to the page prop of ListWindow
ConfirmModal Twig Bootstrap confirmation modal, used by ListWindow for deletions
Card Twig Bootstrap card; its content is escaped unless contentIsHtml is set

Every template can be overridden in templates/bundles/TeknooEastUXBundle/components/<Name>.html.twig. Texts are translation keys of the east_ux domain, with an English catalogue; labels supplied by the application (menu entries, list headers, sub-form labels) keep its default domain. See documentation/components.md and documentation/theming.md.

The East bridge

A page is rendered by an East recipe, and the Live Components it holds receive the view parameters of this recipe:

# config/routes/item.yaml
app_item_list:
    path: '/items/'
    methods: ['GET', 'POST']
    defaults:
        _controller: 'teknoo.east.common.endpoint.crud.list'
        loader: '@App\Loader\ItemLoader'
        defaultOrderDirection: 'ASC'
        itemsPerPage: 20
        template: '@@App/item/list.html.twig'
        errorTemplate: '@@App/Error/<error>.html.twig'
{# templates/item/list.html.twig #}
<twig:ListWindow
    title="{{ 'app.item.title.list'|trans }}"
    objectsCollection="{{ objectsCollection }}"
    pageCount="{{ pageCount }}"
    page="{{ page }}"
    itemComponent="ItemRow"
    tableHeaders="{{ ['app.item.table.name', 'app.item.table.actions'] }}"
    newFormRoute="app_item_new"
    newFormName="App\\Form\\Type\\ItemType"
/>
<div id="dynamic-modals"></div>

When a page button is clicked, the ListWindow is re-rendered: POST /_components/ListWindow runs the recipe of /items/ again for the new page, and the component receives the new objectsCollection and pageCount. The "New" button asks the component renderer for a FormWindow on /items/new, appends it to #dynamic-modals and opens it: the window is re-rendered by the creation recipe, which builds the form. Components never load objects themselves. See documentation/live-components.md.

Menu

namespace App\Menu;

use Teknoo\East\Common\Contracts\User\UserInterface;
use Teknoo\East\UX\Contracts\Menu\MenuGeneratorInterface;
use Teknoo\East\UX\Menu\Location;
use Teknoo\East\UX\Menu\MenuItem;
use Teknoo\East\UX\Menu\MenuService;

use function in_array;

class AdminMenu implements MenuGeneratorInterface
{
    public function generate(MenuService $menuService, Location $location, UserInterface $user, array $roles): void
    {
        if (Location::TopLeft === $location && in_array('ROLE_ADMIN', $roles, true)) {
            $menuService->getSection('app.menu.administration', $location)
                ->addItem(new MenuItem(name: 'app.menu.users', route: 'app_admin_user_list'));
        }
    }
}
# config/services.yaml
services:
    App\Menu\AdminMenu:
        tags: ['teknoo.east.ux.menu.generator']
<twig:MenuBar :user="app.user ? app.user.wrappedUser : null" />

The menu service caches the sections per user and location in its instance, and is reset between two requests (kernel.reset). See documentation/menu.md.

Assets

AssetMapper and StimulusBundle

assets/package.json is read by Symfony Flex (the package has the symfony-ux keyword) and by StimulusBundle. When the library is installed or updated, Flex writes the @teknoo/east-ux entries into the application's assets/controllers.json (the seven controllers) and importmap.php (@teknoo/east-ux/styles/east-ux.css, and @hotwired/stimulus and bootstrap when they are missing), each file only if it already exists: commit both files first, then require the library and commit the entries Flex adds. A later composer install leaves them unchanged.

The application imports the stylesheet in its assets/app.js:

import './stimulus_bootstrap.js';
import 'bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import '@teknoo/east-ux/styles/east-ux.css';
import './styles/app.css';

bin/console debug:asset-map lists the assets of the library under @teknoo/east-ux/ (for example @teknoo/east-ux/controllers/form_window_controller.js and @teknoo/east-ux/styles/east-ux.css).

npm

The same assets/package.json is a valid npm package. In an application still on npm (a package.json and no importmap.php), Flex adds "@teknoo/east-ux": "file:vendor/teknoo/east-ux/assets" to package.json when the library is installed or updated (in devDependencies, or where the key already is: move it to dependencies, the browser loads the package at runtime), and writes the seven controllers into assets/controllers.json if that file exists. Run npm install after Composer. The application imports the controllers from @teknoo/east-ux/dist/controllers/, registers them with their identifiers (form-window, form-window-dynamic-loader, list-window-refresh, window-close, window-drag, delete-item, clipboard) and loads @teknoo/east-ux/dist/styles/east-ux.css. Without a bundler, its import map needs the prefix entry "@teknoo/east-ux/": "<public URL of node_modules>/@teknoo/east-ux/".

See documentation/stimulus.md for the controllers and the contract with the page (#dynamic-modals, delete-confirm-modal, the list-window:refresh event, the X-Requested-With header and the CSRF token).

Support this project

This project is free and will remain free. It is fully supported by commercial activities of SASU Teknoo Software and EIRL Richard DELOGE. If you like it and help me maintain it and evolve it, don't hesitate to support me on Patreon or Github.

Thanks :) Richard.

Credits

EIRL Richard Déloge - https://deloge.io - Lead developer. SASU Teknoo Software - https://teknoo.software

About Teknoo Software

Teknoo Software is a PHP software editor, founded by Richard Déloge, as part of EIRL Richard Déloge. Teknoo Software's goals : Provide to our partners and to the community a set of high quality services or software, sharing knowledge and skills.

License

East UX is licensed under the 3-Clause BSD License - see the LICENSE file for details.

Contribute :)

You are welcome to contribute to this project. Fork it on Github