[TASK] Use jQuery UI's "sortable" in page module 62/37262/4
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Wed, 25 Feb 2015 10:11:56 +0000 (11:11 +0100)
committerBenjamin Mack <benni@typo3.org>
Sun, 1 Mar 2015 20:18:37 +0000 (21:18 +0100)
Port the code to use jQuery UI's "sortable" instead of draggable and
droppable, which solves some issues:

- The element position is stored after dropping it, solving the
"hang" effect.
- In "Languages" view, the elements are now properly movable. Moving
the elements between languages, which is error prone, is now not
possible anymore (it was possible before, but it was not saved).

Resolves: #65311
Releases: master
Change-Id: I5af23e258e057eb8c855760bc2ad5fa6989e6686
Reviewed-on: http://review.typo3.org/37262
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benjamin Mack <benni@typo3.org>
Tested-by: Benjamin Mack <benni@typo3.org>
typo3/sysext/backend/Classes/View/PageLayoutView.php
typo3/sysext/backend/Resources/Public/JavaScript/LayoutModule/DragDrop.js
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/structure/_module_web_page.less
typo3/sysext/t3skin/Resources/Private/Styles/TYPO3/visual/_module_web_page.less
typo3/sysext/t3skin/Resources/Public/Css/visual/t3skin.css

index dbfcd23..2ca8da7 100644 (file)
@@ -443,7 +443,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                                $defLanguageCount[$key] = array();
                                        }
                                        // Start wrapping div
-                                       $content[$key] .= '<div class="t3-page-ce-wrapper';
+                                       $content[$key] .= '<div data-colpos="' . $key . '" data-language-uid="' . $lP . '" class="t3js-sortable t3js-sortable-lang t3js-sortable-lang-' . $lP . ' t3-page-ce-wrapper';
                                        if (count($contentRecordsPerColumn[$key]) === 0) {
                                                $content[$key] .= ' t3-page-ce-empty';
                                        }
@@ -451,15 +451,14 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                        // Add new content at the top most position
                                        $content[$key] .= '
                                        <div class="t3-page-ce" data-page="' . (int)$id . '" id="' . str_replace('.', '', uniqid('', TRUE)) . '">
-                                               <div class="t3-page-ce-dropzone" id="colpos-' . $key . '-' . 'page-' . $id . '-' . uniqid('', TRUE) . '">
-                                                       <div class="t3-page-ce-wrapper-new-ce">
-                                                               <a href="#" onclick="' . htmlspecialchars($this->newContentElementOnClick($id, $key, $lP))
-                                                                       . '" title="' . $this->getLanguageService()->getLL('newContentElement', TRUE) . '" class="btn btn-default btn-sm">'
-                                                                       . IconUtility::getSpriteIcon('actions-document-new')
-                                                                       . ' '
-                                                                       . $this->getLanguageService()->getLL('content', TRUE) . '</a>
-                                                       </div>
+                                               <div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-' . $key . '-' . 'page-' . $id . '-' . uniqid('', TRUE) . '">
+                                                       <a href="#" onclick="' . htmlspecialchars($this->newContentElementOnClick($id, $key, $lP))
+                                                               . '" title="' . $this->getLanguageService()->getLL('newContentElement', TRUE) . '" class="btn btn-default btn-sm">'
+                                                               . IconUtility::getSpriteIcon('actions-document-new')
+                                                               . ' '
+                                                               . $this->getLanguageService()->getLL('content', TRUE) . '</a>
                                                </div>
+                                               <div class="t3-page-ce-dropzone-available"></div>
                                        </div>
                                        ';
                                        $editUidList = '';
@@ -497,16 +496,13 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                                                . $this->tt_content_drawItem($row, $isRTE) . '</div>';
                                                        $singleElementHTML .= '<div class="t3-page-ce-body-inner">' . $innerContent . '</div>'
                                                                . $this->tt_content_drawFooter($row);
-                                                       // NOTE: this is the end tag for <div class="t3-page-ce-body">
-                                                       // because of bad (historic) conception, starting tag has to be placed inside tt_content_drawHeader()
-                                                       $singleElementHTML .= '</div>';
                                                        $statusHidden = $this->isDisabled('tt_content', $row) ? ' t3-page-ce-hidden' : '';
