Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
815 changes: 609 additions & 206 deletions src/lib/openFolder.js

Large diffs are not rendered by default.

614 changes: 614 additions & 0 deletions src/sidebarApps/files/dragMove.js

Large diffs are not rendered by default.

179 changes: 179 additions & 0 deletions src/sidebarApps/files/dragMove.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// Written without inset, flex gap or unguarded color-mix() so it still works
// on old Android WebViews.
$ease: cubic-bezier(0.2, 0, 0, 1);
$spring: cubic-bezier(0.34, 1.56, 0.64, 1);

body.file-dragging {
cursor: grabbing;
user-select: none;
-webkit-user-select: none;
}

#sidebar .container.files {
.tile[data-type="file"],
.tile[data-type="dir"] {
transition:
opacity 160ms ease,
transform 160ms $ease,
background-color 180ms ease;

// Long press recognised: the row lifts slightly, ready to be dragged.
&.drag-armed {
transform: scale(0.97);
background-color: rgba(127, 127, 127, 0.14);
}

&.drag-source {
opacity: 0.35;
}
}

// The whole folder the entries would land in lights up.
.list.collapsible.drop-target {
border-radius: 8px;
background-color: rgba(127, 127, 127, 0.1);
background-color: color-mix(in srgb, var(--active-color) 10%, transparent);
box-shadow: inset 0 0 0 1px rgba(127, 127, 127, 0.35);
box-shadow: inset 0 0 0 1px
color-mix(in srgb, var(--active-color) 45%, transparent);

> .tile:first-child {
border-radius: 8px;
}
}

// Closed folder about to spring open.
.list.collapsible.drop-pending > .tile:first-child {
animation: file-drop-pending 650ms ease-in-out;
}

.tile.drop-flash {
animation: file-drop-flash 600ms ease-out;
}
}

.file-drag-ghost {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
pointer-events: none;
will-change: transform;

&.settling {
opacity: 0;
transition:
transform 240ms $ease,
opacity 240ms ease;
}

.chip {
position: relative;
display: flex;
align-items: center;
box-sizing: border-box;
height: 34px;
max-width: 220px;
padding: 0 10px 0 6px;
border-radius: 10px;
border: 1px solid var(--popup-border-color, var(--border-color));
background-color: var(--popup-background-color);
color: var(--popup-text-color);
font-size: 13px;
box-shadow:
0 14px 30px -8px rgba(0, 0, 0, 0.45),
0 2px 6px rgba(0, 0, 0, 0.2);
animation: file-drag-pick 260ms $spring;

> :first-child {
flex: none;
width: 22px;
height: 22px;
min-width: 0;
margin-right: 6px;
font-size: 15px;
line-height: 22px;
text-align: center;
background-size: 16px;
background-position: center;
background-repeat: no-repeat;
}
}

.name {
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

.badge {
flex: none;
min-width: 18px;
height: 18px;
box-sizing: border-box;
margin-left: 8px;
padding: 0 5px;
border-radius: 9px;
background-color: var(--active-color);
color: var(--active-text-color);
font-size: 11px;
font-weight: 600;
line-height: 18px;
text-align: center;
}

// Several entries: cards stacked behind the chip.
&.multi .chip::before,
&.multi .chip::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
box-sizing: border-box;
border-radius: 10px;
border: 1px solid var(--popup-border-color, var(--border-color));
background-color: var(--popup-background-color);
}

&.multi .chip::before {
transform: translate(4px, 4px) rotate(2deg);
opacity: 0.7;
}

&.multi .chip::after {
transform: translate(8px, 8px) rotate(4deg);
opacity: 0.4;
}
}

@keyframes file-drag-pick {
from {
transform: scale(0.85);
opacity: 0;
}
}

@keyframes file-drop-pending {
50% {
background-color: rgba(127, 127, 127, 0.22);
}
}

@keyframes file-drop-flash {
from {
background-color: rgba(127, 127, 127, 0.3);
background-color: color-mix(in srgb, var(--active-color) 35%, transparent);
}
}

body.no-animation {
.file-drag-ghost,
.file-drag-ghost * {
animation: none !important;
transition: none !important;
}
}
25 changes: 21 additions & 4 deletions src/sidebarApps/files/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import "./style.scss";
import Sidebar from "components/sidebar";
import fileIcons from "lib/fileIcons";
import settings from "lib/settings";
import dragMove from "./dragMove";
import selection from "./selection";

/**@type {HTMLElement} */
let container;
Expand All @@ -25,6 +27,10 @@ function initApp(el) {
container.setAttribute("data-msg", strings["open folder"]);
container.style.overflowX = "auto";
container.addEventListener("click", clickHandler);
// Drag listens first so a long press can become a drag before selection
// or the context menu sees it.
dragMove.init(container);
selection.init(container);
editorManager.on(
["new-file", "int-open-file-list", "remove-file"],
(position) => {
Expand All @@ -49,6 +55,12 @@ function onSelected(el) {
// Phone sidebars and inactive sidebar apps are detached, so document-wide
// icon updates cannot reach them until this view is shown again.
fileIcons.refreshRenderedIcons(container);
selection.syncBar();
// Heights measured while the sidebar was detached come out wrong.
const $openFolder = container.querySelector(
":scope > .list:not(.hidden):not(.file-list)",
);
if ($openFolder) fixHeight($openFolder);
const $scrollableLists = container.getAll(":scope .scroll[data-scroll-top]");
$scrollableLists.forEach(($el) => {
$el.scrollTop = $el.dataset.scrollTop;
Expand Down Expand Up @@ -79,18 +91,23 @@ function clickHandler(e) {
export function fixHeight(target) {
const lists = Array.from(container.getAll(":scope > div"));
const ITEM_HEIGHT = 30;
// Headers are taller than rows (36px, 40px on wide screens); measure them so
// the expanded list doesn't overflow below the sidebar.
const headerHeight =
container.querySelector(":scope > .list > .tile")?.offsetHeight ||
ITEM_HEIGHT;

let height = (lists.length - 1) * ITEM_HEIGHT;
let height = (lists.length - 1) * headerHeight;
let activeFileList;

if (settings.value.openFileListPos === settings.OPEN_FILE_LIST_POS_SIDEBAR) {
const [firstList] = lists;
if (firstList.classList.contains("file-list")) {
activeFileList = firstList;
if (firstList.unclasped) {
const heightOffset = height - ITEM_HEIGHT;
const heightOffset = height - headerHeight;
const totalHeight =
ITEM_HEIGHT * activeFileList.$ul.children.length + ITEM_HEIGHT;
ITEM_HEIGHT * activeFileList.$ul.children.length + headerHeight;
const maxHeight =
lists.length === 1 || !lists.slice(1).find((list) => list.unclasped)
? window.innerHeight
Expand All @@ -99,7 +116,7 @@ export function fixHeight(target) {

activeFileList.style.maxHeight = `${minHeight}px`;
activeFileList.style.height = `${minHeight}px`;
height += minHeight - ITEM_HEIGHT;
height += minHeight - headerHeight;
}
}
}
Expand Down
Loading
Loading