Commit 97ae4974 authored by Noel Gordon's avatar Noel Gordon Committed by Commit Bot

[dragdrop] Add files-ng drag drop chip

The files app drag drop chip is rendered in a #drag-container element,
that must be visible to the browser, but hidden with z-index:-1 behind
the files app #dialog-container (invisible to users).

The files-ng drag drop chip differs from non-files-ng in its CSS style
and also its HTML structure.

Prefix existing #drag-container CSS with body:not(.files-ng). Move the
#drop-label CSS into that group, add the same prefix and TODO comments
about removing the #drop-label element.

Add a CSS body.files-ng #drag-container group, used to style drag drop
container structure for the files-ng case. Add renderThumbnailFilesNg_
to create that structure. Add notes to remove renderThumbnail_ used by
non-files-ng case.

Update to code to render the files-ng or non-files-ng drag thumbnails.
For the files-ng RTL case, move the drag image grab point to the right
by its clientWidth, scaled by the device pixel ratio, to satisfy

  dragStartEvent.dataTransfer.setDragImage(element, x, y)

whose x,y co-ords work in screen units, rather than device independent
co-ords like CSS pixel (px) units. For touch initiated drags, there is
no need to reposition in the y-direction since the files-ng design has
enough room to avoid a finger obscuring the drag drop chip content.

Add code comments about "dropping directories on themselves", and code
a plain for-loop over the selection Array rather than Array.find(). It
is easier to reason about, and it is faster. Saves a function call per
loop iteration and it helps if the code is fast since it's called from
a drag event handler.

