Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
dbba3b8
UX FormCollection
stakovicz May 3, 2021
83ec2ea
Remove hard coded property_name __name__
stakovicz May 4, 2021
d983cf0
PHP CS Fixer
stakovicz May 4, 2021
a35ff32
First jests
stakovicz May 4, 2021
e1d7574
Rename CollectionType > UXCollectionType
stakovicz May 6, 2021
409c8e7
Rename CollectionType > UXCollectionType
stakovicz May 6, 2021
a624f25
DependencyInjection Clean
stakovicz May 6, 2021
f4ba011
Fix .gitattributes
stakovicz May 6, 2021
7741907
Move default values
stakovicz May 7, 2021
66defab
Predefined theme or not
stakovicz May 23, 2021
145b1cb
Update src/FormCollection/README.md
stakovicz May 24, 2021
2b900cc
Update src/FormCollection/README.md
stakovicz May 24, 2021
25c1454
Update src/FormCollection/README.md
stakovicz May 24, 2021
d66250d
Update src/FormCollection/Resources/views/form_theme_div.html.twig
stakovicz May 24, 2021
2db95e0
Update src/FormCollection/README.md
stakovicz May 24, 2021
8f8b5c2
Update src/FormCollection/Resources/views/form_theme_table.html.twig
stakovicz May 24, 2021
4a68faa
Split in 4 options
stakovicz May 24, 2021
a170618
Default startIndex value
stakovicz Jun 6, 2021
13561e7
Update src/FormCollection/Resources/views/form_theme_div.html.twig
stakovicz Jul 21, 2021
fb847c6
Update src/FormCollection/Resources/views/form_theme_div.html.twig
stakovicz Jul 21, 2021
740d19b
Update src/FormCollection/Resources/views/form_theme_table.html.twig
stakovicz Jul 21, 2021
c57d86e
Update src/FormCollection/Resources/views/form_theme_table.html.twig
stakovicz Jul 21, 2021
0bbac2f
Fix coding-style-js
stakovicz Nov 6, 2021
edfe323
Prettier
stakovicz Nov 6, 2021
086c3a3
Rebase and refresh the code
stakovicz May 21, 2022
480dbb5
fix TU
stakovicz May 21, 2022
3223fae
change buttons attr
stakovicz May 21, 2022
911f18f
Move logic from twig files to form types
alexander-schranz Jul 18, 2022
4cfa27d
Fix types and not used imports
alexander-schranz Jul 18, 2022
0976a9e
Allow overriding the data-controller via attr
alexander-schranz Jul 18, 2022
dc18974
Simplify controller integration
alexander-schranz Jul 18, 2022
9ee00dc
Fix handling of nested blocks
alexander-schranz Jul 18, 2022
d294592
Move docs to index.rst
alexander-schranz Jul 18, 2022
4edfefb
Fix add problem
alexander-schranz Jul 19, 2022
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
3 changes: 3 additions & 0 deletions src/FormCollection/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.gitattributes export-ignore
/.gitignore export-ignore
/Resources/assets/test export-ignore
32 changes: 32 additions & 0 deletions src/FormCollection/DependencyInjection/FormCollectionExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\FormCollection\DependencyInjection;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\UX\FormCollection\Form\UXCollectionType;

/**
* @internal
*/
class FormCollectionExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$container
->setDefinition('form.ux_collection', new Definition(UXCollectionType::class))
->addTag('form.type')
->setPublic(false)
;
}
}
35 changes: 35 additions & 0 deletions src/FormCollection/Form/UXCollectionEntryToolbarType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Symfony\UX\FormCollection\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @final
* @experimental
*/
class UXCollectionEntryToolbarType extends AbstractType
{
Comment on lines +14 to +15
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UXCollectionEntryToolbarType and UXCollectionToolbarType allows to add additional buttons later via a form extension. Example can be a Copy & Paste Functionality.

public function buildForm(FormBuilderInterface $builder, array $options): void
{
if ($options['allow_delete']) {
$builder->add(
'uxCollectionEntryDeleteButton',
ButtonType::class,
$options['delete_options'],
);
}
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'label' => false,
'allow_delete' => false,
'delete_options' => [],
]);
}
}
58 changes: 58 additions & 0 deletions src/FormCollection/Form/UXCollectionEntryType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Symfony\UX\FormCollection\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @final
* @experimental
*
* @internal only for internal usage to add the toolbar to every entry of a collection
*/
class UXCollectionEntryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add(
'entry',
$options['entry_type'],
$options['entry_options'],
);
}

