Skip to content

Commit

Permalink
Merge pull request #778 from Stremio/feat/multiselect-menu-scroll-to-…
Browse files Browse the repository at this point in the history
…view

feat(MultiSelectMenu): scroll into view
  • Loading branch information
kKaskak authored Jan 3, 2025
2 parents b5d073b + a4dd1e2 commit fe3aade
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 27 deletions.
70 changes: 47 additions & 23 deletions src/common/MultiselectMenu/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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]);

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
useEffect(() => {
if (menuOpen && selectedOption && containerRef.current) {
const selectedNode = optionsRef.current.get(selectedOption.value);
if (selectedNode) {
selectedNode.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}
{
options
.filter((option: MultiselectMenuOption) => !option.hidden)
.map((option: MultiselectMenuOption, index) => (
<Option
key={index}
option={option}
onSelect={onSelect}
selectedOption={selectedOption}
/>
))
}
}, [menuOpen, selectedOption]);

return (
<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) => (
<Option
key={option.id}
ref={handleSetOptionRef(option.value)}
option={option}
onSelect={onSelect}
selectedOption={selectedOption}
/>
))
}
</div>
);
Expand Down
10 changes: 6 additions & 4 deletions src/common/MultiselectMenu/Dropdown/Option/Option.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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]);

Expand All @@ -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}
Expand All @@ -32,7 +33,6 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
selected && !option.level ?
<div className={styles['icon']} />
: null

}
{
option.level ?
Expand All @@ -41,6 +41,8 @@ const Option = ({ option, selectedOption, onSelect }: Props) => {
}
</Button>
);
};
});

Option.displayName = 'Option';

export default Option;

0 comments on commit fe3aade

Please sign in to comment.