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

fixed nodepanel behavoiur

This commit is contained in:
Fedor Katurov 2019-11-25 18:36:06 +07:00
parent e19297207e
commit d74c9ee739
3 changed files with 79 additions and 102 deletions

View file

@ -1,21 +1,14 @@
import React, { import React, { FC, useMemo, useState, useEffect, useRef, useCallback } from 'react';
FC, import { ImageSwitcher } from '../ImageSwitcher';
useMemo, import * as styles from './styles.scss';
useState, import { INode } from '~/redux/types';
useEffect, import classNames from 'classnames';
useRef, import { UPLOAD_TYPES } from '~/redux/uploads/constants';
useCallback import { NODE_SETTINGS } from '~/redux/node/constants';
} from "react"; import { getURL } from '~/utils/dom';
import { ImageSwitcher } from "../ImageSwitcher"; import { PRESETS } from '~/constants/urls';
import * as styles from "./styles.scss"; import { LoaderCircle } from '~/components/input/LoaderCircle';
import { INode } from "~/redux/types"; import { throttle } from 'throttle-debounce';
import classNames from "classnames";
import { UPLOAD_TYPES } from "~/redux/uploads/constants";
import { NODE_SETTINGS } from "~/redux/node/constants";
import { getURL } from "~/utils/dom";
import { PRESETS } from "~/constants/urls";
import { LoaderCircle } from "~/components/input/LoaderCircle";
import { throttle } from "throttle-debounce";
interface IProps { interface IProps {
is_loading: boolean; is_loading: boolean;
@ -24,14 +17,9 @@ interface IProps {
updateLayout: () => void; updateLayout: () => void;
} }
const getX = event => const getX = event => (event.touches ? event.touches[0].clientX : event.clientX);
event.touches ? event.touches[0].clientX : event.clientX;
const NodeImageSlideBlock: FC<IProps> = ({ const NodeImageSlideBlock: FC<IProps> = ({ node, is_loading, updateLayout }) => {
node,
is_loading,
updateLayout
}) => {
const [current, setCurrent] = useState(0); const [current, setCurrent] = useState(0);
const [height, setHeight] = useState(320); const [height, setHeight] = useState(320);
const [max_height, setMaxHeight] = useState(960); const [max_height, setMaxHeight] = useState(960);
@ -50,10 +38,7 @@ const NodeImageSlideBlock: FC<IProps> = ({
const images = useMemo( const images = useMemo(
() => () =>
(node && (node && node.files && node.files.filter(({ type }) => type === UPLOAD_TYPES.IMAGE)) || [],
node.files &&
node.files.filter(({ type }) => type === UPLOAD_TYPES.IMAGE)) ||
[],
[node] [node]
); );
@ -81,26 +66,34 @@ const NodeImageSlideBlock: FC<IProps> = ({
[refs, heights, setHeights, images] [refs, heights, setHeights, images]
); );
const onImageLoad = useCallback( const onImageLoad = useCallback(index => () => setLoaded({ ...loaded, [index]: true }), [
index => () => setLoaded({ ...loaded, [index]: true }), setLoaded,
[setLoaded, loaded] loaded,
); ]);
// update outside hooks // update outside hooks
useEffect(() => updateLayout(), [loaded, height, images]); useEffect(() => updateLayout(), [loaded, height, images]);
useEffect(() => updateSizes(), [refs, current, loaded, images]); useEffect(() => updateSizes(), [refs, current, loaded, images]);
useEffect(() => { useEffect(() => {
if (!wrap || !wrap.current) return; const timeout = setTimeout(updateLayout, 300);
if (!wrap || !wrap.current) return () => clearTimeout(timeout);
const { width } = wrap.current.getBoundingClientRect(); const { width } = wrap.current.getBoundingClientRect();
const fallback = (width * 9) / 16; const fallback = (width * 9) / 16;
if (is_loading) return setHeight(fallback); if (is_loading) {
setHeight(fallback);
return () => clearTimeout(timeout);
}
const selected = Math.abs(-offset / width); const selected = Math.abs(-offset / width);
if (!heights[Math.round(selected)]) return setHeight(fallback); if (!heights[Math.round(selected)]) {
setHeight(fallback);
return () => clearTimeout(timeout);
}
const prev = Math.max(heights[Math.floor(selected)] || fallback, fallback); const prev = Math.max(heights[Math.floor(selected)] || fallback, fallback);
const next = Math.max(heights[Math.ceil(selected)] || fallback, fallback); const next = Math.max(heights[Math.ceil(selected)] || fallback, fallback);
@ -115,9 +108,8 @@ const NodeImageSlideBlock: FC<IProps> = ({
} }
// update layout after all manipulations // update layout after all manipulations
const timeout = setTimeout(() => updateLayout(), 500);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [is_dragging, wrap, offset, heights, max_height, images, is_loading]); }, [is_dragging, wrap, offset, heights, max_height, images, is_loading, updateLayout]);
const onDrag = useCallback( const onDrag = useCallback(
event => { event => {
@ -133,13 +125,7 @@ const NodeImageSlideBlock: FC<IProps> = ({
const { width: wrap_width } = wrap.current.getBoundingClientRect(); const { width: wrap_width } = wrap.current.getBoundingClientRect();
setOffset( setOffset(
Math.min( Math.min(Math.max(initial_offset + getX(event) - initial_x, wrap_width - slide_width), 0)
Math.max(
initial_offset + getX(event) - initial_x,
wrap_width - slide_width
),
0
)
); );
}, },
[is_dragging, initial_x, setOffset, initial_offset] [is_dragging, initial_x, setOffset, initial_offset]
@ -152,9 +138,7 @@ const NodeImageSlideBlock: FC<IProps> = ({
const { width: slide_width } = slide.current.getBoundingClientRect(); const { width: slide_width } = slide.current.getBoundingClientRect();
const shift = (initial_offset - offset) / wrap_width; // percent / 100 const shift = (initial_offset - offset) / wrap_width; // percent / 100
const diff = const diff = initial_offset - (shift > 0 ? Math.ceil(shift) : Math.floor(shift)) * wrap_width;
initial_offset -
(shift > 0 ? Math.ceil(shift) : Math.floor(shift)) * wrap_width;
const new_offset = const new_offset =
Math.abs(shift) > 0.25 Math.abs(shift) > 0.25
? Math.min(Math.max(diff, wrap_width - slide_width), 0) // next or prev slide ? Math.min(Math.max(diff, wrap_width - slide_width), 0) // next or prev slide
@ -189,24 +173,24 @@ const NodeImageSlideBlock: FC<IProps> = ({
useEffect(() => updateMaxHeight(), [images]); useEffect(() => updateMaxHeight(), [images]);
useEffect(() => { useEffect(() => {
window.addEventListener("resize", updateSizes); window.addEventListener('resize', updateSizes);
window.addEventListener("resize", updateMaxHeight); window.addEventListener('resize', updateMaxHeight);
window.addEventListener("mousemove", onDrag); window.addEventListener('mousemove', onDrag);
window.addEventListener("touchmove", onDrag); window.addEventListener('touchmove', onDrag);
window.addEventListener("mouseup", stopDragging); window.addEventListener('mouseup', stopDragging);
window.addEventListener("touchend", stopDragging); window.addEventListener('touchend', stopDragging);
return () => { return () => {
window.removeEventListener("resize", updateSizes); window.removeEventListener('resize', updateSizes);
window.removeEventListener("resize", updateMaxHeight); window.removeEventListener('resize', updateMaxHeight);
window.removeEventListener("mousemove", onDrag); window.removeEventListener('mousemove', onDrag);
window.removeEventListener("touchmove", onDrag); window.removeEventListener('touchmove', onDrag);
window.removeEventListener("mouseup", stopDragging); window.removeEventListener('mouseup', stopDragging);
window.removeEventListener("touchend", stopDragging); window.removeEventListener('touchend', stopDragging);
}; };
}, [onDrag, stopDragging, updateMaxHeight, updateSizes]); }, [onDrag, stopDragging, updateMaxHeight, updateSizes]);
@ -219,13 +203,10 @@ const NodeImageSlideBlock: FC<IProps> = ({
); );
return ( return (
<div <div className={classNames(styles.wrap, { [styles.is_loading]: is_loading })} ref={wrap}>
className={classNames(styles.wrap, { [styles.is_loading]: is_loading })}
ref={wrap}
>
<div <div
className={classNames(styles.placeholder, { className={classNames(styles.placeholder, {
[styles.is_loading]: is_loading || !loaded[current] [styles.is_loading]: is_loading || !loaded[current],
})} })}
> >
<div> <div>
@ -247,7 +228,7 @@ const NodeImageSlideBlock: FC<IProps> = ({
style={{ style={{
height, height,
transform: `translate(${offset}px, 0)`, transform: `translate(${offset}px, 0)`,
width: `${images.length * 100}%` width: `${images.length * 100}%`,
}} }}
onMouseDown={startDragging} onMouseDown={startDragging}
onTouchStart={startDragging} onTouchStart={startDragging}
@ -257,14 +238,14 @@ const NodeImageSlideBlock: FC<IProps> = ({
images.map((file, index) => ( images.map((file, index) => (
<div <div
className={classNames(styles.image_wrap, { className={classNames(styles.image_wrap, {
is_active: index === current && loaded[index] is_active: index === current && loaded[index],
})} })}
ref={setRef(index)} ref={setRef(index)}
key={node.updated_at + file.id} key={node.updated_at + file.id}
> >
<img <img
className={styles.image} className={styles.image}
src={getURL(file, PRESETS["1600"])} src={getURL(file, PRESETS['1600'])}
alt="" alt=""
key={file.id} key={file.id}
onLoad={onImageLoad(index)} onLoad={onImageLoad(index)}

View file

@ -1,16 +1,9 @@
import React, { import React, { FC, useCallback, useEffect, useRef, useState, memo } from 'react';
FC, import * as styles from './styles.scss';
useCallback, import { INode } from '~/redux/types';
useEffect, import { createPortal } from 'react-dom';
useRef, import { NodePanelInner } from '~/components/node/NodePanelInner';
useState, import pick from 'ramda/es/pick';
memo
} from "react";
import * as styles from "./styles.scss";
import { INode } from "~/redux/types";
import { createPortal } from "react-dom";
import { NodePanelInner } from "~/components/node/NodePanelInner";
import pick from "ramda/es/pick";
interface IProps { interface IProps {
node: Partial<INode>; node: Partial<INode>;
@ -28,40 +21,29 @@ interface IProps {
} }
const NodePanel: FC<IProps> = memo( const NodePanel: FC<IProps> = memo(
({ ({ node, layout, can_edit, can_like, can_star, is_loading, onEdit, onLike, onStar }) => {
node,
layout,
can_edit,
can_like,
can_star,
is_loading,
onEdit,
onLike,
onStar
}) => {
const [stack, setStack] = useState(false); const [stack, setStack] = useState(false);
const ref = useRef(null); const ref = useRef(null);
const getPlace = useCallback(() => { const getPlace = useCallback(() => {
if (!ref.current) return; if (!ref.current) return;
const { offsetTop } = ref.current; const { bottom } = ref.current.getBoundingClientRect();
const { height } = ref.current.getBoundingClientRect();
const { scrollY, innerHeight } = window;
setStack(offsetTop > scrollY + innerHeight - height); setStack(bottom > window.innerHeight);
}, [ref]); }, [ref]);
useEffect(() => getPlace(), [layout]);
useEffect(() => { useEffect(() => {
getPlace(); window.addEventListener('scroll', getPlace);
window.addEventListener("scroll", getPlace); window.addEventListener('resize', getPlace);
window.addEventListener("resize", getPlace);
return () => { return () => {
window.removeEventListener("scroll", getPlace); window.removeEventListener('scroll', getPlace);
window.removeEventListener("resize", getPlace); window.removeEventListener('resize', getPlace);
}; };
}, [layout]); }, [layout, getPlace]);
return ( return (
<div className={styles.place} ref={ref}> <div className={styles.place} ref={ref}>

View file

@ -14,6 +14,10 @@
padding: 0 $gap; padding: 0 $gap;
bottom: 0; bottom: 0;
position: fixed; position: fixed;
@include tablet {
padding: 0;
}
} }
} }
@ -27,7 +31,17 @@
padding: $gap; padding: $gap;
background: $node_bg; background: $node_bg;
height: 72px; height: 72px;
@include outer_shadow(); @include outer_shadow();
@include tablet {
border-radius: 0;
}
@include can_backdrop {
backdrop-filter: blur(15px);
background: transparentize($node_bg, 0.2);
}
} }
.title { .title {