progress on filter panel

This commit is contained in:
MathieuG-P
2022-11-21 00:05:20 +01:00
parent f815695298
commit f9edf33097
7 changed files with 248 additions and 31 deletions
+19 -14
View File
@@ -1876,7 +1876,7 @@
},
"@types/archiver": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-5.3.1.tgz",
"resolved": "https://npm.zone51.tf/@types%2farchiver/-/archiver-5.3.1.tgz",
"integrity": "sha512-wKYZaSXaDvTZuInAWjCeGG7BEAgTWG2zZW0/f7IYFcoHB2X2d9lkVFnrOlXl3W6NrvO6Ml3FLLu8Uksyymcpnw==",
"dev": true,
"requires": {
@@ -2396,7 +2396,7 @@
},
"@types/yargs": {
"version": "17.0.13",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz",
"resolved": "https://registry.npmjs.org/@types%2fyargs/-/yargs-17.0.13.tgz",
"integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==",
"dev": true,
"requires": {
@@ -2998,7 +2998,7 @@
},
"archiver": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.1.tgz",
"resolved": "https://npm.zone51.tf/archiver/-/archiver-5.3.1.tgz",
"integrity": "sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==",
"requires": {
"archiver-utils": "^2.1.0",
@@ -3012,7 +3012,7 @@
},
"archiver-utils": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
"resolved": "https://npm.zone51.tf/archiver-utils/-/archiver-utils-2.1.0.tgz",
"integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
"requires": {
"glob": "^7.1.4",
@@ -4053,7 +4053,7 @@
},
"compress-commons": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.1.tgz",
"resolved": "https://npm.zone51.tf/compress-commons/-/compress-commons-4.1.1.tgz",
"integrity": "sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==",
"requires": {
"buffer-crc32": "^0.2.13",
@@ -4281,12 +4281,12 @@
},
"crc-32": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
"resolved": "https://npm.zone51.tf/crc-32/-/crc-32-1.2.2.tgz",
"integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
},
"crc32-stream": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
"resolved": "https://npm.zone51.tf/crc32-stream/-/crc32-stream-4.0.2.tgz",
"integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
"requires": {
"crc-32": "^1.2.0",
@@ -6895,7 +6895,7 @@
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"resolved": "https://npm.zone51.tf/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
"fs-extra": {
@@ -9447,7 +9447,7 @@
},
"lazystream": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
"resolved": "https://npm.zone51.tf/lazystream/-/lazystream-1.0.1.tgz",
"integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
"requires": {
"readable-stream": "^2.0.5"
@@ -11650,6 +11650,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"react-range": {
"version": "1.8.14",
"resolved": "https://registry.npmjs.org/react-range/-/react-range-1.8.14.tgz",
"integrity": "sha512-v2nyD5106rHf9dwHzq+WRlhCes83h1wJRHIMFjbZsYYsO6LF4mG/mR3cH7Cf+dkeHq65DItuqIbLn/3jjYjsHg=="
},
"react-refresh": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.12.0.tgz",
@@ -11758,7 +11763,7 @@
},
"readdir-glob": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.2.tgz",
"resolved": "https://npm.zone51.tf/readdir-glob/-/readdir-glob-1.1.2.tgz",
"integrity": "sha512-6RLVvwJtVwEDfPdn6X6Ille4/lxGl0ATOY4FN/B9nxQcgOazvvI0nodiD19ScKq0PvA/29VpaOQML36o5IzZWA==",
"requires": {
"minimatch": "^5.1.0"
@@ -11766,7 +11771,7 @@
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"resolved": "https://npm.zone51.tf/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"requires": {
"balanced-match": "^1.0.0"
@@ -11774,7 +11779,7 @@
},
"minimatch": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
"resolved": "https://npm.zone51.tf/minimatch/-/minimatch-5.1.0.tgz",
"integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
"requires": {
"brace-expansion": "^2.0.1"
@@ -12940,7 +12945,7 @@
},
"tar-stream": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
"resolved": "https://npm.zone51.tf/tar-stream/-/tar-stream-2.2.0.tgz",
"integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
"requires": {
"bl": "^4.0.3",
@@ -14130,7 +14135,7 @@
},
"zip-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
"resolved": "https://npm.zone51.tf/zip-stream/-/zip-stream-4.1.0.tgz",
"integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
"requires": {
"archiver-utils": "^2.1.0",
+1
View File
@@ -240,6 +240,7 @@
"react-colorful": "^5.6.1",
"react-dom": "^18.2.0",
"react-fitty": "^1.0.1",
"react-range": "^1.8.14",
"react-router-dom": "^6.3.0",
"react-visibility-sensor": "^5.1.1",
"regedit": "^5.1.1",
@@ -1,30 +1,211 @@
import { MapFilter, MapTag } from "shared/models/maps/beat-saver.model"
import {motion} from "framer-motion"
import { MutableRefObject, useRef } from "react"
import { MutableRefObject, useRef, useState } from "react"
import { MAP_TYPES } from "renderer/partials/map-tags/map-types"
import { MAP_STYLES } from "renderer/partials/map-tags/map-styles"
import { BsmCheckbox } from "../shared/bsm-checkbox.component"
import { diffColors } from "./map-item.component"
import { getTrackBackground, Range } from 'react-range';
import { useThemeColor } from "renderer/hooks/use-theme-color.hook"
import { getCorrectTextColor } from "renderer/helpers/correct-text-color"
import { hour_to_s, min_to_s } from "renderer/helpers/time-utils"
import dateFormat from "dateformat"
export type Props = {
className?: string,
ref?: MutableRefObject<undefined>
playlist?: boolean,
filter?: MapFilter
onChange?: () => void
onChange?: (filter: MapFilter) => void
}
export function FilterPanel({className, ref, playlist = false, filter, onChange}: Props) {
return !playlist ? (
<motion.div ref={ref} className={className} initial={{scale: 0}} animate={{scale: 1}} exit={{scale: 0}}>
<div className="w-full h-full">
<div className="flex flex-row flex-wrap grow">
{[...MAP_TYPES, ...MAP_STYLES].map(tag => (
<span>{tag}</span>
))}
const MIN_NPS = 0;
const MAX_NPS = 17;
const MIN_DURATION = 0;
const MAX_DURATION = min_to_s(30);
const [npss, setNpss] = useState<number[]>([filter?.minNps || MIN_NPS, filter?.maxNps || MAX_NPS]);
const [durations, setDurations] = useState<number[]>([filter?.minDuration || MIN_DURATION, filter?.maxDuration || MAX_DURATION]);
const firstColor = useThemeColor("first-color");
const thumbTextColor = getCorrectTextColor(firstColor);
const isTagEnabled = (tag: MapTag): boolean => {
return filter?.enabledTags?.some(t => t === tag) || filter?.excludedTags?.some(t => t === tag);
}
const isTagExcluded = (tag: MapTag): boolean => {
return filter?.excludedTags?.some(t => t === tag);
}
const secToTimer = (sec: number): string => {
const date = new Date(0);
date.setSeconds(sec);
return sec > 3600 ? dateFormat(date, "h:MM:ss") : dateFormat(date, "MM:ss");
}
const handleRangeChange = ([min, max]: number[]) => {
setNpss(() => [min, max]);
onChange?.({...filter, minNps: min, maxNps: max});
}
const handleRangeDurationChange = ([min, max]: number[]) => {
setDurations([min, max]);
onChange?.({...filter, minDuration: min, maxDuration: max});
}
console.log(hour_to_s(2));
return !playlist ? (
<motion.div ref={ref} className={className} initial={{opacity: 0}} animate={{opacity: 1}} exit={{opacity: 0}}>
<div className="w-full h-6 grid grid-cols-2 gap-x-10 px-4 mb-5 pt-2">
<div>
<Range
values={npss}
min={MIN_NPS}
max={MAX_NPS}
step={.1}
onChange={handleRangeChange}
renderTrack={({props, children}) => (
<div
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
className="w-full rounded-full h-1"
{...props}
style={{
...props.style,
background: getTrackBackground({
values: npss,
colors: ["#ccc", firstColor, "#ccc"],
min: MIN_NPS,
max: MAX_NPS
}),
}}
>
{children}
</div>
)}
renderThumb={({ index, props }) => (
<div
className="relative w-4 h-4 rounded-full shadow-center shadow-black brightness-150 flex justify-center outline-none"
{...props}
style={{
...props.style,
backgroundColor: firstColor
}}
>
<span className={`absolute top-[calc(100%+4px)] font-bold w-8 h-5 text-center rounded-md align-middle flex justify-center items-center shadow-sm shadow-black ${npss[index] === MAX_NPS ? "text-xl" : "text-sm"}`} style={{backgroundColor: firstColor, color: thumbTextColor}}>{
npss[index] === MAX_NPS ? "∞" : npss[index] === MIN_NPS ? "NPS" : npss[index].toFixed(1)
}</span>
</div>
)}
/>
</div>
<div>
<Range
values={durations}
min={MIN_DURATION}
max={MAX_DURATION}
step={5}
onChange={handleRangeDurationChange}
renderTrack={({props, children}) => (
<div
onMouseDown={props.onMouseDown}
onTouchStart={props.onTouchStart}
className="w-full rounded-full h-1"
{...props}
style={{
...props.style,
background: getTrackBackground({
values: durations,
colors: ["#ccc", firstColor, "#ccc"],
min: MIN_DURATION,
max: MAX_DURATION
}),
}}
>
{children}
</div>
)}
renderThumb={({ index, props }) => (
<div
className="relative w-4 h-4 rounded-full shadow-center shadow-black brightness-150 flex justify-center outline-none"
{...props}
style={{
...props.style,
backgroundColor: firstColor
}}
>
<span className={`absolute top-[calc(100%+4px)] font-bold min-w-[40px] h-5 text-center rounded-md align-middle flex justify-center items-center shadow-sm shadow-black ${durations[index] === MAX_DURATION ? "text-xl" : "text-sm"}`} style={{backgroundColor: firstColor, color: thumbTextColor}}>{
durations[index] === MAX_DURATION ? "∞" : durations[index] === MIN_DURATION ? "Durée" : secToTimer(durations[index])
}</span>
</div>
)}
/>
</div>
</div>
</div>
</motion.div>
) : (
<div className="w-full h-full flex gap-x-2">
<section className="shrink-0">
<h2 className="mb-1 uppercase text-sm">general</h2>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.automapper}/>
<span className="grow">AI</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.ranked}/>
<span className="grow">Rancked</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.curated}/>
<span className="grow">Curated</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.verified}/>
<span className="grow">Verified Mapper</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.fullSpread}/>
<span className="grow">Full Spread</span>
</div>
<h2 className="my-1 uppercase text-sm">requirements</h2>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.chroma}/>
<span className="grow">Chroma</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.noodle}/>
<span className="grow">Noodle</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.me}/>
<span className="grow">Mapping Extensions</span>
</div>
<div className="flex justify-start items-center h-6 z-20 relative py-0.5">
<BsmCheckbox className="h-full aspect-square relative bg-inherit mr-1" disabled={filter?.cinema}/>
<span className="grow">Cinema</span>
</div>
</section>
<section className="grow">
<h2 className="uppercase text-sm mb-1">TAGS</h2>
<div className="w-full flex flex-row flex-wrap items-start justify-start content-start gap-1 mb-2">
{MAP_TYPES.map(tag => (
<span key={tag} className={`text-sm text-black rounded-md px-1 font-bold cursor-pointer ${(!isTagEnabled(tag)) && "opacity-40 hover:opacity-100"}`} style={{backgroundColor: isTagExcluded(tag) ? diffColors.Expert : diffColors.Normal}}>{tag}</span>
))}
</div>
<div className="w-full flex flex-row flex-wrap items-start justify-start content-start gap-1">
{MAP_STYLES.map(tag => (
<span key={tag} className={`text-sm text-black rounded-md px-1 font-bold cursor-pointer ${(!isTagEnabled(tag)) && "opacity-40 hover:opacity-100"}`} style={{backgroundColor: isTagExcluded(tag) ? diffColors.Expert : diffColors.Easy}}>{tag}</span>
))}
</div>
</section>
</div>
</motion.div>
) : (
<></>
)
)
}
@@ -214,7 +214,7 @@ export function MapItem({hash, title, autor, songAutor, coverUrl, songUrl, autor
)
}
const diffColors: Record<BsvMapDifficultyType, string> = {
export const diffColors: Record<BsvMapDifficultyType, string> = {
Easy: "#00FF9E",
Normal: "#00FFFF",
Hard: "#FFA700",
@@ -5,6 +5,7 @@ import { LocalMapsListPanel } from "./local-maps-list-panel.component"
import {AnimatePresence, motion} from "framer-motion"
import { BsmDropdownButton } from "../shared/bsm-dropdown-button.component"
import { FilterPanel } from "./filter-panel.component"
import { MapFilter } from "shared/models/maps/beat-saver.model"
type Props = {
oneBlock?: boolean,
@@ -14,6 +15,20 @@ type Props = {
export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
const [tabIndex, setTabIndex] = useState(0);
const [filter, setFilter] = useState<MapFilter>({
automapper: false,
chroma: false,
cinema: false,
curated: false,
enabledTags: [],
excludedTags: [],
fullSpread: false,
me: false,
noodle: false,
ranked: false,
verified: false,
})
return (
<>
@@ -23,10 +38,10 @@ export function MapsPlaylistsPanel({version, oneBlock = false}: Props) {
<div className="h-full rounded-full bg-main-color-2 grow p-[6px]">
<input type="text" name="" id="" className="h-full w-full bg-main-color-1 rounded-full px-2" placeholder="Rechercher" />
</div>
<BsmDropdownButton className="h-full relative z-10 flex justify-center" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1" icon="search" text="Filtres" withBar={false}>
<FilterPanel className="absolute top-[calc(100%+3px)] bg-main-color-3 origin-top w-[400px]"/>
<BsmDropdownButton className="h-full relative z-[1] flex justify-center" buttonClassName="flex items-center justify-center h-full rounded-full px-2 py-1" icon="search" text="Filtres" withBar={false}>
<FilterPanel className="absolute top-[calc(100%+3px)] bg-main-color-3 origin-top w-[500px] h-fit p-2 rounded-md shadow-md shadow-black" filter={{excludedTags: ["Accuracy"]}}/>
</BsmDropdownButton>
<BsmDropdownButton className="h-full flex aspect-square relative rounded-full z-10" buttonClassName="rounded-full h-full w-full p-[6px]" icon="three-dots" withBar={false}>
<BsmDropdownButton className="h-full flex aspect-square relative rounded-full z-[1]" buttonClassName="rounded-full h-full w-full p-[6px]" icon="three-dots" withBar={false}>
<></>
</BsmDropdownButton>
</nav>
+14
View File
@@ -0,0 +1,14 @@
const SECONDS_IN_MINUTE = 60;
const MINUTES_IN_HOUR = 60;
export function min_to_s(minutes: number): number{
return minutes * SECONDS_IN_MINUTE;
}
export function hour_to_min(hours: number): number{
return hours * MINUTES_IN_HOUR;
}
export function hour_to_s(hours: number): number{
return hours * min_to_s(MINUTES_IN_HOUR);
}
@@ -152,6 +152,7 @@ export interface MapFilter {
chroma?: boolean,
cinema?: boolean,
noodle?: boolean,
me?: boolean,
curated?: boolean,
verified?: boolean,
from?: number,