From 701cf184b3d01f8960934aba8248be3c3f6f1217 Mon Sep 17 00:00:00 2001 From: Hannes Giesenow Date: Mon, 18 Dec 2023 16:02:32 +0100 Subject: [PATCH] Icon selection dropdown --- Resources/encore/ez.config.manager.js | 29 +++++++ Resources/public/css/elbformat-icon-edit.css | 84 +++++++++++++++++++ .../public/css/elbformat-icon-preview.css | 12 +++ Resources/public/js/elbformat-icon-edit.js | 60 +++++++++++++ config/services.yaml | 15 ++-- doc/development.md | 21 ++++- src/ElbformatIconBundle.php | 4 + src/Form/Type/IconType.php | 22 ++++- src/IconSet/IconSetManager.php | 2 +- templates/icon.html.twig | 5 +- templates/icon_field.html.twig | 13 ++- 11 files changed, 248 insertions(+), 19 deletions(-) create mode 100644 Resources/encore/ez.config.manager.js create mode 100644 Resources/public/css/elbformat-icon-edit.css create mode 100644 Resources/public/css/elbformat-icon-preview.css create mode 100644 Resources/public/js/elbformat-icon-edit.js diff --git a/Resources/encore/ez.config.manager.js b/Resources/encore/ez.config.manager.js new file mode 100644 index 0000000..26f1b62 --- /dev/null +++ b/Resources/encore/ez.config.manager.js @@ -0,0 +1,29 @@ +// See https://doc.ibexa.co/en/3.3/extending/import_assets_from_bundle/#configuration-from-main-project-files +const path = require('path'); + +module.exports = (eZConfig, eZConfigManager) => { + // Edit + eZConfigManager.add({ + eZConfig, + entryName: 'ezplatform-admin-ui-content-edit-parts-js', + newItems: [ + path.resolve(__dirname, '../public/js/elbformat-icon-edit.js') + ] + }); + eZConfigManager.add({ + eZConfig, + entryName: 'ezplatform-admin-ui-content-edit-parts-css', + newItems: [ + path.resolve(__dirname, '../public/css/elbformat-icon-edit.css') + ] + }); + // Preview + eZConfigManager.add({ + eZConfig, + entryName: 'ezplatform-admin-ui-layout-css', + newItems: [ + path.resolve(__dirname, '../public/css/elbformat-icon-preview.css') + ] + }); + +}; \ No newline at end of file diff --git a/Resources/public/css/elbformat-icon-edit.css b/Resources/public/css/elbformat-icon-edit.css new file mode 100644 index 0000000..f8fcb36 --- /dev/null +++ b/Resources/public/css/elbformat-icon-edit.css @@ -0,0 +1,84 @@ +/* Inspired by https://codepen.io/giannisrig/pen/ywBWOV */ +.elbformat-icon-select { + display: none; +} +.icon-dropdown-wrapper { + width: 100%; + max-width: 34.375rem; + height: calc(1.5em + 0.75rem + 2px); + position: relative; + border:1px solid #878b90; +} + .icon-dropdown-wrapper .icon-dropdown-trigger { + width: 100%; + height: 100%; + background-color: #fff; + border: 0; + padding: 2px 10px; + transition: 0.2s ease-in; + cursor:pointer; + text-align: left; + } + .icon-dropdown-wrapper .icon-dropdown-trigger::before { + content: '˅'; + position: absolute; + top: 7px; + right: 10px; + } + .icon-dropdown-wrapper.open .icon-dropdown-trigger::before { + top: 5px; + right: 10px; + transform: rotate(180deg); + } + .icon-dropdown-wrapper .icon-dropdown-trigger .icon-container { + width: 50px; + height: 30px; + padding-right: 10px; + display: inline-block; + } + + .icon-dropdown-wrapper .icon-dropdown-list { + width: 100%; + display:none; + z-index: 1; + position: absolute; + background-color: #fff; + left: 0; + top: 37px; + box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3); + max-height: 300px; + overflow: scroll; + } + .icon-dropdown-wrapper.open .icon-dropdown-list { + display:block; + } + + .icon-dropdown-wrapper .icon-dropdown-trigger button, + .icon-dropdown-wrapper .icon-dropdown-list button { + width: 100%; + height: 54px; + line-height: 54px; + border: 0; + padding: 2px 10px; + cursor: pointer; + transition:0.2s ease-in; + text-align: left; + overflow: hidden; + } + .icon-dropdown-wrapper .icon-dropdown-list button:hover { + background-color:#e5e5e5; + } + .icon-dropdown-wrapper .icon-dropdown-list button:not(:last-child){ + border-bottom: 1px solid #e5e5e5; + } + .icon-dropdown-wrapper .icon-dropdown-list .icon-container { + width: 50px; + height: 50px; + padding-right: 10px; + display: inline-block; + } + .icon-dropdown-wrapper .icon-dropdown-trigger .icon-container img, + .icon-dropdown-wrapper .icon-dropdown-list .icon-container img { + max-width: 90%; + max-height: 90%; + } diff --git a/Resources/public/css/elbformat-icon-preview.css b/Resources/public/css/elbformat-icon-preview.css new file mode 100644 index 0000000..4519005 --- /dev/null +++ b/Resources/public/css/elbformat-icon-preview.css @@ -0,0 +1,12 @@ +.ez-content-preview .elbformat-icon { + text-align: left; +} + .ez-content-preview .elbformat-icon .icon-container { + height: 18px; + width: 30px; + display: inline-block; + } + .ez-content-preview .elbformat-icon .icon-container img { + max-height: 90%; + max-width: 90%; + } diff --git a/Resources/public/js/elbformat-icon-edit.js b/Resources/public/js/elbformat-icon-edit.js new file mode 100644 index 0000000..c4c0a37 --- /dev/null +++ b/Resources/public/js/elbformat-icon-edit.js @@ -0,0 +1,60 @@ +// Inspired by https://codepen.io/giannisrig/pen/ywBWOV +window.addEventListener('load', function() { + document.querySelectorAll('.elbformat-icon-select').forEach((iconDropdown) => { + // Hidden field to carry the form data + let hiddenField = document.createElement('input'); + hiddenField.setAttribute('type','hidden'); + hiddenField.setAttribute('name', iconDropdown.getAttribute('name')); + let value = iconDropdown.value || ''; + hiddenField.setAttribute('value', value); + iconDropdown.after(hiddenField); + + // Wrapper for new widget + let wrapper = document.createElement('div'); + wrapper.classList.add('icon-dropdown-wrapper'); + iconDropdown.after(wrapper); + + let button = document.createElement('button'); + button.classList.add('icon-dropdown-trigger'); + wrapper.appendChild(button); + let iconList = document.createElement('div'); + iconList.classList.add('icon-dropdown-list'); + wrapper.appendChild(iconList); + + // Extract icons + let items = JSON.parse(iconDropdown.getAttribute('data-choices')); + for (const [key, tmpl] of Object.entries(items)) { + let container = document.createElement('button'); + container.setAttribute('value', key) + let iconContainer = document.createElement('div'); + iconContainer.classList.add('icon-container'); + iconContainer.innerHTML = tmpl; + container.appendChild(iconContainer); + container.innerHTML+=key + iconList.appendChild(container); + if (value===key) { + button.innerHTML = container.innerHTML; + } + // Click on entry + container.addEventListener('click',(e) => { + button.innerHTML = container.innerHTML; + hiddenField.value = key; + wrapper.classList.remove('open'); + e.preventDefault(); + return false; + }); + }; + + // Open/Close + button.addEventListener('click',(e) => { + if (wrapper.classList.contains('open')) { + wrapper.classList.remove('open'); + } else { + wrapper.classList.add('open'); + } + e.preventDefault(); + return false; + }); + }); + +}); \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 16df04e..b3c1c91 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -1,5 +1,5 @@ services: - Elbformat\IbexaIconFieldtype\FieldType\Icon\Type: + Elbformat\IconBundle\FieldType\Icon\Type: arguments: $serializer: '@eZ\Publish\SPI\FieldType\ValueSerializerInterface' $validator: '@Symfony\Component\Validator\Validator\ValidatorInterface' @@ -8,22 +8,23 @@ services: - { name: ezplatform.field_type.form_mapper.value, fieldType: icon } - { name: ezplatform.field_type.form_mapper.definition, fieldType: icon } - Elbformat\IbexaIconFieldtype\FieldType\Icon\SearchField: + Elbformat\IconBundle\FieldType\Icon\SearchField: class: '%ezpublish.fieldType.indexable.unindexed.class%' tags: - { name: ezplatform.field_type.indexable, alias: icon } - Elbformat\IbexaIconFieldtype\Form\Type\IconType: + Elbformat\IconBundle\Form\Type\IconType: arguments: - $iconSetManager: '@Elbformat\IbexaIconFieldtype\IconSet\IconSetManager' + $iconSetManager: '@Elbformat\IconBundle\IconSet\IconSetManager' + $twig: '@twig' tags: - { name: form.type } - Elbformat\IbexaIconFieldtype\Form\Type\IconSettingsType: + Elbformat\IconBundle\Form\Type\IconSettingsType: arguments: - $iconSetManager: '@Elbformat\IbexaIconFieldtype\IconSet\IconSetManager' + $iconSetManager: '@Elbformat\IconBundle\IconSet\IconSetManager' tags: - { name: form.type } - Elbformat\IbexaIconFieldtype\IconSet\IconSetManager: + Elbformat\IconBundle\IconSet\IconSetManager: arguments: $configs: [] # Will be set from bundle config diff --git a/doc/development.md b/doc/development.md index f0b2230..3f7b1a5 100644 --- a/doc/development.md +++ b/doc/development.md @@ -1,3 +1,4 @@ +## Local development For local development you can use docker-compose. ```bash docker-compose run php sh @@ -13,4 +14,22 @@ export XDEBUG_MODE="debug" Run tests ```bash vendor/bin/phpunit -``` \ No newline at end of file +``` + +## In-Place development +If you want to test out how it integrates into ibexa, it's the easiest way to integrate the bundle into your project directly. +By adding it as "vcs" your are able to push the changes you made right from your vendor folder. +Add the following to your `composer.json` +```json +{ + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/elbformat/icon-bundle" + } + ] +} +``` +and then run `composer require --prefer-source elbformat/icon-bundle:dev-main`. + +Make sure you have **git** installed inside docker, when usin a docker setup. \ No newline at end of file diff --git a/src/ElbformatIconBundle.php b/src/ElbformatIconBundle.php index 1382191..4021ce0 100644 --- a/src/ElbformatIconBundle.php +++ b/src/ElbformatIconBundle.php @@ -7,5 +7,9 @@ class ElbformatIconBundle extends Bundle { + public function getPath(): string + { + return realpath(__DIR__.'/..'); + } } \ No newline at end of file diff --git a/src/Form/Type/IconType.php b/src/Form/Type/IconType.php index 2fbf33e..007dcee 100644 --- a/src/Form/Type/IconType.php +++ b/src/Form/Type/IconType.php @@ -9,19 +9,33 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Twig\Environment; final class IconType extends AbstractType { public function __construct( private readonly IconSetManager $iconSetManager, - ) { } + private readonly Environment $twig, + ) + { + } public function buildForm(FormBuilderInterface $builder, array $options): void { $iconSet = $options['icon_set']; - $builder->add('icon', ChoiceType::class,[ - 'choices' => $this->iconSetManager->getSet($iconSet)->getList() + $iconList = $this->iconSetManager->getSet($iconSet)->getList(); + $iconTemplates = []; + foreach($iconList as $icon) { + $iconTemplates[$icon] = $this->twig->render('@ElbformatIconFieldtype/icon.html.twig',['icon' => $icon,'iconset' => $iconSet]); + } + $builder->add('icon', ChoiceType::class, [ + 'choices' => $iconList, + 'label' => false, + 'attr' => [ + 'class' => 'elbformat-icon-select', + 'data-choices' => json_encode($iconTemplates) + ] ]); } @@ -31,6 +45,6 @@ public function configureOptions(OptionsResolver $resolver): void 'data_class' => Value::class, 'icon_set' => null, ]); - $resolver->addAllowedTypes('icon_set','string'); + $resolver->addAllowedTypes('icon_set', 'string'); } } \ No newline at end of file diff --git a/src/IconSet/IconSetManager.php b/src/IconSet/IconSetManager.php index 018bae7..b408816 100644 --- a/src/IconSet/IconSetManager.php +++ b/src/IconSet/IconSetManager.php @@ -19,7 +19,7 @@ public function __construct(array $configs) $items = $setConfig['items']; } if (null !== ($setConfig['folder']??null)) { - $finder = (new Finder())->files()->in($setConfig['folder']); + $finder = (new Finder())->files()->in($setConfig['folder'])->depth(0); if (null !== ($setConfig['pattern']??null)) { $finder = $finder->name($setConfig['pattern']); } diff --git a/templates/icon.html.twig b/templates/icon.html.twig index 8189c42..c48764c 100644 --- a/templates/icon.html.twig +++ b/templates/icon.html.twig @@ -5,8 +5,9 @@ @var iconset string #} {% if icon is not null %} - {% if "set2" == iconset %} - + + {% else %} + {% endif %} {% endif %} \ No newline at end of file diff --git a/templates/icon_field.html.twig b/templates/icon_field.html.twig index 2611835..c7dadab 100644 --- a/templates/icon_field.html.twig +++ b/templates/icon_field.html.twig @@ -1,6 +1,11 @@ {% block icon_field %} - {{ include('@ElbformatIconFieldtype/icon.html.twig', { - icon: field.value.icon, - iconset: fieldSettings.iconset - }, with_context=false) }} +
+
+ {{ include('@ElbformatIconFieldtype/icon.html.twig', { + icon: field.value.icon, + iconset: fieldSettings.iconset + }, with_context=false) }} +
+ {{ field.value.icon }} +
{% endblock %}