Improve scrolling experience

This commit is contained in:
dominik-zeglen 2019-10-15 12:33:14 +02:00
parent c33102b472
commit ae14076ceb
5 changed files with 58 additions and 30 deletions

View file

@ -0,0 +1,3 @@
<svg width="15" height="10" viewBox="0 0 15 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.23278 6.21084L13 -6.40628e-08L14.4656 1.3609L7.23278 9.15006L-1.15036e-05 1.3609L1.46558 -5.68248e-07L7.23278 6.21084Z" fill="#06847B"/>
</svg>

After

Width:  |  Height:  |  Size: 292 B

View file

@ -2,7 +2,6 @@ import { storiesOf } from "@storybook/react";
import React from "react"; import React from "react";
import Form from "@saleor/components/Form"; import Form from "@saleor/components/Form";
import { maybe } from "@saleor/misc";
import CardDecorator from "@saleor/storybook/CardDecorator"; import CardDecorator from "@saleor/storybook/CardDecorator";
import Decorator from "@saleor/storybook/Decorator"; import Decorator from "@saleor/storybook/Decorator";
import { ChoiceProvider } from "@saleor/storybook/mock"; import { ChoiceProvider } from "@saleor/storybook/mock";

View file

@ -7,9 +7,13 @@ import { makeStyles } from "@material-ui/styles";
import classNames from "classnames"; import classNames from "classnames";
import { GetItemPropsOptions } from "downshift"; import { GetItemPropsOptions } from "downshift";
import React from "react"; import React from "react";
import SVG from "react-inlinesvg";
import { FormattedMessage } from "react-intl"; import { FormattedMessage } from "react-intl";
import useElementScroll from "@saleor/hooks/useElementScroll"; import chevronDown from "@assets/images/ChevronDown.svg";
import useElementScroll, {
isScrolledToBottom
} from "@saleor/hooks/useElementScroll";
import { FetchMoreProps } from "@saleor/types"; import { FetchMoreProps } from "@saleor/types";
import Hr from "../Hr"; import Hr from "../Hr";
@ -35,11 +39,29 @@ export interface SingleAutocompleteSelectFieldContentProps
const useStyles = makeStyles( const useStyles = makeStyles(
(theme: Theme) => ({ (theme: Theme) => ({
arrowContainer: {
position: "relative"
},
arrowInnerContainer: {
alignItems: "center",
background: theme.palette.grey[50],
bottom: 0,
display: "flex",
height: 30,
justifyContent: "center",
opacity: 1,
position: "absolute",
transition: theme.transitions.duration.short + "ms",
width: "100%"
},
content: { content: {
maxHeight: menuItemHeight * maxMenuItems + theme.spacing.unit * 2, maxHeight: menuItemHeight * maxMenuItems + theme.spacing.unit * 2,
overflow: "scroll", overflow: "scroll",
padding: 8 padding: 8
}, },
hide: {
opacity: 0
},
hr: { hr: {
margin: `${theme.spacing.unit}px 0` margin: `${theme.spacing.unit}px 0`
}, },
@ -53,22 +75,14 @@ const useStyles = makeStyles(
justifyContent: "center" justifyContent: "center"
}, },
root: { root: {
borderRadius: 4, borderBottomLeftRadius: 8,
borderBottomRightRadius: 8,
left: 0, left: 0,
marginTop: theme.spacing.unit, marginTop: theme.spacing.unit,
overflow: "hidden",
position: "absolute", position: "absolute",
right: 0, right: 0,
zIndex: 22 zIndex: 22
},
shadow: {
"&$shadowLine": {
boxShadow: `0px -5px 10px 0px ${theme.palette.grey[800]}`
}
},
shadowLine: {
boxShadow: `0px 0px 0px 0px ${theme.palette.grey[50]}`,
height: 1,
transition: theme.transitions.duration.short + "ms"
} }
}), }),
{ {
@ -114,10 +128,7 @@ const SingleAutocompleteSelectFieldContent: React.FC<
const scrollPosition = useElementScroll(anchor); const scrollPosition = useElementScroll(anchor);
const [calledForMore, setCalledForMore] = React.useState(false); const [calledForMore, setCalledForMore] = React.useState(false);
const scrolledToBottom = anchor.current const scrolledToBottom = isScrolledToBottom(anchor, scrollPosition, 50);
? scrollPosition.y + anchor.current.clientHeight + offset >=
anchor.current.scrollHeight
: false;
React.useEffect(() => { React.useEffect(() => {
if (!calledForMore && onFetchMore && scrolledToBottom) { if (!calledForMore && onFetchMore && scrolledToBottom) {
@ -133,7 +144,7 @@ const SingleAutocompleteSelectFieldContent: React.FC<
}, [loading]); }, [loading]);
return ( return (
<Paper className={classes.root} square> <Paper className={classes.root}>
<div className={classes.content} ref={anchor}> <div className={classes.content} ref={anchor}>
{choices.length > 0 || displayCustomValue ? ( {choices.length > 0 || displayCustomValue ? (
<> <>
@ -219,11 +230,15 @@ const SingleAutocompleteSelectFieldContent: React.FC<
</MenuItem> </MenuItem>
)} )}
</div> </div>
<div className={classes.arrowContainer}>
<div <div
className={classNames(classes.shadowLine, { className={classNames(classes.arrowInnerContainer, {
[classes.shadow]: !scrolledToBottom && choices.length > 0 [classes.hide]: scrolledToBottom && choices.length > 0
})} })}
/> >
<SVG src={chevronDown} />
</div>
</div>
</Paper> </Paper>
); );
}; };

View file

@ -1,20 +1,30 @@
import throttle from "lodash-es/throttle"; import throttle from "lodash-es/throttle";
import { MutableRefObject, useEffect, useState } from "react"; import { MutableRefObject, useEffect, useState } from "react";
function getPosition(anchor?: HTMLElement) { export type Position = Record<"x" | "y", number>;
function getPosition(anchor?: HTMLElement): Position {
if (!!anchor) { if (!!anchor) {
return { return {
x: anchor.scrollLeft, x: anchor.scrollLeft,
y: anchor.scrollTop y: anchor.scrollTop
}; };
} }
return { return undefined;
x: 0,
y: 0
};
} }
function useElementScroll(anchor: MutableRefObject<HTMLElement>) { export function isScrolledToBottom(
anchor: MutableRefObject<HTMLElement>,
position: Position,
offset: number = 0
) {
return !!anchor.current && position
? position.y + anchor.current.clientHeight + offset >=
anchor.current.scrollHeight
: false;
}
function useElementScroll(anchor: MutableRefObject<HTMLElement>): Position {
const [scroll, setScroll] = useState(getPosition(anchor.current)); const [scroll, setScroll] = useState(getPosition(anchor.current));
useEffect(() => { useEffect(() => {

View file

@ -10,7 +10,8 @@ function createSingleAutocompleteSelectHandler(
change(event); change(event);
const value = event.target.value; const value = event.target.value;
setSelected(choices.find(category => category.value === value).label); const choice = choices.find(category => category.value === value)
setSelected(choice ? choice.label : value);
}; };
} }