Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
## 🎨 Bootstrap Admin UI

* [Getting started](bootstrap-admin-ui/getting-started.md)
* [Assets](bootstrap-admin-ui/assets.md)
* Components
* [Copy to clipboard](bootstrap-admin-ui/components/copy-to-clipboard.md)

Expand Down
82 changes: 82 additions & 0 deletions docs/bootstrap-admin-ui/assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
description: >-
How the Bootstrap Admin UI package loads its stylesheets, scripts and Stimulus controllers.
---

# Assets

## Prebuilt assets (default)

The package ships prebuilt assets in its `public/` directory, installed by `assets:install` under `public/bundles/syliusbootstrapadminui/`.
They are loaded through the following hookables of the admin base layout:

| Hook | Hookable | Content |
|---------------------------------|--------------|---------------------------------------------------------------------------------------|
| `sylius_admin.base#stylesheets` | `styles` | Tabler/Bootstrap styles of the admin panel |
| `sylius_admin.base#stylesheets` | `symfony_ux` | Stylesheets of the Symfony UX packages (Tom Select, Live Components) |
| `sylius_admin.base#javascripts` | `javascripts`| Scripts of the admin panel |
| `sylius_admin.base#javascripts` | `symfony_ux` | A Stimulus application with the Symfony UX controllers and the package's controllers |

Nothing else is needed: this works whether your application uses Webpack Encore, AssetMapper or no frontend tooling at all.

## Using your own Stimulus application (AssetMapper)

If your application starts its own Stimulus application, for instance with AssetMapper, disable both `symfony_ux` hookables and load your importmap instead.
Follow the [Using AssetMapper](../getting-started.md#using-assetmapper) guide.

## Stimulus controllers

The package provides the following Stimulus controllers:

| Identifier | Used by |
|---------------------|------------------------------------------------------------------------------|
| `copy-to-clipboard` | the [copy to clipboard](components/copy-to-clipboard.md) components |
| `theme-switcher` | the theme switcher of the navbar |

They are registered by the prebuilt `symfony_ux` Stimulus application. When you disable it, register them in your own application by adding the `@sylius/bootstrap-admin-ui` package to your `assets/controllers.json` file:

{% code title="assets/controllers.json" lineNumbers="true" %}
```json
{
"controllers": {
"@sylius/bootstrap-admin-ui": {
"copy-to-clipboard": {
"enabled": true,
"fetch": "eager"
},
"theme-switcher": {
"enabled": true,
"fetch": "eager"
}
}
}
}
```
{% endcode %}

{% hint style="warning" %}
If they are not registered, the matching components are still rendered but do nothing (for instance, the dark/light theme button of the navbar), and no error is reported.
{% endhint %}

### Versions without the `@sylius/bootstrap-admin-ui` controllers package

Older versions of the package do not expose their controllers to AssetMapper, so the `controllers.json` entry above fails. Register the controllers directory yourself instead: StimulusBundle then registers them with their file names (`copy-to-clipboard`, `theme-switcher`).

{% code title="config/packages/asset_mapper.yaml" lineNumbers="true" %}
```yaml
framework:
asset_mapper:
paths:
assets/: ''
vendor/sylius/bootstrap-admin-ui/assets/controllers: sylius-bootstrap-admin-ui/controllers
```
{% endcode %}

{% code title="config/packages/stimulus.yaml" lineNumbers="true" %}
```yaml
stimulus:
controller_paths:
- '%kernel.project_dir%/assets/controllers'
- '%kernel.project_dir%/vendor/sylius/bootstrap-admin-ui/assets/controllers'
```
{% endcode %}
43 changes: 18 additions & 25 deletions docs/bootstrap-admin-ui/components/copy-to-clipboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,33 +132,26 @@ Register the controller **once in the application's existing Stimulus applicatio

### AssetMapper

Follow the [AssetMapper setup](../../getting-started.md#using-assetmapper) to disable the package's `symfony_ux` entrypoint when starting your own Stimulus application. Expose the controller source directory to AssetMapper:

```yaml
# config/packages/asset_mapper.yaml
framework:
asset_mapper:
paths:
'%kernel.project_dir%/vendor/sylius/bootstrap-admin-ui/assets/controllers': 'sylius/bootstrap-admin-ui/controllers'
```

Add a local importmap entry (this does not install another JavaScript package):

```bash
php bin/console importmap:require '@sylius/bootstrap-admin-ui/copy-to-clipboard' --path=./vendor/sylius/bootstrap-admin-ui/assets/controllers/copy-to-clipboard-controller.js
```

In the module where your application already starts Stimulus:

```js
import { startStimulusApp } from '@symfony/stimulus-bundle';
import CopyToClipboardController from '@sylius/bootstrap-admin-ui/copy-to-clipboard';

const app = startStimulusApp();
app.register('copy-to-clipboard', CopyToClipboardController);
Follow the [AssetMapper setup](../../getting-started.md#using-assetmapper) to disable the package's `symfony_ux` entrypoint when starting your own Stimulus application.

The package exposes its controllers to AssetMapper. Enable the controller in your `assets/controllers.json` file, StimulusBundle then registers it with the `copy-to-clipboard` identifier:

{% code title="assets/controllers.json" lineNumbers="true" %}
```json
{
"controllers": {
"@sylius/bootstrap-admin-ui": {
"copy-to-clipboard": {
"enabled": true,
"fetch": "eager"
}
}
}
}
```
{% endcode %}

If `app` is already exported by another module, import and reuse it instead of calling `startStimulusApp()` again. The application's importmap must also provide `@hotwired/stimulus`, as in a normal Symfony Stimulus installation.
See the [Assets](../assets.md#stimulus-controllers) page for the other controllers of the package. The application's importmap must also provide `@hotwired/stimulus`, as in a normal Symfony Stimulus installation.

### Webpack Encore / an existing Sylius Stimulus application

Expand Down
2 changes: 2 additions & 0 deletions docs/bootstrap-admin-ui/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Install the package using Composer and Symfony Flex:
composer require sylius/bootstrap-admin-ui
```

The package works out of the box with its prebuilt assets. If your application starts its own Stimulus application (for instance with AssetMapper), read the [Assets](assets.md) page.

## Configuring the CRUD templates

CRUD templates are split into configurable blocks.
Expand Down
115 changes: 94 additions & 21 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,30 +76,101 @@ Now, it's your turn!

### Using AssetMapper

To prevent duplicate Ajax calls, disable the auto-initialized Stimulus app and Symfony UX stylesheets from the `sylius/bootstrap-admin-ui` package, so you can take control of Stimulus initialization in your own code.
By default, the `sylius/bootstrap-admin-ui` package loads prebuilt assets that start their own Stimulus application (the `symfony_ux` hookables).
If your application uses [AssetMapper](https://symfony.com/doc/current/frontend/asset_mapper.html) and starts its own Stimulus application, disable these prebuilt assets to avoid running two Stimulus applications (and duplicate Ajax calls), and take control of Stimulus initialization in your own code.

#### Disabling Stimulus app & Symfony UX stylesheets from third party package
#### Preparing Stimulus for AssetMapper

First, you need to disable the Stimulus App started by the `sylius/bootstrap-admin-ui` package and add a custom javascript app hook for the asset mapper.
StimulusBundle needs an `assets/controllers.json` file and an `assets/controllers/` directory.
When the StimulusBundle recipe was applied on a Webpack Encore project, the `assets/controllers/` directory may not exist. In this case, Symfony Flex fails with `The "assets/controllers" directory does not exist` and does not add `@symfony/stimulus-bundle` to your `importmap.php`.

Create the directory if needed:

```bash
mkdir -p assets/controllers
touch assets/controllers/.gitignore
```

Then declare the Symfony UX controllers used by the admin panel, with the same autoimported stylesheets as the prebuilt assets:

{% code title="assets/controllers.json" lineNumbers="true" %}
```json
{
"controllers": {
"@symfony/ux-autocomplete": {
"autocomplete": {
"enabled": true,
"fetch": "eager",
"autoimport": {
"tom-select/dist/css/tom-select.default.css": false,
"tom-select/dist/css/tom-select.bootstrap4.css": false,
"tom-select/dist/css/tom-select.bootstrap5.css": true
}
}
},
"@symfony/ux-live-component": {
"live": {
"enabled": true,
"fetch": "eager",
"autoimport": {
"@symfony/ux-live-component/dist/live.min.css": true
}
}
},
"@sylius/bootstrap-admin-ui": {
"copy-to-clipboard": {
"enabled": true,
"fetch": "eager"
},
"theme-switcher": {
"enabled": true,
"fetch": "eager"
}
}
},
"entrypoints": []
}
```
{% endcode %}

{% hint style="info" %}
The `@sylius/bootstrap-admin-ui` entry registers the Stimulus controllers shipped with the package (used by the [copy to clipboard](bootstrap-admin-ui/components/copy-to-clipboard.md) components and the theme switcher of the navbar).
Without it, these components are rendered but do nothing, without any error.
{% endhint %}

Make sure StimulusBundle and Stimulus are in your `importmap.php`:

```bash
php bin/console importmap:require @symfony/stimulus-bundle @hotwired/stimulus
```

The `@symfony/ux-autocomplete` and `@symfony/ux-live-component` packages also need their importmap entries (such as `tom-select` and the `tom-select/dist/css/*.css` files). Symfony Flex adds them when it installs these packages; if one is missing, add it with `php bin/console importmap:require`.

#### Disabling the prebuilt Stimulus app & Symfony UX stylesheets

Disable the `symfony_ux` hookables of the `sylius/bootstrap-admin-ui` package and add your own hookable that loads your importmap entrypoint.

Load the importmap in the `sylius_admin.base#stylesheets` hook (in the `<head>`) rather than in the `sylius_admin.base#javascripts` one (at the end of the `<body>`):
the stylesheets imported by your JavaScript (Tom Select, Live Components, your own CSS) are then loaded before the page is rendered, which avoids a flash of unstyled content.
Hookables are rendered by descending priority, and the package stylesheets use the default priority (`0`): a negative priority makes sure your stylesheets come after them and can override them.

{% tabs %}
{% tab title="YAML" %}
{% code lineNumbers="true" %}
{% code title="config/packages/sylius_bootstrap_admin_ui.yaml" lineNumbers="true" %}
```yaml
# config/packages/sylius_bootstrap_admin_ui.yaml
# ...
sylius_twig_hooks:
hooks:
# ...
# Disabling Symfony UX stylesheets
'sylius_admin.base#stylesheets':
# Disabling Symfony UX stylesheets
symfony_ux:
enabled: false

'sylius_admin.base#javascripts':
enabled: false
# New hookable, rendered after the package stylesheets
app:
priority: 200
template: 'base/javascripts/app.html.twig'
template: 'base/stylesheets/app.html.twig'
priority: -100

'sylius_admin.base#javascripts':
# Disabling Stimulus App
symfony_ux:
enabled: false
Expand All @@ -122,28 +193,28 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'symfony_ux' => [
'enabled' => false,
],
// New hookable, rendered after the package stylesheets
'app' => [
'template' => 'base/stylesheets/app.html.twig',
'priority' => -100,
],
],

'sylius_admin.base#javascripts' => [
// New hook
'app' => [
'priority' => 200,
'template' => 'base/javascripts/app.html.twig',
],
// Disabling Stimulus App
// Disabling Stimulus App
'symfony_ux' => [
'enabled' => false,
],
],
],
],
]);
};
```
{% endcode %}
{% endtab %}
{% endtabs %}

{% code title="base/javascripts/app.html.twig" lineNumbers="true" %}
{% code title="templates/base/stylesheets/app.html.twig" lineNumbers="true" %}
```twig
{{ importmap('app') }}
```
Expand All @@ -165,3 +236,5 @@ const app = startStimulusApp();
import './bootstrap.js';
// ...
```

The controllers declared in `assets/controllers.json` and the ones in your `assets/controllers/` directory are registered automatically.
24 changes: 24 additions & 0 deletions src/BootstrapAdminUi/assets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@sylius/bootstrap-admin-ui",
"description": "Stimulus controllers of the Sylius Bootstrap Admin UI package.",
"license": "MIT",
"private": true,
"symfony": {
"controllers": {
"copy-to-clipboard": {
"main": "controllers/copy-to-clipboard-controller.js",
"name": "copy-to-clipboard",
"webpackMode": "eager",
"fetch": "eager",
"enabled": true
},
"theme-switcher": {
"main": "controllers/theme-switcher-controller.js",
"name": "theme-switcher",
"webpackMode": "eager",
"fetch": "eager",
"enabled": true
}
}
}
}
28 changes: 28 additions & 0 deletions src/BootstrapAdminUi/src/Symfony/SyliusBootstrapAdminUiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Sylius\BootstrapAdminUi\Symfony;

use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
Expand Down Expand Up @@ -42,6 +43,18 @@ public function prependExtension(ContainerConfigurator $container, ContainerBuil

$loader->load('services.php');

if ($this->isAssetMapperAvailable($builder)) {
// Exposes the bundle Stimulus controllers to AssetMapper,
// so they can be enabled from the application "assets/controllers.json" file.
$builder->prependExtensionConfig('framework', [
'asset_mapper' => [
'paths' => [
dirname(__DIR__, 2) . '/assets/controllers' => '@sylius/bootstrap-admin-ui/controllers',
],
],
]);
}

if (!isset($bundles['SyliusAdminUiBundle'])) {
return;
}
Expand All @@ -51,4 +64,19 @@ public function prependExtension(ContainerConfigurator $container, ContainerBuil
'hook_name_section_separator' => '#',
]);
}

private function isAssetMapperAvailable(ContainerBuilder $builder): bool
{
if (!interface_exists(AssetMapperInterface::class)) {
return false;
}

/** @var array<string, array{path: string}> $bundlesMetadata */
$bundlesMetadata = $builder->getParameter('kernel.bundles_metadata');
if (!isset($bundlesMetadata['FrameworkBundle'])) {
return false;
}

return is_file($bundlesMetadata['FrameworkBundle']['path'] . '/Resources/config/asset_mapper.php');
}
}
Loading