Створіть пакет UX

Дата оновлення перекладу 2024-05-29

Створіть пакет UX

Tip

Перед тим як це читати, радимо переглянути Кращі практики для повторно використовуваних пакетів.

Ось декілька секретів, щоб зробити так, щоб ваш пакет встановлювався як пакет UX.

Файл composer.json

Ваш файл composer.json повинен мати ключове слово symfony-ux:

1
2
3
{
    "keywords": ["symfony-ux"]
}

Розташування ресурсів

Ваші ресурси мають бути розташовані в одному з наступних каталогів з файлом package.json, щоб Flex міг їх обробляти під час встановлення/оновлення:

  • /assets (рекомендовано)
  • /Resources/assets
  • /src/Resources/assets

Файл package.json

Ваш файл package.json повинен містити конфігурацію symfony з визначеними контролерами, а також додавати обовʼязкові пакети до peerDependencies і importmap (список пакетів у importmap має бути таким самим, як і у peerDependencies):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    "name": "@acme/feature",
    "version": "1.0.0",
    "symfony": {
        "controllers": {
            "slug": {
                "main": "dist/controller.js",
                "fetch": "eager",
                "enabled": true,
                "autoimport": {
                    "@acme/feature/dist/bootstrap4-theme.css": false,
                    "@acme/feature/dist/bootstrap5-theme.css": true
                }
            }
        },
        "importmap": {
            "@hotwired/stimulus": "^3.0.0",
            "slugify": "^1.6.5"
        }
    },
    "peerDependencies": {
        "@hotwired/stimulus": "^3.0.0",
        "slugify": "^1.6.5"
    }
}

У цьому випадку буде розкрито файл, розташований за адресою [assets directory]/dist/controller.js.

Tip

Ви можете або написати сирий JS у цьому файлі dist/controller.js, або ви можете, наприклад, написати свій контролер за допомогою TypeScript і трансполювати його в JavaScript.

Ось приклад, як це зробити:

  1. Додайте наступне до вашого файлу package.json:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    {
        "scripts": {
            "build": "babel src --extensions .ts -d dist"
        },
        "devDependencies": {
            "@babel/cli": "^7.20.7",
            "@babel/core": "^7.20.12",
            "@babel/plugin-proposal-class-properties": "^7.18.6",
            "@babel/preset-env": "^7.20.2",
            "@babel/preset-typescript": "^7.18.6",
            "@hotwired/stimulus": "^3.2.1",
            "typescript": "^4.9.5"
        }
    }
  2. Додайте наступне до вашого файлу babel.config.js (повинен бути розташований поруч з вашим файлом package.json):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    module.exports = {
        presets: [
            ['@babel/preset-env', {
                "loose": true,
                "modules": false
            }],
            ['@babel/preset-typescript', { allowDeclareFields: true }]
        ],
        assumptions: {
            superIsCallableConstructor: false,
        },
    };
  3. Запустіть npm install для встановлення нових залежностей.
  4. Напишіть ваш контролер Stimulus за допомогою TypeScript у файлі src/controller.ts.
  5. Запустіть npm run build, щоб трансполювати ваш контролер TypeScript у JavaScript.

Щоб використовувати ваш контролер у шаблоні (наприклад, визначеному у вашому пакунку), ви можете використовувати його таким чином:

1
2
3
4
5
6
7
8
9
10
<div
    {{ stimulus_controller('acme/feature/slug', { modal: 'my-value' }) }}
    {#
        will render:
        data-controller="acme--feature--slug"
        data-acme--feature--slug-modal-value="my-value"
    #}
>
    ...
</div>

Не забудьте додати symfony/stimulus-bundle:^2.9 як залежність композитора для використання функцій Twig stimulus_* .

Tip

Іменування контролерів: У цьому прикладі name пакета PHP буде acme/feature, а ім'я контролера в package.json - slug. Отже, повне ім'я контролера для Stimulus буде таким acme--feature--slug, хоча у функції stimulus_controller() ви можете використовувати acme/feature/slug.

Кожен контролер має ряд опцій у файлі package.json:

????? ????
enabled ?? ??? ???? ????????? ????????? ?? ?????????????.
main ???? ?? ????? ??????????.
fetch ?? ??????????? ?????????? ?? ?????????? ??? ???????????? ????????. ?????????????? eager (?? ?????????????), ??? ???????? ????????? ?? ?????????? ? JavaScript, ???? ?????????????? ??? ???????????? ????????. ?????????????? lazy, ??? ??????? ????????? ? ?????????? ???????????? ? ???????? ????? ? ????????????? ?? ?????? ??????????, ???? (? ????) ?? ???????? ?'????????? HTML ????????? ?????.
autoimport ?????? ?????? ??? ??????? ? ???????????. ???????, ?????????, ???? ? ?????? ?????? CSS ??????? ??? ????????????????? ????????-?????????? (?????????, Bootstrap 4 ??? 5, Tailwind CSS...). ???????? ??? ???? ??'????? ? ??????? ? ?????? ?????? ? ??????? ????????? ??? ??????? ?????, ??? ?????????? ?? ???????? ??????????? ????.

Особливості Asset Mapper

Щоб змусити ресурси вашого пакету працювати з AssetMapper, ви повинні додати конфігурацію importmap як показано вище у ваш файл package.json, і додати деяку конфігурацію до контейнера:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
namespace Acme\FeatureBundle;

use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class AcmeFeatureBundle extends AbstractBundle
{
    public function prependExtension(ContainerConfigurator $configurator, ContainerBuilder $container): void
    {
        if (!$this->isAssetMapperAvailable($container)) {
            return;
        }

        $container->prependExtensionConfig('framework', [
            'asset_mapper' => [
                'paths' => [
                    __DIR__ . '/../assets/dist' => '@acme/feature-bundle',
                ],
            ],
        ]);
    }

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

        // перевірте, чи встановлено FrameworkBundle 6.3 або новіше
        $bundlesMetadata = $container->getParameter('kernel.bundles_metadata');
        if (!isset($bundlesMetadata['FrameworkBundle'])) {
            return false;
        }

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