diff --git a/src/common/MultiselectMenu/Dropdown/Dropdown.less b/src/common/MultiselectMenu/Dropdown/Dropdown.less
new file mode 100644
index 000000000..1e15419ef
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Dropdown.less
@@ -0,0 +1,30 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+.dropdown {
+ background: var(--modal-background-color);
+ display: none;
+ position: absolute;
+ width: 100%;
+ top: 100%;
+ left: 0;
+ z-index: 10;
+ box-shadow: var(--outer-glow);
+ border-radius: var(--border-radius);
+ overflow: hidden;
+
+ &.open {
+ display: block;
+ }
+
+ .back-button {
+ display: flex;
+ align-items: center;
+ gap: 0 0.5rem;
+ padding: 0.75rem;
+ color: var(--primary-foreground-color);
+
+ .back-button-icon {
+ width: 1.5rem;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Dropdown.tsx b/src/common/MultiselectMenu/Dropdown/Dropdown.tsx
new file mode 100644
index 000000000..1c09de925
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Dropdown.tsx
@@ -0,0 +1,53 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import React from 'react';
+import Button from 'stremio/common/Button';
+import { useTranslation } from 'react-i18next';
+import classNames from 'classnames';
+import Option from './Option';
+import Icon from '@stremio/stremio-icons/react';
+import styles from './Dropdown.less';
+
+type Props = {
+ options: MultiselectMenuOption[];
+ selectedOption?: MultiselectMenuOption | null;
+ menuOpen: boolean | (() => void);
+ level: number;
+ setLevel: (level: number) => void;
+ onSelect: (option: MultiselectMenuOption) => void;
+};
+
+const Dropdown = ({ level, setLevel, options, onSelect, selectedOption, menuOpen }: Props) => {
+ const { t } = useTranslation();
+ const onBackButtonClick = () => {
+ setLevel(level - 1);
+ };
+
+ return (
+
+ {
+ level > 0 ?
+
+ : null
+ }
+ {
+ options
+ .filter((option: MultiselectMenuOption) => !option.hidden)
+ .map((option: MultiselectMenuOption) => (
+
+ ))
+
+
+ }
+
+ );
+};
+
+export default Dropdown;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Option/Option.less b/src/common/MultiselectMenu/Dropdown/Option/Option.less
new file mode 100644
index 000000000..067934dfc
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Option/Option.less
@@ -0,0 +1,30 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+.option {
+ font-size: var(--font-size-normal);
+ color: var(--primary-foreground-color);
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ padding: 1rem;
+
+ .label {
+ flex: 1;
+ color: var(--primary-foreground-color);
+ }
+
+ .icon {
+ flex: none;
+ // display: none;
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 100%;
+ margin-left: 1rem;
+ background-color: var(--secondary-accent-color);
+ opacity: 1;
+ }
+
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.15);
+ }
+}
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Option/Option.tsx b/src/common/MultiselectMenu/Dropdown/Option/Option.tsx
new file mode 100644
index 000000000..7ade6dfa5
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Option/Option.tsx
@@ -0,0 +1,42 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import React, { useMemo } from 'react';
+import classNames from 'classnames';
+import Button from 'stremio/common/Button';
+import styles from './Option.less';
+import Icon from '@stremio/stremio-icons/react';
+
+type Props = {
+ option: MultiselectMenuOption;
+ selectedOption?: MultiselectMenuOption | null;
+ onSelect: (option: MultiselectMenuOption) => void;
+};
+
+const Option = ({ option, selectedOption, onSelect }: Props) => {
+ // consider using option.id === selectedOption?.id instead
+ const selected = useMemo(() => option?.value === selectedOption?.value, [option, selectedOption]);
+
+ return (
+
+ );
+};
+
+export default Option;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/Option/index.ts b/src/common/MultiselectMenu/Dropdown/Option/index.ts
new file mode 100644
index 000000000..6004f7754
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/Option/index.ts
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import Option from './Option';
+
+export default Option;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/Dropdown/index.ts b/src/common/MultiselectMenu/Dropdown/index.ts
new file mode 100644
index 000000000..ce3622a25
--- /dev/null
+++ b/src/common/MultiselectMenu/Dropdown/index.ts
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import Dropdown from './Dropdown';
+
+export default Dropdown;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/MultiselectMenu.less b/src/common/MultiselectMenu/MultiselectMenu.less
new file mode 100644
index 000000000..156e2035d
--- /dev/null
+++ b/src/common/MultiselectMenu/MultiselectMenu.less
@@ -0,0 +1,34 @@
+
+@border-radius: 2.75rem;
+
+.multiselect-menu {
+ background-color: var(--overlay-color);
+ position: relative;
+ min-width: 150px;
+ overflow: visible;
+ border-radius: @border-radius;
+ &.disabled {
+ pointer-events: none;
+ opacity: 0.3;
+ }
+
+ .multiselect-button {
+ color: var(--primary-foreground-color);
+ background-color: rgba(255, 255, 255, 0.05);
+ padding: 0.75rem 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 0 0.5rem;
+ border-radius: @border-radius;
+
+ .icon {
+ width: 1.5rem;
+ color: var(--primary-foreground-color);
+
+ &.open {
+ transform: rotate(180deg);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/MultiselectMenu.tsx b/src/common/MultiselectMenu/MultiselectMenu.tsx
new file mode 100644
index 000000000..5eb073eca
--- /dev/null
+++ b/src/common/MultiselectMenu/MultiselectMenu.tsx
@@ -0,0 +1,56 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import React from 'react';
+import Button from 'stremio/common/Button';
+import useBinaryState from 'stremio/common/useBinaryState';
+import Dropdown from './Dropdown';
+import classNames from 'classnames';
+import Icon from '@stremio/stremio-icons/react';
+import styles from './MultiselectMenu.less';
+
+type Props = {
+ className?: string,
+ title?: string;
+ options: MultiselectMenuOption[];
+ selectedOption?: MultiselectMenuOption;
+ onSelect: () => void;
+};
+
+const MultiselectMenu = ({ className, title, options, selectedOption, onSelect }: Props) => {
+ const [menuOpen, , closeMenu, toggleMenu] = useBinaryState(false);
+ const multiselectMenuRef = React.useRef(null);
+ const [level, setLevel] = React.useState(0);
+
+ const onOptionSelect = (option: MultiselectMenuOption) => {
+ option.level ? setLevel(level + 1) : onSelect(), closeMenu();
+ };
+
+ return (
+
+
+ {
+ menuOpen ?
+
+ : null
+ }
+
+ );
+};
+
+export default MultiselectMenu;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/index.ts b/src/common/MultiselectMenu/index.ts
new file mode 100644
index 000000000..e526218cd
--- /dev/null
+++ b/src/common/MultiselectMenu/index.ts
@@ -0,0 +1,5 @@
+// Copyright (C) 2017-2024 Smart code 203358507
+
+import MultiselectMenu from './MultiselectMenu';
+
+export default MultiselectMenu;
\ No newline at end of file
diff --git a/src/common/MultiselectMenu/types.d.ts b/src/common/MultiselectMenu/types.d.ts
new file mode 100644
index 000000000..a1724ab9b
--- /dev/null
+++ b/src/common/MultiselectMenu/types.d.ts
@@ -0,0 +1,9 @@
+type MultiselectMenuOption = {
+ id?: number;
+ label: string;
+ value: string;
+ destination?: string;
+ default?: boolean;
+ hidden?: boolean;
+ level?: MultiselectMenuOption[];
+};
\ No newline at end of file
diff --git a/src/common/index.js b/src/common/index.js
index 8046dab15..c582a13ca 100644
--- a/src/common/index.js
+++ b/src/common/index.js
@@ -15,6 +15,7 @@ const MetaPreview = require('./MetaPreview');
const MetaRow = require('./MetaRow');
const ModalDialog = require('./ModalDialog');
const Multiselect = require('./Multiselect');
+const { default: MultiselectMenu } = require('./MultiselectMenu');
const { HorizontalNavBar, VerticalNavBar } = require('./NavBar');
const PaginationInput = require('./PaginationInput');
const PlayIconCircleCentered = require('./PlayIconCircleCentered');
@@ -63,6 +64,7 @@ module.exports = {
MetaRow,
ModalDialog,
Multiselect,
+ MultiselectMenu,
HorizontalNavBar,
VerticalNavBar,
PaginationInput,
diff --git a/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js b/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js
index d75080d01..6ecc2c815 100644
--- a/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js
+++ b/src/routes/MetaDetails/VideosList/SeasonsBar/SeasonsBar.js
@@ -5,9 +5,10 @@ const PropTypes = require('prop-types');
const classnames = require('classnames');
const { t } = require('i18next');
const { default: Icon } = require('@stremio/stremio-icons/react');
-const { Button, Multiselect } = require('stremio/common');
+const { Button } = require('stremio/common');
const SeasonsBarPlaceholder = require('./SeasonsBarPlaceholder');
const styles = require('./styles');
+const { MultiselectMenu } = require('stremio/common');
const SeasonsBar = ({ className, seasons, season, onSelect }) => {
const options = React.useMemo(() => {
@@ -16,8 +17,8 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => {
label: season > 0 ? `${t('SEASON')} ${season}` : t('SPECIAL')
}));
}, [seasons]);
- const selected = React.useMemo(() => {
- return [String(season)];
+ const selectedSeason = React.useMemo(() => {
+ return { label: String(season), value: String(season) };
}, [season]);
const prevNextButtonOnClick = React.useCallback((event) => {
if (typeof onSelect === 'function') {
@@ -61,12 +62,11 @@ const SeasonsBar = ({ className, seasons, season, onSelect }) => {
Prev
- 0 ? `${t('SEASON')} ${season}` : t('SPECIAL')}
- direction={'bottom-left'}
options={options}
- selected={selected}
+ title={season > 0 ? `${t('SEASON')} ${season}` : t('SPECIAL')}
+ selectedOption={selectedSeason}
onSelect={seasonOnSelect}
/>