Bug: 1002394
Change-Id: I1befcb6f69eca1c7f64591fbf0aca6bb0a69448e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2247987Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Reviewed-by: default avatarAlex Danilo <adanilo@chromium.org>
Reviewed-by: default avatarNoel Gordon <noel@chromium.org>
Commit-Queue: Noel Gordon <noel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#779690}
parent 6b54f2a0
...@@ -2768,14 +2768,14 @@ body[block-hosted-docs] .file.dim-hosted { ...@@ -2768,14 +2768,14 @@ body[block-hosted-docs] .file.dim-hosted {
} }
/* Invisible container for elements representing files while dragging. */ /* Invisible container for elements representing files while dragging. */
#drag-container { body:not(.files-ng) #drag-container {
left: 0; left: 0;
position: fixed; position: fixed;
top: 0; top: 0;
z-index: -1; /* below .dialog-container */ z-index: -1; /* below .dialog-container */
} }
#drag-container .drag-contents { body:not(.files-ng) #drag-container .drag-contents {
background-color: #fafafa; background-color: #fafafa;
border: 1px solid #bbb; border: 1px solid #bbb;
border-radius: 3px; border-radius: 3px;
...@@ -2788,17 +2788,30 @@ body[block-hosted-docs] .file.dim-hosted { ...@@ -2788,17 +2788,30 @@ body[block-hosted-docs] .file.dim-hosted {
transition: opacity 200ms ease-in; transition: opacity 200ms ease-in;
} }
#drag-container .drag-contents.for-image { body:not(.files-ng) #drop-label {
background-color: rgba(0, 0, 0, 80%);
border-radius: 2px;
color: white;
display: none; /* hidden until activated */
font-size: 12px;
height: 24px;
line-height: 24px;
padding: 0 8px;
position: absolute;
z-index: 1000; /* same height as files-tooltip */
}
body:not(.files-ng) #drag-container .drag-contents.for-image {
flex: none; flex: none;
padding: 2px; padding: 2px;
} }
#drag-container .thumbnail-item { body:not(.files-ng) #drag-container .thumbnail-item {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
#drag-container .label { body:not(.files-ng) #drag-container .label {
flex: auto; flex: auto;
font-weight: bold; font-weight: bold;
line-height: 24px; line-height: 24px;
...@@ -2809,6 +2822,80 @@ body[block-hosted-docs] .file.dim-hosted { ...@@ -2809,6 +2822,80 @@ body[block-hosted-docs] .file.dim-hosted {
white-space: nowrap; white-space: nowrap;
} }
body.files-ng #drag-container {
background-color: transparent;
height: calc(12px + 40px + 8px + 8px);
left: 0;
position: fixed;
top: 0;
width: calc(8px + 192px + 8px + 8px);
z-index: -1; /* below .dialog-container */
}
body.files-ng #drag-container .drag-box {
background-color: white;
border-radius: 2px;
box-shadow: 0 1px 2px 0 rgba(var(--google-grey-800-rgb), 30%),
0 2px 6px 2px rgba(var(--google-grey-800-rgb), 15%);
box-sizing: border-box;
height: 40px;
position: absolute;
width: 192px;
}
body.files-ng #drag-container .drag-contents {
align-items: center;
display: flex;
left: 8px;
padding-inline-end: 2px;
padding-inline-start: 8px;
right: 8px;
top: 12px;
}
body.files-ng #drag-container .drag-multiple {
left: calc(8px + 8px);
right: calc(8px + 8px);
top: calc(12px + 8px);
}
body.files-ng #drag-container .detail-icon {
border-radius: 50%;
flex: none;
}
body.files-ng #drag-container .detail-icon[file-type-icon] {
color: var(--google-grey-700);
}
body.files-ng #drag-container .label {
color: var(--google-grey-900);
overflow: hidden;
padding: 0 10px;
text-overflow: ellipsis;
white-space: nowrap;
}
body.files-ng #drag-container .drag-bubble {
background-color: var(--google-blue-600);
border-radius: 12px;
color: white;
font-size: 14px;
font-weight: 700;
height: 24px;
line-height: 24px;
max-width: fit-content;
padding: 0 8px;
position: absolute;
right: calc(8px * 0.5);
white-space: nowrap;
}
html[dir=rtl] body.files-ng #drag-container .drag-bubble {
left: calc(8px * 0.5);
right: unset;
}
/* TODO(fukino): Gather menu-related definitions into one place. */ /* TODO(fukino): Gather menu-related definitions into one place. */
cr-menu#file-context-menu { cr-menu#file-context-menu {
min-width: 208px; min-width: 208px;
...@@ -3544,6 +3631,11 @@ files-toggle-ripple { ...@@ -3544,6 +3631,11 @@ files-toggle-ripple {
z-index: 505; /* Must be above the scroll bar (500). */ z-index: 505; /* Must be above the scroll bar (500). */
} }
files-toast {
/* Must be above the splitter (500) but below menu-like UI (550). */
z-index: 520;
}
/* /*
* Preventing FOUC * Preventing FOUC
*/ */
...@@ -3551,21 +3643,3 @@ cr-input:not(:defined), ...@@ -3551,21 +3643,3 @@ cr-input:not(:defined),
files-tooltip:not(:defined) { files-tooltip:not(:defined) {
display: none; display: none;
} }
#drop-label {
background-color: rgba(0, 0, 0, 80%);
border-radius: 2px;
color: white;
display: none; /* hidden until activated */
font-size: 12px;
height: 24px;
line-height: 24px;
padding: 0 8px;
position: absolute;
z-index: 1000; /* same height as files-tooltip */
}
files-toast {
/* Must be above the splitter (500) but below menu-like UI (550). */
z-index: 520;
}
...@@ -697,6 +697,41 @@ class FileTransferController { ...@@ -697,6 +697,41 @@ class FileTransferController {
return container; return container;
} }
/**
* Renders a drag-and-drop thumbnail. TODO(files-ng): remove renderThumbnail_
* and its strings, preloadedThumbnailImagePromise_, constants, etc.
*
* @return {!HTMLElement} Thumbnail element.
* @private
*/
renderThumbnailFilesNg_() {
const entry = this.selectionHandler_.selection.entries[0];
const index = this.selectionHandler_.selection.indexes[0];
const items = this.selectionHandler_.selection.entries.length;
const container = /** @type {!HTMLElement} */ (
this.document_.body.querySelector('#drag-container'));
const multiple = items > 1 ? 'block' : 'none';
container.innerHTML = `
<div class='drag-box drag-multiple' style='display:${multiple}'></div>
<div class='drag-box drag-contents'>
<div class='detail-icon'></div><div class='label'>${entry.name}</div>
</div>
<div class='drag-bubble' style='display:${multiple}'>${items}</div>
`;
const icon = container.querySelector('.detail-icon');
const thumbnail = this.listContainer_.currentView.getThumbnail(index);
if (thumbnail) {
icon.style.backgroundImage = thumbnail.style.backgroundImage;
icon.style.backgroundSize = 'cover';
} else {
icon.setAttribute('file-type-icon', FileType.getIcon(entry));
}
return container;
}
/** /**
* @param {!cr.ui.List} list Drop target list * @param {!cr.ui.List} list Drop target list
* @param {!Event} event A dragstart event of DOM. * @param {!Event} event A dragstart event of DOM.
...@@ -732,34 +767,43 @@ class FileTransferController { ...@@ -732,34 +767,43 @@ class FileTransferController {
return; return;
} }
const dt = /** @type {DragEvent} */ (event).dataTransfer; const dataTransfer = /** @type {DragEvent} */ (event).dataTransfer;
const canCopy = this.canCopyOrDrag(); const canCopy = this.canCopyOrDrag();
const canCut = this.canCutOrDrag(); const canCut = this.canCutOrDrag();
if (canCopy || canCut) { if (canCopy || canCut) {
if (canCopy && canCut) { if (canCopy && canCut) {
this.cutOrCopy_(dt, 'all'); this.cutOrCopy_(dataTransfer, 'all');
} else if (canCopy) { } else if (canCopy) {
this.cutOrCopy_(dt, 'copyLink'); this.cutOrCopy_(dataTransfer, 'copyLink');
} else { } else {
this.cutOrCopy_(dt, 'move'); this.cutOrCopy_(dataTransfer, 'move');
} }
} else { } else {
event.preventDefault(); event.preventDefault();
return; return;
} }
const dragThumbnail = this.renderThumbnail_(); const thumbnail = {element: null, x: 0, y: 0};
let yOffset = 0;
// Position the drag image above the start point for touch intiated drag. if (util.isFilesNg()) {
if (this.touching_) { thumbnail.element = this.renderThumbnailFilesNg_();
const thumbNailExtent = dragThumbnail.getBoundingClientRect(); if (this.document_.querySelector(':root[dir=rtl]')) {
yOffset = thumbNailExtent.height; thumbnail.x = thumbnail.element.clientWidth * window.devicePixelRatio;
}
} else {
thumbnail.element = this.renderThumbnail_();
// Move drag image above the start point for touch initiated drags.
if (this.touching_) {
thumbnail.y = thumbnail.element.getBoundingClientRect().height;
}
} }
dt.setDragImage(dragThumbnail, 0, yOffset);
dataTransfer.setDragImage(thumbnail.element, thumbnail.x, thumbnail.y);
window[DRAG_AND_DROP_GLOBAL_DATA] = { window[DRAG_AND_DROP_GLOBAL_DATA] = {
sourceRootURL: dt.getData('fs/sourceRootURL'), sourceRootURL: dataTransfer.getData('fs/sourceRootURL'),
missingFileContents: dt.getData('fs/missingFileContents') missingFileContents: dataTransfer.getData('fs/missingFileContents'),
}; };
} }
...@@ -792,14 +836,22 @@ class FileTransferController { ...@@ -792,14 +836,22 @@ class FileTransferController {
if (!entry && !onlyIntoDirectories) { if (!entry && !onlyIntoDirectories) {
entry = this.directoryModel_.getCurrentDirEntry(); entry = this.directoryModel_.getCurrentDirEntry();
} }
const effectAndLabel = const effectAndLabel =
this.selectDropEffect_(event, this.getDragAndDropGlobalData_(), entry); this.selectDropEffect_(event, this.getDragAndDropGlobalData_(), entry);
event.dataTransfer.dropEffect = effectAndLabel.getDropEffect(); event.dataTransfer.dropEffect = effectAndLabel.getDropEffect();
event.preventDefault(); event.preventDefault();
const label = effectAndLabel.getLabel();
if (util.isFilesNg()) {
return;
}
// TODO(files-ng): the #drop-label is not used in files-ng, remove this
// code and update the effectAndLabel class to remove its label code.
if (!this.dropLabel_) { if (!this.dropLabel_) {
this.dropLabel_ = document.querySelector('div#drop-label'); this.dropLabel_ = document.querySelector('div#drop-label');
} }
const label = effectAndLabel.getLabel();
if (label) { if (label) {
this.dropLabel_.innerText = label; this.dropLabel_.innerText = label;
this.dropLabel_.style.left = event.pageX + 'px'; this.dropLabel_.style.left = event.pageX + 'px';
...@@ -875,6 +927,8 @@ class FileTransferController { ...@@ -875,6 +927,8 @@ class FileTransferController {
this.clearDropTarget_(); this.clearDropTarget_();
this.lastEnteredTarget_ = null; this.lastEnteredTarget_ = null;
} }
// TODO(files-ng): dropLabel_ is not used in files-ng, remove it.
if (this.dropLabel_) { if (this.dropLabel_) {
this.dropLabel_.style.display = 'none'; this.dropLabel_.style.display = 'none';
} }
...@@ -935,15 +989,17 @@ class FileTransferController { ...@@ -935,15 +989,17 @@ class FileTransferController {
// Set the new drop target. // Set the new drop target.
this.dropTarget_ = domElement; this.dropTarget_ = domElement;
if (!domElement || !destinationEntry.isDirectory) { if (!domElement || !destinationEntry.isDirectory) {
return; return;
} }
if (this.selectionHandler_.selection.entries.find(element => { // Disallow dropping a folder on itself.
return util.isSameEntry(element, destinationEntry); assert(destinationEntry.isDirectory);
})) { const entries = this.selectionHandler_.selection.entries;
return; for (let i = 0; i < entries.length; i++) {
if (util.isSameEntry(entries[i], destinationEntry)) {
return;
}
} }
// Add accept class if the domElement can accept the drag. // Add accept class if the domElement can accept the drag.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment