diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc index a3b8868cd0..b124da3b8e 100644 --- a/core/modules/file/file.field.inc +++ b/core/modules/file/file.field.inc @@ -149,6 +149,12 @@ function template_preprocess_file_upload_help(&$variables) { if (!empty($description)) { $descriptions[] = FieldFilteredMarkup::create($description); } + if (isset($upload_validators['file_validate_extensions'])) { + $descriptions[] = t('Allowed types: @extensions.', ['@extensions' => $upload_validators['file_validate_extensions'][0]]); + } + if (isset($upload_validators['file_validate_size'])) { + $descriptions[] = t('Up to @size.', ['@size' => format_size($upload_validators['file_validate_size'][0])]); + } if (isset($cardinality)) { if ($cardinality == -1) { $descriptions[] = t('Unlimited number of files can be uploaded to this field.'); @@ -157,12 +163,6 @@ function template_preprocess_file_upload_help(&$variables) { $descriptions[] = \Drupal::translation()->formatPlural($cardinality, 'One file only.', 'Maximum @count files.'); } } - if (isset($upload_validators['file_validate_size'])) { - $descriptions[] = t('@size limit.', ['@size' => format_size($upload_validators['file_validate_size'][0])]); - } - if (isset($upload_validators['file_validate_extensions'])) { - $descriptions[] = t('Allowed types: @extensions.', ['@extensions' => $upload_validators['file_validate_extensions'][0]]); - } if (isset($upload_validators['file_validate_image_resolution'])) { $max = $upload_validators['file_validate_image_resolution'][0]; diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php index 1003bb094c..ada5b031b6 100644 --- a/core/modules/file/src/Element/ManagedFile.php +++ b/core/modules/file/src/Element/ManagedFile.php @@ -186,11 +186,10 @@ public static function uploadAjaxCallback(&$form, FormStateInterface &$form_stat $current_file_count = $form_state->get('file_upload_delta_initial'); if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']) { $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content'; - $form[$current_file_count]['#attributes']['class'][] = 'managed-file'; } // Otherwise just add the new content class on a placeholder. else { - $form['#suffix'] .= ''; + $form['#suffix'] .= ''; } $status_messages = ['#type' => 'status_messages']; diff --git a/core/themes/seven/css/components/dropzone.css b/core/themes/seven/css/components/dropzone.css index 1b76b34e11..a831042f0a 100644 --- a/core/themes/seven/css/components/dropzone.css +++ b/core/themes/seven/css/components/dropzone.css @@ -1,369 +1,170 @@ /** - * Field Widgets. + * Dropzone. */ -.field--widget-image-image, -.field--widget-file-generic, -.field--widget-image-image td, -.field--widget-file-generic td { +.dropzone { position: relative; + display: flex; + width: 100%; + height: 100%; + min-height: 100px; + border: 1px solid #bfbfbf; + border-radius: 2px; + background: #fcfcfa; } -/** - * Remove Button. - */ -.field--widget-image-image .remove-button, -.field--widget-file-generic .remove-button, -#edit-settings-default-image .remove-button { - position: absolute; - top: 10px; - right: 10px; - text-align: right; - line-height: 17px; - text-transform: lowercase; - min-width: 95px; - font-weight: normal; - background: url(../../../../misc/icons/787878/ex.svg) right 0 no-repeat; - border: 0; - margin: 0; - padding: 0 1.5em; - float: right; - width: auto; - text-shadow: 0; -} - -[dir="rtl"] .field--widget-image-image .remove-button, -[dir="rtl"] .field--widget-file-generic .remove-button, -[dir="rtl"] #edit-settings-default-image .remove-button { - margin: 0 0 0 10px; - float: left; - text-align: left; - background-position: left 0; -} - -.field--widget-image-image .remove-button:focus, -.field--widget-file-generic .remove-button:focus, -#edit-settings-default-image .remove-button:focus { - box-shadow: none; - outline: none; -} - -.field--widget-image-image .remove-button:hover, -.field--widget-file-generic .remove-button:hover, -#edit-settings-default-image .remove-button:hover { - background: url(../../../../misc/icons/ee0000/ex.svg) right 0 no-repeat; - border: 0; - box-shadow: none; - color: #ee0000; -} - -[dir="rtl"] .field--widget-image-image .remove-button:hover, -[dir="rtl"] .field--widget-file-generic .remove-button:hover, -[dir="rtl"] #edit-settings-default-image .remove-button:hover { - background-position: left 0; -} - -/** - * Remove Buttons Ajax Throbber. - */ -.field--widget-image-image .remove-button + .ajax-progress, -.field--widget-file-generic .remove-button + .ajax-progress, -#edit-settings-default-image .remove-button + .ajax-progress { - position: absolute; - right: 85px; - top: 8px; +/* Dropzone trigger area. */ +.dropzone .dropzone-trigger { + flex: 0 0 100px; + min-height: 100px; + border-right: 1px solid #bfbfbf; + background: url("../../../../misc/icons/bebebe/dropzone-new.svg") 0 center no-repeat; } - -[dir="rtl"] .field--widget-image-image .remove-button + .ajax-progress, -[dir="rtl"] .field--widget-file-generic .remove-button + .ajax-progress, -[dir="rtl"] #edit-settings-default-image .remove-button + .ajax-progress { - right: auto; - left: 85px; +[dir="rtl"] .dropzone .dropzone-trigger { + border-right: none; + border-left: 1px solid #bfbfbf; } -/** - * Upload Buttons Ajax Throbber. - */ -.field--widget-image-image .upload-button + .ajax-progress, -.field--widget-file-generic .upload-button + .ajax-progress, -#edit-settings-default-image .upload-button + .ajax-progress { +.dropzone .js-form-file { position: absolute; - left: 50px; - top: 50%; - margin: -10px 0 0 -13px; + top: 0; + left: -100px; + display: inline-block; + width: 100px; + height: 100%; + min-height: 100px; + cursor: pointer; + opacity: 0; } - -[dir="rtl"] .field--widget-image-image .upload-button + .ajax-progress, -[dir="rtl"] .field--widget-file-generic .upload-button + .ajax-progress, -[dir="rtl"] #edit-settings-default-image .upload-button + .ajax-progress { +[dir="rtl"] .dropzone .js-form-file { + right: -100px; left: auto; - right: 50px; - margin: -10px -13px 0 0; -} - -/** - * Image Widget. - */ -.field--widget-image-image .dropzone-trigger, -#edit-settings-default-image .dropzone-trigger { - display: none; } -.field--widget-image-image .image-widget-data .form-item { - width: 100%; - float: left; -} - -.field--widget-image-image .image-preview img { - border: 0; - max-width: 100px; - height: auto; - padding: 0 10px 0 0; - float: left; -} - -[dir="rtl"] .field--widget-image-image .image-preview img { - float: right; - margin: 0 0 0 10px; -} - -/** - * Multi-Image Uploads Table. - */ -.dropzone-enabled table { - margin: 10px 0; -} -.dropzone-enabled table tr td:nth-child(2), -.dropzone-enabled table tr td:last-child { - vertical-align: top; -} -.dropzone-enabled table tr th:last-child, -.dropzone-enabled table tr td:last-child { - text-align: right; -} -.field--widget-file-generic td .form-managed-file .form-item { - padding: 10px 0 0 0; +.dropzone .dropzone-trigger.is-hovering { + background-position: -100px center; } -@media screen and (min-width: 414px) { - .field--widget-image-image td .image-widget-data .form-item { - padding-left: 27px; - } - .field--widget-file-generic td .form-managed-file .form-item { - padding: 10px 0 0 27px; - } +.dropzone .dropzone-trigger.is-hidden { + background-image: none; } -.dropzone-enabled table tr th:nth-child(2), -.dropzone-enabled table tr td:nth-child(2), -.dropzone-enabled table tr th:last-child, -.dropzone-enabled table tr td:last-child { - width: 30%; - white-space: nowrap; +/* Dropzone no-trigger area. */ +.dropzone .dropzone-no-trigger .js-dropzone-add-button { + margin: 20px 10px 5px 10px; } -.form-text { - width: 100%; -} -.field--type-image .details-wrapper, -.field--type-file .details-wrapper { - overflow: auto; +.dropzone .dropzone-no-trigger .js-dropzone-add-button:before { + margin-left: -0.2em; + content: "+"; + font-weight: 900; } -.dropzone-enabled table { - margin: 10px 0; - width: auto; +.dropzone .dropzone-no-trigger .description { + display: table-cell; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + max-width: 500px; + padding: 0 10px 20px 10px; } -@media screen and (min-width: 414px) { - .dropzone-enabled table tr th:last-child, - .dropzone-enabled table tr td:last-child, - .dropzone-enabled table tr th:nth-child(2), - .dropzone-enabled table tr td:nth-child(2) { - width: 30%; - white-space: nowrap; - } - .dropzone-enabled table { - table-layout: fixed; - } +.dropzone .dropzone-no-trigger .description li { + display: inline-block; } -@media screen and (min-width: 767px) { - .dropzone-enabled table tr th:last-child, - .dropzone-enabled table tr td:last-child, - .dropzone-enabled table tr th:nth-child(2), - .dropzone-enabled table tr td:nth-child(2) { - width: 25%; - white-space: nowrap; - } - .dropzone .image-widget .form-text { - width: auto; - } +.dropzone .dropzone-no-trigger .description li:after { + padding: 0 5px; + content: "\00b7"; } -.field--widget-image-image td .image-widget-data { - float: none; +.dropzone .dropzone-no-trigger .description li:last-child:after { + content: none; } -.field--widget-image-image td .image-preview img { - padding-bottom: 10px; +.dropzone .dropzone-no-trigger .image-preview { + display: none; } - -.field--widget-image-image td .image-widget-data .form-item { - margin-top: 1em; +.dropzone .dropzone-no-trigger .messages { + margin: 10px 10px 0 18px; } -.field--widget-file-generic td .form-managed-file .form-item { - padding: 0 0 0 27px; +/** + * Upload buttons AJAX throbber. + */ +.dropzone .upload-button + .ajax-progress { + position: absolute; + top: 50px; + left: -50px; + margin: -10px -15px; } - -[dir="rtl"] .field--widget-file-generic td .form-managed-file .form-item { - padding: 0 27px 0 0; +[dir="rtl"] .dropzone .upload-button + .ajax-progress { + right: -50px; + left: auto; } /** - * Dropzone. + * Single file/image widget after upload. */ -.dropzone-wrapper { - width: 100%; +.form-managed-file-wrapper.dropzone-enabled.has-file { border: 1px solid #bfbfbf; border-radius: 2px; background: #fcfcfa; - box-sizing: border-box; -} -.dropzone { - position: relative; - width: 100%; - height: 100%; - min-height: 100px; } -.dropzone.empty { - display: table; -} -.dropzone.empty > div { - display: table-cell; - vertical-align: top; - box-sizing: border-box; -} -span.ajax-new-content.managed-file { - display: block; -} -.dropzone .has-file, -.dropzone .form-managed-file { - padding: 10px; + +.form-managed-file-wrapper.dropzone-enabled.has-file .form-managed-file { + margin-left: 100px; + padding: 1em; } -.dropzone.empty .form-managed-file { - padding: 0; +[dir="rtl"] .form-managed-file-wrapper.dropzone-enabled.has-file .form-managed-file { + margin-right: 100px; + margin-left: 0; } - -/* Hide error message. */ -.dropzone .messages--error { display: none; } - -/* Form file. */ -.dropzone .js-form-file { +.form-managed-file-wrapper.dropzone-enabled.has-file .form-managed-file:before { position: absolute; - left: 0; top: 0; - cursor: pointer; - min-height: 100px; - height: 100%; + left: -100px; + display: block; width: 100px; - display: inline-block; - opacity: 0; -} - -[dir="rtl"] .dropzone .js-form-file { - right: 0; - left: auto; -} - -.dropzone .file { - background: none; - padding: 0; -} - -.dropzone .dropzone-trigger { - position: relative; - text-align: center; - min-height: 100px; height: 100%; - width: 100px; + min-height: 100px; + content: ""; border-right: 1px solid #bfbfbf; - background: url('../../../../misc/icons/bebebe/dropzone-new.svg') -200px center no-repeat; + background: url("../../../../misc/icons/bebebe/dropzone-new.svg") -200px center no-repeat; } - -[dir="rtl"] .dropzone .dropzone-trigger { +[dir="rtl"] .form-managed-file-wrapper.dropzone-enabled.has-file .form-managed-file:before { + right: -100px; + left: auto; border-right: none; border-left: 1px solid #bfbfbf; } -.dropzone.empty .dropzone-trigger, -#edit-settings-default-image .dropzone.empty .dropzone-trigger { - display: table-cell; - background-position: 0 center; -} - -.dropzone .dropzone-trigger.is-hovering { - background-position: -100px center; -} - -.dropzone .dropzone-trigger.is-complete, -.field--type-image .dropzone .dropzone-trigger.is-complete, -.field--type-file .dropzone .dropzone-trigger.is-complete { - background-position: -200px center; -} - -.dropzone.empty .dropzone-trigger.is-complete { - background-position: 0 center; -} - -.dropzone.empty .image-preview { - padding-top: 10px; - padding-left: 10px; -} - -@media screen and (min-width: 767px) { - .dropzone.empty .image-widget { - float: left; - } +.form-managed-file-wrapper.dropzone-enabled.has-file .image-widget.form-managed-file { + margin-right: 0; + margin-left: 0; + padding: 0; } - -.dropzone-description { - padding: 10px; - -moz-box-sizing: border-box; - -webkit-box-sizing: border-box; - box-sizing: border-box; - display: table-cell; +[dir="rtl"] .form-managed-file-wrapper.dropzone-enabled.has-file .image-widget.form-managed-file { + margin-right: 0; } - -.dropzone-preview { - border: 1px solid #ccc; - border-radius: 2px; - background: #fdfdfc; - margin-bottom: 1em; padding: 0.5em 1em; +.form-managed-file-wrapper.dropzone-enabled.has-file .image-widget.form-managed-file:before { + content: none; } -.dropzone-buttons { +.form-managed-file-wrapper.dropzone-enabled.has-file .image-widget .image-preview { + flex: 0 0 100px; + width: 100px; padding: 0; - margin: 0 10px 10px 0; - list-style: none; -} - -[dir="rtl"] .dropzone-buttons { - margin: 0 0 10px 10px; + border-right: 1px solid #bfbfbf; } - -.dropzone-buttons li { - display: inline-block; - margin: 0 10px 0 0; +[dir="rtl"] .form-managed-file-wrapper.dropzone-enabled.has-file .image-widget .image-preview { + border-right: none; + border-left: 1px solid #bfbfbf; } -[dir="rtl"] .dropzone-buttons li { - margin: 0 0 0 10px; +.form-managed-file-wrapper.dropzone-enabled.has-file .image-widget .image-preview img { + margin: 10px; } -.dropzone .dropzone-trigger.is-hidden, -.field--type-image .dropzone .dropzone-trigger.is-hidden, -.field--type-file .dropzone .dropzone-trigger.is-hidden { - background-image: none; +.form-managed-file-wrapper.dropzone-enabled.has-file .image-widget .image-widget-data { + padding: 1em; } - diff --git a/core/themes/seven/css/components/managed-file.css b/core/themes/seven/css/components/managed-file.css new file mode 100644 index 0000000000..2e8dc05134 --- /dev/null +++ b/core/themes/seven/css/components/managed-file.css @@ -0,0 +1,148 @@ +/** + * Image widget. + */ +.form-type-managed-file .form-managed-file { + position: relative; + display: block; +} + +/** + * Image widget. + */ +.form-type-managed-file .image-widget { + position: relative; + display: flex; +} + +.form-type-managed-file .image-widget .image-preview { + padding: 0 1em 0 0; +} +[dir="rtl"] .form-type-managed-file .image-widget .image-preview { + padding: 0 0 0 1em; +} + +.form-type-managed-file .image-widget .image-preview img { + width: 80px; + max-width: 80px; + height: auto; + border: 0; +} + +.form-type-managed-file .description ul { + margin: 0; + padding: 0; + list-style: none; +} + +/** + * Multi uploads table. + */ +.form-type-managed-file table { + width: 100%; + margin: 10px 0; + table-layout: fixed; +} + +.form-type-managed-file table tr td:first-child { + display: flex; +} + +.form-type-managed-file table tr.draggable td:first-child a.tabledrag-handle { + flex: 0 0 40px; +} + +.form-type-managed-file table tr th:nth-child(2), +.form-type-managed-file table tr td:nth-child(2) { + width: 40px; + vertical-align: top; + white-space: nowrap; +} + +.form-type-managed-file table tr th:last-child, +.form-type-managed-file table tr td:last-child { + position: relative; + width: 20px; + text-align: right; + vertical-align: top; + white-space: nowrap; +} + +.form-type-managed-file table tr th:last-child { + font-size: 0; +} + +/** + * Remove button. + */ +.form-type-managed-file .remove-button { + position: absolute; + top: 10px; + right: 10px; + width: 17px; + margin: 0; + padding: 0; + text-transform: lowercase; + border: 0; + background: url(../../../../misc/icons/787878/ex.svg) right 0 no-repeat; + font-size: 0; + line-height: 17px; +} +[dir="rtl"] .form-type-managed-file .remove-button { + right: auto; + left: 10px; + background-position: left 0; +} + +.form-type-managed-file .remove-button:focus { + outline: none; + box-shadow: none; +} + +.form-type-managed-file .remove-button:hover { + color: #e00; + border: 0; + background: url(../../../../misc/icons/ee0000/ex.svg) right 0 no-repeat; + box-shadow: none; +} +[dir="rtl"] .form-type-managed-file .remove-button:hover { + background-position: left 0; +} + +/** + * Remove buttons ajax throbber. + */ +.form-type-managed-file .remove-button + .ajax-progress { + position: absolute; + top: 8px; + right: 25px; +} +[dir="rtl"] .form-type-managed-file .remove-button + .ajax-progress { + right: auto; + left: 25px; +} + +/** + * Stop input elements from breaking out of their container. + */ +.form-type-managed-file .form-item { + margin-top: 0; +} + +/** + * Stop input elements from breaking out of their container. + */ +.form-type-managed-file input.form-text { + width: 100%; + max-width: 500px; +} + +/** + * Default image field. + */ +#edit-settings-default-image .form-managed-file-wrapper.dropzone-enabled.has-file { + min-height: 100px; +} +#edit-settings-default-image .form-managed-file-wrapper.dropzone-enabled.has-file .description { + margin-left: 100px; + padding: 0 1em 1em; +} diff --git a/core/themes/seven/js/dropzone.es6.js b/core/themes/seven/js/dropzone.es6.js new file mode 100644 index 0000000000..1fa975aaa7 --- /dev/null +++ b/core/themes/seven/js/dropzone.es6.js @@ -0,0 +1,192 @@ +(function($, Drupal, drupalSettings) { + /** + * Initializes a dropzone for managed file fields. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches a dropzone to managed file fields. + */ + Drupal.behaviors.dropzone = { + attach(context) { + // Add 'dropzone-enabled' class to managed file fields. + $(context) + .find('.form-managed-file-wrapper') + .addClass('dropzone-enabled'); + + // Add dropzone HTML to managed file fields. + $(context) + .find('.js-form-file') + .once('managed-file-dropzone') + .each(function() { + const $wrapper = $(this).parents('.form-managed-file-wrapper'); + + // Create extra button and add to wrapper. + let buttonText = 'Add file'; + if (this.hasAttribute('multiple')) { + buttonText = 'Add files'; + } + const $button = $( + `` + ); + $button.insertAfter($wrapper.find('.form-managed-file')); + + // Create dropzone HTML. + const $dropzone = $(`