Commit 012172e5 authored by Gábor Hojtsy's avatar Gábor Hojtsy
Browse files

Issue #3020716 by seanB, lauriii, phenaproxima, Gábor Hojtsy, dww,...

Issue #3020716 by seanB, lauriii, phenaproxima, Gábor Hojtsy, dww, andrewmacpherson, alexpott, larowlan, xjm, benjifisher: Add vertical tabs style menu to media library
parent e80f9052
......@@ -529,11 +529,139 @@ display:
defaults:
fields: false
access: false
filters: false
filter_groups: false
arguments: false
display_description: ''
access:
type: perm
options:
perm: 'view media'
filters:
status:
id: status
table: media_field_data
field: status
relationship: none
group_type: group
admin_label: ''
operator: '='
value: '1'
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: media
entity_field: status
plugin_id: boolean
name:
id: name
table: media_field_data
field: name
relationship: none
group_type: group
admin_label: ''
operator: contains
value: ''
group: 1
exposed: true
expose:
operator_id: name_op
label: Name
description: ''
use_operator: false
operator: name_op
identifier: name
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
anonymous: '0'
administrator: '0'
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: media
entity_field: name
plugin_id: string
filter_groups:
operator: AND
groups:
1: AND
arguments:
bundle:
id: bundle
table: media_field_data
field: bundle
relationship: none
group_type: group
admin_label: ''
default_action: ignore
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
glossary: false
limit: 0
case: none
path_case: none
transform_dash: false
break_phrase: false
entity_type: media
entity_field: bundle
plugin_id: string
cache_metadata:
max-age: -1
contexts:
......
field.widget.settings.media_library_widget:
type: mapping
label: 'Media library widget settings'
mapping:
media_types:
type: sequence
label: 'Allowed media types, in display order'
sequence:
type: string
label: 'Media type ID'
......@@ -2,6 +2,22 @@
* @file media_library.module.css
*/
.media-library-wrapper {
display: flex;
}
.media-library-menu {
margin: 0;
padding: 0;
}
/* @todo Use a class instead of the li element.
https://www.drupal.org/project/drupal/issues/3029227 */
.media-library-menu li {
list-style: none;
padding: 0;
}
.media-library-views-form > .form-actions {
flex-basis: 100%;
}
......@@ -33,10 +49,10 @@
.media-library-item .js-click-to-select-checkbox {
position: absolute;
display: block;
z-index: 1;
top: 5px;
right: 0;
display: block;
}
.media-library-item__status {
......@@ -69,6 +85,15 @@
pointer-events: none;
}
.media-library-widget-modal .ui-dialog-buttonpane {
display: flex;
align-items: center;
}
.media-library-widget-modal .ui-dialog-buttonpane .form-actions {
flex: 1;
}
@media screen and (max-width: 600px) {
.media-library-view .form-actions {
flex-basis: 100%;
......
......@@ -5,6 +5,84 @@
* @see https://www.drupal.org/project/drupal/issues/2980769
*/
.media-library-wrapper {
margin: -1em;
}
/**
* @todo Reuse or build on vertical tabs styling for the media library menu.
* https://www.drupal.org/project/drupal/issues/3023767
*/
.media-library-menu {
display: block;
width: 600px;
max-width: 20%;
margin: 0;
padding: 0;
border-bottom: 1px solid #ccc;
background-color: #e6e5e1;
line-height: 1;
}
[dir="rtl"] .media-library-menu {
margin: 0;
}
/* @todo Use a class instead of the li element.
https://www.drupal.org/project/drupal/issues/3029227 */
.media-library-menu li {
display: block;
}
.media-library-menu__link {
position: relative;
display: block;
box-sizing: border-box;
padding: 10px 15px 15px;
text-decoration: none;
border-bottom: 1px solid #b3b2ad;
background-color: #f2f2f0;
text-shadow: 0 1px hsla(0, 0%, 100%, 0.6);
}
.media-library-menu__link:active,
.media-library-menu__link:hover,
.media-library-menu__link:focus {
background: #fcfcfa;
text-shadow: none;
}
.media-library-menu__link:focus,
.media-library-menu__link:active {
outline: none;
}
.media-library-menu__link.active {
z-index: 1;
margin-right: -1px;
color: #000;
border-right: 1px solid #fcfcfa;
border-bottom: 1px solid #b3b2ad;
background-color: #fff;
box-shadow: 0 5px 5px -5px hsla(0, 0%, 0%, 0.3);
}
[dir="rtl"] .media-library-menu__link.active {
margin-right: 0;
margin-left: -1px;
border-right: 0;
border-left: 1px solid #fcfcfa;
}
.media-library-content {
width: 100%;
padding: 1em;
border-left: 1px solid #b3b2ad;
outline: none;
}
[dir="rtl"] .media-library-content {
border-right: 1px solid #b3b2ad;
border-left: 0;
}
.media-library-views-form__header .form-item {
margin-right: 8px;
}
......@@ -15,12 +93,12 @@
.media-library-item {
justify-content: center;
width: 180px;
margin: 16px 16px 2px 2px;
transition: border-color 0.2s, color 0.2s, background 0.2s;
vertical-align: top;
border: 1px solid #dbdbdb;
margin: 16px 16px 2px 2px;
width: 180px;
background: #fff;
transition: border-color 0.2s, color 0.2s, background 0.2s;
}
.media-library-view {
......@@ -33,14 +111,14 @@
.media-library-view .media-library-view--form-actions {
clear: left;
margin: 0.75em 0;
align-self: flex-end;
margin: 0.75em 0;
}
.media-library-item .field--name-thumbnail {
background-color: #ebebeb;
overflow: hidden;
text-align: center;
background-color: #ebebeb;
}
.media-library-item .field--name-thumbnail img {
......@@ -52,10 +130,10 @@
.media-library-item.is-hover,
.media-library-item.checked,
.media-library-item.is-focus {
border-color: #40b6ff;
margin: 14px 14px 0 0;
border-width: 3px;
border-color: #40b6ff;
border-radius: 3px;
margin: 14px 14px 0 0;
}
.media-library-item.checked {
......@@ -76,11 +154,11 @@
}
.media-library-item__status {
padding: 5px 10px;
color: #e4e4e4;
font-style: italic;
background: #666;
padding: 5px 10px;
font-size: 12px;
font-style: italic;
}
.media-library-item .views-field-operations {
......@@ -88,20 +166,20 @@
}
.media-library-item .views-field-operations .dropbutton-wrapper {
display: inline-block;
position: absolute;
right: 5px;
bottom: 5px;
display: inline-block;
}
.media-library-item__attributes {
position: absolute;
bottom: 0;
display: block;
padding: 5px;
overflow: hidden;
max-width: calc(100% - 10px);
max-height: calc(100% - 50px);
overflow: hidden;
padding: 5px;
background: white;
}
......@@ -111,10 +189,10 @@
.media-library-item__name a {
display: block;
text-decoration: underline;
overflow: hidden;
margin: 2px;
white-space: nowrap;
overflow: hidden;
text-decoration: underline;
text-overflow: ellipsis;
}
......@@ -126,13 +204,13 @@
}
.media-library-item__name a:focus {
border: 2px solid;
margin: 0;
border: 2px solid;
}
.media-library-item__type {
font-size: 12px;
color: #696969;
font-size: 12px;
}
.media-library-select-all {
......@@ -157,8 +235,8 @@
.media-library-widget__toggle-weight {
position: absolute;
right: 5px;
top: 5px;
right: 5px;
}
.media-library-item .form-item {
......@@ -181,13 +259,13 @@
height: 24px;
margin: 5px;
padding: 0;
background: url("../../../misc/icons/787878/ex.svg") #fff center no-repeat;
background-size: 16px 16px;
transition: 0.2s border-color;
color: transparent;
border: 2px solid #ccc;
border-radius: 20px;
color: transparent;
background: url("../../../misc/icons/787878/ex.svg") #fff center no-repeat;
background-size: 16px 16px;
text-shadow: none;
transition: 0.2s border-color;
}
.media-library-item .media-library-item__remove:hover,
......@@ -202,7 +280,6 @@
.media-library-upload__media,
.media-library-upload__file {
display: flex;
flex-wrap: wrap;
padding: 20px 0 20px 0;
}
......@@ -224,12 +301,16 @@
}
.media-library-upload__media-preview {
margin-right: 20px;
width: 220px;
background: #ebebeb;
display: flex;
align-items: center;
justify-content: center;
align-items: center;
width: 220px;
margin-right: 20px;
background: #ebebeb;
}
[dir="rtl"] .media-library-upload__media-preview {
margin-right: 0;
margin-left: 20px;
}
.media-library-upload__media-preview img {
......@@ -239,8 +320,8 @@
/* @todo Remove or re-work in https://www.drupal.org/node/2985168 */
.media-library-widget .media-library-item__name a,
.media-library-view.view-display-id-widget .media-library-item__name a {
color: black;
text-decoration: none;
color: black;
}
@media screen and (max-width: 600px) {
......@@ -248,8 +329,8 @@
width: 150px;
}
.media-library-item .field--name-thumbnail img {
height: 150px;
width: 150px;
height: 150px;
}
.media-library-item .views-field-operations .dropbutton-wrapper {
position: relative;
......
......@@ -5,6 +5,11 @@
(($, Drupal) => {
/**
* Allows users to select an element which checks a hidden checkbox.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior for selecting media library item.
*/
Drupal.behaviors.ClickToSelect = {
attach(context) {
......
/**
* @file media_library.widget.js
*/
(($, Drupal, window) => {
/**
* Wrapper object for the current state of the media library.
*/
Drupal.MediaLibrary = {
/**
* When a user interacts with the media library we want the selection to
* persist as long as the media library modal is opened. We temporarily
* store the selected items while the user filters the media library view or
* navigates to different tabs.
*/
currentSelection: [],
};
/**
* Warn users when clicking outgoing links from the library or widget.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior to links in the media library.
*/
Drupal.behaviors.MediaLibraryWidgetWarn = {
attach(context) {
$('.js-media-library-item a[href]', context)
.once('media-library-warn-link')
.on('click', e => {
const message = Drupal.t(
'Unsaved changes to the form will be lost. Are you sure you want to leave?',
);
const confirmation = window.confirm(message);
if (!confirmation) {
e.preventDefault();
}
});
},
};
/**
* Load media library content through AJAX.
*
* Standard AJAX links (using the 'use-ajax' class) replace the entire library
* dialog. When navigating to a media type through the vertical tabs, we only
* want to load the changed library content. This is not only more efficient,
* but also provides a more accessible user experience for screen readers.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches behavior to vertical tabs in the media library.
*
* @todo Remove when the AJAX system adds support for replacing a specific
* selector via a link.
* https://www.drupal.org/project/drupal/issues/3026636
*/
Drupal.behaviors.MediaLibraryTabs = {
attach(context) {
const $menu = $('.js-media-library-menu');
$menu
.find('a', context)
.once('media-library-menu-item')
.on('click', e => {
e.preventDefault();
e.stopPropagation();
// Replace the library content.
const ajaxObject = Drupal.ajax({
wrapper: 'media-library-content',
url: e.currentTarget.href,
dialogType: 'ajax',
progress: {
type: 'fullscreen',
message: Drupal.t('Please wait...'),
},
});
// Override the AJAX success callback to shift focus to the media
// library content.
ajaxObject.success = function(response, status) {
// Remove the progress element.
if (this.progress.element) {
$(this.progress.element).remove();
}
if (this.progress.object) {
this.progress.object.stopMonitoring();
}
$(this.element).prop('disabled', false);
// Execute the AJAX commands.
Object.keys(response || {}).forEach(i => {
if (response[i].command && this.commands[response[i].command]) {
this.commands[response[i].command](this, response[i], status);
}
});
// Set focus to the media library content.
document.getElementById('media-library-content').focus();
// Remove any response-specific settings so they don't get used on
// the next call by mistake.
this.settings = null;
};
ajaxObject.execute();
// Set the active tab.
$menu.find('.active-tab').remove();
$menu.find('a').removeClass('active');
$(e.currentTarget)
.addClass('active')
.html(
Drupal.t(
'@title<span class="active-tab visually-hidden"> (active tab)</span>',
{ '@title': $(e.currentTarget).html() },
),
);
});
},
};