-                                                       $singleElementHTML = '<div class="t3-page-ce' . $statusHidden . '" id="element-tt_content-'
+                                                       $singleElementHTML = '<div class="t3-page-ce t3js-page-ce-sortable ' . $statusHidden . '" id="element-tt_content-'
                                                                . $row['uid'] . '" data-table="tt_content" data-uid="' . $row['uid'] . '">' . $singleElementHTML . '</div>';
                                                        if ($this->tt_contentConfig['languageMode']) {
                                                                $singleElementHTML .= '<div class="t3-page-ce">';
                                                        }
-                                                       $singleElementHTML .= '<div class="t3-page-ce-dropzone" id="colpos-' . $key . '-' . 'page-' . $id .
+                                                       $singleElementHTML .= '<div class="t3js-page-new-ce t3-page-ce-wrapper-new-ce" id="colpos-' . $key . '-' . 'page-' . $id .
                                                                '-' . str_replace('.', '', uniqid('', TRUE)) . '">';
                                                        // Add icon "new content element below"
                                                        if (!$disableMoveAndNewButtons) {
@@ -521,16 +517,14 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                                                        $onClick = BackendUtility::editOnClick($params, $this->backPath);
                                                                }
                                                                $singleElementHTML .= '
-                                                                       <div class="t3-page-ce-wrapper-new-ce">
-                                                                               <a href="#" onclick="' . htmlspecialchars($onClick) . '" title="'
-                                                                                       . $this->getLanguageService()->getLL('newContentElement', TRUE) . '" class="btn btn-default btn-sm">'
-                                                                                       . IconUtility::getSpriteIcon('actions-document-new')
-                                                                                       . ' '
-                                                                                       . $this->getLanguageService()->getLL('content', TRUE) . '</a>
-                                                                       </div>
+                                                                       <a href="#" onclick="' . htmlspecialchars($onClick) . '" title="'
+                                                                               . $this->getLanguageService()->getLL('newContentElement', TRUE) . '" class="btn btn-default btn-sm">'
+                                                                               . IconUtility::getSpriteIcon('actions-document-new')
+                                                                               . ' '
+                                                                               . $this->getLanguageService()->getLL('content', TRUE) . '</a>
                                                                ';
                                                        }
