Update to the playlist picker
All checks were successful
Deploy to production / build (push) Successful in 20s
All checks were successful
Deploy to production / build (push) Successful in 20s
This commit is contained in:
@@ -24,6 +24,7 @@ import 'react-fontpicker-ts/dist/index.css';
|
||||
import '@gfazioli/mantine-border-animate/styles.css';
|
||||
import '@gfazioli/mantine-parallax/styles.css';
|
||||
import '@gfazioli/mantine-text-animate/styles.css';
|
||||
import '@gfazioli/mantine-reflection/styles.css';
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
||||
import { Homepage, Settings } from 'pages';
|
||||
import FontPicker from 'react-fontpicker-ts';
|
||||
|
||||
@@ -6,8 +6,8 @@ import { useEffect, useState } from 'react';
|
||||
import { FiChevronRight } from 'react-icons/fi';
|
||||
import { textClasses } from 'styles';
|
||||
import type { Theme, ThemePlaylist } from 'types';
|
||||
import { ThemePlaylistCard } from './components/ThemePlaylistCard';
|
||||
import { ThemePlaylistModal } from './components/ThemePlaylistModal';
|
||||
import { ThemePlaylistStack } from './components/ThemePlaylistStack';
|
||||
|
||||
export function ThemePlaylists() {
|
||||
const themesEndpoint = 'api/themes';
|
||||
@@ -134,7 +134,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
)}
|
||||
<Box>
|
||||
<ThemePlaylistCard
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={{
|
||||
id: undefined,
|
||||
name: 'All',
|
||||
@@ -165,7 +165,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
)}
|
||||
<Box>
|
||||
<ThemePlaylistCard
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={{
|
||||
id: undefined,
|
||||
name: 'Backgrounds',
|
||||
@@ -196,7 +196,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
)}
|
||||
<Box>
|
||||
<ThemePlaylistCard
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={{
|
||||
id: undefined,
|
||||
name: 'No Backgrounds',
|
||||
@@ -227,7 +227,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
)}
|
||||
<Box>
|
||||
<ThemePlaylistCard
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={{
|
||||
id:
|
||||
themePlaylists?.find(
|
||||
@@ -289,7 +289,7 @@ export function ThemePlaylists() {
|
||||
/>
|
||||
)}
|
||||
<Box>
|
||||
<ThemePlaylistCard
|
||||
<ThemePlaylistStack
|
||||
cardPlaylist={cardPlaylist}
|
||||
handleOpen={handleOpen}
|
||||
allThemes={allThemeData}
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
Group,
|
||||
Overlay,
|
||||
ScrollArea,
|
||||
Stack,
|
||||
Text,
|
||||
} from '@mantine/core';
|
||||
import { ActionIcon, Button, Overlay, ScrollArea, Stack, Text } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { DeleteItemModal } from 'components';
|
||||
import { useCommonHooks } from 'hooks';
|
||||
@@ -33,7 +25,7 @@ export function ThemePlaylistCard({
|
||||
cardPlaylist?.themeList?.name === 'Time of Day' ? '[]' : cardPlaylist.themeList,
|
||||
);
|
||||
return (
|
||||
<Group
|
||||
<Stack
|
||||
gap={0}
|
||||
align='center'
|
||||
justify='center'
|
||||
@@ -212,6 +204,6 @@ export function ThemePlaylistCard({
|
||||
)}
|
||||
</Stack>
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
import { Parallax } from '@gfazioli/mantine-parallax';
|
||||
import { Reflection } from '@gfazioli/mantine-reflection';
|
||||
import { ActionIcon, Box, Group, Stack, Text } from '@mantine/core';
|
||||
import { useDebouncedCallback, useDisclosure } from '@mantine/hooks';
|
||||
import { DeleteItemModal } from 'components';
|
||||
import { useCommonHooks } from 'hooks';
|
||||
import { useState } from 'react';
|
||||
import { FiEdit, FiTrash } from 'react-icons/fi';
|
||||
import { textClasses } from 'styles';
|
||||
import type { Theme, ThemePlaylist } from 'types';
|
||||
import { ThemeCard } from '../../Themes';
|
||||
|
||||
export function ThemePlaylistStack({
|
||||
cardPlaylist,
|
||||
handleOpen,
|
||||
allThemes,
|
||||
}: {
|
||||
cardPlaylist: any;
|
||||
handleOpen: (cardPlaylist: ThemePlaylist) => void;
|
||||
allThemes: Theme[];
|
||||
}) {
|
||||
const [hover, setHover] = useState(false);
|
||||
const { setThemeName, setCurrentThemePlaylist, handleDelete, currentUser } =
|
||||
useCommonHooks();
|
||||
const [indexOffset, setIndexOffset] = useState(0);
|
||||
const defaultLists = ['All', 'Backgrounds', 'No Backgrounds'];
|
||||
const [modalOpened, { open: openModal, close: closeModal }] = useDisclosure(false);
|
||||
const themeList = JSON.parse(
|
||||
cardPlaylist?.themeList?.name === 'Time of Day' ? '[]' : cardPlaylist.themeList,
|
||||
);
|
||||
|
||||
const missingTheme = {
|
||||
id: undefined,
|
||||
name: 'default',
|
||||
backgroundImage: 'none',
|
||||
backgroundColor: '#EEEEEE',
|
||||
searchColor: '#00000033',
|
||||
primaryColor: '#222222',
|
||||
accentColor: '#5c5c5c',
|
||||
borderColor: '#5c5c5c',
|
||||
randomEnabled: 1,
|
||||
textShadowsEnabled: 0,
|
||||
appShadowsEnabled: 0,
|
||||
themeFont: currentUser.defaultFont,
|
||||
};
|
||||
|
||||
const presentationProps = {
|
||||
initialRotationX: 60,
|
||||
initialRotationY: 0,
|
||||
initialRotationZ: 0,
|
||||
initialSkewX: -30,
|
||||
initialSkewY: 30,
|
||||
disabled: true,
|
||||
};
|
||||
|
||||
const handleMouseWheel = useDebouncedCallback((e: any) => {
|
||||
e.stopPropagation();
|
||||
if (e.deltaY > 0) {
|
||||
setIndexOffset((indexOffset % themeList.length) - 1);
|
||||
} else {
|
||||
setIndexOffset((indexOffset % themeList.length) + 1);
|
||||
}
|
||||
}, 75);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
gap={0}
|
||||
w={300}
|
||||
pos={'relative'}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
style={{
|
||||
background: hover
|
||||
? 'radial-gradient(var(--mantine-color-search-0), transparent)'
|
||||
: undefined,
|
||||
cursor: 'pointer',
|
||||
borderRadius: '100%',
|
||||
}}
|
||||
onClick={() => {
|
||||
setCurrentThemePlaylist(cardPlaylist.name);
|
||||
setThemeName('random');
|
||||
}}
|
||||
>
|
||||
{modalOpened && (
|
||||
<DeleteItemModal
|
||||
modalOpened={modalOpened}
|
||||
handleClose={closeModal}
|
||||
handleDelete={() => handleDelete(cardPlaylist, 'api/themePlaylists')}
|
||||
title='Delete Theme Playlist'
|
||||
body={
|
||||
<Stack gap={0} w={'100%'} h={'150px'} align='center'>
|
||||
<Text>Are you sure you want to delete this Theme Playlist?</Text>
|
||||
<Text
|
||||
className={textClasses.deleteItem}
|
||||
classNames={textClasses}
|
||||
>{`"${cardPlaylist.name}"`}</Text>
|
||||
<Text c='red'>This action cannot be undone!</Text>
|
||||
</Stack>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{cardPlaylist?.name === 'Time of Day' ? (
|
||||
<Reflection>
|
||||
<Stack
|
||||
w='100%'
|
||||
h='200px'
|
||||
justify='center'
|
||||
pos='relative'
|
||||
gap={16}
|
||||
style={{ overflow: 'hidden' }}
|
||||
>
|
||||
<Parallax
|
||||
{...presentationProps}
|
||||
style={{ position: 'absolute', top: -60, zIndex: 3 }}
|
||||
>
|
||||
<ThemeCard
|
||||
cardTheme={
|
||||
allThemes.find((theme: Theme) => theme.id === Number(themeList[0])) ||
|
||||
missingTheme
|
||||
}
|
||||
allThemes={allThemes}
|
||||
themePlaylists={[]}
|
||||
setSelectedTheme={() => {}}
|
||||
openThemeModal={() => {}}
|
||||
enabled={false}
|
||||
management={false}
|
||||
/>
|
||||
</Parallax>
|
||||
<Parallax
|
||||
{...presentationProps}
|
||||
style={{ position: 'absolute', top: -60, zIndex: 2 }}
|
||||
>
|
||||
<ThemeCard
|
||||
cardTheme={
|
||||
allThemes.find((theme: Theme) => theme.id === Number(themeList[1])) ||
|
||||
missingTheme
|
||||
}
|
||||
allThemes={allThemes}
|
||||
themePlaylists={[]}
|
||||
setSelectedTheme={() => {}}
|
||||
openThemeModal={() => {}}
|
||||
enabled={false}
|
||||
management={false}
|
||||
/>
|
||||
</Parallax>
|
||||
<Parallax
|
||||
{...presentationProps}
|
||||
style={{ position: 'absolute', top: -60, zIndex: 1 }}
|
||||
>
|
||||
<ThemeCard
|
||||
cardTheme={
|
||||
allThemes.find((theme: Theme) => theme.id === Number(themeList[2])) ||
|
||||
missingTheme
|
||||
}
|
||||
allThemes={allThemes}
|
||||
themePlaylists={[]}
|
||||
setSelectedTheme={() => {}}
|
||||
openThemeModal={() => {}}
|
||||
enabled={false}
|
||||
management={false}
|
||||
/>
|
||||
</Parallax>
|
||||
<Parallax
|
||||
{...presentationProps}
|
||||
style={{ position: 'absolute', top: -60, zIndex: 0 }}
|
||||
>
|
||||
<ThemeCard
|
||||
cardTheme={
|
||||
allThemes.find((theme: Theme) => theme.id === Number(themeList[3])) ||
|
||||
missingTheme
|
||||
}
|
||||
allThemes={allThemes}
|
||||
themePlaylists={[]}
|
||||
setSelectedTheme={() => {}}
|
||||
openThemeModal={() => {}}
|
||||
enabled={false}
|
||||
management={false}
|
||||
/>
|
||||
</Parallax>
|
||||
</Stack>
|
||||
</Reflection>
|
||||
) : (
|
||||
<Reflection>
|
||||
<Stack
|
||||
w='100%'
|
||||
h='200px'
|
||||
justify='start'
|
||||
pos='relative'
|
||||
gap={0}
|
||||
style={{
|
||||
overflow: 'hidden',
|
||||
transition: 'all 0.5s ease-in-out',
|
||||
overscrollBehavior: 'contain',
|
||||
}}
|
||||
onWheel={handleMouseWheel}
|
||||
>
|
||||
{themeList
|
||||
.sort((a: number, b: number) => (a > b ? 1 : -1))
|
||||
.map((item: number, index: number) => {
|
||||
let spot = index + indexOffset;
|
||||
if (spot <= -1) {
|
||||
spot = index + indexOffset + themeList.length;
|
||||
} else if (spot >= themeList.length) {
|
||||
spot = index + indexOffset - themeList.length;
|
||||
} else {
|
||||
spot = index + indexOffset;
|
||||
}
|
||||
const theme: any = allThemes.find((theme: Theme) => theme.id === item);
|
||||
return (
|
||||
<Box
|
||||
key={theme.id}
|
||||
style={{
|
||||
transition: 'all 0.3s ease-in-out',
|
||||
position: 'absolute',
|
||||
top: 16 * (spot + 1),
|
||||
zIndex: themeList.length - spot,
|
||||
}}
|
||||
>
|
||||
<Parallax {...presentationProps}>
|
||||
<ThemeCard
|
||||
key={theme.id}
|
||||
cardTheme={theme}
|
||||
allThemes={allThemes}
|
||||
themePlaylists={[]}
|
||||
setSelectedTheme={() => {}}
|
||||
openThemeModal={() => {}}
|
||||
enabled={false}
|
||||
management={false}
|
||||
/>
|
||||
</Parallax>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Reflection>
|
||||
)}
|
||||
|
||||
<Group
|
||||
w={'100%'}
|
||||
pos={'absolute'}
|
||||
justify='space-between'
|
||||
bottom={0}
|
||||
style={{ zIndex: 1000 }}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
paddingRight: '.25rem',
|
||||
paddingLeft: '.25rem',
|
||||
backgroundColor: 'var(--mantine-color-border-0)',
|
||||
borderRadius: '4px',
|
||||
color: 'var(--mantine-color-primary-0)',
|
||||
}}
|
||||
>
|
||||
{cardPlaylist.name}
|
||||
</Text>
|
||||
<Group gap={0}>
|
||||
{!defaultLists.includes(cardPlaylist.name) && (
|
||||
<ActionIcon
|
||||
style={{
|
||||
paddingRight: '.25rem',
|
||||
paddingLeft: '.25rem',
|
||||
backgroundColor: 'var(--mantine-color-border-0)',
|
||||
borderRadius: '4px',
|
||||
color: 'var(--mantine-color-background-0)',
|
||||
}}
|
||||
onClick={() => {
|
||||
handleOpen(cardPlaylist);
|
||||
}}
|
||||
>
|
||||
<FiEdit />
|
||||
</ActionIcon>
|
||||
)}
|
||||
{cardPlaylist.default === 0 && (
|
||||
<ActionIcon
|
||||
color='red'
|
||||
style={{
|
||||
paddingRight: '.25rem',
|
||||
paddingLeft: '.25rem',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
onClick={() => {
|
||||
openModal();
|
||||
}}
|
||||
>
|
||||
<FiTrash color='black' />
|
||||
</ActionIcon>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user