[FEAT] Chakra V3 full operational

This commit is contained in:
Edouard DUPIN 2025-01-25 01:34:11 +01:00
parent c489fabb77
commit 83bfeda4ca
53 changed files with 281 additions and 834 deletions

View File

@ -26,6 +26,7 @@ import { USERS, USERS_COLLECTION } from '@/service/session';
import { hashLocalData } from '@/utils/sso'; import { hashLocalData } from '@/utils/sso';
import { Toaster } from './components/ui/toaster'; import { Toaster } from './components/ui/toaster';
import { systemTheme } from './theme/theme'; import { systemTheme } from './theme/theme';
import { CloseButton } from './components/ui/close-button';
const AppEnvHint = () => { const AppEnvHint = () => {
const dialog = useDisclosure(); const dialog = useDisclosure();
@ -88,11 +89,6 @@ const AppEnvHint = () => {
<DialogRoot open={dialog.open} onOpenChange={dialog.onClose}> <DialogRoot open={dialog.open} onOpenChange={dialog.onClose}>
<DialogContent> <DialogContent>
<DialogHeader>Outils développeurs</DialogHeader> <DialogHeader>Outils développeurs</DialogHeader>
<DialogTrigger asChild>
<Button variant="outline" size="sm">
{dialog.open ? "Close" : "Open"} Dialog
</Button>
</DialogTrigger>
<DialogBody> <DialogBody>
<Stack> <Stack>
<Text>User</Text> <Text>User</Text>
@ -111,7 +107,7 @@ const AppEnvHint = () => {
</Stack> </Stack>
</DialogBody> </DialogBody>
<DialogFooter> <DialogFooter>
<Button onClick={onClose}>Apply</Button> <Button onClick={onClose}>Close</Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
</DialogRoot> </DialogRoot>

View File

@ -2,12 +2,8 @@ import { SyntheticEvent, useEffect, useRef, useState } from 'react';
import { import {
Box, Box,
Button,
Flex, Flex,
IconButton, IconButton,
Slider,
SliderRoot,
SliderThumb,
SliderTrack, SliderTrack,
Text, Text,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
@ -35,6 +31,7 @@ import { DataUrlAccess } from '@/utils/data-url-access';
import { useColorModeValue } from '@/components/ui/color-mode'; import { useColorModeValue } from '@/components/ui/color-mode';
import { isNullOrUndefined } from '@/utils/validator'; import { isNullOrUndefined } from '@/utils/validator';
import { Icon } from './Icon'; import { Icon } from './Icon';
import { Slider } from './ui/slider';
export enum PlayMode { export enum PlayMode {
PLAY_ONE, PLAY_ONE,
@ -44,10 +41,10 @@ export enum PlayMode {
} }
const playModeIcon = { const playModeIcon = {
[PlayMode.PLAY_ONE]: <MdLooksOne size="30px" />, [PlayMode.PLAY_ONE]: <MdLooksOne style={{ width: "100%", height: "100%" }} />,
[PlayMode.PLAY_ALL]: <MdTrendingFlat size="30px" />, [PlayMode.PLAY_ALL]: <MdTrendingFlat style={{ width: "100%", height: "100%" }} />,
[PlayMode.PLAY_ONE_LOOP]: <MdRepeatOne size="30px" />, [PlayMode.PLAY_ONE_LOOP]: <MdRepeatOne style={{ width: "100%", height: "100%" }} />,
[PlayMode.PLAY_ALL_LOOP]: <MdRepeat size="30px" />, [PlayMode.PLAY_ALL_LOOP]: <MdRepeat style={{ width: "100%", height: "100%" }} />,
}; };
export type AudioPlayerProps = {}; export type AudioPlayerProps = {};
@ -89,9 +86,8 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
const backColor = useColorModeValue('back.100', 'back.800'); const backColor = useColorModeValue('back.100', 'back.800');
const configButton = { const configButton = {
borderRadius: 'full', borderRadius: 'full',
backgroundColor: '#00000000', backgroundColor: 'transparent',
_hover: { _hover: {
boxShadow: 'outline-over',
bgColor: 'brand.500', bgColor: 'brand.500',
}, },
width: "50px", width: "50px",
@ -209,6 +205,14 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
const onChangeStateToPause = () => { const onChangeStateToPause = () => {
setIsPlaying(false); setIsPlaying(false);
}; };
const marks = () => {
const minutes = Math.floor(duration / 60);
const result: number[] = [];
for (let i = 1; i <= minutes; i++) {
result.push(60 * i);
}
return result;
}
return ( return (
<> <>
{!isNullOrUndefined(trackOffset) && ( {!isNullOrUndefined(trackOffset) && (
@ -253,20 +257,21 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
{dataGender && ` / ${dataGender.name}`} {dataGender && ` / ${dataGender.name}`}
</Text> </Text>
<Box width="full" paddingX="15px"> <Box width="full" paddingX="15px">
<Slider.Root <Slider
defaultValue={[0]} defaultValue={[0]}
value={[timeProgress]} value={[timeProgress]}
min={0} min={0}
max={duration} max={duration}
step={0.1} step={0.1}
onChange={onSeek} onValueChange={(e) => onSeek(e.value)}
variant="outline" variant="outline"
// focusThumbOnChange={false} colorPalette="brand"
marks={marks()}
//focusCapture={false}
> >
<SliderTrack bg="gray.200" height="10px" borderRadius="full"> <SliderTrack bg="brand.200" height="10px" borderRadius="full">
{/* <SliderFilledTrack bg="brand.600" /> */}
</SliderTrack> </SliderTrack>
</Slider.Root> </Slider>
</Box> </Box>
<Flex> <Flex>
<Text <Text
@ -288,44 +293,51 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
{...configButton} {...configButton}
aria-label={'Play'} aria-label={'Play'}
onClick={onPlay} onClick={onPlay}
variant="ghost"
> >
{isPlaying ? ( {isPlaying ? (
<MdPause size="30px" /> <MdPause style={{ width: "100%", height: "100%" }} />
) : ( ) : (
<MdPlayArrow size="30px" /> <MdPlayArrow style={{ width: "100%", height: "100%" }} />
)} )}
</IconButton> </IconButton>
<IconButton <IconButton
{...configButton} {...configButton}
aria-label={'Stop'} aria-label={'Stop'}
onClick={onStop} onClick={onStop}
><MdStop size="30px" /></IconButton> variant="ghost"
><MdStop style={{ width: "100%", height: "100%" }} /></IconButton>
<IconButton <IconButton
{...configButton} {...configButton}
aria-label={'Previous track'} aria-label={'Previous track'}
onClick={onNavigatePrevious} onClick={onNavigatePrevious}
marginLeft="auto" marginLeft="auto"
><MdNavigateBefore size="30px" /> </IconButton> variant="ghost"
><MdNavigateBefore style={{ width: "100%", height: "100%" }} /> </IconButton>
<IconButton <IconButton
{...configButton} {...configButton}
aria-label={'jump 15sec in past'} aria-label={'jump 15sec in past'}
onClick={onFastRewind} onClick={onFastRewind}
><MdFastRewind size="30px" /></IconButton> variant="ghost"
><MdFastRewind style={{ width: "100%", height: "100%" }} /></IconButton>
<IconButton <IconButton
{...configButton} {...configButton}
aria-label={'jump 15sec in future'} aria-label={'jump 15sec in future'}
onClick={onFastForward} onClick={onFastForward}
variant="ghost"
><MdFastForward style={{ width: "100%", height: "100%" }} /></IconButton> ><MdFastForward style={{ width: "100%", height: "100%" }} /></IconButton>
<IconButton <IconButton
{...configButton} {...configButton}
aria-label={'Next track'} aria-label={'Next track'}
marginRight="auto" marginRight="auto"
onClick={onNavigateNext} onClick={onNavigateNext}
variant="ghost"
><MdNavigateNext style={{ width: "100%", height: "100%" }} /></IconButton> ><MdNavigateNext style={{ width: "100%", height: "100%" }} /></IconButton>
<IconButton <IconButton
{...configButton} {...configButton}
aria-label={'continue to the end'} aria-label={'continue to the end'}
onClick={onTypePlay} onClick={onTypePlay}
variant="ghost"
>{playModeIcon[playingMode]}</IconButton> >{playModeIcon[playingMode]}</IconButton>
</Flex> </Flex>
</Flex> </Flex>

View File

@ -29,6 +29,7 @@ export const Icon = forwardRef<HTMLDivElement, IconProps>(
minWidth="100%" minWidth="100%"
height="100%" height="100%"
color={color} color={color}
asChild
> >
{IconEl} {IconEl}
</Box> </Box>

View File

@ -4,7 +4,7 @@ import { Flex, FlexProps } from '@chakra-ui/react';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { PageLayout } from '@/components/Layout/PageLayout'; import { PageLayout } from '@/components/Layout/PageLayout';
import { colors } from '@/theme/foundations/colors'; import { colors } from '@/theme/colors';
import { useColorModeValue } from '@/components/ui/color-mode'; import { useColorModeValue } from '@/components/ui/color-mode';
export type LayoutProps = FlexProps & { export type LayoutProps = FlexProps & {

View File

@ -7,10 +7,14 @@ import {
IconButton, IconButton,
Text, Text,
useDisclosure, useDisclosure,
Button,
ConditionalValue,
Span,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
LuAlignJustify, LuAlignJustify,
LuArrowBigLeft, LuArrowBigLeft,
LuKeySquare,
LuLogIn, LuLogIn,
LuLogOut, LuLogOut,
LuMoon, LuMoon,
@ -21,8 +25,8 @@ import { useNavigate } from 'react-router-dom';
import { useServiceContext } from '@/service/ServiceContext'; import { useServiceContext } from '@/service/ServiceContext';
import { SessionState } from '@/service/SessionState'; import { SessionState } from '@/service/SessionState';
import { colors } from '@/theme/foundations/colors'; import { colors } from '@/theme/colors';
import { requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso'; import { requestOpenSite, requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
import { useSessionService } from '@/service/session'; import { useSessionService } from '@/service/session';
import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md'; import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu'; import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu';
@ -33,17 +37,12 @@ import {
DrawerHeader, DrawerHeader,
DrawerRoot, DrawerRoot,
} from '@/components/ui/drawer'; } from '@/components/ui/drawer';
import { Button } from '../ui/themed';
export const TOP_BAR_HEIGHT = '50px'; export const TOP_BAR_HEIGHT = '50px';
export const BUTTON_TOP_BAR_PROPERTY = { export const BUTTON_TOP_BAR_PROPERTY = {
bg: 'back.100', variant: "ghost" as ConditionalValue<"ghost" | "outline" | "solid" | "subtle" | "surface" | "plain" | undefined>,
color: 'brand.900', //colorPalette: "brand",
borderRadius: 0,
border: 0,
_hover: { background: 'back.300' },
_focus: { border: 'none' },
fontSize: '20px', fontSize: '20px',
textTransform: 'uppercase', textTransform: 'uppercase',
height: TOP_BAR_HEIGHT, height: TOP_BAR_HEIGHT,
@ -77,6 +76,9 @@ export const TopBar = ({ title, children }: TopBarProps) => {
clearToken(); clearToken();
requestSignOut(); requestSignOut();
}; };
const onKarso = (): void => {
requestOpenSite();
};
const onSelectAdd = () => { const onSelectAdd = () => {
navigate('/add'); navigate('/add');
}; };
@ -108,10 +110,12 @@ export const TopBar = ({ title, children }: TopBarProps) => {
zIndex={200} zIndex={200}
> >
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}> <Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}>
<LuAlignJustify /> <HStack>
<Text paddingLeft="3px" fontWeight="bold"> <LuAlignJustify />
Karusic <Text paddingLeft="3px" fontWeight="bold">
</Text> Karusic
</Text>
</HStack>
</Button> </Button>
{title && ( {title && (
<Text <Text
@ -120,6 +124,7 @@ export const TopBar = ({ title, children }: TopBarProps) => {
textTransform="uppercase" textTransform="uppercase"
marginRight="auto" marginRight="auto"
userSelect="none" userSelect="none"
color="brand.500"
> >
{title} {title}
</Text> </Text>
@ -128,7 +133,7 @@ export const TopBar = ({ title, children }: TopBarProps) => {
<Flex right="0"> <Flex right="0">
{session?.state !== SessionState.CONNECTED && ( {session?.state !== SessionState.CONNECTED && (
<> <>
<Button {...BUTTON_TOP_BAR_PROPERTY} theme="@primary" onClick={onSignIn}> <Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onSignIn}>
<LuLogIn /> <LuLogIn />
<Text paddingLeft="3px" fontWeight="bold"> <Text paddingLeft="3px" fontWeight="bold">
Sign-in Sign-in
@ -157,7 +162,7 @@ export const TopBar = ({ title, children }: TopBarProps) => {
><MdSupervisedUserCircle /></IconButton> ><MdSupervisedUserCircle /></IconButton>
</MenuTrigger> </MenuTrigger>
<MenuContent> <MenuContent>
<MenuItem value="user" valueText="user" _hover={{}} color={useColorModeValue('brand.800', 'brand.200')}> <MenuItem value="user" valueText="user" color={useColorModeValue('brand.800', 'brand.200')}>
<MdSupervisedUserCircle /> <MdSupervisedUserCircle />
<Box flex="1">Sign in as {session?.login ?? 'Fail'}</Box> <Box flex="1">Sign in as {session?.login ?? 'Fail'}</Box>
</MenuItem> </MenuItem>
@ -166,6 +171,9 @@ export const TopBar = ({ title, children }: TopBarProps) => {
<MenuItem value="Sign-out" valueText="Sign-out" onClick={onSignOut}> <MenuItem value="Sign-out" valueText="Sign-out" onClick={onSignOut}>
<LuLogOut /> Sign-out <LuLogOut /> Sign-out
</MenuItem> </MenuItem>
<MenuItem value="karso" valueText="Karso" onClick={onKarso}>
<LuKeySquare /> Karso (SSO)
</MenuItem>
{colorMode === 'light' ? ( {colorMode === 'light' ? (
<MenuItem value="set-dark" valueText="set-dark" onClick={toggleColorMode}> <MenuItem value="set-dark" valueText="set-dark" onClick={toggleColorMode}>
<LuMoon /> Set dark mode <LuMoon /> Set dark mode
@ -196,45 +204,50 @@ export const TopBar = ({ title, children }: TopBarProps) => {
color={useColorModeValue('brand.900', 'brand.50')} color={useColorModeValue('brand.900', 'brand.50')}
textTransform="uppercase" textTransform="uppercase"
> >
<HStack height={TOP_BAR_HEIGHT}> <HStack
{...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
<LuArrowBigLeft /> <LuArrowBigLeft />
<Text as="span" paddingLeft="3px"> <Span paddingLeft="3px">
Karusic Karusic
</Text> </Span>
</HStack> </HStack>
</DrawerHeader> </DrawerHeader>
<DrawerBody paddingX="0px"> <DrawerBody paddingX="0px">
<Box marginY="3" />
<Button <Button
background="#00000000" background="#00000000"
borderRadius="0px" borderRadius="0px"
onClick={onSelectHome} onClick={onSelectHome}
width="full" width="full"
{...BUTTON_TOP_BAR_PROPERTY}
> >
<MdHome /> <MdHome style={{ width: "45px", height: "45px" }} />
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto"> <Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
Home Home
</Text> </Text>
</Button> </Button>
<hr /> <Box marginY="5" marginX="10" height="2px" background="brand.600" />
<Button <Button
background="#00000000" background="#00000000"
borderRadius="0px" borderRadius="0px"
onClick={onSelectOnAir} onClick={onSelectOnAir}
width="full" width="full"
{...BUTTON_TOP_BAR_PROPERTY}
> >
<MdOutlinePlaylistPlay /> <MdOutlinePlaylistPlay style={{ width: "45px", height: "45px" }} />
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto"> <Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
On air On air
</Text> </Text>
</Button> </Button>
<hr /> <Box marginY="5" marginX="10" height="2px" background="brand.600" />
<Button <Button
background="#00000000" background="#00000000"
borderRadius="0px" borderRadius="0px"
onClick={onSelectAdd} onClick={onSelectAdd}
width="full" width="full"
{...BUTTON_TOP_BAR_PROPERTY}
> >
<MdOutlineUploadFile /> <MdOutlineUploadFile style={{ width: "45px", height: "45px" }} />
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto"> <Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
Add Media Add Media
</Text> </Text>

View File

@ -37,7 +37,7 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
flex={1} flex={1}
> >
<Span <Span
// align="left" textAlign="left"
fontSize="20px" fontSize="20px"
fontWeight="bold" fontWeight="bold"
userSelect="none" userSelect="none"
@ -48,7 +48,7 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
{dataAlbum?.name} {dataAlbum?.name}
</Span> </Span>
<Span <Span
// align="left" textAlign="left"
fontSize="15px" fontSize="15px"
userSelect="none" userSelect="none"
marginRight="auto" marginRight="auto"

View File

@ -1,4 +1,4 @@
import { useState } from 'react'; import { ReactNode, useState } from 'react';
import { LuMenu } from 'react-icons/lu'; import { LuMenu } from 'react-icons/lu';
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../ui/menu'; import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../ui/menu';
@ -6,6 +6,7 @@ import { Button } from '../ui/button';
export type MenuElement = { export type MenuElement = {
icon?: ReactNode;
name: string; name: string;
onClick: () => void; onClick: () => void;
}; };
@ -33,8 +34,9 @@ export const ContextMenu = ({ elements }: ContextMenuProps) => {
<MenuContent <MenuContent
data-testid="context-menu_content"> data-testid="context-menu_content">
{elements?.map((data) => ( {elements?.map((data) => (
<MenuItem key={data.name} value={data.name} onClick={data.onClick} <MenuItem key={data.name} value={data.name} onClick={data.onClick} height="65px" fontSize="25px"
data-testid="context-menu_item"> data-test-id="context-menu_item" >
{data.icon}
{data.name} {data.name}
</MenuItem> </MenuItem>
))} ))}

View File

@ -1,14 +1,11 @@
import { RefObject } from 'react'; import { RefObject } from 'react';
import {
NumberInput,
} from '@chakra-ui/react';
import { FormGroup } from '@/components/form/FormGroup'; import { FormGroup } from '@/components/form/FormGroup';
import { UseFormidableReturn } from '@/components/form/Formidable'; import { UseFormidableReturn } from '@/components/form/Formidable';
import { NumberInputField, NumberInputProps, NumberInputRoot } from '../ui/number-input';
export type FormNumberProps = Pick< export type FormNumberProps = Pick<
NumberInput.RootProps, NumberInputProps,
'step' | 'defaultValue' | 'min' | 'max' 'step' | 'defaultValue' | 'min' | 'max'
> & { > & {
form: UseFormidableReturn; form: UseFormidableReturn;
@ -30,23 +27,26 @@ export const FormNumber = ({
defaultValue, defaultValue,
...rest ...rest
}: FormNumberProps) => { }: FormNumberProps) => {
const onEvent = (value) => {
form.setValues({ [variableName]: value.value });
}
return ( return (
<FormGroup <FormGroup
isModify={form.isModify[variableName]} isModify={form.isModify[variableName]}
onRestore={() => form.restoreValue({ [variableName]: true })} onRestore={() => form.restoreValue({ [variableName]: true })}
{...rest} {...rest}
> >
<NumberInput.Root <NumberInputRoot
ref={ref} ref={ref}
value={form.values[variableName]} value={form.values[variableName]}
onValueChange={(value) => form.setValues({ [variableName]: value })} onValueChange={onEvent}
step={step} step={step}
defaultValue={defaultValue} defaultValue={defaultValue}
min={min} min={min}
max={max} max={max}
> >
<NumberInput.Input /> <NumberInputField />
</NumberInput.Root> </NumberInputRoot>
</FormGroup> </FormGroup>
); );
}; };

View File

@ -23,7 +23,7 @@ export const DisplayGender = ({ dataGender }: DisplayGenderProps) => {
data={dataGender?.covers} data={dataGender?.covers}
size="100" size="100"
height="full" height="full"
//TODO: iconEmpty={LuDisc3} iconEmpty={<LuDisc3 />}
/> />
<Flex <Flex
direction="column" direction="column"

View File

@ -140,7 +140,7 @@ export const AlbumEditPopUp = ({ }: AlbumEditPopUpProps) => {
//initialFocusRef={initialRef} //initialFocusRef={initialRef}
//finalFocusRef={finalRef} //finalFocusRef={finalRef}
//closeOnOverlayClick={false} //closeOnOverlayClick={false}
onOpenChange={onClose} //onOpenChange={onClose}
open={true} open={true}
data-testid="album-edit-pop-up" data-testid="album-edit-pop-up"
> >
@ -169,9 +169,7 @@ export const AlbumEditPopUp = ({ }: AlbumEditPopUpProps) => {
<Button <Button
onClick={disclosure.onOpen} onClick={disclosure.onOpen}
marginRight="auto" marginRight="auto"
//theme="@danger" colorPalette="@danger"
variant="outline"
colorScheme=""
disabled={countTracksOfAnAlbum !== 0} disabled={countTracksOfAnAlbum !== 0}
> >
<MdDeleteForever /> Remove Media <MdDeleteForever /> Remove Media

View File

@ -26,7 +26,6 @@ import { useArtistService, useSpecificArtist } from '@/service/Artist';
import { useServiceContext } from '@/service/ServiceContext'; import { useServiceContext } from '@/service/ServiceContext';
import { useCountTracksOfAnArtist } from '@/service/Track'; import { useCountTracksOfAnArtist } from '@/service/Track';
import { isNullOrUndefined } from '@/utils/validator'; import { isNullOrUndefined } from '@/utils/validator';
//import { Button } from '../ui/themed';
export type ArtistEditPopUpProps = {}; export type ArtistEditPopUpProps = {};
@ -167,7 +166,7 @@ export const ArtistEditPopUp = ({ }: ArtistEditPopUpProps) => {
<Button <Button
onClick={disclosure.onOpen} onClick={disclosure.onOpen}
marginRight="auto" marginRight="auto"
theme="@danger" colorPalette="@danger"
disabled={countTracksOnAnArtist !== 0} disabled={countTracksOnAnArtist !== 0}
> >
<MdDeleteForever /> Remove Media <MdDeleteForever /> Remove Media
@ -218,7 +217,7 @@ export const ArtistEditPopUp = ({ }: ArtistEditPopUpProps) => {
<Button <Button
onClick={() => setAdmin((value) => !value)} onClick={() => setAdmin((value) => !value)}
marginRight="auto" marginRight="auto"
variant="@danger" colorPalette={admin ? undefined : "@danger"}
> >
{admin ? ( {admin ? (
<> <>

View File

@ -4,6 +4,7 @@ import {
Flex, Flex,
Text, Text,
useDisclosure, useDisclosure,
Button
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
MdAdminPanelSettings, MdAdminPanelSettings,
@ -26,7 +27,6 @@ import { useGenderService, useSpecificGender } from '@/service/Gender';
import { useServiceContext } from '@/service/ServiceContext'; import { useServiceContext } from '@/service/ServiceContext';
import { useCountTracksOfAGender } from '@/service/Track'; import { useCountTracksOfAGender } from '@/service/Track';
import { isNullOrUndefined } from '@/utils/validator'; import { isNullOrUndefined } from '@/utils/validator';
import { Button } from '../ui/themed';
export type GenderEditPopUpProps = {}; export type GenderEditPopUpProps = {};
@ -166,7 +166,7 @@ export const GenderEditPopUp = ({ }: GenderEditPopUpProps) => {
<Button <Button
onClick={disclosure.onOpen} onClick={disclosure.onOpen}
marginRight="auto" marginRight="auto"
theme="@danger" colorPalette="@danger"
disabled={countTracksOnAGender !== 0} disabled={countTracksOnAGender !== 0}
> >
<MdDeleteForever /> Remove gender <MdDeleteForever /> Remove gender

View File

@ -4,10 +4,11 @@ import {
Flex, Flex,
Progress, Progress,
Text, Text,
Button,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog'; import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
import { Button } from '../ui/themed';
export type PopUpUploadProgressProps = { export type PopUpUploadProgressProps = {
title: string; title: string;
@ -88,7 +89,7 @@ export const PopUpUploadProgress = ({
</DialogBody> </DialogBody>
<DialogFooter> <DialogFooter>
{isFinished ? ( {isFinished ? (
<Button onClick={onClose} theme="@success"> <Button onClick={onClose} colorPalette="green">
Ok Ok
</Button> </Button>
) : ( ) : (

View File

@ -3,6 +3,7 @@ import { useRef, useState } from 'react';
import { import {
Text, Text,
useDisclosure, useDisclosure,
Button,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog'; import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
@ -25,7 +26,6 @@ import { useOrderedGenders } from '@/service/Gender';
import { useServiceContext } from '@/service/ServiceContext'; import { useServiceContext } from '@/service/ServiceContext';
import { useSpecificTrack, useTrackService } from '@/service/Track'; import { useSpecificTrack, useTrackService } from '@/service/Track';
import { isNullOrUndefined } from '@/utils/validator'; import { isNullOrUndefined } from '@/utils/validator';
import { Button } from '../ui/themed';
export type TrackEditPopUpProps = {}; export type TrackEditPopUpProps = {};
@ -113,7 +113,7 @@ export const TrackEditPopUp = ({ }: TrackEditPopUpProps) => {
<Button <Button
onClick={disclosure.onOpen} onClick={disclosure.onOpen}
marginRight="auto" marginRight="auto"
theme="@danger" colorPalette="@danger"
> >
<MdDeleteForever /> Remove Media <MdDeleteForever /> Remove Media
</Button> </Button>

View File

@ -113,14 +113,13 @@ export const SelectMultiple = ({
{selectedOptions.map((data) => ( {selectedOptions.map((data) => (
<Flex align="flex-start" key={data[keyKey]}> <Flex align="flex-start" key={data[keyKey]}>
<Tag.Root <Tag.Root
size="md" size="xl"
key="md"
borderRadius="5px" borderRadius="5px"
variant="solid" variant="surface"
backgroundColor="green.500" backgroundColor="green.800"
> >
<Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label> <Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label>
<Tag.CloseTrigger onClick={() => selectValue(data)} /> <Tag.CloseTrigger boxSize="5" onClick={() => selectValue(data)} />
</Tag.Root> </Tag.Root>
</Flex> </Flex>
))} ))}

View File

@ -113,7 +113,7 @@ export const SelectSingle = ({
showList ? (currentSearch ?? '') : (selectedOptions?.name ?? (hasSuggestion ? `suggest: ${currentSearch}` : '')) showList ? (currentSearch ?? '') : (selectedOptions?.name ?? (hasSuggestion ? `suggest: ${currentSearch}` : ''))
} }
backgroundColor={ backgroundColor={
showList || !selectedOptions ? undefined : 'green.500' showList || !selectedOptions ? undefined : 'green.800'
} }
borderRadius="5px 0 0 5px" borderRadius="5px 0 0 5px"
/> />

View File

@ -23,9 +23,9 @@ export const DisplayTrack = ({
data={track?.covers} data={track?.covers}
size="50" size="50"
height="full" height="full"
/* TODO: iconEmpty={ iconEmpty={
trackActive?.id === track.id ? LuPlay : LuMusic2 trackActive?.id === track.id ? <LuPlay /> : <LuMusic2 />
} */ }
onClick={onClick} onClick={onClick}
/> />
<Flex <Flex

View File

@ -30,11 +30,11 @@ export const DisplayTrackFull = ({
data-testid="display-track-full"> data-testid="display-track-full">
<Covers <Covers
data={track?.covers} data={track?.covers}
size="50" size="60px"
marginY="auto" marginY="auto"
/* TODO: iconEmpty={ iconEmpty={
trackActive?.id === track.id ? LuPlay : LuMusic2 trackActive?.id === track.id ? <LuPlay /> : <LuMusic2 />
} */ }
onClick={onClick} onClick={onClick}
/> />
<Flex <Flex

View File

@ -0,0 +1,24 @@
import { NumberInput as ChakraNumberInput } from "@chakra-ui/react"
import * as React from "react"
export interface NumberInputProps extends ChakraNumberInput.RootProps {}
export const NumberInputRoot = React.forwardRef<
HTMLDivElement,
NumberInputProps
>(function NumberInput(props, ref) {
const { children, ...rest } = props
return (
<ChakraNumberInput.Root ref={ref} variant="outline" {...rest}>
{children}
<ChakraNumberInput.Control>
<ChakraNumberInput.IncrementTrigger />
<ChakraNumberInput.DecrementTrigger />
</ChakraNumberInput.Control>
</ChakraNumberInput.Root>
)
})
export const NumberInputField = ChakraNumberInput.Input
export const NumberInputScrubber = ChakraNumberInput.Scrubber
export const NumberInputLabel = ChakraNumberInput.Label

View File

@ -1,7 +0,0 @@
import buttonRecipe from '@/theme/recipes/button';
import { chakra } from '@chakra-ui/react';
// we export the element with the application recipe theme.
// cf doc: https://www.chakra-ui.com/docs/theming/recipes
export const Button = chakra("button", buttonRecipe)

View File

@ -10,7 +10,7 @@ export const Error401 = () => {
<TopBar /> <TopBar />
<PageLayoutInfoCenter padding="25px"> <PageLayoutInfoCenter padding="25px">
<Center> <Center>
<MdControlCamera size="250px" color="red.600" /> <MdControlCamera style={{ width: "250px", height: "250px", color: "orange" }} />
</Center> </Center>
<Box textAlign="center"> <Box textAlign="center">
<Heading>Erreur 401</Heading> <Heading>Erreur 401</Heading>

View File

@ -10,10 +10,10 @@ export const Error403 = () => {
<TopBar /> <TopBar />
<PageLayoutInfoCenter padding="25px"> <PageLayoutInfoCenter padding="25px">
<Center> <Center>
<MdDangerous size="250px" color="orange.600" /> <MdDangerous style={{ width: "250px", height: "250px", color: "red" }} />
</Center> </Center>
<Box textAlign="center"> <Box textAlign="center">
<Heading>Erreur 401</Heading> <Heading>Erreur 403</Heading>
<Text color="orange.600">Cette page vous est interdite</Text> <Text color="orange.600">Cette page vous est interdite</Text>
<Link href="/"> <Link href="/">
Retour à l'accueil Retour à l'accueil

View File

@ -10,7 +10,7 @@ export const Error404 = () => {
<TopBar /> <TopBar />
<PageLayoutInfoCenter padding="25px"> <PageLayoutInfoCenter padding="25px">
<Center> <Center>
<MdSignpost size="250px" /> <MdSignpost style={{ width: "250px", height: "250px", color: "aqua" }} />
</Center> </Center>
<Box textAlign="center"> <Box textAlign="center">
<Heading>Erreur 404</Heading> <Heading>Erreur 404</Heading>

View File

@ -2,51 +2,28 @@ import React, { FC } from 'react';
import { import {
AlertDescription, AlertDescription,
AlertRoot,
AlertTitle, AlertTitle,
Box, Box,
Collapsible, Alert,
useDisclosure,
Text,
HStack,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { import {
FallbackProps, FallbackProps,
ErrorBoundary as ReactErrorBoundary, ErrorBoundary as ReactErrorBoundary,
} from 'react-error-boundary'; } from 'react-error-boundary';
import { Button } from '@/components/ui/themed';
import { LuChevronUp, LuChevronDown } from 'react-icons/lu';
const ErrorFallback = ({ error }: FallbackProps) => { const ErrorFallback = ({ error }: FallbackProps) => {
const { open, onToggle } = useDisclosure();
return ( return (
<Box p="4" m="auto"> <Box padding="8" margin="auto">
<AlertRoot status="error" borderRadius="md"> <Alert.Root status="error" borderRadius="md">
{/* <AlertIcon /> */} <Alert.Indicator height="75px" width="75px" />
<Box flex="1"> <Box flex="1">
<AlertTitle>An unexpected error has occurred.</AlertTitle> <AlertTitle fontWeight="bold" fontSize="35px">An unexpected error has occurred.</AlertTitle>
<AlertDescription display="block" lineHeight="1.4"> <AlertDescription padding="5" marginTop="3" fontSize="20px" lineHeight="1.4">
<Button <br />
theme="@secondary" {error.message}
color="red.800"
//size="sm"
onClick={onToggle}
>
<HStack>
<Text>Show details</Text>{' '}
{open ? <LuChevronUp /> : <LuChevronDown />}
</HStack>
</Button>
<Collapsible.Root open={open}>
<Collapsible.Content>
<Box mt={4} fontFamily="monospace">
{error.message}
</Box>
</Collapsible.Content>
</Collapsible.Root>
</AlertDescription> </AlertDescription>
</Box> </Box>
</AlertRoot> </Alert.Root>
</Box> </Box>
); );
}; };

View File

@ -1,6 +1,6 @@
import { Box, Button, Flex, Text } from '@chakra-ui/react'; import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { LuDisc3 } from 'react-icons/lu'; import { LuDisc3 } from 'react-icons/lu';
import { MdEdit } from 'react-icons/md'; import { Md1kPlus, MdAdd, MdEdit, MdPlusOne } from 'react-icons/md';
import { Route, Routes, useNavigate, useParams } from 'react-router-dom'; import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
import { Covers } from '@/components/Cover'; import { Covers } from '@/components/Cover';
@ -10,7 +10,6 @@ import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar'; import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp'; import { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp'; import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
import { DisplayTrack } from '@/components/track/DisplayTrack';
import { DisplayTrackFull } from '@/components/track/DisplayTrackFull'; import { DisplayTrackFull } from '@/components/track/DisplayTrackFull';
import { useActivePlaylistService } from '@/service/ActivePlaylist'; import { useActivePlaylistService } from '@/service/ActivePlaylist';
import { useSpecificAlbum } from '@/service/Album'; import { useSpecificAlbum } from '@/service/Album';
@ -76,7 +75,7 @@ export const AlbumDetailPage = () => {
> >
<Covers <Covers
data={dataAlbum?.covers} data={dataAlbum?.covers}
// TODO: iconEmpty={LuDisc3} iconEmpty={<LuDisc3 />}
slideshow slideshow
/> />
<Flex direction="column" width="80%" marginRight="auto"> <Flex direction="column" width="80%" marginRight="auto">
@ -121,11 +120,16 @@ export const AlbumDetailPage = () => {
contextMenu={[ contextMenu={[
{ {
name: 'Edit', name: 'Edit',
icon: <MdEdit />,
onClick: () => { onClick: () => {
navigate(`/album/${albumId}/edit-track/${data.id}`); navigate(`/album/${albumId}/edit-track/${data.id}`);
}, },
}, },
{ name: 'Add Playlist', onClick: () => { } }, {
icon: <MdAdd />,
name: 'Add Playlist',
onClick: () => { }
},
]} ]}
data-testid="Album-detail-page_display-detail" data-testid="Album-detail-page_display-detail"
/> />

View File

@ -1,6 +1,6 @@
import { Box, Button, Flex, Text } from '@chakra-ui/react'; import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { LuDisc3, LuUser } from 'react-icons/lu'; import { LuDisc3, LuUser } from 'react-icons/lu';
import { MdEdit, MdPerson } from 'react-icons/md'; import { MdAdd, MdEdit, MdPerson } from 'react-icons/md';
import { Route, Routes, useNavigate, useParams } from 'react-router-dom'; import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
import { Covers } from '@/components/Cover'; import { Covers } from '@/components/Cover';
@ -67,7 +67,7 @@ export const ArtistAlbumDetailPage = () => {
data={dataArtist?.covers} data={dataArtist?.covers}
size="35px" size="35px"
borderRadius="full" borderRadius="full"
// TODO: iconEmpty={MdPerson} iconEmpty={<MdPerson />}
/> />
<Text fontSize="24px" fontWeight="bold"> <Text fontSize="24px" fontWeight="bold">
{dataArtist?.name} {dataArtist?.name}
@ -94,7 +94,7 @@ export const ArtistAlbumDetailPage = () => {
> >
<Covers <Covers
data={dataAlbum?.covers} data={dataAlbum?.covers}
// TODO: iconEmpty={LuDisc3} iconEmpty={<LuDisc3 />}
slideshow slideshow
/> />
<Flex direction="column" width="80%" marginRight="auto"> <Flex direction="column" width="80%" marginRight="auto">
@ -137,13 +137,18 @@ export const ArtistAlbumDetailPage = () => {
contextMenu={[ contextMenu={[
{ {
name: 'Edit', name: 'Edit',
icon: <MdEdit />,
onClick: () => { onClick: () => {
navigate( navigate(
`/artist/${artistIdInt}/album/${albumId}/edit-track/${data.id}` `/artist/${artistIdInt}/album/${albumId}/edit-track/${data.id}`
); );
}, },
}, },
{ name: 'Add Playlist', onClick: () => { } }, {
icon: <MdAdd />,
name: 'Add Playlist',
onClick: () => { }
},
]} ]}
/> />
</Box> </Box>

View File

@ -70,7 +70,7 @@ export const ArtistDetailPage = () => {
> >
<Covers <Covers
data={dataArtist?.covers} data={dataArtist?.covers}
// TODO: iconEmpty={LuUser} iconEmpty={<LuUser />}
slideshow slideshow
/> />
<Flex direction="column" width="80%" marginRight="auto"> <Flex direction="column" width="80%" marginRight="auto">

View File

@ -59,7 +59,7 @@ export const ArtistsPage = () => {
<SearchInput onChange={setFilterName} /> <SearchInput onChange={setFilterName} />
<Tooltip.Root aria-label="Random play"> <Tooltip.Root aria-label="Random play">
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onRandomPlay}> <Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onRandomPlay}>
<MdOutlineForkRight /> <MdOutlineForkRight style={{ width: "100%", height: "100%" }} />
</Button> </Button>
</Tooltip.Root> </Tooltip.Root>
</TopBar> </TopBar>
@ -86,7 +86,7 @@ export const ArtistsPage = () => {
data={data.covers} data={data.covers}
size={BASE_WRAP_ICON_SIZE} size={BASE_WRAP_ICON_SIZE}
height="full" height="full"
// iconEmpty={LuUser} iconEmpty={<LuUser />}
/> />
<Flex <Flex
direction="column" direction="column"
@ -97,11 +97,10 @@ export const ArtistsPage = () => {
overflowX="hidden" overflowX="hidden"
> >
<Text <Text
/*align="left"*/ textAlign="left"
/*noOfLines={[1, 2]}*/ /*noOfLines={[1, 2]}*/
> >
<Span <Span
as="span"
fontSize="20px" fontSize="20px"
fontWeight="bold" fontWeight="bold"
userSelect="none" userSelect="none"
@ -118,7 +117,7 @@ export const ArtistsPage = () => {
))} ))}
</HStack> </HStack>
<EmptyEnd /> <EmptyEnd />
</PageLayout> </PageLayout >
</> </>
); );
}; };

View File

@ -75,7 +75,7 @@ export const GenderDetailPage = () => {
> >
<Covers <Covers
data={dataGender?.covers} data={dataGender?.covers}
// TODO: iconEmpty={LuDisc3} iconEmpty={<LuDisc3 />}
slideshow slideshow
/> />
<Flex direction="column" width="80%" marginRight="auto"> <Flex direction="column" width="80%" marginRight="auto">
@ -131,7 +131,7 @@ export const GenderDetailPage = () => {
<Route path="edit-track/:trackId" element={<TrackEditPopUp />} /> <Route path="edit-track/:trackId" element={<TrackEditPopUp />} />
<Route path="edit-gender/:genderId" element={<GenderEditPopUp />} /> <Route path="edit-gender/:genderId" element={<GenderEditPopUp />} />
</Routes> </Routes>
</PageLayout> </PageLayout >
</> </>
); );
}; };

View File

@ -5,6 +5,7 @@ import {
Input, Input,
Table, Table,
Text, Text,
Button,
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import { LuTrash } from 'react-icons/lu'; import { LuTrash } from 'react-icons/lu';
import { MdCloudUpload } from 'react-icons/md'; import { MdCloudUpload } from 'react-icons/md';
@ -31,7 +32,6 @@ import { useGenderService, useOrderedGenders } from '@/service/Gender';
import { useServiceContext } from '@/service/ServiceContext'; import { useServiceContext } from '@/service/ServiceContext';
import { useTrackService } from '@/service/Track'; import { useTrackService } from '@/service/Track';
import { isNullOrUndefined } from '@/utils/validator'; import { isNullOrUndefined } from '@/utils/validator';
import { Button } from '@/components/ui/themed';
export class ElementList { export class ElementList {
constructor( constructor(
@ -540,7 +540,7 @@ export const AddPage = () => {
</Table.Root> </Table.Root>
<Flex marginY="15px"> <Flex marginY="15px">
<Button <Button
theme="@primary" colorPalette="brand"
onClick={sendFile} onClick={sendFile}
disabled={!needSend} disabled={!needSend}
marginLeft="auto" marginLeft="auto"

View File

@ -4,13 +4,16 @@ import { Route, Routes, useNavigate } from 'react-router-dom';
import { EmptyEnd } from '@/components/EmptyEnd'; import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout'; import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter'; import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { TopBar } from '@/components/TopBar/TopBar'; import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp'; import { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp';
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp'; import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
import { useActivePlaylistService } from '@/service/ActivePlaylist'; import { useActivePlaylistService } from '@/service/ActivePlaylist';
import { useColorModeValue } from '@/components/ui/color-mode'; import { useColorModeValue } from '@/components/ui/color-mode';
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing'; import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
import { DisplayTrackFullId } from '@/components/track/DisplayTrackFullId'; import { DisplayTrackFullId } from '@/components/track/DisplayTrackFullId';
import { MdCleanHands, MdClear, MdDryCleaning, MdEdit, MdKeyboardDoubleArrowDown, MdKeyboardDoubleArrowUp, MdLeakRemove, MdRemove, MdRemoveFromQueue, MdRemoveShoppingCart } from 'react-icons/md';
import { Button } from '@/components/ui/button';
import { LuTrash2 } from 'react-icons/lu';
export const OnAirPage = () => { export const OnAirPage = () => {
const { playInList } = useActivePlaylistService(); const { playInList } = useActivePlaylistService();
@ -53,9 +56,39 @@ export const OnAirPage = () => {
setNewPlaylist(newList); setNewPlaylist(newList);
} }
}; };
const removePreviousTrack = (index: number) => {
if (!playTrackList) {
console.log('Fail to remove element of on air...');
return;
}
const newList = playTrackList.slice(index);
let newPlayed = trackOffset;
if (newPlayed != undefined) {
if (index < newPlayed) {
newPlayed = newPlayed - index;
} else {
newPlayed = 0;
}
playInList(newPlayed, newList);
} else {
setNewPlaylist(newList);
}
}
const removeNextTrack = (index: number) => {
if (!playTrackList) {
console.log('Fail to remove element of on air...');
return;
}
const newList = playTrackList.slice(0, index + 1);
playInList(trackOffset, newList);
}
const clean = () => {
setNewPlaylist([]);
}
console.log(`playTrackList = ${JSON.stringify(playTrackList, null, 2)}`); console.log(`playTrackList = ${JSON.stringify(playTrackList, null, 2)}`);
if (!playTrackList) { if (!playTrackList || playTrackList.length == 0) {
return ( return (
<> <>
<TopBar title="Album detail" /> <TopBar title="Album detail" />
@ -68,6 +101,12 @@ export const OnAirPage = () => {
return ( return (
<> <>
<TopBar title="On Air ..."> <TopBar title="On Air ...">
<Button
{...BUTTON_TOP_BAR_PROPERTY}
onClick={clean}
>
<LuTrash2 />
</Button>
</TopBar> </TopBar>
<PageLayout> <PageLayout>
<Flex <Flex
@ -78,7 +117,7 @@ export const OnAirPage = () => {
width="80%" width="80%"
> >
{!playTrackList && <>No playing</>} {!playTrackList && <>No playing</>}
{playTrackList && playTrackList?.map((data) => ( {playTrackList && playTrackList?.map((data, index) => (
<Box <Box
minWidth="100%" minWidth="100%"
//height="60px" //height="60px"
@ -99,16 +138,32 @@ export const OnAirPage = () => {
contextMenu={[ contextMenu={[
{ {
name: 'Edit', name: 'Edit',
icon: <MdEdit />,
onClick: () => { onClick: () => {
navigate(`edit-track/${data}`); navigate(`edit-track/${data}`);
}, },
}, },
{ {
name: 'Remove from playlist', name: 'Remove previous',
icon: <MdKeyboardDoubleArrowUp />,
onClick: () => {
removePreviousTrack(index);
},
},
{
name: 'Remove',
icon: <MdRemove />,
onClick: () => { onClick: () => {
removeTrack(data); removeTrack(data);
}, },
}, },
{
name: 'Remove After',
icon: <MdKeyboardDoubleArrowDown />,
onClick: () => {
removeNextTrack(index);
},
},
]} ]}
/> />
</Box> </Box>

View File

@ -14,7 +14,7 @@ export type ActivePlaylistServiceProps = {
trackActive?: Track; trackActive?: Track;
setNewPlaylist: (listIds: number[]) => void; setNewPlaylist: (listIds: number[]) => void;
setNewPlaylistShuffle: (listIds: number[]) => void; setNewPlaylistShuffle: (listIds: number[]) => void;
playInList: (id: number, listIds: number[]) => void; playInList: (id: number | undefined, listIds: number[]) => void;
play: (id: number) => void; play: (id: number) => void;
previous: () => void; previous: () => void;
next: () => void; next: () => void;
@ -65,7 +65,7 @@ export const useActivePlaylistServiceWrapped = (
[setPlayTrackList, setTrackOffset] [setPlayTrackList, setTrackOffset]
); );
const playInList = useCallback( const playInList = useCallback(
(id: number, listIds: number[]) => { (id: number | undefined, listIds: number[]) => {
console.log(`Request paly in list: ${id} in ${listIds}`); console.log(`Request paly in list: ${id} in ${listIds}`);
setPlayTrackList(listIds); setPlayTrackList(listIds);
setTrackOffset(id); setTrackOffset(id);

View File

@ -111,4 +111,5 @@ export const colors = {
success: green, success: green,
error: red, error: red,
warning: orange, warning: orange,
danger: red,
} as const; } as const;

View File

@ -1,9 +0,0 @@
import { colors } from './colors';
import { shadows } from './shadows';
const foundations = {
colors,
shadows,
};
export default foundations;

View File

@ -1,23 +0,0 @@
import { SystemConfig } from '@chakra-ui/react';
import { colors } from './colors';
const createOutline = (colorScheme = 'gray') => {
return { value: `0 0 0 3px ${colorScheme}.500/3` };
}
export const shadows = {
outline: createOutline('brand'),
// 'outline-brand': '0 0 0 1px brand.900',
// 'outline-gray': createOutline('gray'),
// 'outline-over': `4px 4px 5px #00000088`,
// 'outline-darkgray': `0 0 0 3px gray.500/8`,
// 'outline-success': createOutline('success'),
// 'outline-warning': createOutline('warning'),
// 'outline-error': createOutline('error'),
// 'outline-doing': createOutline('doing'),
// 'outline-paused': createOutline('paused'),
//layout: '0 0 24px 1px rgba(0, 0, 0, 0.05)',
//smooth: 'inset 0px 0px 16px rgba(0, 0, 0, 0.05)',
// smooth-light is used for dark backgrounds
//'smooth-light': 'inset 0px 0px 16px rgba(255, 255, 255, 0.1)',
};

View File

@ -1,24 +0,0 @@
export default {
sizes: {
'2xs': {
fontSize: '0.5em',
},
xs: {
fontSize: '0.6em',
},
sm: {
fontSize: '0.7em',
},
md: {
fontSize: '0.8em',
textTransform: 'none',
},
lg: {
fontSize: '0.9em',
textTransform: 'none',
},
},
defaultProps: {
size: 'md',
},
};

View File

@ -1,117 +0,0 @@
import { defineRecipe, defineStyle, RecipeVariantRecord, SystemStyleObject } from '@chakra-ui/react';
// https://medium.com/@a.heydari.dev/simplifying-chakra-ui-v3-recipes-vs-chakra-factory-a-developers-perspective-4020b62f1b4d
// const shimmer = keyframes`
// 100% {
// transform: translateX(100%);
// }
// `;
export const customVariant = ({ bg, bgHover, bgActive, color, colorHover, boxShadowHover }) => {
return defineStyle({
bg,
color,
border: '1px solid transparent',
_focus: {
border: '1px solid',
borderColor: 'black',
},
_hover: {
bg: bgHover,
color: colorHover,
boxShadow: boxShadowHover,
_disabled: {
bg,
boxShadow: 'none',
},
},
_active: {
bg: bgActive,
},
});
};
const buttonRecipe = defineRecipe({
base: {
borderRadius: 0,
background: "green",
},
variants: {
variant: {
"@primary":
customVariant({
bg: { _light: 'brand.600', _dark: 'brand.300' },
bgHover: { _light: 'brand.700', _dark: 'brand.400' },
bgActive: { _light: 'brand.600', _dark: 'brand.300' },
color: { _light: 'white', _dark: 'brand.900' },
colorHover: { _light: 'brand.800', _dark: 'brand.100' },
boxShadowHover: 'outline-over'
}),
"@secondary":
customVariant({
bg: { _light: 'brand.100', _dark: 'brand.900' },
bgHover: { _light: 'brand.200', _dark: 'brand.800' },
bgActive: { _light: 'brand.300', _dark: 'brand.700' },
color: { _light: 'brand.700', _dark: 'brand.50' },
colorHover: { _light: 'brand.800', _dark: 'brand.100' },
boxShadowHover: 'outline-over',
}),
"@danger":
customVariant({
bg: { _light: 'error.600', _dark: 'error.600' },
bgHover: { _light: 'error.700', _dark: 'error.500' },
bgActive: { _light: 'error.600', _dark: 'error.500' },
color: { _light: 'white', _dark: 'error.900' },
colorHover: { _light: 'error.700', _dark: 'error.900' },
boxShadowHover: 'outline-over',
}),
"@success":
customVariant({
bg: { _light: 'green.300', _dark: 'green.300' },
bgHover: { _light: 'green.400', _dark: 'green.400' },
bgActive: { _light: 'green.500', _dark: 'green.400' },
color: { _light: 'white', _dark: 'green.900' },
colorHover: { _light: 'green.500', _dark: 'green.900' },
boxShadowHover: 'outline-over',
}),
"@progress":
defineStyle({
bg: { _light: `brand.500`, _dark: `brand.300` },
overflow: 'hidden',
/*
_after: !props.isLoading
? {
content: '""',
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
transform: 'translateX(-100%)',
bgGradient: `linear(90deg, brand.100/0 0%, brand.100/2 20%, brand.100/5 60%, v.100/0`,
}
: undefined,
*/
}),
"@menu": defineStyle({
bg: 'back.100',
color: 'brand.900',
borderRadius: 0,
border: 0,
_hover: { background: 'back.300' },
_focus: { border: 'none' },
fontSize: '20px',
textTransform: 'uppercase',
}),
},
},
});
console.log(`buttonRecipe: ${JSON.stringify(buttonRecipe, null, 2)}`);
export default buttonRecipe;

View File

@ -1,5 +0,0 @@
export default {
defaultProps: {
colorScheme: 'brand',
},
};

View File

@ -1,29 +0,0 @@
import { defineRecipe } from '@chakra-ui/react';
const drawerRecipe = defineRecipe({
base: {
bg: { _light: 'white', _dark: 'gray.800' },
color: { _light: 'gray.900', _dark: 'whiteAlpha.900' },
boxShadow: { _light: 'lg', _dark: 'dark-lg' },
padding: 4,
borderRadius: 'md',
},
variants: {
variant: {
solid: {
bg: { _light: 'blue.500', _dark: 'blue.300' },
color: 'white',
},
outline: {
border: '1px solid',
borderColor: { _light: 'gray.300', _dark: 'whiteAlpha.300' },
bg: { _light: 'white', _dark: 'gray.900' },
},
}
},
defaultVariants: {
variant: 'solid',
},
});
export default drawerRecipe;

View File

@ -1,20 +0,0 @@
import { defineRecipe } from '@chakra-ui/react';
const flexTheme = defineRecipe({
variants: {
variant: {
'@menu': {
bg: { _light: 'back.100', _dark: 'back.800' },
color: { _light: 'brand.900', _dark: 'brand.100' },
borderRadius: 0,
border: 0,
_hover: { background: { _light: 'back.300', _dark: 'back.600' } },
_focus: { border: 'none' },
fontSize: '20px',
},
},
},
});
export default flexTheme;

View File

@ -1,13 +0,0 @@
export { default as Badge } from './badge';
export { default as button } from './button';
export { default as Checkbox } from './checkbox';
export { default as Input } from './input';
//export { default as NumberInput } from './numberInput.ts_';
export { default as Popover } from './popover';
export { default as Radio } from './radio';
export { default as Select } from './select';
export { default as Switch } from './switch';
export { default as Textarea } from './textarea';
//export { default as Modal } from './modal';
export { default as Flex } from './flex';
export { default as Drawer } from './drawer';

View File

@ -1,21 +0,0 @@
import { defineRecipe } from '@chakra-ui/react';
const inputTheme = defineRecipe({
variants: {
variant: {
outline: {
field: {
bg: { _light: 'gray.50', _dark: 'whiteAlpha.50' },
borderColor: { _light: 'gray.200', _dark: 'whiteAlpha.100' },
color: { _light: 'gray.800', _dark: 'gray.50' },
_focus: {
borderColor: { _light: 'gray.800', _dark: 'gray.50' },
boxShadow: `0 0 0 1px gray.800`,
},
},
},
},
},
});
export default inputTheme;

View File

@ -1,17 +0,0 @@
import { modalAnatomy as parts } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(parts.keys);
const baseStyle = definePartsStyle({
header: {
textAlign: 'center',
},
});
const modalTheme = defineMultiStyleConfig({
baseStyle,
});
export default modalTheme;

View File

@ -1,27 +0,0 @@
import { numberInputAnatomy } from '@chakra-ui/anatomy';
import { createMultiStyleConfigHelpers } from '@chakra-ui/react';
import { getColor, mode } from '@chakra-ui/theme-tools';
const { definePartsStyle, defineMultiStyleConfig } =
createMultiStyleConfigHelpers(numberInputAnatomy.keys);
const baseStyle = definePartsStyle((props) => {
return {
field: {
border: 0,
_focusVisible: {
borderColor: {_light:'brand.500', _dark:'brand.300'},
boxShadow: `0 0 0 1px 'brand.500'`,
ring: '1px',
ringColor: {_light:'brand.500', _dark:'brand.300'},
ringOffset: '1px',
ringOffsetColor: {_light:'brand.500', _dark:'brand.300'},
},
},
};
});
export default defineMultiStyleConfig({
baseStyle,
});

View File

@ -1,77 +0,0 @@
export default {
sizes: {
'3xs': {
content: {
width: '3xs',
},
},
'2xs': {
content: {
width: '2xs',
},
},
xs: {
content: {
width: 'xs',
},
},
sm: {
content: {
width: 'sm',
},
},
md: {
content: {
width: 'md',
},
},
lg: {
content: {
width: 'lg',
},
},
xl: {
content: {
width: 'xl',
},
},
'2xl': {
content: {
width: '2xl',
},
},
'3xl': {
content: {
width: '3xl',
},
},
'4xl': {
content: {
width: '4xl',
},
},
'5xl': {
content: {
width: '5xl',
},
},
'6xl': {
content: {
width: '6xl',
},
},
'7xl': {
content: {
width: '7xl',
},
},
'8xl': {
content: {
width: '8xl',
},
},
},
defaultProps: {
size: 'xs',
},
};

View File

@ -1,5 +0,0 @@
export default {
defaultProps: {
colorScheme: 'brand',
},
};

View File

@ -1,20 +0,0 @@
import { defineRecipe } from '@chakra-ui/react';
const selectTheme = defineRecipe({
variants: {
variant: {
outline: {
field: {
bg: { _light: 'gray.50', _dark: 'whiteAlpha.50' },
borderColor: { _light: 'blackAlpha.100', _dark: 'whiteAlpha.100' },
_focus: {
borderColor: { _light: 'brand.500', _dark: 'brand.300' },
boxShadow: `0 0 0 1px black`,
},
},
},
},
},
});
export default selectTheme;

View File

@ -1,5 +0,0 @@
export default {
defaultProps: {
colorScheme: 'brand',
},
};

View File

@ -1,18 +0,0 @@
import { defineRecipe } from '@chakra-ui/react';
const textAreaTheme = defineRecipe({
variants: {
variant: {
outline: {
bg: { _light: 'gray.50', _dark: 'whiteAlpha.50' },
borderColor: { _light: 'blackAlpha.100', _dark: 'whiteAlpha.100' },
_focus: {
borderColor: { _light: 'brand.500', _dark: 'brand.300' },
boxShadow: `0 0 0 1px brand.500`,
},
},
}
},
});
export default textAreaTheme;

View File

@ -1,67 +0,0 @@
import { Group, VStack } from '@chakra-ui/react';
import { HiMinus, HiPlus } from 'react-icons/hi';
const meta = {
title: 'StyleGuide/Buttons',
};
export default meta;
export const Default = {
render: () => (
<Group>
<Button>Default Button</Button>
<IconButton aria-label="Add" ><HiPlus /></IconButton>
</Group>
),
};
export const Primary = {
render: () => (
<Group>
<Button theme="@primary">Primary Button</Button>
<IconButton theme="@primary" aria-label="Add" ><HiPlus /></IconButton>
</Group>
),
};
export const Secondary = {
render: () => (
<Group>
<Button theme="@secondary">Secondary Button</Button>
<IconButton theme="@secondary" aria-label="Add" ><HiPlus /></IconButton>
</Group>
),
};
export const Danger = {
render: () => (
<Group>
<Button theme="@danger">Danger Button</Button>
<IconButton theme="@danger" aria-label="Remove"><HiMinus /></IconButton>
</Group>
),
};
export const Progress = {
render: () => (
<Group>
<VStack>
<Button variant="solid" colorScheme="brand">
Progress Button with Brand ColorScheme
</Button>
<Button variant="solid" colorScheme="error">
Progress Button with Error ColorScheme
</Button>
<Button variant="solid">
Button
</Button>
<IconButton
variant="solid"
theme="@danger"
aria-label="Remove"
><HiMinus /></IconButton>
</VStack>
</Group>
),
};

View File

@ -1,66 +0,0 @@
import { Box, Flex, FlexProps, HStack } from '@chakra-ui/react';
const Color = ({ children, ...rest }: FlexProps) => (
<Flex flex="1" h="16" p="2" {...rest}>
<Box
bg="white"
display="inline-block"
px="2"
py="1"
m="auto"
fontSize="xs"
fontWeight="bold"
borderRadius="md"
>
{children}
</Box>
</Flex>
);
const Colors = ({ colorScheme = 'gray', ...rest }) => (
<HStack
gap="0"
overflow="hidden"
boxShadow="lg"
color={`${colorScheme}.700`}
borderRadius="md"
{...rest}
>
<Color bg={`${colorScheme}.50`}>50</Color>
<Color bg={`${colorScheme}.100`}>100</Color>
<Color bg={`${colorScheme}.200`}>200</Color>
<Color bg={`${colorScheme}.300`}>300</Color>
<Color bg={`${colorScheme}.400`}>400</Color>
<Color bg={`${colorScheme}.500`}>500</Color>
<Color bg={`${colorScheme}.600`}>600</Color>
<Color bg={`${colorScheme}.700`}>700</Color>
<Color bg={`${colorScheme}.800`}>800</Color>
<Color bg={`${colorScheme}.900`}>900</Color>
</HStack>
);
const meta = {
title: 'StyleGuide/Colors',
};
export default meta;
export const Brand = {
render: () => <Colors colorScheme="brand" />,
};
export const Gray = {
render: () => <Colors colorScheme="gray" />,
};
export const Error = {
render: () => <Colors colorScheme="error" />,
};
export const Warning = {
render: () => <Colors colorScheme="warning" />,
};
export const Success = {
render: () => <Colors colorScheme="success" />,
};

View File

@ -1,8 +1,5 @@
import * as recipes from './recipes';
import { createSystem, defaultConfig, mergeConfigs, SystemConfig } from "@chakra-ui/react" import { createSystem, defaultConfig, mergeConfigs, SystemConfig } from "@chakra-ui/react"
import { colors } from "./foundations/colors" import { colors } from "./colors"
import { shadows } from './foundations/shadows';
import buttonRecipe from './recipes/button';
const baseTheme: SystemConfig = { const baseTheme: SystemConfig = {
globalCss: { globalCss: {
@ -19,99 +16,28 @@ const baseTheme: SystemConfig = {
} }
}, },
theme: { theme: {
//recipes: {...recipes}, slotRecipes: {
recipes: { dialog: {
button: buttonRecipe, slots: [
buttonqsdqsd: { "header"
],
base: { base: {
"display": "inline-flex", header: {
"appearance": "none", fontWeight: "bold",
"alignItems": "center", fontSize: "2xl",
"justifyContent": "center", color: { _dark: "brand.400", _light: "brand.500" }
"userSelect": "none",
"position": "relative",
"borderRadius": "l2",
"whiteSpace": "nowrap",
"verticalAlign": "middle",
"borderWidth": "1px",
"borderColor": "transparent",
"cursor": "button",
"flexShrink": "0",
"outline": "0",
"lineHeight": "1.2",
"isolation": "isolate",
"fontWeight": "medium",
"transitionProperty": "common",
"transitionDuration": "moderate",
"focusVisibleRing": "outside",
"_disabled": {
"layerStyle": "disabled"
}, },
"_icon": { },
"flexShrink": "0" },
},
recipes: {
button: {
base: {
borderRadius: 0,
_hover: {
//boxShadow: "3px 5px 8px gray"
} }
}, },
"variants": {
"variant": {
"solid": {
//"bg": "colorPalette.solid",
"bg": "brand.500",
"color": "colorPalette.contrast",
"_hover": {
"bg": "colorPalette.solid/90"
},
"_expanded": {
"bg": "colorPalette.solid/90"
}
},
"QSDQDS": {
},
"@primary": {
bg: {
_light: "brand.600",
_dark: "brand.300"
},
color: {
_light: "white",
_dark: "brand.900"
},
border: "1px solid transparent",
_focus: {
border: "1px solid",
borderColor: "black"
},
"_hover": {
"bg": {
"_light": "brand.700",
"_dark": "brand.400"
},
"color": {
"_light": "brand.800",
"_dark": "brand.100"
},
"boxShadow": "outline-over",
"_disabled": {
"bg": {
"_light": "brand.600",
"_dark": "brand.300"
},
"boxShadow": "none"
}
},
"_active": {
"bg": {
"_light": "brand.600",
"_dark": "brand.300"
}
}
},
}
},
"defaultVariants": {
//"size": "md",
//"variant": "solid"
}
}, },
}, },
tokens: { tokens: {
@ -120,26 +46,31 @@ const baseTheme: SystemConfig = {
body: { value: `Roboto, Helvetica, Arial, "sans-serif"` }, body: { value: `Roboto, Helvetica, Arial, "sans-serif"` },
}, },
colors, colors,
// spacing: {
// vGutter: { value: '6.25rem' },
// },
shadows
}, },
// semanticTokens: { semanticTokens: {
// colors: { colors: {
// brand: { "@danger": {
// solid: { value: "{colors.brand.500}" }, solid: { value: "{colors.danger.500}" },
// contrast: { value: "{colors.brand.100}" }, contrast: { value: "{colors.danger.100}" },
// fg: { value: "{colors.brand.700}" }, fg: { value: "{colors.danger.900}" },
// muted: { value: "{colors.brand.100}" }, muted: { value: "{colors.danger.100}" },
// subtle: { value: "{colors.brand.200}" }, subtle: { value: "{colors.danger.200}" },
// emphasized: { value: "{colors.brand.300}" }, emphasized: { value: "{colors.danger.300}" },
// focusRing: { value: "{colors.brand.500}" }, focusRing: { value: "{colors.danger.500}" },
// }, },
// }, "brand": {
// }, solid: { value: "{colors.brand.500}" },
contrast: { value: "{colors.brand.100}" },
fg: { value: "{colors.brand.900}" },
muted: { value: "{colors.brand.100}" },
subtle: { value: "{colors.brand.200}" },
emphasized: { value: "{colors.brand.300}" },
focusRing: { value: "{colors.brand.500}" },
},
},
},
}, },
}; };
const config = mergeConfigs(defaultConfig, baseTheme); const config = mergeConfigs(defaultConfig, baseTheme);
//console.log("defaultConfig: " + JSON.stringify(defaultConfig, null, 2)); //console.log("defaultConfig: " + JSON.stringify(defaultConfig, null, 2));
export const systemTheme = createSystem(config); export const systemTheme = createSystem(config);

View File

@ -1,5 +1,5 @@
import { ThemeTypings } from '@chakra-ui/react'; import { ThemeTypings } from '@chakra-ui/react';
import { colors } from '@/theme/foundations/colors'; import { colors } from '@/theme/colors';
export type ColorSchemes = ThemeTypings['colorSchemes'] | keyof typeof colors; export type ColorSchemes = ThemeTypings['colorSchemes'] | keyof typeof colors;