-                                                       $singleElementHTML .= '</div></div>';
+                                                       $singleElementHTML .= '</div></div><div class="t3-page-ce-dropzone-available"></div></div>';
                                                        if ($this->defLangBinding && $this->tt_contentConfig['languageMode']) {
                                                                $defLangBinding[$key][$lP][$row[$lP ? 'l18n_parent' : 'uid']] = $singleElementHTML;
                                                        } else {
@@ -606,7 +600,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                                        $grid .= '<td valign="top"' .
                                                                ($colSpan > 0 ? ' colspan="' . $colSpan . '"' : '') .
                                                                ($rowSpan > 0 ? ' rowspan="' . $rowSpan . '"' : '') .
-                                                               ' data-colpos="' . (int)$columnConfig['colPos'] . '" class="t3-gridCell t3-page-column t3-page-column-' . $columnKey .
+                                                               ' data-colpos="' . (int)$columnConfig['colPos'] . '" data-language-uid="' . $lP . '" class="t3js-page-lang-column-' . $lP . ' t3-gridCell t3-page-column t3-page-column-' . $columnKey .
                                                                ((!isset($columnConfig['colPos']) || $columnConfig['colPos'] === '') ? ' t3-gridCell-unassigned' : '') .
                                                                ((isset($columnConfig['colPos']) && $columnConfig['colPos'] !== '' && !$head[$columnKey]) || !GeneralUtility::inList($this->tt_contentConfig['activeCols'], $columnConfig['colPos']) ? ' t3-gridCell-restricted' : '') .
                                                                ($colSpan > 0 ? ' t3-gridCell-width' . $colSpan : '') .
@@ -661,7 +655,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                        // Header:
                                        $lP = (int)$lP;
                                        $cCont[$lP] = '
-                                               <td valign="top" class="t3-page-lang-column">
+                                               <td valign="top" class="t3-page-lang-column" data-language-uid="' . $lP . '">
                                                        <h3>' . htmlspecialchars($this->tt_contentConfig['languageCols'][$lP]) . '</h3>
                                                </td>';
 
@@ -692,13 +686,15 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                // Add headers:
                                $out .= '<tr>' . implode($cCont) . '</tr>';
                                $out .= '<tr>' . implode($sCont) . '</tr>';
+                               unset($cCont, $sCont);
+
                                // Traverse previously built content for the columns:
                                foreach ($languageColumn as $cKey => $cCont) {
-                                       $out .= '
-                                       <tr>
-                                               <td valign="top" class="t3-gridCell t3-page-column t3-page-lang-column">' . implode(('</td>' . '
-                                               <td valign="top" class="t3-gridCell t3-page-column t3-page-lang-column">'), $cCont) . '</td>
-                                       </tr>';
+                                       $out .= '<tr>';
+                                       foreach ($cCont as $languageId => $columnContent) {
+                                               $out .= '<td valign="top" class="t3-gridCell t3-page-column t3-page-lang-column t3js-page-lang-column t3js-page-lang-column-' . $languageId . '">' . $columnContent . '</td>';
+                                       }
+                                       $out .= '</tr>';
                                        if ($this->defLangBinding) {
                                                // "defLangBinding" mode
                                                foreach ($defLanguageCount[$cKey] as $defUid) {
@@ -727,7 +723,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                                // Finally, wrap it all in a table and add the language selector on top of it:
                                $out = $languageSelector . '
                                        <div class="t3-lang-gridContainer">
-                                               <table cellpadding="0" cellspacing="0" class="t3-page-langMode">
+                                               <table cellpadding="0" cellspacing="0" class="t3-page-columns t3-page-langMode">
                                                        ' . $out . '
                                                </table>
                                        </div>';
@@ -1301,7 +1297,7 @@ class PageLayoutView extends \TYPO3\CMS\Recordlist\RecordList\AbstractDatabaseRe
                // Display info from records fields:
                if (count($info)) {
                        $content = '<div class="t3-page-ce-info">
-                               ' . implode('<br />', $info) . '
+                               ' . implode('<br>', $info) . '
                                </div>';
                }
                // Wrap it
index 915468e..6b9adb5 100644 (file)
  * this JS code does the drag+drop logic for the Layout module (Web => Page)
  * based on jQuery UI
  */
-define(['jquery', 'jquery-ui/draggable', 'jquery-ui/droppable'], function ($) {
+define(['jquery', 'jquery-ui/sortable'], function ($) {
 
        var DragDrop = {
                contentIdentifier: '.t3-page-ce',
-               dragIdentifier: '.t3-page-ce-dragitem',
-               dragHeaderIdentifier: '.t3-page-ce-header',
-               dropZoneIdentifier: '.t3-page-ce-dropzone',
+               dragIdentifier: '.t3-row-header',
+               dropZoneAvailableIdentifier: '.t3-page-ce-dropzone-available',
+               dropPossibleClass: 't3-page-ce-dropzone-possible',
+               sortableItemsIdentifier: '.t3js-page-ce-sortable',
                columnIdentifier: '.t3-page-column',
-               validDropZoneClass: 't3-page-ce-dropzone-available',
-               dropPossibleHoverClass: 't3-page-ce-drop-possible'
+               langClassPrefix: '.t3js-sortable-lang-'
        };
 
        /**
         * initializes Drag+Drop for all content elements on the page
         */
        DragDrop.initialize = function() {
-               $(this.contentIdentifier).draggable({
-                       handle: this.dragHeaderIdentifier,
-                       scope: 'tt_content',
-                       cursor: 'move',
-                       distance: 20,
-                       addClasses: 'active-drag',
-                       revert: 'invalid',
-                       zIndex: 100,
-                       start: function(evt, ui) {
-                               DragDrop.onDragStart($(this));
-                       },
-                       stop: function(evt, ui) {
-                               DragDrop.onDragStop($(this));
-                       }
-               });
-
-               $(this.dropZoneIdentifier).droppable({
-                       accept: this.contentIdentifier,
-                       scope: 'tt_content',
-                       tolerance: 'pointer',
-                       over: function(evt, ui) {
-                               DragDrop.onDropHoverOver($(ui.draggable), $(this));
-                       },
-                       out: function(evt, ui) {
-                               DragDrop.onDropHoverOut($(ui.draggable), $(this));
-                       },
-                       drop: function(evt, ui) {
-                               DragDrop.onDrop($(ui.draggable), $(this));
-                       }
+               $('td[data-language-uid]').each(function() {
+                       var connectWithClassName = DragDrop.langClassPrefix + $(this).data('language-uid');
+                       $(connectWithClassName).sortable({
+                               items: DragDrop.sortableItemsIdentifier,
+                               connectWith: connectWithClassName,
+                               handle: DragDrop.dragIdentifier,
+                               distance: 20,
+                               cursor: 'move',
+                               helper: 'clone',
+                               placeholder: DragDrop.dropPossibleClass,
+                               tolerance: 'pointer',
+                               start: function(e, ui) {
+                                       DragDrop.onSortStart($(this), ui);
+                               },
+                               stop: function(e, ui) {
+                                       DragDrop.onSortStop($(this), ui);
+                               },
+                               change: function(e, ui) {
+                                       DragDrop.onSortChange($(this), ui);
+                               },
+                               update: function(e, ui) {
+                                       DragDrop.onSortUpdate($(this), ui);
+                               },
+                               receive: function(e, ui) {
+                                       DragDrop.onSortUpdate($(this), ui, $(this).data('colpos'));
+                               }
+                       }).disableSelection();
                });
        };
 
        /**
-        * called when a draggable is selected to be moved
-        * @param $element a jQuery object for the draggable
-        * @private
+        * Called when an item is about to be moved
         */
-       DragDrop.onDragStart = function($element) {
-               // Add css class for the drag shadow
-               $element.children(DragDrop.dragIdentifier).addClass('dragitem-shadow');
-               // Hide create new element button
-               $element.children(DragDrop.dropZoneIdentifier).addClass('drag-start');
-               $element.closest(DragDrop.columnIdentifier).removeClass('active');
+       DragDrop.onSortStart = function($container, ui) {
+               var $item = $(ui.item),
+                       $helper = $(ui.helper),
+                       $placeholder = $(ui.placeholder);
+
+               $placeholder.height($item.height() - $helper.find('.t3js-page-new-ce').height());
+               DragDrop.changeDropzoneVisibility($container, $item);
 
-               // make the dropzones visible (all except the previous one in the current list)
-               var $previousDropZone = $element.prev().children(DragDrop.dropZoneIdentifier);
-               $(DragDrop.dropZoneIdentifier).not($previousDropZone).addClass(DragDrop.validDropZoneClass);
+               // show all dropzones, except the own
+               $helper.find(DragDrop.dropZoneAvailableIdentifier).removeClass('active');
+               $container.parents('table.t3-page-columns').find('.t3js-page-new-ce').hide();
        };
 
        /**
-        * called when a draggable is released
-        * @param $element a jQuery object for the draggable
-        * @private
+        * Called when the sorting stopped
         */
-       DragDrop.onDragStop = function($element) {
-               // Remove css class for the drag shadow
-               $element.children(DragDrop.dragIdentifier).removeClass('dragitem-shadow');
-               // Show create new element button
-               $element.children(DragDrop.dropZoneIdentifier).removeClass('drag-start');
-               $element.closest(DragDrop.columnIdentifier).addClass('active');
-               $('.' + DragDrop.validDropZoneClass).removeClass(DragDrop.validDropZoneClass);
+       DragDrop.onSortStop = function($container, ui) {
+               var $allColumns = $container.parents('table.t3-page-columns');
+               $allColumns.find('.t3js-page-new-ce').show();
+               $allColumns.find(DragDrop.dropZoneAvailableIdentifier + '.active').removeClass('active');
        };
 
        /**
-        * adds CSS classes when hovering over a dropzone
-        * @param $draggableElement
-        * @param $droppableElement
-        * @private
+        * Called when the index of the element in the sortable list has changed
         */
-       DragDrop.onDropHoverOver = function($draggableElement, $droppableElement) {
-               if ($droppableElement.hasClass(DragDrop.validDropZoneClass)) {
-                       $droppableElement.addClass(DragDrop.dropPossibleHoverClass);
-                       $draggableElement.addClass(DragDrop.dropPossibleHoverClass);
-               }
+       DragDrop.onSortChange = function($container, ui) {
+               var $placeholder = $(ui.placeholder);
+               DragDrop.changeDropzoneVisibility($container, $placeholder);
        };
 
-       /**
-        * removes the CSS classes after hovering out of a dropzone again
-        * @param $draggableElement
-        * @param $droppableElement
-        * @private
-        */
-       DragDrop.onDropHoverOut = function($draggableElement, $droppableElement) {
-               $droppableElement.removeClass(DragDrop.dropPossibleHoverClass);
-               $draggableElement.removeClass(DragDrop.dropPossibleHoverClass);
+       DragDrop.changeDropzoneVisibility = function($container, $subject) {
+               var $prev = $subject.prev(':visible'),
+                       droppableClassName = '.t3js-sortable-lang-' + $container.data('language-uid');
+
+               if ($prev.length === 0) {
+                       $prev = $subject.prevUntil(':visible').last().prev();
+               }
+               $container.parents('table.t3-page-columns').find(droppableClassName).find(DragDrop.contentIdentifier + ':not(.ui-sortable-helper)').not($prev).find(DragDrop.dropZoneAvailableIdentifier).addClass('active');
+               $prev.find(DragDrop.dropZoneAvailableIdentifier + '.active').removeClass('active');
        };
 
        /**
-        * this method does the whole logic when a draggable is dropped on to a dropzone
-        * sending out the request and afterwards move the HTML element in the right place.
-        *
-        * @param $draggableElement
-        * @param $droppableElement
-        * @private
+        * Called when the new position of the element gets stored
         */
-       DragDrop.onDrop = function($draggableElement, $droppableElement) {
-               var oldColumn = DragDrop.getColumnPositionForElement($draggableElement),
-                       newColumn = DragDrop.getColumnPositionForElement($droppableElement);
-
-               $droppableElement.removeClass(DragDrop.dropPossibleHoverClass);
-               $draggableElement.removeClass(DragDrop.dropPossibleHoverClass);
+       DragDrop.onSortUpdate = function($container, ui, newColumn) {
+               var $selectedItem = $(ui.item),
+                       contentElementUid = parseInt($selectedItem.data('uid'));
 
                // send an AJAX requst via the AjaxDataHandler
-               var contentElementUid = parseInt($draggableElement.data('uid'));
                if (contentElementUid > 0) {
                        var parameters = {};
                        // add the information about a possible column position change
-                       if (newColumn !== oldColumn) {
+                       if (typeof newColumn !== 'undefined') {
                                parameters['data'] = {tt_content: {}};
                                parameters['data']['tt_content'][contentElementUid] = {colPos: parseInt(newColumn)};
                        }
-
-                       var targetContentElementUid = $droppableElement.closest(DragDrop.contentIdentifier).data('uid');
-                       // the item was moved to the top of the colPos, so the page ID is used here
-                       if (typeof targetContentElementUid === 'undefined') {
-                               // the actual page is needed
-                               targetContentElementUid = parseInt($droppableElement.closest(DragDrop.contentIdentifier).data('page'));
-                       } else {
-                               // the negative value of the content element after where it should be moved
-                               targetContentElementUid = 0-parseInt(targetContentElementUid);
-                       }
-
-                       parameters['cmd'] = {tt_content: {}};
-                       parameters['cmd']['tt_content'][contentElementUid] = {move: targetContentElementUid};
-                       // fire the request, and show a message if it has failed
-                       require(['TYPO3/CMS/Backend/AjaxDataHandler'], function(DataHandler) {
-                               DataHandler.process(parameters).done(function(result) {
-                                       if (!result.hasErrors) {
-                                               // insert draggable on the new position
-                                               $draggableElement.detach().css({top: 0, left: 0})
-                                                       .insertAfter($droppableElement.closest(DragDrop.contentIdentifier));
-                                       }
-                               });
-                       });
                }
-       };
 
-       /**
-        * returns the next "upper" container colPos parameter inside the code
-        * @param $element
-        * @return int|null the colPos
-        */
-       DragDrop.getColumnPositionForElement = function($element) {
-               var $columnContainer = $element.closest(DragDrop.columnIdentifier);
-               if ($columnContainer.length && $columnContainer.data('colpos') !== 'undefined') {
-                       return $columnContainer.data('colpos');
+               var targetContentElementUid = $selectedItem.prev().data('uid');
+               // the item was moved to the top of the colPos, so the page ID is used here
+               if (typeof targetContentElementUid === 'undefined') {
+                       // the actual page is needed
+                       targetContentElementUid = parseInt($container.find(DragDrop.contentIdentifier).first().data('page'));
                } else {
-                       return null;
+                       // the negative value of the content element after where it should be moved
+                       targetContentElementUid = parseInt(targetContentElementUid) * -1;
                }
+
+               parameters['cmd'] = {tt_content: {}};
+               parameters['cmd']['tt_content'][contentElementUid] = {move: targetContentElementUid};
+               // fire the request, and show a message if it has failed
+               require(['TYPO3/CMS/Backend/AjaxDataHandler'], function(DataHandler) {
+                       DataHandler.process(parameters).done(function(result) {
+                               if (result.hasErrors) {
+                                       $container.sortable('cancel');
+                               }
+                       });
+               });
        };
 
        /**
index ca24f7d..155d4d7 100644 (file)
@@ -49,6 +49,7 @@ td.t3-page-lang-column h3 {
 
 .t3-page-ce-wrapper {
        padding: 0 0 5px 0;
+       min-height: 2em;
 }
 
 .t3-page-ce-droptarget {
@@ -57,10 +58,11 @@ td.t3-page-lang-column h3 {
 
 .t3-page-ce {
        min-width: 150px;
+       margin: 12px 0 0;
 }
 
-.t3-page-ce-dropzone {
-       margin: 12px 0;
+.t3-page-ce-dropzone-available.active {
+               height: 2em;
 }
 
 .t3-page-ce-dragitem {
@@ -97,6 +99,10 @@ td.t3-page-lang-column h3 {
        float: left;
 }
 
+.t3-page-ce-body {
+       margin-bottom: 12px;
+}
+
 .t3-page-ce-body-inner {
        padding: 7px;
 }
@@ -114,12 +120,12 @@ div.t3-page-lang-copyce {
        padding: 1px 12px 0 12px;
 }
 
-div.t3-page-lang-copyce {
-       margin-top: 30px;
+div.t3-page-ce div.t3-page-ce {
+       padding: 1px 0 0;
 }
 
-.t3-page-ce:hover .t3-page-ce-body {
-       margin: 0;
+div.t3-page-lang-copyce {
+       margin-top: 30px;
 }
 
 #ext-cms-layout-db-layout-php h2 {
index c14b594..ac8f84c 100644 (file)
@@ -54,7 +54,7 @@
 }
 
 .t3-page-ce.active-drag {
-    z-index: 4500;
+       z-index: 4500;
 }
 
 .t3-page-ce .t3-page-ce-body,
@@ -185,13 +185,17 @@ td.t3-gridCell-restricted div.t3-row-header div {
 }
 
 .t3-page-ce-dropzone.t3-page-ce-dropzone-available.t3-page-ce-drop-possible {
-    background-color: #73ab61;
+       background-color: #73ab61;
 }
 
-.t3-page-ce-dropzone.t3-page-ce-dropzone-available {
+.t3-page-ce-dropzone-available.active {
        background-color: #c8d9c3;
 }
 
+.t3-page-ce-dropzone-possible {
+       background-color: #73ab61;
+}
+
 .t3-page-ce-dragitem .t3-page-ce-header:hover {
        cursor: move;
 }
index 8bf0191..3ed38fd 100644 (file)
@@ -11573,15 +11573,17 @@ td.t3-page-lang-column h3 {
 }
 .t3-page-ce-wrapper {
   padding: 0 0 5px 0;
+  min-height: 2em;
 }
 .t3-page-ce-droptarget {
   padding: 5px;
 }
 .t3-page-ce {
   min-width: 150px;
+  margin: 12px 0 0;
 }
-.t3-page-ce-dropzone {
-  margin: 12px 0;
+.t3-page-ce-dropzone-available.active {
+  height: 2em;
 }
 .t3-page-ce-dragitem {
   margin-bottom: 10px;
@@ -11610,6 +11612,9 @@ td.t3-page-lang-column h3 {
 .t3-page-ce-icons-move span {
   float: left;
 }
+.t3-page-ce-body {
+  margin-bottom: 12px;
+}
 .t3-page-ce-body-inner {
   padding: 7px;
 }
@@ -11623,12 +11628,12 @@ td.t3-gridCell div.t3-page-ce,
 div.t3-page-lang-copyce {
   padding: 1px 12px 0 12px;
 }
+div.t3-page-ce div.t3-page-ce {
+  padding: 1px 0 0;
+}
 div.t3-page-lang-copyce {
   margin-top: 30px;
 }
-.t3-page-ce:hover .t3-page-ce-body {
-  margin: 0;
-}
 #ext-cms-layout-db-layout-php h2 {
   margin-bottom: 0px;
 }
@@ -12036,9 +12041,12 @@ td.t3-gridCell-restricted div.t3-row-header div {
 .t3-page-ce-dropzone.t3-page-ce-dropzone-available.t3-page-ce-drop-possible {
   background-color: #73ab61;
 }
-.t3-page-ce-dropzone.t3-page-ce-dropzone-available {
+.t3-page-ce-dropzone-available.active {
   background-color: #c8d9c3;
 }
+.t3-page-ce-dropzone-possible {
+  background-color: #73ab61;
+}
 .t3-page-ce-dragitem .t3-page-ce-header:hover {
   cursor: move;
 }