Make manual proportion list grow if there is space (#784)

Co-authored-by: lucas lelievre <loucass003@gmail.com>
This commit is contained in:
Uriel
2023-07-27 09:41:47 -03:00
committed by GitHub
parent f607693d83
commit 9c980f06f9
4 changed files with 77 additions and 41 deletions

View File

@@ -77,7 +77,7 @@ export function MainLayoutRoute({
>
<div
className={classNames(
'flex flex-col rounded-xl w-full overflow-hidden mobile:overflow-y-auto',
'flex flex-col rounded-xl w-full overflow-clip mobile:overflow-y-auto',
background && 'bg-background-70'
)}
>

View File

@@ -6,7 +6,6 @@ import {
useEffect,
useRef,
UIEvent,
MouseEvent,
useMemo,
} from 'react';
import {
@@ -17,6 +16,8 @@ import {
import { useLocaleConfig } from '../../../../i18n/config';
import { Typography } from '../../../commons/Typography';
import { ArrowDownIcon, ArrowUpIcon } from '../../../commons/icon/ArrowIcons';
import { useBreakpoint } from '../../../../hooks/breakpoint';
import { debounce } from '../../../../hooks/timeout';
function IncrementButton({
children,
@@ -52,8 +53,14 @@ export function BodyProportions({
const { bodyParts, dispatch, state, setRatioMode } = useManualProportions();
const { l10n } = useLocalization();
const { currentLocales } = useLocaleConfig();
const { isTall } = useBreakpoint('tall');
const srcollerRef = useRef<HTMLDivElement | null>(null);
const offsetItems = isTall ? 2 : 1;
const itemsToDisplay = offsetItems * 2 + 1;
const itemHeight = 80;
const scrollHeight = itemHeight * itemsToDisplay;
const scrollerRef = useRef<HTMLDivElement | null>(null);
const cmFormat = Intl.NumberFormat(currentLocales, {
style: 'unit',
@@ -78,41 +85,48 @@ export function BodyProportions({
}, [type]);
useEffect(() => {
if (srcollerRef.current && bodyParts.length > 0) {
moveToIndex(1);
if (scrollerRef.current && bodyParts.length > 0) {
selectId(bodyParts[offsetItems].label);
}
}, [srcollerRef, bodyParts.length]);
}, [scrollerRef, bodyParts.length]);
const handleUIEvent = (e: UIEvent<HTMLDivElement>) => {
const target = e.target as HTMLDivElement;
const itemHeight = target.offsetHeight / 3;
const itemHeight = target.offsetHeight / itemsToDisplay;
const atSnappingPoint = target.scrollTop % itemHeight === 0;
const index = Math.round(target.scrollTop / itemHeight);
const elem = scrollerRef.current?.childNodes[
index + offsetItems
] as HTMLDivElement;
elem.scrollIntoView({ behavior: 'smooth', block: 'center' });
if (atSnappingPoint) {
const index = target.scrollTop / itemHeight;
const elem = srcollerRef.current?.childNodes[index + 1] as HTMLDivElement;
const elem = scrollerRef.current?.childNodes[
index + offsetItems
] as HTMLDivElement;
const id = elem.getAttribute('itemid');
if (id) selectNew(id);
}
};
const clickPart = (id: string) => (e: MouseEvent<HTMLDivElement>) => {
const target = e.target as HTMLDivElement;
target.scrollIntoView({ behavior: 'smooth', block: 'center' });
const moveToId = (id: string) => {
if (!scrollerRef.current) return;
const index = bodyParts.findIndex(({ label }) => label === id);
scrollerRef.current.scrollTo({
top: index * itemHeight,
behavior: 'smooth',
});
};
const clickPart = (id: string) => () => {
moveToId(id);
selectNew(id);
};
const moveToIndex = (index: number, smooth = true) => {
// We add one because of the offset placeholder
const elem = srcollerRef.current?.childNodes[index + 1] as HTMLDivElement;
elem?.scrollIntoView({
behavior: smooth ? 'smooth' : 'auto',
block: 'center',
});
const id = elem.getAttribute('itemid');
const selectId = (id: string) => {
moveToId(id);
if (id) selectNew(id);
};
@@ -170,15 +184,12 @@ export function BodyProportions({
}, [state]);
const move = (action: 'next' | 'prev') => {
const elem = srcollerRef.current?.querySelector(
const elem = scrollerRef.current?.querySelector(
`div[itemid=${state.currentLabel}]`
);
const moveId = (id: string, elem: HTMLDivElement) => {
elem?.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
const moveId = (id: string) => {
moveToId(id);
selectNew(id);
};
@@ -186,14 +197,14 @@ export function BodyProportions({
const prevElem = elem?.previousSibling as HTMLDivElement;
const prevId = prevElem.getAttribute('itemid');
if (!prevId) return;
moveId(prevId, prevElem);
moveId(prevId);
}
if (action === 'next') {
const nextElem = elem?.nextSibling as HTMLDivElement;
const nextId = nextElem.getAttribute('itemid');
if (!nextId) return;
moveId(nextId, nextElem);
moveId(nextId);
}
};
@@ -264,7 +275,7 @@ export function BodyProportions({
className={classNames(
'h-12 w-32 rounded-lg bg-background-60 flex flex-col justify-center',
'items-center fill-background-10',
srcollerRef?.current?.scrollTop ?? 0 > 0
scrollerRef?.current?.scrollTop ?? 0 > 0
? 'opacity-100 active:bg-accent-background-30'
: 'opacity-50'
)}
@@ -273,11 +284,17 @@ export function BodyProportions({
</div>
</div>
<div
ref={srcollerRef}
onScroll={handleUIEvent}
className="h-60 flex-grow flex-col overflow-y-auto snap-y snap-mandatory snap-always no-scrollbar"
ref={scrollerRef}
onScroll={debounce(handleUIEvent, 150)} // Debounce at 150ms to match the animation speed and prevent snaping between two animations
className={classNames(
'flex-grow flex-col overflow-y-auto',
'no-scrollbar'
)}
style={{ height: scrollHeight }}
>
<div className="h-20 snap-start "></div>
{Array.from({ length: offsetItems }).map((_, index) => (
<div style={{ height: itemHeight }} key={index}></div>
))}
{bodyParts.map((part) => {
const { label, value: originalValue, type, ...props } = part;
const value =
@@ -292,7 +309,8 @@ export function BodyProportions({
key={label}
itemID={label}
onClick={clickPart(label)}
className="snap-start h-20 flex-col flex justify-center"
style={{ height: itemHeight }}
className="flex-col flex justify-center"
>
<div
className={classNames(
@@ -320,7 +338,13 @@ export function BodyProportions({
</div>
);
})}
<div className="h-20 snap-start"></div>
{Array.from({ length: offsetItems }).map((_, index) => (
<div
className="h-20"
style={{ height: itemHeight }}
key={index}
></div>
))}
</div>
<div className="flex justify-center">
<div
@@ -328,9 +352,9 @@ export function BodyProportions({
className={classNames(
'h-12 w-32 rounded-lg bg-background-60 flex flex-col justify-center',
'items-center fill-background-10',
srcollerRef?.current?.scrollTop !==
(srcollerRef?.current?.scrollHeight ?? 0) -
(srcollerRef?.current?.offsetHeight ?? 0)
scrollerRef?.current?.scrollTop !==
(scrollerRef?.current?.scrollHeight ?? 0) -
(scrollerRef?.current?.offsetHeight ?? 0)
? 'opacity-100 active:bg-accent-background-30'
: 'opacity-50'
)}

View File

@@ -22,3 +22,14 @@ export const useDebouncedEffect = (effect: () => void, deps: any[], delay: numbe
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...(deps || []), delay]);
};
export const debounce = <F extends (...args: any) => any>(func: F, waitFor: number) => {
let timeout = 0;
const debounced = (...args: any) => {
window.clearTimeout(timeout);
timeout = window.setTimeout(() => func(...args), waitFor);
};
return debounced as (...args: Parameters<F>) => ReturnType<F>;
};

View File

@@ -163,6 +163,7 @@ const config = {
'md-max': { raw: 'not (min-width: 1100px)' },
lg: '1300px',
xl: '1600px',
tall: { raw: '(min-height: 800px)'}
},
extend: {
colors: {