public function buildView(FormView $view, FormInterface $form, array $options): void
{
$form->add('toolbar', UXCollectionEntryToolbarType::class, [
'allow_delete' => $options['allow_delete'],
'delete_options' => $options['delete_options'],
]);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'entry_type' => TextType::class,
'entry_options' => [],
'label' => false,
'allow_delete' => false,
'delete_options' => [],
'row_attr' => [
'data-collection-target' => 'entry',
],
]);

$entryOptionsNormalizer = function (OptionsResolver $options, $value) {
$value['inherit_data'] = true;

return $value;
};

$resolver->addNormalizer('entry_options', $entryOptionsNormalizer);
}
}
35 changes: 35 additions & 0 deletions src/FormCollection/Form/UXCollectionToolbarType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Symfony\UX\FormCollection\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @final
* @experimental
*/
class UXCollectionToolbarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
if ($options['allow_add']) {
$builder->add(
'uxCollectionAddButton',
ButtonType::class,
$options['add_options'],
);
}
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'label' => false,
'allow_add' => false,
'add_options' => [],
]);
}
}
93 changes: 93 additions & 0 deletions src/FormCollection/Form/UXCollectionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

namespace Symfony\UX\FormCollection\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @final
* @experimental
*/
class UXCollectionType extends AbstractType
{
public function getParent(): string
{
return CollectionType::class;
}

public function buildView(FormView $view, FormInterface $form, array $options): void
{
$form->add('toolbar', UXCollectionToolbarType::class, [
'allow_add' => $options['allow_add'],
'add_options' => $options['add_options'],
]);
}

public function configureOptions(OptionsResolver $resolver): void
{
$addOptionsNormalizer = function (Options $options, $value) {
$value['block_name'] = 'add_button';
$value['attr'] = \array_merge([
'data-' . $options['attr']['data-controller'] . '-target' => 'addButton',
'data-action' => $options['attr']['data-controller'] . '#add',
], $value['attr'] ?? []);

return $value;
};

$deleteOptionsNormalizer = function (Options $options, $value) {
$value['block_name'] = 'delete_button';
$value['attr'] = \array_merge([
'data-' . $options['attr']['data-controller'] . '-target' => 'deleteButton',
'data-action' => $options['attr']['data-controller'] . '#delete',
], $value['attr'] ?? []);

return $value;
};

$attrNormalizer = function (Options $options, $value) {
if (!isset($value['data-controller'])) {
$value['data-controller'] = 'collection';
}
$value['data-' . $value['data-controller'] . '-prototype-name-value'] = $options['prototype_name'];

return $value;
};

$entryTypeNormalizer = function (OptionsResolver $options, $value) {
return UXCollectionEntryType::class;
};

$entryOptionsNormalizer = function (OptionsResolver $options, $value) {
return [
'row_attr' => [
'data-' . $options['attr']['data-controller'] . '-target' => 'entry',
],
'label' => false,
'allow_delete' => $options['allow_delete'],
'delete_options' => $options['delete_options'],
'entry_type' => $options['ux_entry_type'] ?? null,
'entry_options' => $value,
];
};

$resolver->setDefaults([
'ux_entry_type' => TextType::class,
'add_options' => [],
'delete_options' => [],
'original_entry_type' => [],
]);

$resolver->setNormalizer('add_options', $addOptionsNormalizer);
$resolver->setNormalizer('delete_options', $deleteOptionsNormalizer);
$resolver->setNormalizer('attr', $attrNormalizer);
$resolver->addNormalizer('entry_type', $entryTypeNormalizer);
$resolver->addNormalizer('entry_options', $entryOptionsNormalizer);
}
}
22 changes: 22 additions & 0 deletions src/FormCollection/FormCollectionBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\UX\FormCollection;

