mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-24 19:11:49 +01:00
series implemented (with jank) + readme update
Co-authored-by: William Oldham <wegg7250@gmail.com> Co-authored-by: James Hawkins <jhawki2005@gmail.com>
This commit is contained in:
parent
0287bdad57
commit
f66637a185
129
src/components/Dropdown.tsx
Normal file
129
src/components/Dropdown.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import { Icon, Icons } from "components/Icon";
|
||||
import React, {
|
||||
MouseEventHandler,
|
||||
SyntheticEvent,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
|
||||
import { Backdrop, useBackdrop } from "components/layout/Backdrop";
|
||||
import { ButtonControl } from "./buttons/ButtonControl";
|
||||
|
||||
export interface OptionItem {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface DropdownProps {
|
||||
open: boolean;
|
||||
setOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
selectedItem: string;
|
||||
setSelectedItem: (value: string) => void;
|
||||
options: Array<OptionItem>;
|
||||
}
|
||||
|
||||
export interface OptionProps {
|
||||
option: OptionItem;
|
||||
onClick: MouseEventHandler<HTMLDivElement>;
|
||||
tabIndex?: number;
|
||||
}
|
||||
|
||||
function Option({ option, onClick, tabIndex }: OptionProps) {
|
||||
return (
|
||||
<div
|
||||
className="text-denim-700 flex h-10 cursor-pointer items-center space-x-2 px-4 py-2 text-left transition-colors hover:text-white"
|
||||
onClick={onClick}
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
<input type="radio" className="hidden" id={option.id} />
|
||||
<label htmlFor={option.id} className="cursor-pointer ">
|
||||
<div className="item">{option.name}</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const Dropdown = React.forwardRef<HTMLDivElement, DropdownProps>(
|
||||
(props: DropdownProps, ref) => {
|
||||
const [setBackdrop, backdropProps, highlightedProps] = useBackdrop();
|
||||
const [delayedSelectedId, setDelayedSelectedId] = useState(
|
||||
props.selectedItem
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let id: NodeJS.Timeout;
|
||||
|
||||
if (props.open) {
|
||||
setDelayedSelectedId(props.selectedItem);
|
||||
} else {
|
||||
id = setTimeout(() => {
|
||||
setDelayedSelectedId(props.selectedItem);
|
||||
}, 200);
|
||||
}
|
||||
return () => {
|
||||
if (id) clearTimeout(id);
|
||||
};
|
||||
/* eslint-disable-next-line */
|
||||
}, [props.open]);
|
||||
|
||||
const selectedItem: OptionItem =
|
||||
props.options.find((opt) => opt.id === props.selectedItem) ||
|
||||
props.options[0];
|
||||
|
||||
useEffect(() => {
|
||||
setBackdrop(props.open);
|
||||
/* eslint-disable-next-line */
|
||||
}, [props.open]);
|
||||
|
||||
const onOptionClick = (e: SyntheticEvent, option: OptionItem) => {
|
||||
e.stopPropagation();
|
||||
props.setSelectedItem(option.id);
|
||||
props.setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="min-w-[140px]"
|
||||
onClick={() => props.setOpen((open) => !open)}
|
||||
>
|
||||
<div
|
||||
ref={ref}
|
||||
className="relative w-full sm:w-auto"
|
||||
{...highlightedProps}
|
||||
>
|
||||
<ButtonControl
|
||||
{...props}
|
||||
className="sm:justify-left bg-bink-200 hover:bg-bink-300 relative z-20 flex h-10 w-full items-center justify-center space-x-2 rounded-[20px] px-4 py-2 text-white"
|
||||
>
|
||||
<span className="flex-1">{selectedItem.name}</span>
|
||||
<Icon
|
||||
icon={Icons.CHEVRON_DOWN}
|
||||
className={`transition-transform ${
|
||||
props.open ? "rotate-180" : ""
|
||||
}`}
|
||||
/>
|
||||
</ButtonControl>
|
||||
<div
|
||||
className={`bg-denim-300 options-scrollbar absolute top-0 z-10 w-full overflow-y-auto rounded-[20px] pt-[40px] transition-all duration-200 ${
|
||||
props.open
|
||||
? "block max-h-60 opacity-100"
|
||||
: "invisible max-h-0 opacity-0"
|
||||
}`}
|
||||
>
|
||||
{props.options
|
||||
.filter((opt) => opt.id !== delayedSelectedId)
|
||||
.map((opt) => (
|
||||
<Option
|
||||
option={opt}
|
||||
key={opt.id}
|
||||
onClick={(e) => onOptionClick(e, opt)}
|
||||
tabIndex={props.open ? 0 : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Backdrop onClick={() => props.setOpen(false)} {...backdropProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
@ -1,4 +1,4 @@
|
||||
import { versionedStoreBuilder } from 'utils/storage';
|
||||
import { versionedStoreBuilder } from "utils/storage";
|
||||
|
||||
/*
|
||||
version 0
|
||||
@ -33,19 +33,19 @@ version 0
|
||||
*/
|
||||
|
||||
export const VideoProgressStore = versionedStoreBuilder()
|
||||
.setKey('video-progress')
|
||||
.addVersion({
|
||||
version: 0,
|
||||
})
|
||||
.addVersion({
|
||||
version: 1,
|
||||
migrate(data: any) {
|
||||
// TODO migration
|
||||
},
|
||||
create() {
|
||||
return {
|
||||
items: [],
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.setKey("video-progress")
|
||||
.addVersion({
|
||||
version: 0,
|
||||
})
|
||||
.addVersion({
|
||||
version: 1,
|
||||
migrate(data: any) {
|
||||
// TODO migration
|
||||
},
|
||||
create() {
|
||||
return {
|
||||
items: [],
|
||||
};
|
||||
},
|
||||
})
|
||||
.build();
|
||||
|
Loading…
Reference in New Issue
Block a user