mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 21:27:05 +00:00
Merge pull request #778 from Stremio/feat/multiselect-menu-scroll-to-view
feat(MultiSelectMenu): scroll into view
This commit is contained in:
commit
fe3aade35c
2 changed files with 53 additions and 27 deletions
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (C) 2017-2024 Smart code 203358507
|
||||
|
||||
import React from 'react';
|
||||
import React, { useRef, useEffect, useCallback } from 'react';
|
||||
import Button from 'stremio/common/Button';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import classNames from 'classnames';
|
||||
|
|
@ -19,33 +19,57 @@ type Props = {
|
|||
|
||||
const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const optionsRef = useRef(new Map());
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const onBackButtonClick = () => {
|
||||
const handleSetOptionRef = useCallback((value: number) => (node: HTMLButtonElement | null) => {
|
||||
if (node) {
|
||||
optionsRef.current.set(value, node);
|
||||
} else {
|
||||
optionsRef.current.delete(value);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleBackClick = useCallback(() => {
|
||||
setLevel(level - 1);
|
||||
};
|
||||
}, [setLevel, level]);
|
||||
|
||||
useEffect(() => {
|
||||
if (menuOpen && selectedOption && containerRef.current) {
|
||||
const selectedNode = optionsRef.current.get(selectedOption.value);
|
||||
if (selectedNode) {
|
||||
selectedNode.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'nearest'
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [menuOpen, selectedOption]);
|
||||
|
||||
return (
|
||||
<div className={classNames(styles['dropdown'], { [styles['open']]: menuOpen })} role={'listbox'}>
|
||||
{
|
||||
level > 0 ?
|
||||
<Button className={styles['back-button']} onClick={onBackButtonClick}>
|
||||
<Icon name={'caret-left'} className={styles['back-button-icon']} />
|
||||
{t('BACK')}
|
||||
</Button>
|
||||
: null
|
||||
<div
|
||||
className={classNames(styles['dropdown'], { [styles['open']]: menuOpen })}
|
||||
role={'listbox'}
|
||||
ref={containerRef}
|
||||
>
|
||||
{level > 0 ?
|
||||
<Button className={styles['back-button']} onClick={handleBackClick}>
|
||||
<Icon name={'caret-left'} className={styles['back-button-icon']} />
|
||||
{t('BACK')}
|
||||
</Button>
|
||||
: null
|
||||
}
|
||||
{
|
||||
options
|
||||
.filter((option: MultiselectMenuOption) => !option.hidden)
|
||||
.map((option: MultiselectMenuOption, index) => (
|
||||
<Option
|
||||
key={index}
|
||||
option={option}
|
||||
onSelect={onSelect}
|
||||
selectedOption={selectedOption}
|
||||
/>
|
||||
))
|
||||
|
||||
{options
|
||||
.filter((option: MultiselectMenuOption) => !option.hidden)
|
||||
.map((option: MultiselectMenuOption) => (
|
||||
<Option
|
||||
key={option.id}
|
||||
ref={handleSetOptionRef(option.value)}
|
||||
option={option}
|
||||
onSelect={onSelect}
|
||||
selectedOption={selectedOption}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright (C) 2017-2024 Smart code 203358507
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, forwardRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import Button from 'stremio/common/Button';
|
||||
import styles from './Option.less';
|
||||
|
|
@ -12,7 +12,7 @@ type Props = {
|
|||
onSelect: (value: number) => void;
|
||||
};
|
||||
|
||||
const Option = ({ option, selectedOption, onSelect }: Props) => {
|
||||
const Option = forwardRef<HTMLButtonElement, Props>(({ option, selectedOption, onSelect }, ref) => {
|
||||
// consider using option.id === selectedOption?.id instead
|
||||
const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]);
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
|
|||
|
||||
return (
|
||||
<Button
|
||||
ref={ref}
|
||||
className={classNames(styles['option'], { [styles['selected']]: selected })}
|
||||
key={option.id}
|
||||
onClick={handleClick}
|
||||
|
|
@ -32,7 +33,6 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
|
|||
selected && !option.level ?
|
||||
<div className={styles['icon']} />
|
||||
: null
|
||||
|
||||
}
|
||||
{
|
||||
option.level ?
|
||||
|
|
@ -41,6 +41,8 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
|
|||
}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
Option.displayName = 'Option';
|
||||
|
||||
export default Option;
|
||||
|
|
|
|||
Loading…
Reference in a new issue