Skip to content

Commit c89ccca

Browse files
committed
Add Collection component
1 parent aec7c40 commit c89ccca

26 files changed

+785
-0
lines changed

src/Collection/.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/.gitattributes export-ignore
2+
/.gitignore export-ignore
3+
/.symfony.bundle.yaml export-ignore
4+
/phpunit.xml.dist export-ignore
5+
/phpstan.neon.dist export-ignore
6+
/Resources/assets/test export-ignore
7+
/Resources/assets/jest.config.js export-ignore
8+
/Tests export-ignore

src/Collection/.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/.php_cs.cache
2+
/.php_cs
3+
/.phpunit.result.cache
4+
/composer.phar
5+
/composer.lock
6+
/phpunit.xml
7+
/vendor/
8+
/Tests/app/var
9+
/Tests/app/public/build/
10+
node_modules/
11+
package-lock.json
12+
yarn.lock
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
branches: ["2.x"]
2+
maintained_branches: ["2.x"]
3+
doc_dir: "Resources/doc"

src/Collection/CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Contributing
2+
3+
Install the test app:
4+
5+
$ composer install
6+
$ cd Tests/app
7+
$ yarn install
8+
$ yarn build
9+
10+
Start the test app:
11+
12+
$ symfony serve
13+
14+
## Run tests
15+
16+
$ php vendor/bin/simple-phpunit
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\UX\Collection;
13+
14+
use Symfony\Component\HttpKernel\Bundle\Bundle;
15+
16+
/**
17+
* @author Kévin Dunglas <[email protected]>
18+
*/
19+
final class CollectionBundle extends Bundle
20+
{
21+
}

src/Collection/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2021 Kévin Dunglas
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Collection/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Symfony UX Collection
2+
3+
**This repository is a READ-ONLY sub-tree split**. See
4+
https://github.com/symfony/ux to create issues or submit pull requests.
5+
6+
## Resources
7+
8+
- [Documentation](https://symfony.com/bundles/ux-collection/current/index.html)
9+
- [Report issues](https://github.com/symfony/ux/issues) and
10+
[send Pull Requests](https://github.com/symfony/ux/pulls)
11+
in the [main Symfony UX repository](https://github.com/symfony/ux)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Controller } from '@hotwired/stimulus';
2+
3+
const DEFAULT_ITEMS_SELECTOR = ':scope > :is(div, fieldset)';
4+
var ButtonType;
5+
(function (ButtonType) {
6+
ButtonType["Add"] = "add";
7+
ButtonType["Delete"] = "delete";
8+
})(ButtonType || (ButtonType = {}));
9+
class controller extends Controller {
10+
connect() {
11+
this.connectCollection(this.element);
12+
}
13+
connectCollection(parent) {
14+
parent.querySelectorAll('[data-prototype]').forEach((el) => {
15+
const collectionEl = el;
16+
const items = this.getItems(collectionEl);
17+
collectionEl.dataset.currentIndex = items.length.toString();
18+
this.addAddButton(collectionEl);
19+
this.getItems(collectionEl).forEach(itemEl => this.addDeleteButton(collectionEl, itemEl));
20+
});
21+
}
22+
getItems(collectionElement) {
23+
return collectionElement.querySelectorAll(collectionElement.dataset.itemsSelector || DEFAULT_ITEMS_SELECTOR);
24+
}
25+
createButton(collectionEl, buttonType) {
26+
const buttonTemplateID = collectionEl.dataset[`${buttonType}ButtonTemplateId`];
27+
if (buttonTemplateID && 'content' in document.createElement('template')) {
28+
const buttonTemplate = document.getElementById(buttonTemplateID);
29+
if (!buttonTemplate)
30+
throw new Error(`element with ID "${buttonTemplateID}" not found`);
31+
return buttonTemplate.content.cloneNode(true);
32+
}
33+
const button = document.createElement('button');
34+
button.type = 'button';
35+
button.textContent = buttonType === ButtonType.Add ? 'Add' : 'Delete';
36+
return button;
37+
}
38+
addItem(collectionEl) {
39+
const currentIndex = collectionEl.dataset.currentIndex;
40+
collectionEl.dataset.currentIndex++;
41+
const collectionNamePattern = collectionEl.id.replace(/_/g, '(?:_|\\[|]\\[)');
42+
const prototype = collectionEl.dataset.prototype
43+
.replace('__name__label__', currentIndex)
44+
.replace(new RegExp(`(${collectionNamePattern}(?:_|]\\[))__name__`, 'g'), `$1${currentIndex}`);
45+
const fakeEl = document.createElement('div');
46+
fakeEl.innerHTML = prototype;
47+
const itemEl = fakeEl.firstElementChild;
48+
this.connectCollection(itemEl);
49+
this.addDeleteButton(collectionEl, itemEl);
50+
const items = this.getItems(collectionEl);
51+
items.length ? items[items.length - 1].insertAdjacentElement('afterend', itemEl) : collectionEl.prepend(itemEl);
52+
}
53+
addAddButton(collectionEl) {
54+
const addButton = this.createButton(collectionEl, ButtonType.Add);
55+
addButton.onclick = (e) => {
56+
e.preventDefault();
57+
this.addItem(collectionEl);
58+
};
59+
collectionEl.appendChild(addButton);
60+
}
61+
addDeleteButton(collectionEl, itemEl) {
62+
const deleteButton = this.createButton(collectionEl, ButtonType.Delete);
63+
deleteButton.onclick = (e) => {
64+
e.preventDefault();
65+
itemEl.remove();
66+
};
67+
itemEl.appendChild(deleteButton);
68+
}
69+
}
70+
71+
export { controller as default };
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('../../../../jest.config.js');
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@symfony/ux-collection",
3+
"description": "Support for collection embedding with Symfony Form",
4+
"license": "MIT",
5+
"main": "dist/controller.js",
6+
"module": "dist/controller.js",
7+
"version": "1.0.0",
8+
"symfony": {
9+
"controllers": {
10+
"collection": {
11+
"main": "dist/controller.js",
12+
"webpackMode": "eager",
13+
"fetch": "eager",
14+
"enabled": true
15+
}
16+
}
17+
},
18+
"peerDependencies": {
19+
"@hotwired/stimulus": "^3.0.0"
20+
},
21+
"devDependencies": {
22+
"@hotwired/stimulus": "^3.0.0"
23+
}
24+
}

0 commit comments

Comments
 (0)