1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 04:46:40 +07:00

simple drag

This commit is contained in:
Fedor Katurov 2019-10-16 17:18:39 +07:00
parent 1b6a81d27c
commit f42953b460
3 changed files with 208 additions and 2 deletions

View file

@ -0,0 +1,150 @@
import React, {
FC,
useMemo,
useState,
useEffect,
RefObject,
LegacyRef,
useRef,
useCallback,
MouseEventHandler,
} from 'react';
import { ImageSwitcher } from '../ImageSwitcher';
import * as styles from './styles.scss';
import { INode } from '~/redux/types';
import classNames from 'classnames';
import { getImageSize } from '~/utils/dom';
import { UPLOAD_TYPES } from '~/redux/uploads/constants';
interface IProps {
is_loading: boolean;
node: INode;
layout: {};
updateLayout: () => void;
}
const NodeImageSlideBlock: FC<IProps> = ({ node, is_loading, updateLayout }) => {
const [is_animated, setIsAnimated] = useState(false);
const [current, setCurrent] = useState(0);
const [height, setHeight] = useState(320);
const [loaded, setLoaded] = useState<Record<number, boolean>>({});
const refs = useRef<Record<number, HTMLDivElement>>({});
const [heights, setHeights] = useState({});
const [initial_offset, setInitialOffset] = useState(0);
const [initial_x, setInitialX] = useState(0);
const [offset, setOffset] = useState(0);
const [is_dragging, setIsDragging] = useState(false);
const slide = useRef();
const images = useMemo(
() =>
(node && node.files && node.files.filter(({ type }) => type === UPLOAD_TYPES.IMAGE)) || [],
[node]
);
// console.log({ heights });
const updateSizes = useCallback(() => {
const values = Object.keys(refs.current).map(key => {
const ref = refs.current[key];
if (!ref || !ref.getBoundingClientRect) return 0;
return ref.getBoundingClientRect().height;
});
}, [refs]);
const setRef = useCallback(
index => el => {
refs.current[index] = el;
},
[refs, heights, setHeights]
);
const onImageLoad = useCallback(index => () => setLoaded({ ...loaded, [index]: true }), [
setLoaded,
loaded,
]);
// update outside hooks
useEffect(() => updateLayout(), [loaded]);
useEffect(() => {
updateSizes();
//
// if (!refs || !refs.current[current] || !loaded[current]) return setHeight(320);
//
// const el = refs.current[current];
//
// const element_height = el.getBoundingClientRect && el.getBoundingClientRect().height;
//
// setHeight(element_height);
}, [refs, current, loaded]);
// useEffect(() => {
// const timer = setTimeout(() => setIsAnimated(true), 250);
//
// return () => clearTimeout(timer);
// }, []);
const stopDragging = useCallback(() => {
window.removeEventListener('mouseup', stopDragging);
setIsDragging(false);
}, [setIsDragging]);
const startDragging: MouseEventHandler<HTMLDivElement> = useCallback(
event => {
window.addEventListener('mouseup', stopDragging);
setIsDragging(true);
setInitialX(event.clientX);
setInitialOffset(offset);
},
[setIsDragging, stopDragging, setInitialX, offset, setInitialOffset]
);
const onDrag = useCallback(
event => {
if (!is_dragging) return;
setOffset(initial_offset + event.clientX - initial_x);
},
[is_dragging, initial_x, setOffset, initial_offset]
);
return (
<div className={classNames(styles.wrap, { is_loading, is_animated })}>
<div
className={styles.image_container}
style={{
height,
transform: `translate(${offset}px, 0)`,
width: `${images.length * 100}%`,
}}
onMouseDown={startDragging}
onMouseMove={onDrag}
ref={slide}
>
{(is_loading || !loaded[0] || !images.length) && <div className={styles.placeholder} />}
{images.map((file, index) => (
<div
className={classNames(styles.image_wrap, {
is_active: index === current && loaded[index],
})}
ref={setRef(index)}
key={file.id}
>
<img
className={styles.image}
src={getImageSize(file, 'node')}
alt=""
key={file.id}
onLoad={onImageLoad(index)}
/>
</div>
))}
</div>
</div>
);
};
export { NodeImageSlideBlock };

View file

@ -0,0 +1,56 @@
.wrap {
overflow: hidden;
position: relative;
min-width: 0;
width: 100%;
&:global(.is_animated) {
.image_container {
transition: height 0.5s;
}
.image_wrap {
transition: opacity 0.5s;
}
}
}
.image_container {
background: $node_image_bg;
border-radius: $panel_radius 0 0 $panel_radius;
display: flex;
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
user-select: none;
.image {
max-height: 960px;
max-width: 100%;
opacity: 1;
border-radius: $radius $radius 0 0;
}
}
.image_wrap {
width: 100%;
// top: 0;
// left: 0;
// opacity: 0;
pointer-events: none;
touch-action: none;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
&:global(.is_active) {
opacity: 1;
}
}
.placeholder {
background: red;
height: 320px;
}

View file

@ -1,6 +1,6 @@
import { FC } from 'react';
import { IBlock, INode, ValueOf, IComment } from '../types';
import { NodeImageBlock } from '~/components/node/NodeImageBlock';
import { NodeImageSlideBlock } from '~/components/node/NodeImageSlideBlock';
import { ImageEditor } from '~/components/editors/ImageEditor';
import { TextEditor } from '~/components/editors/TextEditor';
import { DIALOGS } from '../modal/constants';
@ -63,7 +63,7 @@ type INodeComponents = Record<
>;
export const NODE_COMPONENTS: INodeComponents = {
[NODE_TYPES.IMAGE]: NodeImageBlock,
[NODE_TYPES.IMAGE]: NodeImageSlideBlock,
};
export const EMPTY_COMMENT: IComment = {