From f42953b46065fafac9fc1505f40aa23e55a727b7 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Wed, 16 Oct 2019 17:18:39 +0700 Subject: [PATCH] simple drag --- .../node/NodeImageSlideBlock/index.tsx | 150 ++++++++++++++++++ .../node/NodeImageSlideBlock/styles.scss | 56 +++++++ src/redux/node/constants.ts | 4 +- 3 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 src/components/node/NodeImageSlideBlock/index.tsx create mode 100644 src/components/node/NodeImageSlideBlock/styles.scss diff --git a/src/components/node/NodeImageSlideBlock/index.tsx b/src/components/node/NodeImageSlideBlock/index.tsx new file mode 100644 index 00000000..c2efff13 --- /dev/null +++ b/src/components/node/NodeImageSlideBlock/index.tsx @@ -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 = ({ node, is_loading, updateLayout }) => { + const [is_animated, setIsAnimated] = useState(false); + const [current, setCurrent] = useState(0); + const [height, setHeight] = useState(320); + const [loaded, setLoaded] = useState>({}); + const refs = useRef>({}); + 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 = 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 ( +
+
+ {(is_loading || !loaded[0] || !images.length) &&
} + + {images.map((file, index) => ( +
+ +
+ ))} +
+
+ ); +}; + +export { NodeImageSlideBlock }; diff --git a/src/components/node/NodeImageSlideBlock/styles.scss b/src/components/node/NodeImageSlideBlock/styles.scss new file mode 100644 index 00000000..c36ee620 --- /dev/null +++ b/src/components/node/NodeImageSlideBlock/styles.scss @@ -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; +} diff --git a/src/redux/node/constants.ts b/src/redux/node/constants.ts index 5f34723a..64bd54c8 100644 --- a/src/redux/node/constants.ts +++ b/src/redux/node/constants.ts @@ -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 = {