{"id":19638420,"url":"https://github.com/arkounay/ux-collection","last_synced_at":"2025-04-05T17:04:08.558Z","repository":{"id":45433522,"uuid":"409237779","full_name":"Arkounay/ux-collection","owner":"Arkounay","description":"Collection for Symfony forms using Symfony UX","archived":false,"fork":false,"pushed_at":"2025-01-30T21:58:12.000Z","size":194,"stargazers_count":40,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T16:03:34.705Z","etag":null,"topics":["symfony","symfony-bundle","symfony-collection","symfony-form","symfony-ux"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Arkounay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-22T14:30:56.000Z","updated_at":"2025-03-06T21:39:38.000Z","dependencies_parsed_at":"2024-06-21T16:41:40.796Z","dependency_job_id":"1b2712d3-7b04-49f1-9b3b-0cc76948bc2e","html_url":"https://github.com/Arkounay/ux-collection","commit_stats":{"total_commits":43,"total_committers":1,"mean_commits":43.0,"dds":0.0,"last_synced_commit":"f26f3af93420b3315908a1f0edf7097f9f87124a"},"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arkounay%2Fux-collection","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arkounay%2Fux-collection/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arkounay%2Fux-collection/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Arkounay%2Fux-collection/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Arkounay","download_url":"https://codeload.github.com/Arkounay/ux-collection/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247369953,"owners_count":20927928,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["symfony","symfony-bundle","symfony-collection","symfony-form","symfony-ux"],"created_at":"2024-11-11T12:38:39.283Z","updated_at":"2025-04-05T17:04:08.518Z","avatar_url":"https://github.com/Arkounay.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ux Collection\n\nSymfony Collections that works out of the box with Symfony UX\n\n![demo-gif](https://github.com/arkounay/ux-collection/raw/master/doc/demo.gif)\n\n---\n#### Note: Incompatible with [Live Components](https://symfony.com/bundles/ux-live-component/current/index.html) for now - use the provided [LiveCollectionType](https://symfony.com/bundles/ux-live-component/current/index.html#using-livecollectiontype) from LiveComponent instead.\n---\n\n## Installation\n\n**Before you start, make sure you have [StimulusBundle](https://symfony.com/bundles/StimulusBundle/current/index.html) configured in your app.**\n\nInstall the bundle using Composer and Symfony Flex:\n\n```sh\ncomposer require arkounay/ux-collection\n```\n\nIf you're using WebpackEncore, install your assets and restart Encore (not needed if you're using AssetMapper):\n\n```sh\nnpm install --force\nnpm run watch\n\n# or use yarn\nyarn install --force\nyarn watch\n```\n\n**If you're using bootstrap 5**, you should disable the sandalone CSS import in `assets\\controllers.json` :\n```json\n\"@arkounay/ux-collection\": {\n    \"collection\": {\n        \"enabled\": true,\n        \"fetch\": \"eager\",\n        \"autoimport\": {\n            \"@arkounay/ux-collection/src/style.css\": true,\n            \"@arkounay/ux-collection/src/style-when-not-using-bootstrap-5.css\": false\n        }\n    },\n    \"tabbed_collection\": {\n        \"enabled\": true,\n        \"fetch\": \"eager\",\n        \"autoimport\": {\n            \"@arkounay/ux-collection/src/tabbed-style.css\": true\n        }\n    }\n}\n```\n\n## Usage\n\nIn a form, use **UxCollectionType**. It works like a classic CollectionType except it has more options :\ne.g: \n\n```php\n    use Arkounay\\Bundle\\UxCollectionBundle\\Form\\UxCollectionType;\n    \n    // ...\n    \n    public function buildForm(FormBuilderInterface $builder, array $options)\n    {\n        $builder\n            // ...\n            -\u003eadd('myCollection', UxCollectionType::class, [\n                'entry_type' =\u003e MyEntryType::class,\n                'allow_add' =\u003e true,\n                'allow_delete' =\u003e true,\n                'allow_drag_and_drop' =\u003e true,\n                'drag_and_drop_filter' =\u003e 'input,textarea,a,button,label',\n                'display_sort_buttons' =\u003e true,\n                'add_label' =\u003e 'Add an item',\n                'min' =\u003e 3,\n                'max' =\u003e 10,\n            ])\n        ;\n    }\n```\n\n## Options\n\n- **allow_add**: will display the Add button (default true)\n- **allow_delete**: will display the Delete button (default true)\n- **allow_drag_and_drop**: will allow the user to change item positions using drag and drop (default true)\n- **drag_and_drop_filter**: when drag and drop is allowed, selectors that do not lead to dragging (default true) \n- **drag_and_drop_prevent_on_filter**: when drag and drop is allowed, calls `event.preventDefault()` when triggered `filter` (default false)\n- **display_sort_buttons**: will display arrow up and down buttons to change item positions (default true)\n- **display_insert_button**: will display an insert button under every collection items (so items can be inserted inside the middle of the collection for example), only if `allow_add` is set to true (default false)\n- **add_label**: The add button label (default \"Add\")\n- **insert_label**: The insert button label (default \"Insert\")\n- **add_wrapper_class**: The class used on the add button wrapper (default \"mb-3\")\n- **add_class**: The class used on the add button (default \"btn btn-outline-secondary collection-add\")\n- **insert_class**: The class used on the insert button (default \"btn btn-outline-secondary btn-collection-insert collection-add\")\n- **position_selector**: If a dom selector is specified and it's targetting an input that is located inside a collection item, it will change this input's value and insert its current position (starting from 0) instead of changing the input's name. (ex: `'.position'`)\n- **min**: The number of minimum items within the collection. When a collection has `allow_add` set to `true` and has less items than `min` upon creation, empty items will be added and the remove button will remain hidden until more items are created. (default 1)\n- **max**: The number of maximum items within the collection. When the collection reaches the maximum number of items, the add button will be hidden. (default null - no limit)\n\n### Nested collections\n\n- [When using nested collections, remember to change the `prototype_name` of the child's collection. It needs to be different than the parent's collection (that defaults to `__name__`)](https://symfony.com/doc/current/reference/forms/types/collection.html#prototype-name)\n- If you're using `position_selector` in both parent and child collections, make sure they are different\n\n### Extend the default behavior\n\nUxCollection allows you to extend its default behavior using a custom Stimulus controller, ie `custom_collection_controller.js`: \n```js\nimport { Controller } from '@hotwired/stimulus';\n\nexport default class extends Controller {\n    \n    connect() {\n        this.element.addEventListener('ux-collection:connect', this._onConnect);\n        this.element.addEventListener('ux-collection:change', this._onChange);\n        this.element.addEventListener('ux-collection:add', this._onAdd);\n        this.element.addEventListener('ux-collection:remove', this._onRemove);\n    }\n\n    disconnect() {\n        this.element.removeEventListener('ux-collection:connect', this._onConnect);\n        this.element.removeEventListener('ux-collection:change', this._onChange);\n        this.element.removeEventListener('ux-collection:add', this._onAdd);\n        this.element.removeEventListener('ux-collection:remove', this._onRemove);\n    }\n\n    _onConnect() {\n        console.log('The custom collection was just created');\n    }\n\n    _onChange() {\n        console.log('The custom collection changed');\n    }\n\n    _onAdd(event) {\n        console.log('An element was added', event.detail);\n    }\n\n    _onRemove(event) {\n        console.log('An element was removed', event.detail);\n    }\n    \n}\n```\n\nThen in your form, add your controller as an HTML attribute:\n\n```php\npublic function buildForm(FormBuilderInterface $builder, array $options)\n{\n    $builder\n        // ...\n        -\u003eadd('collection', UxCollectionType::class, [\n            'attr' =\u003e ['data-controller' =\u003e 'custom-collection']\n        ])\n        // ...\n    ;\n}\n```\n\n### Listening to changes from a parent stimulus controller\n\nIf you have a parent stimulus controller and want to monitor changes in the collection (such as updating a total number of items or adjusting prices in a cart), you can utilize the ux-collection:change dispatched event and directly invoke a parent's controller method. For example if you have a stimulus controller called `parent` wrapping the collection with a `onCollectionChange` method, it will be called if you add the proper action in the form:\n\n```php\n$builder-\u003eadd('collection', UxCollectionType::class, [\n    // ...\n    'attr' =\u003e ['data-action' =\u003e 'ux-collection:change-\u003eparent#onCollectionChange']\n]);\n```\n\n### Note about File inputs\n\nIf your collection contains File inputs, depending on how you use FileType (e.g if you use a collection of VichUploaderBundle), you might have issues when adding/removing/moving items related to how positionning work. Use either the `position_selector` option to fix this, or disable sorting by setting `allow_drag_and_drop` and `display_sort_buttons` to `false`: this way the form name will not change.\n\n\n### EasyAdmin integration\n\nFor [easyadmin](https://github.com/EasyCorp/EasyAdminBundle) 3+ you need to manually specify the form theme by overriding configureCrud in your DashboardController to add the theme `@ArkounayUxCollection/ux_collection_form_theme.html.twig`\n```php\npublic function configureCrud(): Crud\n{\n    return Crud::new()-\u003eaddFormTheme('@ArkounayUxCollection/ux_collection_form_theme.html.twig');\n}\n```\n\nYou will need to configure your admin to use WebpackEncore so Symfony UX is taken into account, for example:\n```php\npublic function configureAssets(Assets $assets): Assets\n{\n    return parent::configureAssets($assets)\n        -\u003eaddWebpackEncoreEntry('app');\n}\n```\n\n### QAG integration\n\nThis bundle is already included in [QAG](https://github.com/Arkounay/QuickAdminGeneratorBundle) and works out of the box\n\n\n## Extra collections type\n\nThere is also **UxHorizontalCollectionType** for collections that need to move horizontally, and **UxTabbedCollectionType** that creates a tab-type collection (works only when bootstrap's used in your project for now, and you will probably need to override the base css a bit for this one - here's a [QuickAdminGeneratorBundle](https://github.com/arkounay/QuickAdminGeneratorBundle) integration example)\n\n![tabbed-demo-gif](https://raw.githubusercontent.com/Arkounay/ux-collection/master/doc/demo-tabbed.gif)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farkounay%2Fux-collection","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Farkounay%2Fux-collection","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Farkounay%2Fux-collection/lists"}