From 3d24de23733f2f4a6940a1f8757723ba0e162b03 Mon Sep 17 00:00:00 2001 From: spleshka Date: Wed, 27 Dec 2017 21:31:59 +0300 Subject: [PATCH 1/6] Added initial version of the modal popup. --- image_widget_crop.libraries.yml | 6 + js/ImageWidgetModalCrop.js | 27 +++ src/Element/ImageCrop.php | 8 +- src/Element/ImageCropModal.php | 164 ++++++++++++++++++ .../FieldWidget/ImageCropModalWidget.php | 87 ++++++++++ 5 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 js/ImageWidgetModalCrop.js create mode 100644 src/Element/ImageCropModal.php create mode 100644 src/Plugin/Field/FieldWidget/ImageCropModalWidget.php diff --git a/image_widget_crop.libraries.yml b/image_widget_crop.libraries.yml index 0c50bce..3f1d362 100644 --- a/image_widget_crop.libraries.yml +++ b/image_widget_crop.libraries.yml @@ -19,3 +19,9 @@ cropper.integration: - core/drupal - core/drupal.debounce - image_widget_crop/cropper + +modal: + js: + js/ImageWidgetModalCrop.js: {} + dependencies: + - core/drupal.dialog.ajax diff --git a/js/ImageWidgetModalCrop.js b/js/ImageWidgetModalCrop.js new file mode 100644 index 0000000..603fb8e --- /dev/null +++ b/js/ImageWidgetModalCrop.js @@ -0,0 +1,27 @@ +/** + * @file + * Defines the behaviors needed for modal widget integration. + */ + +(function ($, Drupal) { + 'use strict'; + + Drupal.behaviors.imageWidgetModalCrop = { + attach: function (context) { + + $('.image-crop-apply', context).click(function() { + var $wrapper = $('.crop-preview-wrapper__value', context); + $.each($('input', $wrapper), function(key, input) { + var name = $(input).attr('name'); + var value = $(input).attr('value'); + $('input[name="' + name + '"]').val(value); + }); + + Drupal.dialog('#drupal-modal').close(); + + }); + + } + }; + +}(jQuery, Drupal)); diff --git a/src/Element/ImageCrop.php b/src/Element/ImageCrop.php index 7b33de9..afd427e 100644 --- a/src/Element/ImageCrop.php +++ b/src/Element/ImageCrop.php @@ -35,7 +35,7 @@ public function getInfo() { '#show_default_crop' => TRUE, '#show_crop_area' => FALSE, '#attached' => [ - 'library' => 'image_widget_crop/cropper.integration', + 'library' => ['image_widget_crop/cropper.integration'], ], '#tree' => TRUE, ]; @@ -180,6 +180,7 @@ public static function processCrop(array &$element, FormStateInterface $form_sta 'data-drupal-iwc' => 'reset', ], '#weight' => -10, + //'#access' => FALSE, ]; // Generation of html List with image & crop information. @@ -241,6 +242,11 @@ public static function processCrop(array &$element, FormStateInterface $form_sta '#value' => $file->id(), ]; } + + /* if (count($crop_types) == 1) { + $element['crop_wrapper'][$list_id]['#access'] = FALSE; + }*/ + } return $element; } diff --git a/src/Element/ImageCropModal.php b/src/Element/ImageCropModal.php new file mode 100644 index 0000000..3e77a52 --- /dev/null +++ b/src/Element/ImageCropModal.php @@ -0,0 +1,164 @@ + 'button', + '#name' => 'modal_crop', + '#value' => t('Edit crop'), + '#ajax' => [ + 'callback' => '\Drupal\image_widget_crop\Element\ImageCropModal::ajaxModal', + ], + ]; + + // Modify the display of the crop area in the default Image Crop Widget. + // We render only "values" here, because this form item is necessary to + // submit form values. All other elements will be shown in the modal dialog. + foreach (Element::children($element['crop_wrapper']) as $crop_type) { + if (empty($element['crop_wrapper'][$crop_type]['crop_container'])) { + continue; + } + $crop_item = &$element['crop_wrapper'][$crop_type]; + + // Covert tab with every crop type into container to avoid visual + // rendering of it in the main form. + $crop_item['#type'] = 'container'; + + // Hide image with crop selection & reset button from the main form. + // We'll show those items in the modal dialog. + $crop_item['crop_container']['image']['#access'] = FALSE; + if (!empty($crop_item['crop_container']['reset'])) { + $crop_item['crop_container']['reset']['#access'] = FALSE; + } + } + + return $element; + } + + /** + * {@inheritdoc} + */ + public function ajaxModal(array &$form, FormStateInterface $form_state) { + + // Get parents of triggering elements. + $parents = $form_state->getTriggeringElement()['#array_parents']; + array_pop($parents); + + $image_crop = NestedArray::getValue($form, $parents); + + // Restore access to crop_reuse message. + if (!empty($image_crop['crop_reuse'])) { + $image_crop['crop_reuse']['#access'] = TRUE; + } + + // We don't want to show the modal button any more. + $image_crop['modal_button']['#access'] = FALSE; + + // Remove "display:none" style recently added to hide it on the main form. + // Now we do need to show it in the modal dialog. + unset($image_crop['crop_wrapper']['#attributes']['style']); + + // Restore access to image crop and "Reset" button of every crop type. + foreach (Element::children($image_crop['crop_wrapper']) as $crop_type) { + if (empty($image_crop['crop_wrapper'][$crop_type]['crop_container'])) { + continue; + } + $crop_item = &$image_crop['crop_wrapper'][$crop_type]['crop_container']; + $crop_item['image']['#access'] = TRUE; + if (!empty($crop_item['reset'])) { + $crop_item['reset']['#access'] = TRUE; + } + } + + $image_crop['actions']['submit'] = [ + '#type' => 'submit', + '#value' => t('Apply'), + '#prefix' => '
', + '#suffix' => '
', + '#attributes' => [ + 'class' => ['button--primary', 'image-crop-apply'], + ], + ]; + + // Build out dialog.ui settings. + $settings = []; + $options = ['width', 'height', 'min_width', 'min_height']; + foreach ($options as $option) { + if (!empty($image_crop['#modal_' . $option])) { + $settings[$option] = $image_crop['#modal_' . $option]; + } + } + + // Get dialog title. + $title = $image_crop['#modal_title']; + + $response = new AjaxResponse(); + $response->addCommand(new OpenModalDialogCommand($title, $image_crop, $settings)); + return $response; + } + +} diff --git a/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php b/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php new file mode 100644 index 0000000..05b21b2 --- /dev/null +++ b/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php @@ -0,0 +1,87 @@ + t('Crop the image'), + 'modal_width' => 0, + 'modal_height' => 0, + 'modal_min_width' => 200, + 'modal_min_height' => 0, + ] + parent::defaultSettings(); + } + + /** + * Form API callback: Processes a image_crop_modal field element. + * + * Expands the image_image type to include the alt and title fields. + * + * This method is assigned as a #process callback in formElement() method. + * + * @return array + * The elements with parents fields. + */ + public static function process($element, FormStateInterface $form_state, $form) { + $element = parent::process($element, $form_state, $form); + + // Add modal specific settings. + if (!empty($element['image_crop'])) { + + // Change the form item type to modal crop. + $element['image_crop']['#type'] = 'image_crop_modal'; + + $element['image_crop'] += [ + '#modal_title' => $element['#modal_title'], + '#modal_width' => $element['#modal_width'], + '#modal_height' => $element['#modal_height'], + '#modal_min_width' => $element['#modal_min_width'], + '#modal_min_height' => $element['#modal_min_height'], + ]; + } + + return $element; + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + return parent::settingsForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { + $element = parent::formElement($items, $delta, $element, $form, $form_state); + + // Add modal specific settings. + $options = ['title', 'width', 'height', 'min_width', 'min_height']; + foreach ($options as $option) { + $element['#modal_' . $option] = $this->getSetting('modal_' . $option); + } + + return $element; + } + +} From bdac3b3f520613c8ad40913031a31582612ef95a Mon Sep 17 00:00:00 2001 From: spleshka Date: Wed, 27 Dec 2017 22:03:53 +0300 Subject: [PATCH 2/6] Added widget settings. --- config/schema/image_widget_crop.schema.yml | 43 ++++++++++ .../FieldWidget/ImageCropModalWidget.php | 78 ++++++++++++++++++- 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/config/schema/image_widget_crop.schema.yml b/config/schema/image_widget_crop.schema.yml index 3838b64..d73e80e 100644 --- a/config/schema/image_widget_crop.schema.yml +++ b/config/schema/image_widget_crop.schema.yml @@ -58,3 +58,46 @@ image_widget_crop.settings: show_crop_area: type: boolean label: 'Always expand crop area' + +field.widget.settings.image_widget_modal_crop: + type: mapping + label: 'Image field display format settings' + mapping: + progress_indicator: + type: string + label: 'Progress indicator' + preview_image_style: + type: string + label: 'Preview image style' + crop_preview_image_style: + type: string + label: 'Preview crop image style' + crop_list: + type: sequence + label: 'The preview image will be cropped' + sequence: + type: string + warn_multiple_usages: + type: boolean + label: 'Warn user when a file have multiple usages' + show_crop_area: + type: boolean + label: 'Always expand crop area' + show_default_crop: + type: boolean + label: 'Show default crop area' + modal_title: + type: string + label: 'Modal dialog title' + modal_width: + type: integer + label: 'Modal dialog width' + modal_height: + type: integer + label: 'Modal dialog height' + modal_min_width: + type: integer + label: 'Modal dialog min width' + modal_min_height: + type: integer + label: 'Modal dialog min height' diff --git a/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php b/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php index 05b21b2..092bfa3 100644 --- a/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php +++ b/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php @@ -66,7 +66,81 @@ public static function process($element, FormStateInterface $form_state, $form) * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state) { - return parent::settingsForm($form, $form_state); + $element = parent::settingsForm($form, $form_state); + + $element['modal'] = [ + '#type' => 'details', + '#title' => t('Modal dialog settings'), + '#open' => TRUE, + ]; + + $element['modal']['modal_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Title'), + '#default_value' => $this->getSetting('modal_title'), + ]; + + $element['modal']['size'] = [ + '#type' => 'fieldset', + '#title' => t('Width X Height'), + '#attributes' => [ + 'class' => ['container-inline'], + ], + ]; + + $element['modal']['size']['modal_width'] = [ + '#type' => 'textfield', + '#title' => $this->t('Width'), + '#title_display' => 'invisible', + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_width'), + '#suffix' => ' x ', + ]; + + $element['modal']['size']['modal_height'] = [ + '#type' => 'textfield', + '#title' => $this->t('Height'), + '#title_display' => 'invisible', + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_height'), + '#suffix' => 'px', + ]; + + $element['modal']['min_size'] = [ + '#type' => 'fieldset', + '#title' => t('Min width X Min height'), + '#attributes' => [ + 'class' => ['container-inline'], + ], + ]; + + $element['modal']['min_size']['modal_min_width'] = [ + '#type' => 'textfield', + '#title' => $this->t('Min width'), + '#title_display' => 'invisible', + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_min_width'), + '#suffix' => ' x ', + ]; + + $element['modal']['min_size']['modal_min_height'] = [ + '#type' => 'textfield', + '#title' => $this->t('Min height'), + '#title_display' => 'invisible', + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_min_height'), + '#suffix' => 'px', + ]; + + $element['modal']['help'] = [ + '#markup' => t('Leave 0 or blank to scale modal dialog automatically.'), + ]; + + return $element; } /** @@ -75,6 +149,8 @@ public function settingsForm(array $form, FormStateInterface $form_state) { public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $element = parent::formElement($items, $delta, $element, $form, $form_state); + $s = $this->getSettings(); + // Add modal specific settings. $options = ['title', 'width', 'height', 'min_width', 'min_height']; foreach ($options as $option) { From 08d880591ae53aee61b1ed7340ecc60d45d06c27 Mon Sep 17 00:00:00 2001 From: spleshka Date: Thu, 28 Dec 2017 10:44:14 +0300 Subject: [PATCH 3/6] Fixed several bugs, added option to hide reset_crop button. --- README.md | 2 + config/install/image_widget_crop.settings.yml | 1 + config/schema/image_widget_crop.schema.yml | 9 ++ image_widget_crop.module | 1 + ..._display.media.image_with_crop.default.yml | 1 + ...y.node.crop_responsive_example.default.yml | 1 + ...splay.node.crop_simple_example.default.yml | 1 + .../optional/image_widget_crop.settings.yml | 1 + .../image_widget_crop_examples.install | 1 + .../src/Form/ImageWidgetCropExamplesForm.php | 1 + src/Element/ImageCrop.php | 34 +++---- src/Element/ImageCropModal.php | 14 ++- src/Form/CropWidgetForm.php | 7 ++ .../FieldWidget/ImageCropModalWidget.php | 88 +++++++++---------- .../Field/FieldWidget/ImageCropWidget.php | 13 ++- 15 files changed, 112 insertions(+), 63 deletions(-) diff --git a/README.md b/README.md index 6183efc..a531d5f 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ $form['image_crop'] = [ '#crop_preview_image_style' => $crop_config->get('settings.crop_preview_ image_style'), '#show_default_crop' => $crop_config->get('settings.show_default_crop'), + '#show_reset_crop' => $crop_config->get('settings.show_reset_crop'), '#show_crop_area' => $crop_config->get('settings.show_crop_area'), '#warn_mupltiple_usages' => $crop_config->get('settings.warn_ mupltiple_usages'), @@ -160,6 +161,7 @@ $form['image_crop'] = [ '#crop_type_list' => ['crop_16_9', 'crop_free'], '#crop_preview_image_style' => 'crop_thumbnail', '#show_default_crop' => FALSE, + '#show_reset_crop' => FALSE, '#show_crop_area' => FALSE, '#warn_mupltiple_usages' => FALSE, ]; diff --git a/config/install/image_widget_crop.settings.yml b/config/install/image_widget_crop.settings.yml index 1886598..6268e51 100644 --- a/config/install/image_widget_crop.settings.yml +++ b/config/install/image_widget_crop.settings.yml @@ -5,3 +5,4 @@ settings: crop_list: [] warn_multiple_usages: FALSE show_default_crop: TRUE + show_reset_crop: TRUE diff --git a/config/schema/image_widget_crop.schema.yml b/config/schema/image_widget_crop.schema.yml index d73e80e..cd5dc78 100644 --- a/config/schema/image_widget_crop.schema.yml +++ b/config/schema/image_widget_crop.schema.yml @@ -27,6 +27,9 @@ field.widget.settings.image_widget_crop: show_default_crop: type: boolean label: 'Show default crop area' + show_reset_crop: + type: boolean + label: 'Show "Reset crop" button' image_widget_crop.settings: type: config_object @@ -55,6 +58,9 @@ image_widget_crop.settings: show_default_crop: type: boolean label: 'Show default crop area' + show_reset_crop: + type: boolean + label: 'Show "Reset crop" button' show_crop_area: type: boolean label: 'Always expand crop area' @@ -86,6 +92,9 @@ field.widget.settings.image_widget_modal_crop: show_default_crop: type: boolean label: 'Show default crop area' + show_reset_crop: + type: boolean + label: 'Show "Reset crop" button' modal_title: type: string label: 'Modal dialog title' diff --git a/image_widget_crop.module b/image_widget_crop.module index 0078b58..e9153a0 100644 --- a/image_widget_crop.module +++ b/image_widget_crop.module @@ -154,6 +154,7 @@ function image_widget_crop_form_file_form_alter(&$form, FormStateInterface $form '#crop_type_list' => $crop_config->get('settings.crop_list'), '#crop_preview_image_style' => $crop_config->get('settings.crop_preview_image_style'), '#show_default_crop' => $crop_config->get('settings.show_default_crop'), + '#show_reset_crop' => $crop_config->get('settings.show_reset_crop'), '#warn_mupltiple_usages' => $crop_config->get('settings.warn_mupltiple_usages'), ]; $form['actions']['submit']['#submit'][] = 'image_widget_crop_form_submit'; diff --git a/modules/image_widget_crop_examples/config/optional/core.entity_form_display.media.image_with_crop.default.yml b/modules/image_widget_crop_examples/config/optional/core.entity_form_display.media.image_with_crop.default.yml index e48b260..a13c70a 100644 --- a/modules/image_widget_crop_examples/config/optional/core.entity_form_display.media.image_with_crop.default.yml +++ b/modules/image_widget_crop_examples/config/optional/core.entity_form_display.media.image_with_crop.default.yml @@ -23,6 +23,7 @@ content: field_media_image: settings: show_default_crop: true + show_reset_crop: true warn_multiple_usages: true preview_image_style: thumbnail crop_preview_image_style: crop_thumbnail diff --git a/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_responsive_example.default.yml b/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_responsive_example.default.yml index dbbbb8e..67037bf 100644 --- a/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_responsive_example.default.yml +++ b/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_responsive_example.default.yml @@ -36,6 +36,7 @@ content: weight: 1 settings: show_default_crop: true + show_reset_crop: true warn_multiple_usages: true preview_image_style: thumbnail crop_preview_image_style: crop_thumbnail diff --git a/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_simple_example.default.yml b/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_simple_example.default.yml index e033fcb..57fe341 100644 --- a/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_simple_example.default.yml +++ b/modules/image_widget_crop_examples/config/optional/core.entity_form_display.node.crop_simple_example.default.yml @@ -37,6 +37,7 @@ content: settings: show_crop_area: true show_default_crop: true + show_reset_crop: true warn_multiple_usages: true preview_image_style: thumbnail crop_preview_image_style: crop_thumbnail diff --git a/modules/image_widget_crop_examples/config/optional/image_widget_crop.settings.yml b/modules/image_widget_crop_examples/config/optional/image_widget_crop.settings.yml index 2478f47..4844a9a 100644 --- a/modules/image_widget_crop_examples/config/optional/image_widget_crop.settings.yml +++ b/modules/image_widget_crop_examples/config/optional/image_widget_crop.settings.yml @@ -8,6 +8,7 @@ settings: crop_free_ratio: crop_free_ratio warn_multiple_usages: false show_default_crop: true + show_reset_crop: true _core: default_config_hash: QjLJUHJAzic-efSYtJnhoz7in-eT3A1PriLW3i11DFg langcode: en diff --git a/modules/image_widget_crop_examples/image_widget_crop_examples.install b/modules/image_widget_crop_examples/image_widget_crop_examples.install index 70a79de..c7bf94b 100644 --- a/modules/image_widget_crop_examples/image_widget_crop_examples.install +++ b/modules/image_widget_crop_examples/image_widget_crop_examples.install @@ -20,6 +20,7 @@ function image_widget_crop_examples_install() { ->getEditable('image_widget_crop.settings') ->set('settings.crop_preview_image_style', 'crop_thumbnail') ->set('settings.show_default_crop', TRUE) + ->set('settings.show_reset_crop', TRUE) ->set('settings.warn_multiple_usages', FALSE) ->set('settings.crop_list', [ 'crop_16_9' => 'crop_16_9', diff --git a/modules/image_widget_crop_examples/src/Form/ImageWidgetCropExamplesForm.php b/modules/image_widget_crop_examples/src/Form/ImageWidgetCropExamplesForm.php index f3b796a..d805de8 100644 --- a/modules/image_widget_crop_examples/src/Form/ImageWidgetCropExamplesForm.php +++ b/modules/image_widget_crop_examples/src/Form/ImageWidgetCropExamplesForm.php @@ -122,6 +122,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#crop_type_list' => ['crop_16_9'], '#crop_preview_image_style' => 'crop_thumbnail', '#show_default_crop' => TRUE, + '#show_reset_crop' => TRUE, '#show_crop_area' => FALSE, '#warn_mupltiple_usages' => TRUE, ]; diff --git a/src/Element/ImageCrop.php b/src/Element/ImageCrop.php index afd427e..166d22b 100644 --- a/src/Element/ImageCrop.php +++ b/src/Element/ImageCrop.php @@ -33,6 +33,7 @@ public function getInfo() { '#crop_type_list' => [], '#warn_multiple_usages' => FALSE, '#show_default_crop' => TRUE, + '#show_reset_crop' => TRUE, '#show_crop_area' => FALSE, '#attached' => [ 'library' => ['image_widget_crop/cropper.integration'], @@ -95,6 +96,11 @@ public static function processCrop(array &$element, FormStateInterface $form_sta $available_crop_types = $iwc_manager->getAvailableCropType(CropType::getCropTypeNames()); $crop_type_list = array_keys($available_crop_types); } + + if (!is_array($crop_type_list)) { + $crop_type_list = [$crop_type_list]; + } + $element['crop_wrapper'] = [ '#type' => 'details', '#title' => t('Crop image'), @@ -172,16 +178,17 @@ public static function processCrop(array &$element, FormStateInterface $form_sta '#weight' => -10, ]; - $element['crop_wrapper'][$type]['crop_container']['reset'] = [ - '#type' => 'button', - '#value' => t('Reset crop'), - '#attributes' => [ - 'class' => ['crop-preview-wrapper__crop-reset'], - 'data-drupal-iwc' => 'reset', - ], - '#weight' => -10, - //'#access' => FALSE, - ]; + if ($element['#show_reset_crop']) { + $element['crop_wrapper'][$type]['crop_container']['reset'] = [ + '#type' => 'button', + '#value' => t('Reset crop'), + '#attributes' => [ + 'class' => ['crop-preview-wrapper__crop-reset'], + 'data-drupal-iwc' => 'reset', + ], + '#weight' => -10, + ]; + } // Generation of html List with image & crop information. $element['crop_wrapper'][$type]['crop_container']['values'] = [ @@ -242,11 +249,6 @@ public static function processCrop(array &$element, FormStateInterface $form_sta '#value' => $file->id(), ]; } - - /* if (count($crop_types) == 1) { - $element['crop_wrapper'][$list_id]['#access'] = FALSE; - }*/ - } return $element; } @@ -402,7 +404,7 @@ public static function validateHardLimit(array $element, FormStateInterface $for '@hard_limit' => $hard_limit[$element_name], '@crop_name' => $crop_type->label(), ] - )); + )); } } } diff --git a/src/Element/ImageCropModal.php b/src/Element/ImageCropModal.php index 3e77a52..8ef3386 100644 --- a/src/Element/ImageCropModal.php +++ b/src/Element/ImageCropModal.php @@ -80,6 +80,9 @@ public static function processCrop(array &$element, FormStateInterface $form_sta // submit form values. All other elements will be shown in the modal dialog. foreach (Element::children($element['crop_wrapper']) as $crop_type) { if (empty($element['crop_wrapper'][$crop_type]['crop_container'])) { + if ($element['crop_wrapper'][$crop_type]['#type'] == 'vertical_tabs') { + $element['crop_wrapper'][$crop_type]['#access'] = FALSE; + } continue; } $crop_item = &$element['crop_wrapper'][$crop_type]; @@ -88,6 +91,14 @@ public static function processCrop(array &$element, FormStateInterface $form_sta // rendering of it in the main form. $crop_item['#type'] = 'container'; + + // That's a small trick to generate (if not exist) & preload image in + // crop image style ahead of display in popup. It prevents popup from + // jump on load and users from waiting for image to generate. + $crop_item['crop_container']['image_preload'] = $crop_item['crop_container']['image']; + unset($crop_item['crop_container']['image_preload']['#attributes']['data-drupal-iwc']); + $crop_item['crop_container']['image_preload']['#attributes']['style'] = 'display:none'; + // Hide image with crop selection & reset button from the main form. // We'll show those items in the modal dialog. $crop_item['crop_container']['image']['#access'] = FALSE; @@ -149,7 +160,8 @@ public function ajaxModal(array &$form, FormStateInterface $form_state) { $options = ['width', 'height', 'min_width', 'min_height']; foreach ($options as $option) { if (!empty($image_crop['#modal_' . $option])) { - $settings[$option] = $image_crop['#modal_' . $option]; + $dialogOption = str_replace(['min_width', 'min_height'], ['minWidth', 'minHeight'], $option); + $settings[$dialogOption] = $image_crop['#modal_' . $option]; } } diff --git a/src/Form/CropWidgetForm.php b/src/Form/CropWidgetForm.php index e9df20d..03c9608 100644 --- a/src/Form/CropWidgetForm.php +++ b/src/Form/CropWidgetForm.php @@ -181,6 +181,12 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $this->settings->get('settings.show_default_crop'), ]; + $form['image_crop']['show_reset_crop'] = [ + '#title' => $this->t('Show "Reset crop" button'), + '#type' => 'checkbox', + '#default_value' => $this->settings->get('settings.show_reset_crop'), + ]; + return parent::buildForm($form, $form_state); } @@ -243,6 +249,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { ->set("settings.css_url", $form_state->getValue('css_url')) ->set("settings.crop_preview_image_style", $form_state->getValue('crop_preview_image_style')) ->set("settings.show_default_crop", $form_state->getValue('show_default_crop')) + ->set("settings.show_reset_crop", $form_state->getValue('show_reset_crop')) ->set("settings.show_crop_area", $form_state->getValue('show_crop_area')) ->set("settings.warn_multiple_usages", $form_state->getValue('warn_multiple_usages')) ->set("settings.crop_list", $form_state->getValue('crop_list')); diff --git a/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php b/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php index 092bfa3..7d8cde0 100644 --- a/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php +++ b/src/Plugin/Field/FieldWidget/ImageCropModalWidget.php @@ -68,89 +68,87 @@ public static function process($element, FormStateInterface $form_state, $form) public function settingsForm(array $form, FormStateInterface $form_state) { $element = parent::settingsForm($form, $form_state); - $element['modal'] = [ - '#type' => 'details', - '#title' => t('Modal dialog settings'), - '#open' => TRUE, - ]; - - $element['modal']['modal_title'] = [ + $element['modal_title'] = [ '#type' => 'textfield', - '#title' => $this->t('Title'), + '#title' => $this->t('Modal dialog title'), '#default_value' => $this->getSetting('modal_title'), ]; - $element['modal']['size'] = [ - '#type' => 'fieldset', - '#title' => t('Width X Height'), - '#attributes' => [ - 'class' => ['container-inline'], - ], - ]; - - $element['modal']['size']['modal_width'] = [ + $element['modal_width'] = [ '#type' => 'textfield', - '#title' => $this->t('Width'), - '#title_display' => 'invisible', + '#title' => $this->t('Modal dialog width, px'), + '#description' => t('Leave 0 or blank to calculate width automatically.'), '#size' => 4, '#element_validate' => ['element_validate_integer_positive'], '#default_value' => $this->getSetting('modal_width'), - '#suffix' => ' x ', ]; - $element['modal']['size']['modal_height'] = [ + $element['modal_height'] = [ '#type' => 'textfield', - '#title' => $this->t('Height'), - '#title_display' => 'invisible', + '#title' => $this->t('Modal dialog height, px'), + '#description' => t('Leave 0 or blank to calculate height automatically.'), '#size' => 4, '#element_validate' => ['element_validate_integer_positive'], '#default_value' => $this->getSetting('modal_height'), - '#suffix' => 'px', ]; - $element['modal']['min_size'] = [ - '#type' => 'fieldset', - '#title' => t('Min width X Min height'), - '#attributes' => [ - 'class' => ['container-inline'], - ], - ]; - - $element['modal']['min_size']['modal_min_width'] = [ + $element['modal_min_width'] = [ '#type' => 'textfield', - '#title' => $this->t('Min width'), - '#title_display' => 'invisible', + '#title' => $this->t('Modal dialog min width, px'), + '#description' => t('Leave 0 or blank to calculate min width automatically.'), '#size' => 4, '#element_validate' => ['element_validate_integer_positive'], '#default_value' => $this->getSetting('modal_min_width'), - '#suffix' => ' x ', ]; - $element['modal']['min_size']['modal_min_height'] = [ + $element['modal_min_height'] = [ '#type' => 'textfield', - '#title' => $this->t('Min height'), - '#title_display' => 'invisible', + '#title' => $this->t('Modal dialog min height, px'), + '#description' => t('Leave 0 or blank to calculate min height automatically.'), '#size' => 4, '#element_validate' => ['element_validate_integer_positive'], '#default_value' => $this->getSetting('modal_min_height'), - '#suffix' => 'px', ]; - $element['modal']['help'] = [ - '#markup' => t('Leave 0 or blank to scale modal dialog automatically.'), - ]; + // Currently we limit display of crop types to 1 in the modal dialog. + /// If someone will need more - it's a good place to start from. + $element['crop_list']['#multiple'] = FALSE; return $element; } + /** + * {@inheritdoc} + * + * @return \Drupal\Core\StringTranslation\TranslatableMarkup[] + * A short summary of the widget settings. + */ + public function settingsSummary() { + $preview = parent::settingsSummary(); + + // Get modal widget settings. + $modal_title = $this->getSetting('modal_title'); + $modal_width = $this->getSetting('modal_width'); + $modal_height = $this->getSetting('modal_height'); + $modal_min_width = $this->getSetting('modal_min_width'); + $modal_min_height = $this->getSetting('modal_min_height'); + + // Format messages for each setting. + $preview[] = $this->t('Modal title: @title', ['@title' => $modal_title]); + $preview[] = $this->t('Modal width: @width', ['@width' => $modal_width ? $modal_width . 'px' : t('Auto')]); + $preview[] = $this->t('Modal height: @height', ['@height' => $modal_height ? $modal_height . 'px' : t('Auto')]); + $preview[] = $this->t('Modal min width: @width', ['@width' => $modal_min_width ? $modal_min_width . 'px' : t('Auto')]); + $preview[] = $this->t('Modal min height: @height', ['@height' => $modal_min_height ? $modal_min_height . 'px' : t('Auto')]); + + return $preview; + } + /** * {@inheritdoc} */ public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { $element = parent::formElement($items, $delta, $element, $form, $form_state); - $s = $this->getSettings(); - // Add modal specific settings. $options = ['title', 'width', 'height', 'min_width', 'min_height']; foreach ($options as $option) { diff --git a/src/Plugin/Field/FieldWidget/ImageCropWidget.php b/src/Plugin/Field/FieldWidget/ImageCropWidget.php index 8d3eeb7..255e025 100644 --- a/src/Plugin/Field/FieldWidget/ImageCropWidget.php +++ b/src/Plugin/Field/FieldWidget/ImageCropWidget.php @@ -101,6 +101,7 @@ public static function defaultSettings() { 'crop_list' => NULL, 'show_crop_area' => FALSE, 'show_default_crop' => TRUE, + 'show_reset_crop' => TRUE, 'warn_multiple_usages' => TRUE, ] + parent::defaultSettings(); } @@ -124,6 +125,7 @@ public static function process($element, FormStateInterface $form_state, $form) '#crop_type_list' => $element['#crop_list'], '#crop_preview_image_style' => $element['#crop_preview_image_style'], '#show_default_crop' => $element['#show_default_crop'], + '#show_reset_crop' => $element['#show_reset_crop'], '#show_crop_area' => $element['#show_crop_area'], '#warn_multiple_usages' => $element['#warn_multiple_usages'], ]; @@ -204,6 +206,12 @@ public function settingsForm(array $form, FormStateInterface $form_state) { '#default_value' => $this->getSetting('show_default_crop'), ]; + $element['show_reset_crop'] = [ + '#title' => $this->t('Show "Reset crop" button'), + '#type' => 'checkbox', + '#default_value' => $this->getSetting('show_reset_crop'), + ]; + $element['warn_multiple_usages'] = [ '#title' => $this->t('Warn the user if the crop is used more than once.'), '#type' => 'checkbox', @@ -233,10 +241,12 @@ public function settingsSummary() { $crop_list = $this->getSetting('crop_list'); $crop_show_button = $this->getSetting('show_crop_area'); $show_default_crop = $this->getSetting('show_default_crop'); + $show_reset_crop = $this->getSetting('show_reset_crop'); $warn_multiple_usages = $this->getSetting('warn_multiple_usages'); $preview[] = $this->t('Always expand crop area: @bool', ['@bool' => ($crop_show_button) ? 'Yes' : 'No']); $preview[] = $this->t('Show default crop area: @bool', ['@bool' => ($show_default_crop) ? 'Yes' : 'No']); + $preview[] = $this->t('Show crop reset button: @bool', ['@bool' => ($show_reset_crop) ? 'Yes' : 'No']); $preview[] = $this->t('Warn the user if the crop is used more than once: @bool', ['@bool' => ($warn_multiple_usages) ? 'Yes' : 'No']); if (isset($image_styles[$image_style_setting])) { @@ -251,7 +261,7 @@ public function settingsSummary() { } if (!empty($crop_list)) { - $preview[] = $this->t('Crop Type used: @list', ['@list' => implode(", ", $crop_list)]); + $preview[] = $this->t('Crop Type used: @list', ['@list' => is_array($crop_list) ? implode(", ", $crop_list) : $crop_list]); } return $preview; @@ -269,6 +279,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $element['#crop_preview_image_style'] = $this->getSetting('crop_preview_image_style'); $element['#show_crop_area'] = $this->getSetting('show_crop_area'); $element['#show_default_crop'] = $this->getSetting('show_default_crop'); + $element['#show_reset_crop'] = $this->getSetting('show_reset_crop'); $element['#warn_multiple_usages'] = $this->getSetting('warn_multiple_usages'); return parent::formElement($items, $delta, $element, $form, $form_state); From 1c3e2184824d0eb9c153ed764a76cc4d8cc8a35b Mon Sep 17 00:00:00 2001 From: spleshka Date: Thu, 28 Dec 2017 13:46:34 +0300 Subject: [PATCH 4/6] Fixed bug with multiple crops per page. --- src/Element/ImageCropModal.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Element/ImageCropModal.php b/src/Element/ImageCropModal.php index 8ef3386..44362d4 100644 --- a/src/Element/ImageCropModal.php +++ b/src/Element/ImageCropModal.php @@ -66,10 +66,11 @@ public static function processCrop(array &$element, FormStateInterface $form_sta } // Add a simple button which opens a modal with image crop selection. + $id_prefix = implode('-', $element['#parents']); $element['modal_button'] = [ '#type' => 'button', - '#name' => 'modal_crop', '#value' => t('Edit crop'), + '#name' => strtr($id_prefix, '-', '_') . '_modal', '#ajax' => [ 'callback' => '\Drupal\image_widget_crop\Element\ImageCropModal::ajaxModal', ], From d9ab17b5fd1a9236fd3434f71af8fc78ca348c7b Mon Sep 17 00:00:00 2001 From: spleshka Date: Thu, 28 Dec 2017 15:40:37 +0300 Subject: [PATCH 5/6] Added integration with Entity Browser. --- src/Element/ImageCropModal.php | 3 + .../FieldWidget/ImageCropBrowserWidget.php | 293 ++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100755 src/Plugin/Field/FieldWidget/ImageCropBrowserWidget.php diff --git a/src/Element/ImageCropModal.php b/src/Element/ImageCropModal.php index 44362d4..52e4bfa 100644 --- a/src/Element/ImageCropModal.php +++ b/src/Element/ImageCropModal.php @@ -71,6 +71,9 @@ public static function processCrop(array &$element, FormStateInterface $form_sta '#type' => 'button', '#value' => t('Edit crop'), '#name' => strtr($id_prefix, '-', '_') . '_modal', + '#attributes' => [ + 'class' => ['edit-crop'], + ], '#ajax' => [ 'callback' => '\Drupal\image_widget_crop\Element\ImageCropModal::ajaxModal', ], diff --git a/src/Plugin/Field/FieldWidget/ImageCropBrowserWidget.php b/src/Plugin/Field/FieldWidget/ImageCropBrowserWidget.php new file mode 100755 index 0000000..1011287 --- /dev/null +++ b/src/Plugin/Field/FieldWidget/ImageCropBrowserWidget.php @@ -0,0 +1,293 @@ + 'crop_thumbnail', + 'crop_list' => NULL, + 'show_crop_area' => FALSE, + 'show_default_crop' => TRUE, + 'show_reset_crop' => TRUE, + 'warn_multiple_usages' => TRUE, + 'modal_title' => t('Crop the image'), + 'modal_width' => 0, + 'modal_height' => 0, + 'modal_min_width' => 200, + 'modal_min_height' => 0, + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $element = parent::settingsForm($form, $form_state); + + $element['crop_preview_image_style'] = [ + '#title' => $this->t('Crop preview image style'), + '#type' => 'select', + '#options' => image_style_options(FALSE), + '#default_value' => $this->getSetting('crop_preview_image_style'), + '#description' => $this->t('The preview image will be shown while editing the content.'), + '#weight' => 15, + ]; + + $iwc_manager = \Drupal::service('image_widget_crop.manager'); + $element['crop_list'] = [ + '#title' => $this->t('Crop Type'), + '#type' => 'select', + '#options' => $iwc_manager->getAvailableCropType(CropType::getCropTypeNames()), + '#empty_option' => $this->t('<@no-preview>', ['@no-preview' => $this->t('no preview')]), + '#default_value' => $this->getSetting('crop_list'), + '#multiple' => TRUE, + '#required' => TRUE, + '#description' => $this->t('The type of crop to apply to your image. If your Crop Type not appear here, set an image style use your Crop Type'), + '#weight' => 16, + ]; + + $element['show_crop_area'] = [ + '#title' => $this->t('Always expand crop area'), + '#type' => 'checkbox', + '#default_value' => $this->getSetting('show_crop_area'), + ]; + + $element['show_default_crop'] = [ + '#title' => $this->t('Show default crop area'), + '#type' => 'checkbox', + '#default_value' => $this->getSetting('show_default_crop'), + ]; + + $element['show_reset_crop'] = [ + '#title' => $this->t('Show "Reset crop" button'), + '#type' => 'checkbox', + '#default_value' => $this->getSetting('show_reset_crop'), + ]; + + $element['warn_multiple_usages'] = [ + '#title' => $this->t('Warn the user if the crop is used more than once.'), + '#type' => 'checkbox', + '#default_value' => $this->getSetting('warn_multiple_usages'), + ]; + + $element['modal_title'] = [ + '#type' => 'textfield', + '#title' => $this->t('Modal dialog title'), + '#default_value' => $this->getSetting('modal_title'), + ]; + + $element['modal_width'] = [ + '#type' => 'textfield', + '#title' => $this->t('Modal dialog width, px'), + '#description' => t('Leave 0 or blank to calculate width automatically.'), + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_width'), + ]; + + $element['modal_height'] = [ + '#type' => 'textfield', + '#title' => $this->t('Modal dialog height, px'), + '#description' => t('Leave 0 or blank to calculate height automatically.'), + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_height'), + ]; + + $element['modal_min_width'] = [ + '#type' => 'textfield', + '#title' => $this->t('Modal dialog min width, px'), + '#description' => t('Leave 0 or blank to calculate min width automatically.'), + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_min_width'), + ]; + + $element['modal_min_height'] = [ + '#type' => 'textfield', + '#title' => $this->t('Modal dialog min height, px'), + '#description' => t('Leave 0 or blank to calculate min height automatically.'), + '#size' => 4, + '#element_validate' => ['element_validate_integer_positive'], + '#default_value' => $this->getSetting('modal_min_height'), + ]; + + return $element; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $preview = parent::settingsSummary(); + + $image_styles = image_style_options(FALSE); + // Unset possible 'No defined styles' option. + unset($image_styles['']); + + // Styles could be lost because of enabled/disabled modules that defines + // their styles in code. + $image_style_setting = $this->getSetting('preview_image_style'); + $crop_preview = $image_styles[$this->getSetting('crop_preview_image_style')]; + $crop_list = $this->getSetting('crop_list'); + $crop_show_button = $this->getSetting('show_crop_area'); + $show_default_crop = $this->getSetting('show_default_crop'); + $show_reset_crop = $this->getSetting('show_reset_crop'); + $warn_multiple_usages = $this->getSetting('warn_multiple_usages'); + + $preview[] = $this->t('Always expand crop area: @bool', ['@bool' => ($crop_show_button) ? 'Yes' : 'No']); + $preview[] = $this->t('Show default crop area: @bool', ['@bool' => ($show_default_crop) ? 'Yes' : 'No']); + $preview[] = $this->t('Show crop reset button: @bool', ['@bool' => ($show_reset_crop) ? 'Yes' : 'No']); + $preview[] = $this->t('Warn the user if the crop is used more than once: @bool', ['@bool' => ($warn_multiple_usages) ? 'Yes' : 'No']); + + if (isset($image_styles[$image_style_setting])) { + $preview[] = $this->t('Preview image style: @style', ['@style' => $image_style_setting]); + } + else { + $preview[] = $this->t('No preview image style'); + } + + if (isset($crop_preview)) { + $preview[] = $this->t('Preview crop zone image style: @style', ['@style' => $crop_preview]); + } + + if (!empty($crop_list)) { + $preview[] = $this->t('Crop Type used: @list', ['@list' => is_array($crop_list) ? implode(", ", $crop_list) : $crop_list]); + } + + // Get modal widget settings. + $modal_title = $this->getSetting('modal_title'); + $modal_width = $this->getSetting('modal_width'); + $modal_height = $this->getSetting('modal_height'); + $modal_min_width = $this->getSetting('modal_min_width'); + $modal_min_height = $this->getSetting('modal_min_height'); + + // Format messages for each setting. + $preview[] = $this->t('Modal title: @title', ['@title' => $modal_title]); + $preview[] = $this->t('Modal width: @width', ['@width' => $modal_width ? $modal_width . 'px' : t('Auto')]); + $preview[] = $this->t('Modal height: @height', ['@height' => $modal_height ? $modal_height . 'px' : t('Auto')]); + $preview[] = $this->t('Modal min width: @width', ['@width' => $modal_min_width ? $modal_min_width . 'px' : t('Auto')]); + $preview[] = $this->t('Modal min height: @height', ['@height' => $modal_min_height ? $modal_min_height . 'px' : t('Auto')]); + + return $preview; + } + + /** + * {@inheritdoc} + */ + protected function displayCurrentSelection($details_id, $field_parents, $entities) { + $current = parent::displayCurrentSelection($details_id, $field_parents, $entities); + + unset($current['#header']); + + $extras = ['meta', 'edit_button', 'remove_button']; + foreach (Element::children($current) as $key) { + $row = &$current[$key]; + + $row['widget'] = [ + '#type' => 'container', + ]; + + foreach ($extras as $extra) { + $row['widget'][$extra] = $row[$extra]; + unset($row[$extra]); + } + + $row['widget']['crop_button'] = [ + '#type' => 'image_crop_modal', + '#file' => $row['display']['#file'], + '#crop_type_list' => $this->getSetting('crop_list'), + '#crop_preview_image_style' => 'crop_thumbnail', + '#show_default_crop' => $this->getSetting('show_default_crop'), + '#show_reset_crop' => $this->getSetting('show_reset_crop'), + '#show_crop_area' => $this->getSetting('show_crop_area'), + '#warn_mupltiple_usages' => $this->getSetting('warn_multiple_usages'), + '#modal_title' => $this->getSetting('modal_title'), + '#modal_width' => $this->getSetting('modal_width'), + '#modal_height' => $this->getSetting('modal_height'), + '#modal_min_width' => $this->getSetting('modal_min_width'), + '#modal_min_height' => $this->getSetting('modal_min_height'), + '#weight' => 10, + ]; + + $row['widget']['edit_button']['#weight'] = 9; + $row['widget']['remove_button']['#weight'] = 11; + } + + return $current; + } + + /** + * {@inheritdoc} + */ + public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { + $ids = empty($values['target_id']) ? [] : explode(' ', trim($values['target_id'])); + $return = []; + foreach ($ids as $id) { + $id = explode(':', $id)[1]; + if (is_array($values['current']) && isset($values['current'][$id])) { + $item_values = [ + 'target_id' => $id, + '_weight' => $values['current'][$id]['_weight'], + ]; + if ($this->fieldDefinition->getType() == 'file') { + if (isset($values['current'][$id]['meta']['description'])) { + $item_values['description'] = $values['current'][$id]['widget']['meta']['description']; + } + if ($this->fieldDefinition->getSetting('display_field') && isset($values['current'][$id]['widget']['meta']['display_field'])) { + $item_values['display'] = $values['current'][$id]['widget']['meta']['display_field']; + } + } + if ($this->fieldDefinition->getType() == 'image') { + if (isset($values['current'][$id]['widget']['meta']['alt'])) { + $item_values['alt'] = $values['current'][$id]['widget']['meta']['alt']; + } + if (isset($values['current'][$id]['widget']['meta']['title'])) { + $item_values['title'] = $values['current'][$id]['widget']['meta']['title']; + } + if (isset($values['current'][$id]['widget']['crop_button'])) { + $item_values['image_crop'] = $values['current'][$id]['widget']['crop_button']; + } + } + $return[] = $item_values; + } + } + + // Return ourself as the structure doesn't match the default. + usort($return, function ($a, $b) { + return SortArray::sortByKeyInt($a, $b, '_weight'); + }); + + return array_values($return); + } + +} From a6b6e22df869a87c0e09fee32af3d443ec697051 Mon Sep 17 00:00:00 2001 From: spleshka Date: Thu, 28 Dec 2017 15:46:25 +0300 Subject: [PATCH 6/6] Enhanced comments. --- js/ImageWidgetModalCrop.js | 5 ++++- src/Element/ImageCrop.php | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/js/ImageWidgetModalCrop.js b/js/ImageWidgetModalCrop.js index 603fb8e..80cdc08 100644 --- a/js/ImageWidgetModalCrop.js +++ b/js/ImageWidgetModalCrop.js @@ -9,16 +9,19 @@ Drupal.behaviors.imageWidgetModalCrop = { attach: function (context) { + // Handle click on "Apply" button in dialog. $('.image-crop-apply', context).click(function() { var $wrapper = $('.crop-preview-wrapper__value', context); + + // Copy crop values from the dialog inside the real form. $.each($('input', $wrapper), function(key, input) { var name = $(input).attr('name'); var value = $(input).attr('value'); $('input[name="' + name + '"]').val(value); }); + // Close the modal dialog. Drupal.dialog('#drupal-modal').close(); - }); } diff --git a/src/Element/ImageCrop.php b/src/Element/ImageCrop.php index 166d22b..14d048b 100644 --- a/src/Element/ImageCrop.php +++ b/src/Element/ImageCrop.php @@ -97,6 +97,7 @@ public static function processCrop(array &$element, FormStateInterface $form_sta $crop_type_list = array_keys($available_crop_types); } + // In some cases we restrict crope list to be a single value. if (!is_array($crop_type_list)) { $crop_type_list = [$crop_type_list]; }