diff --git a/gui/src/components/MainLayout.tsx b/gui/src/components/MainLayout.tsx
index f421ac3b8..b20568d4e 100644
--- a/gui/src/components/MainLayout.tsx
+++ b/gui/src/components/MainLayout.tsx
@@ -77,7 +77,7 @@ export function MainLayoutRoute({
>
diff --git a/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx b/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx
index 3a6d099aa..b56a7bd58 100644
--- a/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx
+++ b/gui/src/components/onboarding/pages/body-proportions/BodyProportions.tsx
@@ -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(null);
+ const offsetItems = isTall ? 2 : 1;
+ const itemsToDisplay = offsetItems * 2 + 1;
+ const itemHeight = 80;
+ const scrollHeight = itemHeight * itemsToDisplay;
+
+ const scrollerRef = useRef(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) => {
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) => {
- 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({
-
+ {Array.from({ length: offsetItems }).map((_, index) => (
+
+ ))}
{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"
>
);
})}
-
+ {Array.from({ length: offsetItems }).map((_, index) => (
+
+ ))}
void, deps: any[], delay: numbe
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [...(deps || []), delay]);
};
+
+export const debounce = 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) => ReturnType;
+};
diff --git a/gui/tailwind.config.ts b/gui/tailwind.config.ts
index 143a68c01..cb6efaedc 100644
--- a/gui/tailwind.config.ts
+++ b/gui/tailwind.config.ts
@@ -163,6 +163,7 @@ const config = {
'md-max': { raw: 'not (min-width: 1100px)' },
lg: '1300px',
xl: '1600px',
+ tall: { raw: '(min-height: 800px)'}
},
extend: {
colors: {