diff --git a/css/entity-reference-layout.css b/css/erl-widget.css similarity index 51% rename from css/entity-reference-layout.css rename to css/erl-widget.css index 84aef39..fdd0223 100644 --- a/css/entity-reference-layout.css +++ b/css/erl-widget.css @@ -1,166 +1,219 @@ -/** - * Colors: - * #a6c6ee - light blue - # #4e8ede - blue - # #0074BD - drupal blue - # #4c8fe0 - darker blue - # #184fc1 - dark blue - # #f5f5f3 - light gray - */ -.field--widget-entity-reference-layout-widget .erl-layout { - background-color: #fff; -} - .field--widget-entity-reference-layout-widget fieldset.erl-field { background: #f5f5f3; border: none; margin: 30px 0; - padding: 60px 30px 0; + padding: 60px 30px; } .field--widget-entity-reference-layout-widget fieldset.erl-field > legend { line-height: 30px; top: 15px; } -.field--widget-entity-reference-layout-widget .layout { - clear: both; +.field--widget-entity-reference-layout-widget .erl-layout { margin-bottom: 30px; -} -.field--widget-entity-reference-layout-widget .erl-field-item { position: relative; - padding: 30px 30px 15px 30px; + padding: 25px 0 0 0; } -.field--widget-entity-reference-layout-widget .erl-field-item--layout { - border: 1px solid #f5f5f3; - box-sizing: border-box; +.field--widget-entity-reference-layout-widget .erl-layout__content { + padding: 0 30px 15px 30px; } -.field--widget-entity-reference-layout-widget .reversed .erl-field-item--layout { - border-color: rgba(0, 0, 0, .25); +.field--widget-entity-reference-layout-widget .erl-layout__regions, +.field--widget-entity-reference-layout-widget .erl-disabled-wrapper { + background: #fff; +} + +.field--widget-entity-reference-layout-widget .erl-layout-select .form-radios { + display: flex; + flex-wrap: wrap; + align-content: flex-start; +} +.field--widget-entity-reference-layout-widget .layout-radio-item { + padding: 5px; + background: none; + cursor: pointer; + margin: 10px 10px 10px 0; + text-align: left; + width: 150px; +} +.field--widget-entity-reference-layout-widget .layout-radio-item input[type=radio] { + display: none; +} +.field--widget-entity-reference-layout-widget .layout-radio-item .form-item { + margin-top: 0; +} +.field--widget-entity-reference-layout-widget .layout-radio-item label { + font-size: small; +} +.field--widget-entity-reference-layout-widget .layout-radio-item .layout-icon-wrapper { + float: left; + margin-right: 5px; +} +.field--widget-entity-reference-layout-widget .layout-radio-item.active { + background: #fff; + outline: 2px dotted blue; } -.field--widget-entity-reference-layout-widget .erl-field-item--layout .form-item { +.field--widget-entity-reference-layout-widget .layout-radio-item .layout-icon { + float: left; margin: 0; } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item { - overflow: hidden; +.field--widget-entity-reference-layout-widget .erl-add-item { + text-align: center; } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item { + +.field--widget-entity-reference-layout-widget .erl-layout.erl-item::before { + content: ""; + top: 15px; + height: 1px; + background-color: #ccc; + position: absolute; + left: 0; + right: 0; + width: 100%; +} +.field--widget-entity-reference-layout-widget .erl-layout .erl-item, +.field--widget-entity-reference-layout-widget .erl-disabled-items .erl-item { border: 1px solid #ffffff; box-sizing: border-box; padding: 30px; transition: all .15s linear; + position: relative; } -.field--widget-entity-reference-layout-widget .reversed .erl-layout-section .erl-field-item { +.field--widget-entity-reference-layout-widget .reversed .erl-item { border-color: rgba(0, 0, 0, .25); } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item .actions .form-wrapper { +.field--widget-entity-reference-layout-widget .erl-item .erl-actions .form-wrapper { background: none; } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item:hover { +.field--widget-entity-reference-layout-widget .erl-layout-region:hover { + border: 1px solid #ccc; +} +.field--widget-entity-reference-layout-widget .erl-layout .erl-item:hover { border-color: #0074bd; } -.field--widget-entity-reference-layout-widget .reversed .erl-layout-section .erl-field-item:hover { +.field--widget-entity-reference-layout-widget .reversed .erl-item:hover { border-color: #fff; } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item:hover:before { +.field--widget-entity-reference-layout-widget .erl-item:hover:before { opacity: 1; } -.field--widget-entity-reference-layout-widget .erl-layout-section { +.field--widget-entity-reference-layout-widget .erl-layout-region { box-sizing: border-box; position: relative; - padding: 0 0 30px; + padding: 0; border: 1px dashed #f5f5f3; + border: 1px dashed #e5e5e3; transition: all .15s linear; + min-height: 30px; + padding: 30px; } -.erl-layout-disabled { - color: gray; - position: relative; - margin-top: 30px; -} -.erl-layout-disabled h4 { - margin: 0; - padding: 10px; + +/* -- Layout Controls -- */ + +.field--widget-entity-reference-layout-widget .erl-layout .layout-controls, +.field--widget-entity-reference-layout-widget .erl-layout .erl-actions { + background: #f5f5f3; } -.field--widget-entity-reference-layout-widget .erl-layout-disabled .erl-layout-section { - position: relative; - min-height: 60px; - padding-bottom: 0; - padding-top: 0; +.field--widget-entity-reference-layout-widget .erl-layout .erl-item .layout-controls, +.field--widget-entity-reference-layout-widget .erl-layout .erl-item .erl-actions{ + background: none; } -.erl-layout-disabled-description { +.field--widget-entity-reference-layout-widget .layout-controls { + height: 30px; + left: 0; position: absolute; - width: 100%; - margin: 0; top: 0; - left: 0; - margin-left: -30px; - margin-top: 20px; - text-align: center; -} -@media (min-width: 1224px) { - .field--widget-entity-reference-layout-widget .erl-layout-section { - padding: 30px; - } -} -.field--widget-entity-reference-layout-widget .reversed .erl-layout-section { - border-color: rgba(0, 0, 0, .25); + width: 90px; + opacity: 0; } -.field--widget-entity-reference-layout-widget .erl-layout-section:hover { - border: 1px solid #ccc; + +.field--widget-entity-reference-layout-widget .erl-item:hover > .layout-controls { + opacity: 1; } -.field--widget-entity-reference-layout-widget .reversed .erl-layout-section:hover { - border-color: rgba(255, 255, 255, .25); +.field--widget-entity-reference-layout-widget .erl-layout-section .erl-item:hover > .layout-controls { + opacity: 1; } -.field--widget-entity-reference-layout-widget .layout-region-label { - visibility: hidden; +.field--widget-entity-reference-layout-widget .erl-layout-section .erl-item .layout-controls { + background: none; } -.field--widget-entity-reference-layout-widget .actions, -.gu-mirror .actions { +.layout-handle, +.layout-down, +.layout-up { + cursor: pointer; + height: 30px; + left: 0; + overflow: hidden; + opacity: .75; position: absolute; - right: 0; top: 0; - padding: 0; - margin: 0; - visibility: hidden; + text-indent: 100%; + width: 30px; } -.gu-mirror { - background: #ffffff; - border: 1px solid #0074bd; - box-shadow: 0px 1px 5px rgba(0, 0, 0, .25); - cursor: grabbing; - padding: 30px; - box-sizing: border-box; - overflow: hidden; +.layout-handle:hover, +.layout-down:hover, +.layout-up:hover { + opacity: 1; + cursor: pointer; } -.gu-mirror:before { - content: ""; - position: absolute; - height: 30px; - width: 30px; - left: 0; - top: 0; +.layout-handle { background: url(../img/icon-move.png) 0 0 no-repeat; background-size: cover; + display: none; } -.reversed .gu-mirror:before { +.reversed .layout-handle { background-image: url(../img/icon-move--reversed.png); } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item.gu-transit { - background: #0074bd; +.dragula-enabled .layout-handle { + display: block; } -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item.gu-transit > * { - visibility: hidden; +.layout-down { + background: url(../img/icon-down.png) 0 0 no-repeat; + background-size: cover; + left: 60px; } -.field--widget-entity-reference-layout-widget .erl-field-item:hover > .actions { - visibility: visible; +.reversed .layout-down { + background-image: url(../img/icon-down--reversed.png); } -.field--widget-entity-reference-layout-widget .actions .form-wrapper { - background: #f5f5f3; - transition: none; +.layout-up { + background: url(../img/icon-up.png) 0 0 no-repeat; + background-size: cover; + left: 30px; } -.field--widget-entity-reference-layout-widget .actions .form-wrapper { - display: flex; +.reversed .layout-up { + background-image: url(../img/icon-up--reversed.png); } -.field--widget-entity-reference-layout-widget .actions input.form-submit-edit, -.field--widget-entity-reference-layout-widget .actions input.form-submit-remove { +.field--widget-entity-reference-layout-widget .erl-layout:first-child > .layout-controls .layout-up, +.field--widget-entity-reference-layout-widget .erl-layout:first-child .erl-item:first-child .layout-up { + pointer-events: none; + opacity: .15; +} +.field--widget-entity-reference-layout-widget .erl-layout:first-child .erl-item:first-child .layout-up { + pointer-events: none; + opacity: .15; +} +.field--widget-entity-reference-layout-widget .erl-layout:last-child > .layout-controls .layout-down, +.field--widget-entity-reference-layout-widget .erl-layout:last-child .erl-item:nth-last-child(2) .layout-down { + pointer-events: none; + opacity: .15; +} +.gu-mirror .layout-down, +.gu-mirror .layout-up { + opacity: .25; +} + +/* -- Item Actions (Edit and Delete Buttons) -- */ +.field--widget-entity-reference-layout-widget .erl-actions, +.gu-mirror .erl-actions { + position: absolute; + right: 0; + top: 0; + padding: 0; + margin: 0; + visibility: hidden; +} +.field--widget-entity-reference-layout-widget .erl-item:hover > .erl-actions { + visibility: visible; +} +.field--widget-entity-reference-layout-widget .erl-actions input.erl-edit, +.field--widget-entity-reference-layout-widget .erl-actions input.erl-remove { cursor: pointer; height: 30px; width: 30px; @@ -173,26 +226,37 @@ transition: all .15s linear; transition: none; } -.field--widget-entity-reference-layout-widget .actions input.form-submit-edit { +.field--widget-entity-reference-layout-widget .erl-actions input.erl-edit { background: url(../img/icon-edit.png) 0 0 no-repeat; background-size: cover; } -.field--widget-entity-reference-layout-widget .reversed .actions input.form-submit-edit { +.field--widget-entity-reference-layout-widget .reversed .erl-actions input.erl-edit { background-image: url(../img/icon-edit--reversed.png); } -.field--widget-entity-reference-layout-widget .actions input.form-submit-remove { +.field--widget-entity-reference-layout-widget .erl-actions input.erl-remove { background: url(../img/icon-delete.png) 0 0 no-repeat; background-size: cover; } -.field--widget-entity-reference-layout-widget .reversed .actions input.form-submit-remove { +.field--widget-entity-reference-layout-widget .reversed .erl-actions input.erl-remove { background-image: url(../img/icon-delete--reversed.png); } -.field--widget-entity-reference-layout-widget .actions input.form-submit-edit:hover, -.field--widget-entity-reference-layout-widget .actions input.form-submit-remove:hover { +.field--widget-entity-reference-layout-widget .erl-actions input.erl-edit:hover, +.field--widget-entity-reference-layout-widget .erl-actions input.erl-remove:hover { opacity: 1; box-shadow: none; } -.erl-add-content { + +/* -- Add content and add section buttons. -- */ +.erl-add-content-wrapper { + position: relative; +} +.erl-add-content__container { + position: absolute; + left: 0; + top: 100%; + width: 100%; +} +.erl-add-content__toggle { position: absolute; bottom: 0; left: 50%; @@ -217,25 +281,22 @@ -webkit-appearance: none; outline: none; } -.reversed .erl-add-content { +.reversed .erl-add-content__toggle { background-image: url(../img/icon-add--reversed.png); } -.erl-add-content.active { +.erl-add-content__toggle.active { transform: rotate(45deg); } -.erl-field-actions { - position: relative; -} -.erl-layout-section:hover button.erl-add-content, -.erl-field-actions:hover button.erl-add-content { +.erl-layout-region:hover button.erl-add-content__toggle { opacity: .75; } -.erl-layout-section:hover button.erl-add-content:hover, -.erl-layout-section:hover button.erl-add-content.active, -.erl-layout-section button.erl-add-content.active { +.erl-layout-region:hover button.erl-add-content__toggle:hover, +.erl-layout-region:hover button.erl-add-content__toggle.active, +.erl-layout-region button.erl-add-content__toggle.active { opacity: 1; } -.erl-add-content--group { + +.erl-add-content__group { width: auto; position: absolute; left: 50%; @@ -248,14 +309,14 @@ margin-left: -15px; box-shadow: 0px 1px 5px rgba(0, 0, 0, .25); } -.erl-add-content--group.hidden { +.erl-add-content__group.hidden { visibility: hidden !important; } -.erl-add-content--item { +.erl-add-content__item { display: block; text-align: left; } -.erl-add-content--group button { +.erl-add-content__group button { background: none; display: block; width: 100%; @@ -264,14 +325,14 @@ text-align: left; padding: 9px 18px; } -.erl-add-content--group button img { +.erl-add-content__group button img { width: 24px; height: 24px; margin-right: 9px; margin-top: -2px; float: left; } -.erl-add-content--group button:hover { +.erl-add-content__group button:hover { background: #e5f1f8; color: #0074bd; display: block; @@ -279,13 +340,22 @@ border: none; } .erl-add-content--single { - width: 150px; - left: 50%; - margin-left: -75px; + padding: 10px 0; + width: 100%; text-align: center; position: absolute; z-index: 10; } +.erl-item > .erl-add-content--single { + opacity: 0; + bottom: -43px; +} +.erl-item:hover > .erl-add-content--single { + opacity: 1; +} +.erl-empty { + position: relative; +} .erl-add-content--single button { background: none; border: none; @@ -303,185 +373,64 @@ font-weight: bold; font-size: large; } -.erl-field-actions.empty button.erl-add-content { - visibility: visible; -} -.erl-field-item--layout { + +/** + * Disabled items. + */ +.erl-disabled-items { + color: gray; position: relative; + margin-top: 30px; } -.erl-field-item--layout:before { - content: ""; - top: 15px; - height: 1px; - background-color: #ccc; - position: absolute; - left: 0; - right: 0; - width: 100%; -} -.erl-field-item--layout.gu-mirror:before { - content: none; +.erl-disabled-items__title { + margin: 0; + padding: 10px; } -.field--widget-entity-reference-layout-widget .layout-controls { - background: #f5f5f3; - height: 30px; - left: 0; +.erl-disabled-items__description { position: absolute; + width: 100%; + margin: 0; top: 0; - width: 90px; - opacity: 0; -} -.field--widget-entity-reference-layout-widget .erl-field-item .erl-add-content--single { - opacity: 0; - margin-top: -60px; - padding: 10px 0; + left: 0; + margin-left: -30px; + margin-top: 20px; + text-align: center; } -.field--widget-entity-reference-layout-widget .erl-field-item:hover > .erl-add-content--single { - opacity: 1; +.erl-disabled-wrapper { + position: relative; + min-height: 60px; + padding-bottom: 0; + padding-top: 0; + border: 1px dashed #e5e5e3; } -.field--widget-entity-reference-layout-widget .erl-field-item:hover > .layout-controls { - opacity: 1; -} -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item:hover > .layout-controls { - opacity: 1; -} -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item .layout-controls { - background: none; +/* -- Dragula Specific Styles -- */ + +.gu-mirror { + background: #fff; + border: 1px solid #0074bd; + box-shadow: 0px 1px 5px rgba(0, 0, 0, .25); + cursor: grabbing; + padding: 30px; + box-sizing: border-box; + overflow: hidden; } -.layout-handle, -.layout-down, -.layout-up { - cursor: pointer; +.gu-mirror:before { + content: ""; + position: absolute; height: 30px; + width: 30px; left: 0; - overflow: hidden; - opacity: .75; - position: absolute; top: 0; - text-indent: 100%; - width: 30px; -} -.layout-handle:hover, -.layout-down:hover, -.layout-up:hover { - opacity: 1; - cursor: pointer; -} -.layout-handle { background: url(../img/icon-move.png) 0 0 no-repeat; background-size: cover; - display: none; } -.reversed .layout-handle { +.reversed .gu-mirror:before { background-image: url(../img/icon-move--reversed.png); } -.dragula-enabled .layout-handle { - display: block; -} -.layout-down { - background: url(../img/icon-down.png) 0 0 no-repeat; - background-size: cover; - left: 60px; -} -.reversed .layout-down { - background-image: url(../img/icon-down--reversed.png); -} -.layout-up { - background: url(../img/icon-up.png) 0 0 no-repeat; - background-size: cover; - left: 30px; -} -.reversed .layout-up { - background-image: url(../img/icon-up--reversed.png); -} -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item:nth-child(1) .layout-up { - pointer-events: none; - opacity: .25; -} -.field--widget-entity-reference-layout-widget .erl-layout-section .erl-field-item:nth-last-child(3) .layout-down { - pointer-events: none; - opacity: .25; -} -.gu-mirror .layout-down, -.gu-mirror .layout-up { - opacity: .25; -} -.field--widget-entity-reference-layout-widget fieldset.erl-entity-form { - background: #fff; - border: none; - margin: 0; - padding: 0; -} -.field--widget-entity-reference-layout-widget .erl-remove-form { - padding: 20px; -} -.field--widget-entity-reference-layout-widget .erl-remove-message { - margin-bottom: 10px; -} -.field--widget-entity-reference-layout-widget .erl-entity-form { - height: 100%; -} -.field--widget-entity-reference-layout-widget .erl-form-container { - height: 100%; - overflow: hidden; -} -.field--widget-entity-reference-layout-widget .erl-form-body { - overflow: auto; - padding: 30px; - box-sizing: border-box; - max-height: 80vh; - margin-bottom: 67px; -} -@media screen and (max-width: 600px) { - .field--widget-entity-reference-layout-widget .erl-form-body { - margin-bottom: 108px; - } -} -.field--widget-entity-reference-layout-widget .erl-form-actions { - width: 100%; - position: absolute; - bottom: 0; - left: 0; - padding: 20px; - background: #fff; - box-sizing: border-box; - background-color: #f0f0f0; -} -.field--widget-entity-reference-layout-widget .erl-layout-section-select .description { - clear: left; -} -.field--widget-entity-reference-layout-widget .erl-layout-section-select .form-radios { - display: flex; - flex-wrap: wrap; - align-content: flex-start; -} -.field--widget-entity-reference-layout-widget .layout-radio-item { - padding: 5px; - background: none; - cursor: pointer; - margin: 10px 10px 10px 0; - text-align: left; - width: 150px; -} -.field--widget-entity-reference-layout-widget .layout-radio-item .form-item { - margin-top: 0; -} -.field--widget-entity-reference-layout-widget .layout-radio-item label { - font-size: small; -} -.field--widget-entity-reference-layout-widget .layout-radio-item .layout-icon-wrapper { - float: left; - margin-right: 5px; -} -.field--widget-entity-reference-layout-widget .layout-radio-item input { - display: none; -} -.field--widget-entity-reference-layout-widget .layout-radio-item.active { - background: #fff; - outline: 2px dotted blue; +.field--widget-entity-reference-layout-widget .erl-item.gu-transit { + background: #0074bd; } -.field--widget-entity-reference-layout-widget .layout-radio-item .layout-icon { - float: left; - margin: 0; +.field--widget-entity-reference-layout-widget .erl-item.gu-transit > * { + visibility: hidden; } diff --git a/css/full-screen-editing.css b/css/full-screen-editing.css deleted file mode 100644 index b692ac4..0000000 --- a/css/full-screen-editing.css +++ /dev/null @@ -1,36 +0,0 @@ -.layout-node-form { - position: relative; - /* overflow: scroll; */ - overflow: auto; - height: 100%; -} -.layout-node-form .layout-region-node-footer { - position: fixed; - width: 100%; - background: #fff; - padding: 0 30px 10px 30px; - z-index: 100; - bottom: 0; - left: 0; - box-shadow: 2px 0px 5px rgba(0, 0, 0, .2); -} -.layout-node-form .layout-region-node-footer__content { - border-top: none; -} - -.layout-node-form .layout-region-node-footer .form-item-status-value { - float: left; - padding: 7px 20px 0 0; -} - -.layout-region-node-main { - float: none !important; - width: auto !important; - clear: both !important; - padding-right: 0 !important; -} -.layout-region-node-secondary { - float: none !important; - width: auto !important; - clear: both !important; -} diff --git a/entity_reference_layout.libraries.yml b/entity_reference_layout.libraries.yml index 86bc8f9..4a81dc9 100644 --- a/entity_reference_layout.libraries.yml +++ b/entity_reference_layout.libraries.yml @@ -20,7 +20,11 @@ entity_reference_layout: dependencies: - entity_reference_layout/dragula -full_screen_editing: +erl_widget: css: layout: - css/full-screen-editing.css: {} + css/erl-widget.css: {} + js: + js/erl-widget.js: {} + dependencies: + - entity_reference_layout/dragula diff --git a/entity_reference_layout.module b/entity_reference_layout.module index 32ebc81..737012d 100644 --- a/entity_reference_layout.module +++ b/entity_reference_layout.module @@ -10,7 +10,6 @@ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\entity_reference_layout\Event\ErlMergeAttributesEvent; -use Drupal\Component\Utility\NestedArray; /** * Implements hook_help(). @@ -37,7 +36,7 @@ function entity_reference_layout_entity_view_alter(array &$build, EntityInterfac $layout_plugin_manager = \Drupal::service('plugin.manager.core.layout'); if (!$layout_plugin_manager->getDefinition($build['#erl_layout'], FALSE)) { $messenger = \Drupal::messenger(); - $messenger->addMessage(t('Layout `%layout_id` is unknown.', ['%layout_id' => $build['#layout']]), 'warning'); + $messenger->addMessage(t('Layout `%layout_id` is unknown.', ['%layout_id' => $build['#erl_layout']]), 'warning'); return; } @@ -130,10 +129,6 @@ function entity_reference_layout_theme() { 'render element' => 'form', 'function' => 'theme_entity_reference_layout_widget', ], - 'entity_reference_layout_ief' => [ - 'render element' => 'form', - 'function' => 'theme_entity_reference_layout_ief', - ], 'entity_reference_layout_radio' => [ 'render element' => 'element', 'function' => 'theme_entity_reference_layout_radio', @@ -184,57 +179,6 @@ function theme_entity_reference_layout_radio($element) { } -/** - * Wraps $element in HTML used for rendering modal. - */ -function _entity_reference_layout_modal_wrappers(array $element) { - $modal_wrapper = [ - '#prefix' => '
', - '#suffix' => '
', - ]; - $element += $modal_wrapper; - return $element; -} - -/** - * Implements hook_alter_form(). - * - * Adds HTML wrappers for modal when appropriate. - */ -function entity_reference_layout_alter_form(&$element, &$form_state) { - $element['#attributes']['class'][] = 'erl-entity-form'; - $element = _entity_reference_layout_modal_wrappers($element); - return $element; -} - -/** - * Implements hook_entity_reference_layout_ief(). - * - * Adds structure and classes to inline entity form. - */ -function theme_entity_reference_layout_ief($variables) { - $form = $variables['form']; - $renderer = \Drupal::service('renderer'); - $container = [ - '#type' => 'container', - '#attributes' => [ - 'class' => [ - 'erl-form-body', - 'scrollable', - ], - ], - ]; - foreach (Element::children($form['inline_entity_form']) as $key) { - if ($key != 'actions') { - $container[$key] = $form['inline_entity_form'][$key]; - unset($form['inline_entity_form'][$key]); - } - } - $form['inline_entity_form']['#attributes']['class'][] = 'erl-form-container'; - $form['inline_entity_form']['erl_form_body'] = $container; - return $renderer->render($form); -} - /** * Merges $layout_options into an $attributes array. * @@ -263,197 +207,200 @@ function entity_reference_layout_merge_attributes(array $attributes, array $layo } /** - * Themes entity reference with layout field widget, using Layouts API. + * Helper function to sort array items by '_weight'. + */ +function _erl_widget_sort_helper($a, $b) { + $a_weight = is_array($a) && isset($a['_weight']['#value']) ? $a['_weight']['#value'] : 0; + $b_weight = is_array($b) && isset($b['_weight']['#value']) ? $b['_weight']['#value'] : 0; + return $a_weight - $b_weight; +} + +/** + * Themes the "ERL" field widget. * * @param array $variables * Contains the form element data from $element['entities']. */ function theme_entity_reference_layout_widget(array $variables) { - $renderer = \Drupal::service('renderer'); $form = $variables['form']; $build = [ - 'content' => [ - // Add a dependable wrapper to use for drag/drop. - '#prefix' => '
', - '#suffix' => '
', - ], - ]; - - // Add more structure to help styling. - foreach (Element::children($form) as $key) { - // If there is a form, wrap it for modal. - if (isset($form[$key]['form'])) { - $form[$key]['form'] = _entity_reference_layout_modal_wrappers($form[$key]['form']); - } - // Add container element for form items. - $copy = ['#type' => 'container', '#attributes' => ['class' => ['erl-field-item']]]; - foreach (Element::children($form[$key]) as $item_key) { - $copy[$item_key] = $form[$key][$item_key]; - unset($form[$key][$item_key]); - } - $form[$key]['item'] = $copy; - if (!empty($form[$key]['#layout'])) { - $form[$key]['#prefix'] = '
'; - $form[$key]['#suffix'] = '
'; - } - - // Add button classes. - if (!empty($form[$key]['item']['actions']['ief_entity_edit'])) { - $form[$key]['item']['actions']['ief_entity_edit']['#attributes']['class'][] = 'form-submit-edit'; - } - if (!empty($form[$key]['item']['actions']['ief_entity_remove'])) { - $form[$key]['item']['actions']['ief_entity_remove']['#attributes']['class'][] = 'form-submit-remove'; - } - - // Add container element for actions. - if (!empty($form[$key]['item']['actions'])) { - $form[$key]['item']['actions'] = [ - '#type' => 'container', - 'actions' => $form[$key]['item']['actions'], - '#weight' => '-1000', - '#attributes' => [ - 'class' => ['container-inline', 'actions'], - ], - ]; - } - } - - // Organize items into nested layouts. - $layout_delta = -1; - - // Create a container for items not associated with a layout. - // This happens when a user changes from one layout to another, - // and the newly selected layout has different regions from - // the original one. - $disabled = [ - '#prefix' => '', - '#type' => 'container', - '#attributes' => [ - 'class' => [ - 'erl-layout-disabled', - ], - ], - 'title' => [ - '#type' => 'html_tag', - '#tag' => 'h4', - '#value' => t('Disabled items'), + '#type' => 'fieldset', + '#id' => $form['#id'], + '#attributes' => ['class' => ['erl-field']], + '#title' => $form['#title'], + // These get moved around with JS. + 'add_more' => ['#weight' => 998] + $form['add_more'], + // Container for layouts. + 'layout_items' => [ + '#type' => 'container', + '#attributes' => ['class' => ['erl-layout-wrapper']], ], - 'container' => [ + // Container for disabled / orphaned items. + 'disabled' => [ '#type' => 'container', - '#attributes' => [ - 'class' => [ - 'erl-layout', - 'erl-layout-section', - ], - ], - 'description' => [ + '#attributes' => ['class' => ['erl-disabled-items']], + '#weight' => 999, + 'title' => [ '#type' => 'html_tag', - '#tag' => 'div', + '#tag' => 'h4', + '#value' => t('Disabled Items'), '#attributes' => [ 'class' => [ - 'erl-layout-disabled-description ', + 'erl-disabled-items__title', + ], + ], + ], + 'items' => [ + '#type' => 'container', + '#attributes' => ['class' => ['erl-disabled-wrapper']], + 'description' => [ + '#type' => 'html_tag', + '#tag' => 'div', + '#attributes' => [ + 'class' => [ + 'erl-disabled-items__description', + ], ], + '#value' => t('Drop items here that you want to keep disabled / hidden, without removing them permanently.'), ], - '#value' => t('Drop items here that you want to keep disabled / hidden, without removing them permanently.'), ], - 'items' => [], ], ]; + $new_items = []; + $form_with_layout = []; + $renderer = \Drupal::service('renderer'); + /* @var \Drupal\Core\Layout\LayoutPluginManager @$layout_plugin_manager */ + $layout_plugin_manager = \Drupal::service('plugin.manager.core.layout'); + $items = []; foreach (Element::children($form) as $key) { + if ($key !== 'add_more') { + // If there is an open entity form, + // move it to it's own section of the widget form. + if (!empty($form[$key]['entity_form'])) { + $build['entity_form'] = $form[$key]['entity_form'] + [ + '#weight' => 1000, + ]; + $build['entity_form']['#attributes']['class'][] = 'erl-entity-form'; + unset($form[$key]['entity_form']); + } + // If there is an open remove confirmation form, + // move it to it's own section of the widget form. + if (!empty($form[$key]['remove_form'])) { + $build['remove_form'] = $form[$key]['remove_form'] + [ + '#weight' => 1000, + ]; + unset($form[$key]['remove_form']); + } + $items[] =& $form[$key]; + } + } + usort($items, '_erl_widget_sort_helper'); + + $has_non_layout_items = FALSE; + $section_key = NULL; + foreach ($items as $key => &$item) { + + // Set the weight for sorted list items. + $item['#weight'] = $key; + + // If this is a layout we'll populate regions below. + $item['#regions'] = []; - // Layout properties: - $layout_element = []; - if (isset($form[$key]['item']['layout_properties'])) { - $layout_element = $form[$key]['item']['layout_properties']; + // Add class for weight element. + if (!empty($item['_weight'])) { + $item['_weight']['#attributes']['class'] = ['erl-weight', 'js-hide']; } - $layout = isset($layout_element['layout']['#value']) ? $layout_element['layout']['#value'] : ''; - $region = isset($layout_element['region']['#value']) ? $layout_element['region']['#value'] : ''; - $parent = isset($layout_element['parent']['#value']) ? $layout_element['parent']['#value'] : -1; - - // Process layout paragraphs. - if (!empty($layout)) { - $layout_delta = $key; - $form[$key]['regions'] = []; - // $form[$key]['#prefix'] = 'key: ' . $key;. - /* @var \Drupal\Core\Layout\LayoutPluginManager @$layout_plugin_manager */ - $layout_plugin_manager = \Drupal::service('plugin.manager.core.layout'); - /* @var \Drupal\Core\Layout\LayoutInterface $layout_instance */ - $layout_instance = $layout_plugin_manager->createInstance($layout); + + // Stash new items for processing later. + if (!empty($item['#is_new'])) { + $new_items[] = $item; + } + // If the element has a layout, structure it as a layout container. + elseif (!empty($item['layout']['#value'])) { + $section_key = $key; + $form_with_layout['section_' . $section_key] = $items[$section_key]; + $form_with_layout['section_' . $section_key]['#attributes']['class'][] = 'erl-layout'; + $layout_instance = $layout_plugin_manager->createInstance($items[$section_key]['layout']['#value'], $items[$section_key]['config']['#value']); foreach ($layout_instance->getPluginDefinition()->getRegionNames() as $region_name) { - $form[$key]['regions'][$region_name] = [ + $form_with_layout['section_' . $section_key]['#regions'][$region_name] = [ '#attributes' => [ 'class' => [ - 'erl-layout-section', - 'erl-layout-delta--' . $key, - 'erl-layout-section--' . $region_name, + 'erl-layout-region', + 'erl-layout-region--' . $region_name, ], ], ]; } } - - // Process paragraphs that go inside regions or that are "disabled". + // If the element has a region, add it into the correct + // region within the layout container. + // Or if the specified region doesn't exist, add it to the list + // of "disabled" items. + elseif (!empty($item['region']['#value']) && isset($form_with_layout['section_' . $section_key]['#regions'][$item['region']['#value']])) { + $form_with_layout['section_' . $section_key]['#regions'][$item['region']['#value']][] = $item; + $has_non_layout_items = TRUE; + } else { - if (isset($parent) && $parent >= 0) { - $layout_key = $parent; - } - else { - $layout_key = $layout_delta; - } - if (isset($form[$layout_key]) && isset($form[$layout_key]['regions'][$region])) { - $form[$layout_key]['regions'][$region]['content'][] = $form[$key]; - } - else { - if ($region) { - \Drupal::messenger()->addMessage(t('One ore more items were disabled.'), 'warning'); - } - $disabled['container']['items'][] = $form[$key]; - } - $form[$key]['#skip'] = TRUE; + $build['disabled']['items'][] = $item; + $has_non_layout_items = TRUE; } } - // Build the render array. - foreach (Element::children($form) as $key) { - $layout_plugin_manager = \Drupal::service('plugin.manager.core.layout'); - $form_item = $form[$key]; + // Move new items into correct position, if applicable. + foreach ($new_items as $key => $item) { + $section_key = $item['#parent_weight']; - if (isset($form_item['#skip'])) { - continue; + // Layout items. + if (empty($item['#new_region'])) { + $item['#weight'] = $item['#parent_weight'] + .5; + $form_with_layout['new_section_' . $key] = $item; } - $item = $form_item; - if (isset($form_item['#layout'])) { - $layout_instance = $layout_plugin_manager->createInstance($form_item['#layout'], $form_item['#layout_config']); - // This builds the render array. - // Add layout attributes: - $attributes = entity_reference_layout_merge_attributes(['class' => ['erl-layout']], $form_item['#layout_options']); - if (!empty($form_item['regions'])) { - $layout_build = NestedArray::mergeDeep(['#attributes' => $attributes], $layout_instance->build($form_item['regions'])); - $item['regions'] = [ - 'content' => $layout_build, - '#weight' => 100, - '#attributes' => [ - 'class' => ['erl-layout'], - ], - ]; + // Items to add into regions. + if (isset($item['#new_region'])) { + $region_name = $item['#new_region']; + if (isset($form_with_layout['section_' . $section_key]['#regions'][$region_name])) { + $form_with_layout['section_' . $section_key]['#regions'][$region_name][] = $item; } } - if (empty($form[$key]['#skip']) - && isset($form[$key]['item']['layout_properties']['parent']['#value']) - && $form[$key]['item']['layout_properties']['parent']['#value'] > -1) { - $parent = $form[$key]['item']['layout_properties']['parent']['#value']; - $item['#weight'] = $parent + .5; + } + + foreach ($form_with_layout as $key => $section) { + if (!empty($section['layout']['#value'])) { + $layout_instance = $layout_plugin_manager->createInstance($section['layout']['#value'], $section['config']['#value']); + $section['preview'] = [ + 'content' => [ + '#type' => 'container', + '#attributes' => ['class' => ['erl-layout__content']], + 'preview' => $section['preview'], + ], + 'regions' => [ + '#weight' => 1000, + '#attributes' => ['class' => ['erl-layout__regions']], + ] + $layout_instance->build($section['#regions']), + ]; } - $build['content'][] = $item; + $section['preview']['#weight'] = 1000; + $build['layout_items'][] = $section; + } + + // If there are no items, don't show the disabled region. + if (!$has_non_layout_items) { + unset($build['disabled']); + } + + // Add a container so Javascript can respond to empty state. + if (count($items) == 0) { + $build['empty_container'] = [ + '#type' => 'container', + '#attributes' => ['class' => ['erl-empty']], + ]; } - // Add "disabled" container to build (anyway) with eventual items into it. - $build['disabled'] = $disabled; - $build['#attached']['drupalSettings']['erlLayoutFields'][$form['#id']] = $form['#id']; - $build['#attached']['library'][] = 'entity_reference_layout/entity_reference_layout'; + $build['#attached']['library'][] = 'entity_reference_layout/erl_widget'; + $build['#attached']['library'][] = 'core/drupal.dialog.ajax'; return $renderer->render($build); } diff --git a/js/entity-reference-layout.js b/js/entity-reference-layout.js deleted file mode 100644 index 2639493..0000000 --- a/js/entity-reference-layout.js +++ /dev/null @@ -1,294 +0,0 @@ -(function ($, Drupal, drupalSettings) { - - 'use strict'; - - var drake; - - Drupal.behaviors.fieldUIEntityReferenceLayout = { - - attach: function attach(context, settings) { - - var updateDisabled = function($container) { - if ($container.find('.erl-layout .erl-field-item').length > 0) { - $container.find('.erl-layout-disabled').show(); - } - else { - $container.find('.erl-layout-disabled').hide(); - } - if ($container.find('.erl-layout-disabled .erl-field-item').length > 0) { - $container.find('.erl-layout-disabled-description').hide(); - } - else { - $container.find('.erl-layout-disabled-description').show(); - } - } - - var updateFields = function($container) { - - // Set deltas: - $container.find('.erl-field-item').each(function(index, item){ - $(item).find('.ief-entity-delta').val(index + ''); - }); - - // Set parents: - $container.parent().find('.erl-field-item .parent-delta').each(function(index, item){ - var d = getParentDelta($(item)); - if (d >= 0) { - $(item).val(d); - } - }); - - // Set regions: - $container.find('.erl-field-item .erl-region-select').each(function(index, item){ - var $el = $(item); - if ($el.parents('.erl-region-section')) { - $el.val(getRegion($el)); - } - }); - }; - - var moveUp = function($item, $container) { - if ($item.parents('.erl-layout-section').length == 0) { - $item = $item.parent(); - } - if ($item.prev().length > 0) { - $item.after($item.prev()); - updateFields($container); - } - } - - var moveDown = function($item, $container) { - if ($item.parents('.erl-layout-section').length == 0) { - $item = $item.parent(); - } - if ($item.next().length > 0) { - $item.before($item.next()); - updateFields($container); - } - } - - var addLayoutControls = function($container) { - $container.find('.erl-field-item').each(function(index, fieldItem){ - var $fieldItem = $(fieldItem); - $fieldItem - .remove('.layout-controls') - .append($('
') - .append($('
').mousedown( - function(){ - moveUp($fieldItem, $container); - } - )) - .append($('
')) - .append($('
').mousedown( - function(){ - moveDown($fieldItem, $container); - } - ) - ) - ); - }); - } - - var getRegion = function($el) { - var regEx = /erl-layout-section--([a-z0-9A-Z_]*)/, - regionName = '', - $container = $el.is('.erl-layout-section') ? $el : $el.parents('.erl-layout-section'); - if ($container.length) { - var matches = $container[0].className.match(regEx); - if (matches && matches.length >= 2) { - regionName = matches[1]; - } - } - return regionName; - } - - var getParentDelta = function($el) { - var regEx = /erl-layout-delta--([0-9]+)/, - delta = -1, - $container = $el.is('.erl-layout-section') ? $el : $el.parents('.erl-layout-section'); - // Has a section parent - if ($container.length) { - var matches = $container[0].className.match(regEx); - if (matches && matches.length >= 2) { - delta = matches[1]; - } - } - return delta; - } - - var getSiblingDelta = function($el) { - var regEx = /erl-layout-delta--([0-9]+)/, - delta = -1, - $container = $el.is('.erl-field-item--layout') ? $el : $el.parents('.erl-field-item--layout:first'); - if ($container.length) { - delta = $container.prev('.erl-field-item--layout').find('> .erl-field-item .ief-entity-delta').val() - } - return delta; - } - - var addNewButton = function($buttonGroup, $optionItem, $section, $erlField, prefix) { - prefix = prefix ? prefix : ''; - var icon = ''; - if (drupalSettings.erlIcons && drupalSettings.erlIcons['icon_' + $optionItem.val()]) { - icon = ''; - } - - $buttonGroup.append($('') - .click(function(e){ - return false; - }) - .mousedown(function(e){ - $erlField.find('.erl-new-item-region').val(getRegion($section)); - $erlField.find('.erl-field-actions select').val($optionItem.val()); - var parent = getParentDelta($section); - if (parent < 0 ) { - parent = getSiblingDelta($section); - } - $erlField.find('.erl-new-item-delta').val(parent); - $erlField.find('.erl-field-actions input.js-form-submit').trigger('mousedown'); - return false; - })); - } - - var buttonGroup = function($types, $section, $erlField) { - var $addButtons = $(''); - $types.each(function(index, elem) { - addNewButton($addButtons, $(elem), $section, $erlField); - }); - var $addContent = $('') - .appendTo($section) - .on('click', function(e){ - $(e.target).focus(); - return false; - }) - .on('click', function(e){ - var $b = $(e.target); - $b.parent().find('.erl-add-content--group').toggleClass('hidden'); - $b.toggleClass('active'); - $b.text($b.text() == '+' ? '-' : '+'); - return false; - }); - $(window).click(function(){ - $erlField.find('.erl-add-content--group').addClass('hidden'); - $erlField.find('.erl-add-content').removeClass('active'); - }); - $section.append($addButtons); - } - - var addRegionButtons = function($erlField) { - var $types = $erlField.find('.erl-field-actions optgroup[label="Content"] option'); - $erlField.find('.erl-layout-section').each(function(index, section) { - if ($(section).parents('.erl-layout-disabled').length == 0) { - buttonGroup($types, $(section), $erlField); - } - }); - } - - var addSectionButtons = function($erlField) { - var $types = $erlField.find('.erl-field-actions optgroup[label="Layout"] option'); - $erlField.parent().find('.erl-field-actions:first').each(function(index, section) { - if ($types.length > 1) { - buttonGroup($types, $(section), $erlField); - } - else { - // Create the "Add section" button above disabled area. - var $addSection = $('
').insertBefore($erlField.find('.erl-layout-disabled')); - addNewButton($addSection, $types, $(section), $erlField, '+ Add '); - - // Add it below all other sections except the first one. - $erlField.find('.erl-field-item--layout > .erl-field-item:gt(0)').each(function(index, item){ - var $addSection = $('
').appendTo($(item)); - addNewButton($addSection, $types, $(item), $erlField, '+ Add '); - }); - } - }); - } - - var enhanceRadioSelect = function() { - $('.layout-radio-item').click(function(){ - $(this).find('input[type=radio]').prop("checked", true).trigger("change"); - $(this).siblings().removeClass('active'); - $(this).addClass('active'); - }); - $('.layout-radio-item').each(function(){ - if ($(this).find('input[type=radio]').prop("checked")) { - $(this).addClass('active'); - } - }); - } - - var editableLayout = function($elem, options) { - var forceLayouts = options.forceLayouts ? true : false; - updateFields($elem); - updateDisabled($elem); - - // Turn on drag and drop if dragula function exists. - if (typeof dragula !== 'undefined') { - $elem.addClass('dragula-enabled'); - if (drake) { - drake.destroy(); - } - drake = dragula([].slice.apply(document.querySelectorAll('.erl-layout-section, .erl-field-item--layout-container')), { - moves: function(el, container, handle) { - return handle.className.toString().indexOf('layout-handle') >= 0; - }, - - accepts: function(el, target, source, sibling) { - // Layouts can never inside another layout. - if ($(el).is('.erl-field-item--layout')) { - if ($(target).parents('.erl-field-item--layout').length) { - return false; - } - } - - // Layouts can not be dropped into disabled (only individual items). - if ($(el).is('.erl-field-item--layout')) { - if ($(target).parents('.erl-layout-disabled').length) { - return false; - } - } - - // Require non-layout items to be dropped in a layout. - if ($(el).is('.erl-field-item')) { - if($(target).parents('.erl-field-item--layout').length == 0 && $(target).parents('.erl-layout-disabled').length == 0) { - return false; - } - } - - return true; - } - }); - drake.on('drop', function(el, target, source, sibling){ - updateFields($elem); - updateDisabled($elem); - }); - - } - addRegionButtons($elem); - addSectionButtons($elem); - addLayoutControls($elem); - - } - - $('.erl-field', context).once('erlBehaviors').each(function(index, item){ - $('#erl-modal').dialog( - { - width: 1000, - appendTo: $('#erl-modal').parents('.erl-field'), - close: function (event, ui){ - $('#erl-modal').find('input[value="Cancel"]').trigger('mousedown'); - }, - open: function (event, ui) { - enhanceRadioSelect(); - }, - modal: true, - title: $('#erl-modal .ief-title').text() - } - ); - editableLayout($(item), {forceLayouts: $(item).hasClass('erl-field-force-layouts')}); - }); - - } - }; - -})(jQuery, Drupal, drupalSettings); diff --git a/js/erl-widget.js b/js/erl-widget.js new file mode 100644 index 0000000..9488738 --- /dev/null +++ b/js/erl-widget.js @@ -0,0 +1,266 @@ +(function ($, Drupal, drupalSettings) { + + 'use strict'; + + var drake; + + Drupal.behaviors.erlWidget = { + attach: function attach(context, settings) { + + var updateFields = function($container) { + // Set deltas: + var delta = -1; + $container.find('.erl-weight, .erl-new-item-delta').each(function(index, item){ + if ($(item).hasClass('erl-weight')) { + delta++; + } + $(item).val(delta + ''); + }); + $container.find('input.erl-region').each(function(index, item){ + $(item).val(getRegion($(item))); + }); + } + + var updateDisabled = function($container) { + if ($container.find('.erl-disabled-items .erl-item').length > 0) { + $container.find('.erl-disabled-items__description').hide(); + } + else { + $container.find('.erl-disabled-items__description').show(); + } + } + + var getRegion = function($el) { + var regEx = /erl-layout-region--([a-z0-9A-Z_]*)/, + regionName = '', + $container = $el.is('.erl-layout-region') ? $el : $el.parents('.erl-layout-region'); + if ($container.length) { + var matches = $container[0].className.match(regEx); + if (matches && matches.length >= 2) { + regionName = matches[1]; + } + } + return regionName; + } + + var moveUp = function($item, $container) { + var $item = $(this).parents('.erl-item:first'), + $container = $item.parent(); + // We're first, jump up to next available region. + if ($item.prev('.erl-item').length == 0) { + // Previous region, same layout. + if ($container.prev('.erl-layout-region').length) { + $container.prev('.erl-layout-region').append($item); + } + // Otherwise jump to last region in previous layout. + else if ($container.closest('.erl-layout').prev().find('.erl-layout-region:last-child').length) { + $container.closest('.erl-layout').prev().find('.erl-layout-region:last-child .erl-add-content__container').before($item); + } + } + else { + $item.after($item.prev()); + } + updateFields($container.closest('.erl-field')); + } + + var moveDown = function($item, $container) { + var $item = $(this).parents('.erl-item:first'), + $container = $item.parent(); + // We're first, jump down to next available region. + if ($item.next('.erl-item').length == 0) { + // Next region, same layout. + if ($container.next('.erl-layout-region').length) { + $container.next('.erl-layout-region').prepend($item); + } + // Otherwise jump to first region in next layout. + else if ($container.closest('.erl-layout').next().find('.erl-layout-region:first-child').length) { + $container.closest('.erl-layout').next().find('.erl-layout-region:first-child .erl-add-content__container').before($item); + } + } + else { + $item.before($item.next()); + } + updateFields($container.closest('.erl-field')); + } + + var enhanceRadioSelect = function() { + $('.layout-radio-item').click(function(){ + $(this).find('input[type=radio]').prop("checked", true).trigger("change"); + $(this).siblings().removeClass('active'); + $(this).addClass('active'); + }); + $('.layout-radio-item').each(function(){ + if ($(this).find('input[type=radio]').prop("checked")) { + $(this).addClass('active'); + } + }); + } + + // Add create item links. + $('.erl-field', context).once('erl-add-item-links').each(function(index, item){ + + var $widget_container = $(item), + $submit_button = $widget_container.find('input.erl-add-item'), + $select = $widget_container.find('select.erl-item-type'), + $region_input = $widget_container.find('.erl-new-item-region'), + $parent_input = $widget_container.find('.erl-new-item-parent'), + $options = $select.find('option').map(function(i, opt){ + var type = $(opt).val(), + label = $(opt).text(); + return $('')[0]; + }), + $button_group = $('