File Property reorder autoscroll.
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
|
window.addEventListener .... done wrong everywhere!
|
||||||
|
|
||||||
|
touch scroll in file propertty dialog.
|
||||||
|
|
||||||
channel bindings help dialog. <ChannelBindingHelpDialog
|
channel bindings help dialog. <ChannelBindingHelpDialog
|
||||||
|
|
||||||
Tooltips on touch ui?
|
|
||||||
|
|
||||||
draggable UI: scrolloffset!?
|
draggable UI: scrolloffset!?
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ div {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.draggable-button-base {
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media not all /* seems to be ok in current chrome (prefers-color-scheme: light) */{
|
@media not all /* seems to be ok in current chrome (prefers-color-scheme: light) */{
|
||||||
input[type="number"].scrollMod::-webkit-outer-spin-button,
|
input[type="number"].scrollMod::-webkit-outer-spin-button,
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ import ButtonBase, { ButtonBaseProps } from "@mui/material/ButtonBase";
|
|||||||
|
|
||||||
export interface DraggableButtonBaseProps extends ButtonBaseProps {
|
export interface DraggableButtonBaseProps extends ButtonBaseProps {
|
||||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
onLongPressStart?: (currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement>) => boolean;
|
onLongPressStart?: (currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>) => boolean;
|
||||||
onLongPressMove?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
onLongPressMove?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
||||||
onLongPressEnd?: (e: React.PointerEvent<HTMLButtonElement>) => void;
|
onLongPressEnd?: (e: React.PointerEvent<HTMLButtonElement>| React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
longPressDelay? : number;
|
instantMouseLongPress?: boolean;
|
||||||
|
longPressDelay?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Point {
|
interface Point {
|
||||||
@@ -58,37 +59,43 @@ function screenToClient(e: React.PointerEvent): Point {
|
|||||||
|
|
||||||
export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
||||||
// Ensure that the props are spread correctly
|
// Ensure that the props are spread correctly
|
||||||
const { onClick, onLongPressStart, onLongPressEnd,longPressDelay, ...rest } = props;
|
const { onClick, onLongPressStart, onLongPressMove:
|
||||||
|
doLongPressMove,onLongPressEnd, longPressDelay,instantMouseLongPress, ...rest } = props;
|
||||||
|
|
||||||
let [hTimeout, setHTimeout] = React.useState<number | null>(null);
|
let [hTimeout, setHTimeout] = React.useState<number | null>(null);
|
||||||
let [pointerId, setPointerId] = React.useState<number | null>(null);
|
let [pointerId, setPointerId] = React.useState<number | null>(null);
|
||||||
let [longPressed, setLongPressed] = React.useState<boolean>(false);
|
let [longPressed, setLongPressed] = React.useState<boolean>(false);
|
||||||
let [clickSuppressed, setClickSuppressed] = React.useState<boolean>(false);
|
let [clickSuppressed, setClickSuppressed] = React.useState<boolean>(false);
|
||||||
let [pointerDownPoint, setPointerDownPoint] = React.useState<Point >({x: 0, y: 0});
|
let [pointerDownPoint, setPointerDownPoint] = React.useState<Point>({ x: 0, y: 0 });
|
||||||
|
let [ longPressedElement, setLongPressedElement ] = React.useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
|
|
||||||
function handleSuppressClick(e: MouseEvent) {
|
function handleSuppressClick(e: MouseEvent) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSuppressClick(false);
|
setSuppressClick(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSuppressClick(value: boolean) {
|
function setSuppressClick(value: boolean) {
|
||||||
|
setClickSuppressed(value);
|
||||||
if (value !== clickSuppressed) {
|
}
|
||||||
setClickSuppressed(value);
|
function startLongPress(currentTarget: HTMLButtonElement, e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>)
|
||||||
if (value) {
|
{
|
||||||
window.addEventListener(
|
if (hTimeout !== null) {
|
||||||
"click",
|
window.clearTimeout(hTimeout);
|
||||||
handleSuppressClick,
|
setHTimeout(null);
|
||||||
{
|
}
|
||||||
once: true,
|
if (props.onLongPressStart) {
|
||||||
capture: true
|
if (!props.onLongPressStart(currentTarget, e)) {
|
||||||
});
|
return;
|
||||||
} else {
|
|
||||||
window.removeEventListener("click", handleSuppressClick);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setLongPressedElement(currentTarget);
|
||||||
|
setLongPressed(true);
|
||||||
|
setSuppressClick(true);
|
||||||
|
currentTarget.style.touchAction = "none"; // prevent scrolling.
|
||||||
|
// console.log("DraggableButtonBase: long press started");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelLongPress() {
|
function cancelLongPress() {
|
||||||
@@ -99,23 +106,106 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
setPointerId(null);
|
setPointerId(null);
|
||||||
setLongPressed(false);
|
setLongPressed(false);
|
||||||
setSuppressClick(false);
|
setSuppressClick(false);
|
||||||
|
if (longPressedElement) {
|
||||||
|
longPressedElement.style.touchAction = "";
|
||||||
|
setLongPressedElement(null);
|
||||||
|
}
|
||||||
|
if (longPressed) {
|
||||||
|
// console.log("DraggableButtonBase: long press canceled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stopLongPress(e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>) {
|
||||||
|
if (longPressed) {
|
||||||
|
if (props.onLongPressEnd) {
|
||||||
|
props.onLongPressEnd(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancelLongPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchMove = (e: TouchEvent) => {
|
||||||
|
if (longPressed) {
|
||||||
|
e.preventDefault(); // Prevent scrolling during long press
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleTouchstart = (e: TouchEvent) => {
|
||||||
|
if (longPressed) {
|
||||||
|
e.preventDefault(); // Prevent default touch behavior during long press
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
let hTouchMove = (e: TouchEvent) => {
|
||||||
|
handleTouchMove(e);
|
||||||
|
}
|
||||||
|
addEventListener("touchmove", hTouchMove, { passive: false, capture: true });
|
||||||
|
|
||||||
|
let hTouchStart = (e: TouchEvent) => {
|
||||||
|
handleTouchstart(e);
|
||||||
|
}
|
||||||
|
addEventListener("touchstart", hTouchStart, { passive: false, capture: true });
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
removeEventListener("touchmove", hTouchMove, { capture: true });
|
||||||
|
removeEventListener("touchstart", hTouchStart, { capture: true });
|
||||||
|
|
||||||
setSuppressClick(false);
|
setSuppressClick(false);
|
||||||
cancelLongPress();
|
cancelLongPress();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (clickSuppressed) {
|
||||||
|
let hclick = (e: MouseEvent) => {
|
||||||
|
handleSuppressClick(e);
|
||||||
|
};
|
||||||
|
window.addEventListener("click",hclick, { capture: true });
|
||||||
|
let hTimeout: number | null = null;
|
||||||
|
hTimeout = window.setTimeout(() => {
|
||||||
|
setSuppressClick(false);
|
||||||
|
hTimeout = null;
|
||||||
|
}, 100);
|
||||||
|
return () => {
|
||||||
|
if (hTimeout !== null) {
|
||||||
|
window.clearTimeout(hTimeout);
|
||||||
|
}
|
||||||
|
window.removeEventListener("click", hclick, { capture: true });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ()=>{};
|
||||||
|
}
|
||||||
|
}, [clickSuppressed]);
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (longPressed) {
|
||||||
|
const suppressTouchMove = (e: TouchEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
window.addEventListener("touchmove", suppressTouchMove, { capture: true, passive: false } );
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("touchmove", suppressTouchMove, {capture: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return () => {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [longPressed]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
{...rest}
|
{...rest}
|
||||||
className="draggable-button-base"
|
style={{ touchAction: longPressed ? "none" : undefined, ...props.style }}
|
||||||
onContextMenu={(e) => {
|
onContextMenu={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
if (e.button === -1) { // touch long press
|
||||||
|
startLongPress(e.currentTarget as HTMLButtonElement,e);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}}
|
}}
|
||||||
onPointerDown={(e) => {
|
onPointerDown={(e) => {
|
||||||
@@ -130,19 +220,14 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
setPointerDownPoint(screenToClient(e));
|
setPointerDownPoint(screenToClient(e));
|
||||||
|
|
||||||
let currentTarget = e.currentTarget as HTMLButtonElement;
|
let currentTarget = e.currentTarget as HTMLButtonElement;
|
||||||
if (longPressDelay === undefined || longPressDelay >= 0)
|
if (longPressDelay === undefined || longPressDelay >= 0) {
|
||||||
{
|
let delay = longPressDelay ?? 1250;
|
||||||
|
if (instantMouseLongPress === true && e.pointerType === "mouse") {
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
setHTimeout(window.setTimeout(() => {
|
setHTimeout(window.setTimeout(() => {
|
||||||
setHTimeout(null);
|
startLongPress(currentTarget, e);
|
||||||
if (props.onLongPressStart) {
|
}, delay));
|
||||||
if (!props.onLongPressStart(currentTarget,e))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setLongPressed(true);
|
|
||||||
setSuppressClick(true);
|
|
||||||
}, longPressDelay??1250));
|
|
||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -150,17 +235,17 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
}}
|
}}
|
||||||
onPointerMove={(e) => {
|
onPointerMove={(e) => {
|
||||||
if (e.pointerId === pointerId) {
|
if (e.pointerId === pointerId) {
|
||||||
if(longPressed) {
|
if (longPressed) {
|
||||||
if (props.onLongPressMove) {
|
if (doLongPressMove) {
|
||||||
props.onLongPressMove(e);
|
doLongPressMove(e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let clientPoint = screenToClient(e);
|
let clientPoint = screenToClient(e);
|
||||||
|
|
||||||
let dx = pointerDownPoint.x- clientPoint.x;
|
let dx = pointerDownPoint.x - clientPoint.x;
|
||||||
let dy = pointerDownPoint.y - clientPoint.y;
|
let dy = pointerDownPoint.y - clientPoint.y;
|
||||||
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
|
if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
|
||||||
cancelLongPress();
|
stopLongPress(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,14 +256,8 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
onPointerUp={(e) => {
|
onPointerUp={(e) => {
|
||||||
if (e.pointerId === pointerId) {
|
if (e.pointerId === pointerId) {
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId);
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
||||||
setPointerId(null);
|
stopLongPress(e);
|
||||||
if (longPressed) {
|
if (e.isPropagationStopped() || e.isDefaultPrevented()) {
|
||||||
if (props.onLongPressEnd) {
|
|
||||||
props.onLongPressEnd(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cancelLongPress();
|
|
||||||
if (e.isPropagationStopped()) {
|
|
||||||
setSuppressClick(true); // only way to cancel the click
|
setSuppressClick(true); // only way to cancel the click
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -187,30 +266,24 @@ export default function DraggableButtonBase(props: DraggableButtonBaseProps) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
// onTouchStart={(e)=> {
|
onTouchStart={(e)=> {
|
||||||
// //e.preventDefault();
|
//e.preventDefault();
|
||||||
// }}
|
}}
|
||||||
// onTouchMove={(e)=> {
|
|
||||||
// //e.preventDefault();
|
|
||||||
// }}
|
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (clickSuppressed) {
|
if (clickSuppressed) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSuppressClick(false);
|
setSuppressClick(false);
|
||||||
} else {
|
} else {
|
||||||
if (props.onClick) {
|
if (onClick) {
|
||||||
props.onClick(e);
|
onClick(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPointerCancelCapture={(e) => {
|
onPointerCancelCapture={(e) => {
|
||||||
if (pointerId !== null) {
|
if (pointerId !== null) {
|
||||||
e.currentTarget.releasePointerCapture(e.pointerId);
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
||||||
if (longPressed && props.onLongPressEnd) {
|
stopLongPress(e);
|
||||||
props.onLongPressEnd(e)
|
|
||||||
}
|
|
||||||
cancelLongPress();
|
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{props.children}
|
{props.children}
|
||||||
|
|||||||
@@ -63,6 +63,13 @@ import HomeIcon from '@mui/icons-material/Home';
|
|||||||
import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDialog';
|
import FilePropertyDirectorySelectDialog from './FilePropertyDirectorySelectDialog';
|
||||||
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
import { getAlbumArtUri, getTrackTitle } from './AudioFileMetadata';
|
||||||
|
|
||||||
|
|
||||||
|
const AUTOSCROLL_TICK_DELAY = 30;
|
||||||
|
const AUTOSCROLL_THRESHOLD = 48;
|
||||||
|
const AUTOSCROLL_SCROLL_PER_SECOND = 500;
|
||||||
|
const AUTOSCROLL_SCROLL_RATE = AUTOSCROLL_SCROLL_PER_SECOND * AUTOSCROLL_TICK_DELAY / 1000;
|
||||||
|
|
||||||
|
|
||||||
interface Point {
|
interface Point {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@@ -93,7 +100,7 @@ const audioFileExtensions: { [name: string]: boolean } = {
|
|||||||
".ra": true
|
".ra": true
|
||||||
};
|
};
|
||||||
|
|
||||||
function screenToClient(currentTarget: HTMLElement, e: React.PointerEvent): Point {
|
function screenToClient(currentTarget: HTMLElement, e: React.PointerEvent | React.MouseEvent): Point {
|
||||||
let rect = currentTarget.getBoundingClientRect();
|
let rect = currentTarget.getBoundingClientRect();
|
||||||
const x = e.clientX + rect.left;
|
const x = e.clientX + rect.left;
|
||||||
const y = e.clientY + rect.right;
|
const y = e.clientY + rect.right;
|
||||||
@@ -157,11 +164,14 @@ export interface FilePropertyDialogState {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DragState {
|
interface DragState {
|
||||||
activeItem: string = "";
|
activeItem: string;
|
||||||
height: number = 0;
|
height: number;
|
||||||
from: number = 0;
|
dragElement: HTMLElement;
|
||||||
to: number = 0;
|
dragElementDy: number;
|
||||||
|
from: number;
|
||||||
|
to: number;
|
||||||
|
lastMousePoint: Point;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default withStyles(
|
export default withStyles(
|
||||||
@@ -323,8 +333,6 @@ export default withStyles(
|
|||||||
|
|
||||||
longPressStartPoint: Point | null = null;
|
longPressStartPoint: Point | null = null;
|
||||||
|
|
||||||
dragFromPosition: number = -1;
|
|
||||||
dragToPosition: number = -1;
|
|
||||||
maxPosition: number = 0;
|
maxPosition: number = 0;
|
||||||
|
|
||||||
handleMultisSelectClose() {
|
handleMultisSelectClose() {
|
||||||
@@ -336,24 +344,29 @@ export default withStyles(
|
|||||||
|
|
||||||
handleLongPressStart(
|
handleLongPressStart(
|
||||||
currentTarget: HTMLButtonElement,
|
currentTarget: HTMLButtonElement,
|
||||||
e: React.PointerEvent<HTMLButtonElement>,
|
e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>,
|
||||||
fileEntry: FileEntry): boolean {
|
fileEntry: FileEntry): boolean {
|
||||||
if (this.state.reordering) {
|
if (this.state.reordering) {
|
||||||
|
|
||||||
if (!this.isTracksDirectory()) {
|
if (!this.isTracksDirectory()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.dragFromPosition = parseInt(currentTarget.getAttribute("data-position") as string, 10);
|
|
||||||
this.dragToPosition = this.dragToPosition;
|
|
||||||
let element = currentTarget;
|
let element = currentTarget;
|
||||||
element.style.position = "relative";
|
let index = parseInt(element.getAttribute("data-position") as string, 10);
|
||||||
element.style.zIndex = "1000";
|
|
||||||
element.style.top = "5px";
|
|
||||||
element.style.left = "5px";
|
|
||||||
element.style.background = isDarkMode() ? "#555" : "#EEF" // xxx: dark mode.
|
|
||||||
if (this.listContainerElementRef) {
|
if (this.listContainerElementRef) {
|
||||||
this.longPressStartPoint = screenToClient(currentTarget,e);
|
this.longPressStartPoint = screenToClient(currentTarget, e);
|
||||||
|
let dragState: DragState = {
|
||||||
|
activeItem: fileEntry.pathname,
|
||||||
|
dragElement: element,
|
||||||
|
height: element.clientHeight,
|
||||||
|
dragElementDy: 0,
|
||||||
|
from: index,
|
||||||
|
to: index,
|
||||||
|
lastMousePoint: {...this.longPressStartPoint}
|
||||||
|
};
|
||||||
|
this.setState({ dragState: dragState });
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if (!this.state.multiSelect) {
|
} else if (!this.state.multiSelect) {
|
||||||
if (fileEntry.isProtected) {
|
if (fileEntry.isProtected) {
|
||||||
@@ -370,6 +383,48 @@ export default withStyles(
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
dragTarget: HTMLButtonElement | null = null;
|
||||||
|
|
||||||
|
|
||||||
|
updateReorderPosition(element: HTMLButtonElement, point: Point) {
|
||||||
|
if (this.longPressStartPoint) {
|
||||||
|
let dragDy = point.y - this.longPressStartPoint.y;
|
||||||
|
let height = element.clientHeight;
|
||||||
|
|
||||||
|
let dragFromPosition = parseInt(element.getAttribute("data-position") as string, 10);
|
||||||
|
|
||||||
|
let elementOffset = dragFromPosition * height;
|
||||||
|
let newOffset = elementOffset + dragDy;
|
||||||
|
if (newOffset < 0) {
|
||||||
|
dragDy = -elementOffset;
|
||||||
|
newOffset = 0;
|
||||||
|
}
|
||||||
|
let maxOffset = (this.maxPosition - 1) * height;
|
||||||
|
if (newOffset > maxOffset) {
|
||||||
|
dragDy = maxOffset - elementOffset;
|
||||||
|
newOffset = maxOffset;
|
||||||
|
}
|
||||||
|
let toPosition = Math.floor((newOffset + height / 2) / height);
|
||||||
|
if (toPosition < 0) {
|
||||||
|
toPosition = 0;
|
||||||
|
}
|
||||||
|
if (toPosition > this.maxPosition) {
|
||||||
|
toPosition = this.maxPosition;
|
||||||
|
}
|
||||||
|
// console.log("drag from " + dragFromPosition + " to " + toPosition + " dy: " + dragDy);
|
||||||
|
this.setState({
|
||||||
|
dragState: {
|
||||||
|
activeItem: element.getAttribute("data-pathname") || "",
|
||||||
|
dragElement: element,
|
||||||
|
height: height,
|
||||||
|
dragElementDy: dragDy,
|
||||||
|
from: dragFromPosition,
|
||||||
|
to: toPosition,
|
||||||
|
lastMousePoint: point
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
handleLongPressMove(e: React.PointerEvent<HTMLButtonElement>) {
|
handleLongPressMove(e: React.PointerEvent<HTMLButtonElement>) {
|
||||||
if (this.state.reordering) {
|
if (this.state.reordering) {
|
||||||
if (!this.isTracksDirectory()) {
|
if (!this.isTracksDirectory()) {
|
||||||
@@ -377,37 +432,10 @@ export default withStyles(
|
|||||||
}
|
}
|
||||||
if (this.listContainerElementRef) {
|
if (this.listContainerElementRef) {
|
||||||
let element = e.target as HTMLButtonElement;
|
let element = e.target as HTMLButtonElement;
|
||||||
let point = screenToClient(e.currentTarget,e);
|
let point = screenToClient(e.currentTarget, e);
|
||||||
if (this.longPressStartPoint) {
|
this.updateReorderPosition(element, point);
|
||||||
let dy = point.y - this.longPressStartPoint.y;
|
this.startAutoScroll()
|
||||||
element.style.left = (5) + "px";
|
|
||||||
element.style.top = (5 + dy) + "px";
|
|
||||||
let height = element.clientHeight;
|
|
||||||
let positionChange_: number = 0;
|
|
||||||
if (dy > 0) {
|
|
||||||
positionChange_ = Math.floor((dy + height / 2) / height);
|
|
||||||
} else {
|
|
||||||
positionChange_ = Math.ceil((dy - height / 2) / height)
|
|
||||||
}
|
|
||||||
let toPosition = this.dragFromPosition + positionChange_;
|
|
||||||
if (toPosition < 0) {
|
|
||||||
toPosition = 0;
|
|
||||||
}
|
|
||||||
if (toPosition > this.maxPosition) {
|
|
||||||
toPosition = this.maxPosition;
|
|
||||||
}
|
|
||||||
if (toPosition !== this.dragToPosition) {
|
|
||||||
this.dragToPosition = toPosition;
|
|
||||||
this.setState({
|
|
||||||
dragState: {
|
|
||||||
activeItem: element.getAttribute("data-pathname") || "",
|
|
||||||
height: height,
|
|
||||||
from: this.dragFromPosition,
|
|
||||||
to: toPosition
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,7 +485,7 @@ export default withStyles(
|
|||||||
this.model.moveAudioFile(
|
this.model.moveAudioFile(
|
||||||
this.state.currentDirectory, from, to);
|
this.state.currentDirectory, from, to);
|
||||||
}
|
}
|
||||||
handleLongPressEnd(e: React.PointerEvent<HTMLButtonElement>, fileEntry: FileEntry) {
|
handleLongPressEnd(e: React.PointerEvent<HTMLButtonElement> | React.MouseEvent<HTMLButtonElement>, fileEntry: FileEntry) {
|
||||||
if (this.state.reordering) {
|
if (this.state.reordering) {
|
||||||
if (!this.isTracksDirectory()) {
|
if (!this.isTracksDirectory()) {
|
||||||
return;
|
return;
|
||||||
@@ -471,14 +499,8 @@ export default withStyles(
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
let element = e.currentTarget as HTMLButtonElement;
|
|
||||||
|
|
||||||
element.style.position = "";
|
|
||||||
element.style.zIndex = "";
|
|
||||||
element.style.top = "";
|
|
||||||
element.style.left = "";
|
|
||||||
element.style.background = ""; // xxx: dark mode.
|
|
||||||
this.setState({ dragState: null });
|
this.setState({ dragState: null });
|
||||||
|
this.stopAutoScroll();
|
||||||
} else if (!this.state.multiSelect) {
|
} else if (!this.state.multiSelect) {
|
||||||
let selectedFile = fileEntry.pathname;
|
let selectedFile = fileEntry.pathname;
|
||||||
if (selectedFile === "") {
|
if (selectedFile === "") {
|
||||||
@@ -526,6 +548,7 @@ export default withStyles(
|
|||||||
this.requestScroll = true;
|
this.requestScroll = true;
|
||||||
}
|
}
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
this.stopAutoScroll();
|
||||||
this.cancelProgressTimeout();
|
this.cancelProgressTimeout();
|
||||||
|
|
||||||
super.componentWillUnmount();
|
super.componentWillUnmount();
|
||||||
@@ -925,7 +948,98 @@ export default withStyles(
|
|||||||
return (<InsertDriveFileIcon style={style} />);
|
return (<InsertDriveFileIcon style={style} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
listContainerElementRef: HTMLDivElement | null = null;
|
private listContainerElementRef: HTMLDivElement | null = null;
|
||||||
|
private scrollContainerElementRef: HTMLDivElement | null = null;
|
||||||
|
private autoScrollTimer: number | null = null;
|
||||||
|
|
||||||
|
handleAutoScrollTick() {
|
||||||
|
let dragState = this.state.dragState;
|
||||||
|
if (!dragState) return;
|
||||||
|
|
||||||
|
let scrollContainer = this.scrollContainerElementRef;
|
||||||
|
if (!scrollContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let scrollBounds_ = scrollContainer.getBoundingClientRect();
|
||||||
|
let scrollClientTop = scrollBounds_.top;
|
||||||
|
let scrollClientBottom = scrollClientTop + scrollContainer.clientHeight;
|
||||||
|
let dragBounds = dragState.dragElement?.getBoundingClientRect();
|
||||||
|
|
||||||
|
let dy = 0;
|
||||||
|
let y = scrollContainer.scrollTop;
|
||||||
|
if (dragBounds.top < scrollClientTop + AUTOSCROLL_THRESHOLD) {
|
||||||
|
dy = -AUTOSCROLL_SCROLL_RATE;
|
||||||
|
|
||||||
|
y = scrollContainer.scrollTop + dy;
|
||||||
|
if (y < 0) y = 0;
|
||||||
|
dy = y - scrollContainer.scrollTop;
|
||||||
|
} else if (dragBounds.bottom > scrollClientBottom - AUTOSCROLL_THRESHOLD) {
|
||||||
|
dy = AUTOSCROLL_SCROLL_RATE;
|
||||||
|
|
||||||
|
y = scrollContainer.scrollTop + dy;
|
||||||
|
let maxScrollTop = scrollContainer.scrollHeight - scrollContainer.clientHeight;
|
||||||
|
if (y > maxScrollTop) {
|
||||||
|
y = maxScrollTop;
|
||||||
|
}
|
||||||
|
dy = y - scrollContainer.scrollTop;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.longPressStartPoint === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dy === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.longPressStartPoint = {
|
||||||
|
x: this.longPressStartPoint.x,
|
||||||
|
y: this.longPressStartPoint.y - dy
|
||||||
|
};
|
||||||
|
let dragElementDy = dragState.lastMousePoint.y - this.longPressStartPoint.y;
|
||||||
|
let originalY = dragState.from * dragState.height;
|
||||||
|
let newY = originalY + dragElementDy;
|
||||||
|
if (newY < 0)
|
||||||
|
{
|
||||||
|
newY = 0;
|
||||||
|
dragElementDy = -originalY;
|
||||||
|
}
|
||||||
|
let maxDy = (this.maxPosition - 1) * dragState.height;
|
||||||
|
if (newY > maxDy) {
|
||||||
|
newY = maxDy;
|
||||||
|
dragElementDy = maxDy - originalY;
|
||||||
|
}
|
||||||
|
let newTo = Math.floor((newY + dragState.height / 2) / dragState.height);
|
||||||
|
|
||||||
|
|
||||||
|
let newDragState = { ...dragState };
|
||||||
|
|
||||||
|
newDragState.dragElementDy = dragElementDy;
|
||||||
|
newDragState.to = newTo;
|
||||||
|
|
||||||
|
// console.log("autoscroll from " + dragState.from + " to " + newTo + " dy: " + dragElementDy);
|
||||||
|
// avoid jitter.
|
||||||
|
newDragState.dragElement.style.top = (newDragState.dragElementDy + 5) + "px";
|
||||||
|
|
||||||
|
this.setState({ dragState: newDragState });
|
||||||
|
scrollContainer.scrollTo({ left: 0, top: y, behavior: "instant" });
|
||||||
|
this.startAutoScroll();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stopAutoScroll() {
|
||||||
|
if (this.autoScrollTimer !== null) {
|
||||||
|
clearTimeout(this.autoScrollTimer);
|
||||||
|
this.autoScrollTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startAutoScroll() {
|
||||||
|
this.stopAutoScroll();
|
||||||
|
|
||||||
|
this.autoScrollTimer = setTimeout(
|
||||||
|
() => { this.handleAutoScrollTick() }, AUTOSCROLL_TICK_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const isTracksDirectory = this.isTracksDirectory();
|
const isTracksDirectory = this.isTracksDirectory();
|
||||||
@@ -1146,7 +1260,9 @@ export default withStyles(
|
|||||||
<DialogContent style={{
|
<DialogContent style={{
|
||||||
paddingLeft: 0, paddingRight: 0, paddingTop: 0, colorScheme: (isDarkMode() ? "dark" : "light"),
|
paddingLeft: 0, paddingRight: 0, paddingTop: 0, colorScheme: (isDarkMode() ? "dark" : "light"),
|
||||||
position: "relative"
|
position: "relative"
|
||||||
}}>
|
}}
|
||||||
|
ref={(element: HTMLDivElement | null) => { this.scrollContainerElementRef = element; }}
|
||||||
|
>
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
paddingLeft: 16, paddingRight: 16, paddingTop: 8, paddingBottom: 8,
|
paddingLeft: 16, paddingRight: 16, paddingTop: 8, paddingBottom: 8,
|
||||||
@@ -1210,7 +1326,10 @@ export default withStyles(
|
|||||||
scrollRef = (element) => { this.onScrollRef(element) };
|
scrollRef = (element) => { this.onScrollRef(element) };
|
||||||
}
|
}
|
||||||
let dragOffset = 0;
|
let dragOffset = 0;
|
||||||
|
let dragLeft = 0;
|
||||||
let dragState = this.state.dragState;
|
let dragState = this.state.dragState;
|
||||||
|
let zIndex: number | undefined = undefined;
|
||||||
|
let background: string | undefined = undefined;
|
||||||
if (dragState && value.metadata) {
|
if (dragState && value.metadata) {
|
||||||
if (myTrackPosition !== dragState.from) {
|
if (myTrackPosition !== dragState.from) {
|
||||||
if (myTrackPosition >= dragState.to && myTrackPosition < dragState.from) {
|
if (myTrackPosition >= dragState.to && myTrackPosition < dragState.from) {
|
||||||
@@ -1218,21 +1337,31 @@ export default withStyles(
|
|||||||
} else if (myTrackPosition > dragState.from && myTrackPosition <= dragState.to) {
|
} else if (myTrackPosition > dragState.from && myTrackPosition <= dragState.to) {
|
||||||
dragOffset = -dragState.height;
|
dragOffset = -dragState.height;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
dragOffset = dragState.dragElementDy + 5;
|
||||||
|
dragLeft = 5;
|
||||||
|
zIndex = 1000;
|
||||||
|
background = isDarkMode() ? "#555" : "#EEF";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let dragButtonStyle: React.CSSProperties = {
|
||||||
|
width: columnWidth, flex: "0 0 auto", height: (value.metadata && !compactVertical) ? 64 : 48,
|
||||||
|
position: "relative",
|
||||||
|
top: dragOffset,
|
||||||
|
left: dragLeft,
|
||||||
|
zIndex: zIndex,
|
||||||
|
background: background,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<DraggableButtonBase key={value.pathname}
|
<DraggableButtonBase key={value.pathname}
|
||||||
longPressDelay={this.state.reordering ? 0 : undefined}
|
longPressDelay={this.state.reordering ? 300 : undefined}
|
||||||
data-position={dataPosition}
|
data-position={dataPosition}
|
||||||
data-pathname={
|
data-pathname={
|
||||||
value.metadata ? value.metadata.fileName : null
|
value.metadata ? value.metadata.fileName : null
|
||||||
}
|
}
|
||||||
|
instantMouseLongPress={this.state.reordering}
|
||||||
|
|
||||||
style={{
|
style={dragButtonStyle}
|
||||||
width: columnWidth, flex: "0 0 auto", height: (value.metadata && !compactVertical) ? 64 : 48,
|
|
||||||
position: "relative",
|
|
||||||
top: dragOffset + "px",
|
|
||||||
}}
|
|
||||||
onClick={(e) => this.handleFileClick(e, value)}
|
onClick={(e) => this.handleFileClick(e, value)}
|
||||||
onDoubleClick={() => { this.onDoubleClickValue(value.pathname); }}
|
onDoubleClick={() => { this.onDoubleClickValue(value.pathname); }}
|
||||||
|
|
||||||
@@ -1559,7 +1688,7 @@ export default withStyles(
|
|||||||
await this.model.renameFilePropertyFile(oldFilePath, newFilePath, this.props.fileProperty);
|
await this.model.renameFilePropertyFile(oldFilePath, newFilePath, this.props.fileProperty);
|
||||||
let index = newFileList.indexOf(file);
|
let index = newFileList.indexOf(file);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
newFileList.splice(index,1);
|
newFileList.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1570,8 +1699,7 @@ export default withStyles(
|
|||||||
if (!this.mounted) {
|
if (!this.mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (newFileList.length === 0 )
|
if (newFileList.length === 0) {
|
||||||
{
|
|
||||||
this.setState({
|
this.setState({
|
||||||
multiSelect: false,
|
multiSelect: false,
|
||||||
selectedFiles: []
|
selectedFiles: []
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ function ToolTipEx(props: ToolTipExProps) {
|
|||||||
if (valueTooltip === undefined) // no timeout if there's a value tooltip
|
if (valueTooltip === undefined) // no timeout if there's a value tooltip
|
||||||
{
|
{
|
||||||
if (t > 0) {
|
if (t > 0) {
|
||||||
console.log("ToolTipEx: starting timeout for ", t);
|
// console.log("ToolTipEx: starting timeout for ", t);
|
||||||
handle = window.setTimeout(() => {
|
handle = window.setTimeout(() => {
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
},t);
|
},t);
|
||||||
@@ -89,7 +89,7 @@ function ToolTipEx(props: ToolTipExProps) {
|
|||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (handle !== null) {
|
if (handle !== null) {
|
||||||
console.log("ToolTipEx: clearing timeout for ", t);
|
// console.log("ToolTipEx: clearing timeout for ", t);
|
||||||
window.clearTimeout(handle);
|
window.clearTimeout(handle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -181,39 +181,39 @@ function ToolTipEx(props: ToolTipExProps) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClickCapture={(e) => {
|
onClickCapture={(e) => {
|
||||||
console.log("ToolTipEx: onClickCapture");
|
// console.log("ToolTipEx: onClickCapture");
|
||||||
handleClickCapture(e);
|
handleClickCapture(e);
|
||||||
}}
|
}}
|
||||||
|
|
||||||
onMouseEnter={(e)=> {
|
onMouseEnter={(e)=> {
|
||||||
console.log("ToolTipEx: onMouseEnter");
|
// console.log("ToolTipEx: onMouseEnter");
|
||||||
|
|
||||||
handleMouseEnter(e);
|
handleMouseEnter(e);
|
||||||
}}
|
}}
|
||||||
onMouseLeave={(e)=> {
|
onMouseLeave={(e)=> {
|
||||||
console.log("ToolTipEx: onMouseLeave");
|
// console.log("ToolTipEx: onMouseLeave");
|
||||||
handleMouseLeave(e);
|
handleMouseLeave(e);
|
||||||
}}
|
}}
|
||||||
|
|
||||||
onPointerCancelCapture={(e) => {
|
onPointerCancelCapture={(e) => {
|
||||||
console.log("ToolTipEx: onPointerCancelCapture");
|
// console.log("ToolTipEx: onPointerCancelCapture");
|
||||||
handlePointerCancel(e);
|
handlePointerCancel(e);
|
||||||
}}
|
}}
|
||||||
onContextMenuCapture={(e) => {
|
onContextMenuCapture={(e) => {
|
||||||
console.log("ToolTipEx: onContextMenuCapture");
|
// console.log("ToolTipEx: onContextMenuCapture");
|
||||||
handleConextMenu(e);
|
handleConextMenu(e);
|
||||||
return false;
|
return false;
|
||||||
}}
|
}}
|
||||||
onPointerDownCapture={(e) => {
|
onPointerDownCapture={(e) => {
|
||||||
console.log("ToolTipEx: onPointerDownCapture");
|
// console.log("ToolTipEx: onPointerDownCapture");
|
||||||
handlePointerDownCapture(e);
|
handlePointerDownCapture(e);
|
||||||
}}
|
}}
|
||||||
onPointerMoveCapture={(e) => {
|
onPointerMoveCapture={(e) => {
|
||||||
console.log("ToolTipEx: onPointerMoveCapture");
|
// console.log("ToolTipEx: onPointerMoveCapture");
|
||||||
handlePointerMoveCapture(e);
|
handlePointerMoveCapture(e);
|
||||||
}}
|
}}
|
||||||
onPointerUpCapture={(e) => {
|
onPointerUpCapture={(e) => {
|
||||||
console.log("ToolTipEx: onPointerUpCapture");
|
// console.log("ToolTipEx: onPointerUpCapture");
|
||||||
handlePointerUpCapture(e);
|
handlePointerUpCapture(e);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user