use Symfony\Component\HttpKernel\Bundle\Bundle;

/**
* @final
* @experimental
*/
class FormCollectionBundle extends Bundle
{
}
19 changes: 19 additions & 0 deletions src/FormCollection/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Copyright (c) 2020-2021 Fabien Potencier

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
14 changes: 14 additions & 0 deletions src/FormCollection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Symfony UX Form Collection

Symfony UX Form collection is a Symfony bundle provides a Stimulus integration
for add and remove of for [Symfony Form Collection Type](https://symfony.com/doc/current/form/collection.html).

**This repository is a READ-ONLY sub-tree split**. See
https://github.com/symfony/ux to create issues or submit pull requests.

## Resources

- [Documentation](https://symfony.com/bundles/ux-form-collection/current/index.html)
- [Report issues](https://github.com/symfony/ux/issues) and
[send Pull Requests](https://github.com/symfony/ux/pulls)
in the [main Symfony UX repository](https://github.com/symfony/ux)
4 changes: 4 additions & 0 deletions src/FormCollection/Resources/assets/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/env"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
68 changes: 68 additions & 0 deletions src/FormCollection/Resources/assets/dist/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Controller } from '@hotwired/stimulus';

class default_1 extends Controller {
constructor() {
super(...arguments);
this.index = 0;
this.controllerName = 'collection';
this.entries = [];
}
connect() {
this.controllerName = this.context.scope.identifier;
this._dispatchEvent('form-collection:pre-connect');
this.entries = [];
this.element.querySelectorAll(':scope > [data-' + this.controllerName + '-target="entry"]').forEach(entry => {
this.entries.push(entry);
});
this._dispatchEvent('form-collection:connect');
}
add() {
const prototypeHTML = this.element.dataset.prototype;
if (!prototypeHTML) {
throw new Error('A "data-prototype" attribute was expected on data-controller="' + this.controllerName + '" element.');
}
const newEntry = this._textToNode(prototypeHTML.replace(new RegExp('/' + this.prototypeNameValue + '/', 'g'), this.index.toString()));
this._dispatchEvent('form-collection:pre-add', {
entry: newEntry,
index: this.index,
});
if (this.entries.length > 0) {
this.entries[this.entries.length - 1].after(newEntry);
}
else {
this.element.prepend(newEntry);
}
this.entries.push(newEntry);
this._dispatchEvent('form-collection:add', {
entry: newEntry,
index: this.index,
});
this.index++;
}
delete(event) {
const clickTarget = event.target;
const entry = clickTarget.closest('[data-' + this.controllerName + '-target="entry"]');
this._dispatchEvent('form-collection:pre-delete', {
entry: entry,
});
entry.remove();
this.entries = this.entries.filter(currentEntry => currentEntry !== entry);
this._dispatchEvent('form-collection:delete', {
entry: entry,
});
}
_textToNode(text) {
const template = document.createElement('template');
text = text.trim();
template.innerHTML = text;
return template.content.firstChild;
}
_dispatchEvent(name, payload = {}) {
this.element.dispatchEvent(new CustomEvent(name, { detail: payload, bubbles: true }));
}
}
default_1.values = {
prototypeName: String,
};

export { default_1 as default };
5 changes: 5 additions & 0 deletions src/FormCollection/Resources/assets/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const config = require('../../../../jest.config.js');

config.setupFilesAfterEnv.push('./test/setup.js');

module.exports = config;
Loading