Compare commits
2 Commits
12223347d3
...
83bfeda4ca
Author | SHA1 | Date | |
---|---|---|---|
83bfeda4ca | |||
c489fabb77 |
@ -29,11 +29,9 @@
|
||||
"*.{ts,tsx,js,jsx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/anatomy": "2.3.4",
|
||||
"@chakra-ui/cli": "3.3.1",
|
||||
"@chakra-ui/react": "3.3.1",
|
||||
"@emotion/react": "11.14.0",
|
||||
"@emotion/styled": "11.14.0",
|
||||
"allotment": "1.20.2",
|
||||
"css-mediaquery": "0.1.2",
|
||||
"dayjs": "1.11.13",
|
||||
|
36
front/pnpm-lock.yaml
generated
36
front/pnpm-lock.yaml
generated
@ -8,9 +8,6 @@ importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
'@chakra-ui/anatomy':
|
||||
specifier: 2.3.4
|
||||
version: 2.3.4
|
||||
'@chakra-ui/cli':
|
||||
specifier: 3.3.1
|
||||
version: 3.3.1(@chakra-ui/react@3.3.1(@emotion/react@11.14.0(@types/react@18.3.8)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))
|
||||
@ -20,9 +17,6 @@ importers:
|
||||
'@emotion/react':
|
||||
specifier: 11.14.0
|
||||
version: 11.14.0(@types/react@18.3.8)(react@18.3.1)
|
||||
'@emotion/styled':
|
||||
specifier: 11.14.0
|
||||
version: 11.14.0(@emotion/react@11.14.0(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1)
|
||||
allotment:
|
||||
specifier: 1.20.2
|
||||
version: 1.20.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@ -378,9 +372,6 @@ packages:
|
||||
'@bcoe/v8-coverage@0.2.3':
|
||||
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
|
||||
|
||||
'@chakra-ui/anatomy@2.3.4':
|
||||
resolution: {integrity: sha512-fFIYN7L276gw0Q7/ikMMlZxP7mvnjRaWJ7f3Jsf9VtDOi6eAYIBRrhQe6+SZ0PGmoOkRaBc7gSE5oeIbgFFyrw==}
|
||||
|
||||
'@chakra-ui/cli@3.3.1':
|
||||
resolution: {integrity: sha512-TTpGVT4RuajxzYjMP95Ba3HU052cmdrYgru77ZGD+IDb/HLATjpXNViFAn1R+ITMCxa4v0zqYEWLY9Ex2L090A==}
|
||||
hasBin: true
|
||||
@ -444,16 +435,6 @@ packages:
|
||||
'@emotion/sheet@1.4.0':
|
||||
resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==}
|
||||
|
||||
'@emotion/styled@11.14.0':
|
||||
resolution: {integrity: sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.0.0-rc.0
|
||||
'@types/react': '*'
|
||||
react: '>=16.8.0'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@emotion/unitless@0.10.0':
|
||||
resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==}
|
||||
|
||||
@ -5469,8 +5450,6 @@ snapshots:
|
||||
|
||||
'@bcoe/v8-coverage@0.2.3': {}
|
||||
|
||||
'@chakra-ui/anatomy@2.3.4': {}
|
||||
|
||||
'@chakra-ui/cli@3.3.1(@chakra-ui/react@3.3.1(@emotion/react@11.14.0(@types/react@18.3.8)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))':
|
||||
dependencies:
|
||||
'@chakra-ui/react': 3.3.1(@emotion/react@11.14.0(@types/react@18.3.8)(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@ -5595,21 +5574,6 @@ snapshots:
|
||||
|
||||
'@emotion/sheet@1.4.0': {}
|
||||
|
||||
'@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@18.3.8)(react@18.3.1))(@types/react@18.3.8)(react@18.3.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.7
|
||||
'@emotion/babel-plugin': 11.13.5
|
||||
'@emotion/is-prop-valid': 1.3.1
|
||||
'@emotion/react': 11.14.0(@types/react@18.3.8)(react@18.3.1)
|
||||
'@emotion/serialize': 1.3.3
|
||||
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1)
|
||||
'@emotion/utils': 1.4.2
|
||||
react: 18.3.1
|
||||
optionalDependencies:
|
||||
'@types/react': 18.3.8
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@emotion/unitless@0.10.0': {}
|
||||
|
||||
'@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)':
|
||||
|
@ -26,6 +26,7 @@ import { USERS, USERS_COLLECTION } from '@/service/session';
|
||||
import { hashLocalData } from '@/utils/sso';
|
||||
import { Toaster } from './components/ui/toaster';
|
||||
import { systemTheme } from './theme/theme';
|
||||
import { CloseButton } from './components/ui/close-button';
|
||||
|
||||
const AppEnvHint = () => {
|
||||
const dialog = useDisclosure();
|
||||
@ -56,6 +57,7 @@ const AppEnvHint = () => {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
as="button"
|
||||
zIndex="100000"
|
||||
position="fixed"
|
||||
top="0"
|
||||
@ -63,7 +65,6 @@ const AppEnvHint = () => {
|
||||
insetEnd="0"
|
||||
h="2px"
|
||||
bg="warning.400"
|
||||
as="button"
|
||||
cursor="pointer"
|
||||
data-test-id="devtools"
|
||||
onClick={dialog.onOpen}
|
||||
@ -84,15 +85,10 @@ const AppEnvHint = () => {
|
||||
>
|
||||
{envName.join(' : ')}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box >
|
||||
<DialogRoot open={dialog.open} onOpenChange={dialog.onClose}>
|
||||
<DialogContent>
|
||||
<DialogHeader>Outils développeurs</DialogHeader>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" size="sm">
|
||||
{dialog.open ? "Close" : "Open"} Dialog
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogBody>
|
||||
<Stack>
|
||||
<Text>User</Text>
|
||||
@ -111,7 +107,7 @@ const AppEnvHint = () => {
|
||||
</Stack>
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
<Button onClick={onClose}>Apply</Button>
|
||||
<Button onClick={onClose}>Close</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</DialogRoot>
|
||||
|
@ -2,12 +2,8 @@ import { SyntheticEvent, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
IconButton,
|
||||
Slider,
|
||||
SliderRoot,
|
||||
SliderThumb,
|
||||
SliderTrack,
|
||||
Text,
|
||||
} from '@chakra-ui/react';
|
||||
@ -35,6 +31,7 @@ import { DataUrlAccess } from '@/utils/data-url-access';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
import { Icon } from './Icon';
|
||||
import { Slider } from './ui/slider';
|
||||
|
||||
export enum PlayMode {
|
||||
PLAY_ONE,
|
||||
@ -44,10 +41,10 @@ export enum PlayMode {
|
||||
}
|
||||
|
||||
const playModeIcon = {
|
||||
[PlayMode.PLAY_ONE]: <MdLooksOne size="30px" />,
|
||||
[PlayMode.PLAY_ALL]: <MdTrendingFlat size="30px" />,
|
||||
[PlayMode.PLAY_ONE_LOOP]: <MdRepeatOne size="30px" />,
|
||||
[PlayMode.PLAY_ALL_LOOP]: <MdRepeat size="30px" />,
|
||||
[PlayMode.PLAY_ONE]: <MdLooksOne style={{ width: "100%", height: "100%" }} />,
|
||||
[PlayMode.PLAY_ALL]: <MdTrendingFlat style={{ width: "100%", height: "100%" }} />,
|
||||
[PlayMode.PLAY_ONE_LOOP]: <MdRepeatOne style={{ width: "100%", height: "100%" }} />,
|
||||
[PlayMode.PLAY_ALL_LOOP]: <MdRepeat style={{ width: "100%", height: "100%" }} />,
|
||||
};
|
||||
|
||||
export type AudioPlayerProps = {};
|
||||
@ -89,9 +86,8 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
|
||||
const backColor = useColorModeValue('back.100', 'back.800');
|
||||
const configButton = {
|
||||
borderRadius: 'full',
|
||||
backgroundColor: '#00000000',
|
||||
backgroundColor: 'transparent',
|
||||
_hover: {
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: 'brand.500',
|
||||
},
|
||||
width: "50px",
|
||||
@ -209,6 +205,14 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
|
||||
const onChangeStateToPause = () => {
|
||||
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 (
|
||||
<>
|
||||
{!isNullOrUndefined(trackOffset) && (
|
||||
@ -253,20 +257,21 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
|
||||
{dataGender && ` / ${dataGender.name}`}
|
||||
</Text>
|
||||
<Box width="full" paddingX="15px">
|
||||
<Slider.Root
|
||||
<Slider
|
||||
defaultValue={[0]}
|
||||
value={[timeProgress]}
|
||||
min={0}
|
||||
max={duration}
|
||||
step={0.1}
|
||||
onChange={onSeek}
|
||||
onValueChange={(e) => onSeek(e.value)}
|
||||
variant="outline"
|
||||
// focusThumbOnChange={false}
|
||||
colorPalette="brand"
|
||||
marks={marks()}
|
||||
//focusCapture={false}
|
||||
>
|
||||
<SliderTrack bg="gray.200" height="10px" borderRadius="full">
|
||||
{/* <SliderFilledTrack bg="brand.600" /> */}
|
||||
<SliderTrack bg="brand.200" height="10px" borderRadius="full">
|
||||
</SliderTrack>
|
||||
</Slider.Root>
|
||||
</Slider>
|
||||
</Box>
|
||||
<Flex>
|
||||
<Text
|
||||
@ -288,44 +293,51 @@ export const AudioPlayer = ({ }: AudioPlayerProps) => {
|
||||
{...configButton}
|
||||
aria-label={'Play'}
|
||||
onClick={onPlay}
|
||||
variant="ghost"
|
||||
>
|
||||
{isPlaying ? (
|
||||
<MdPause size="30px" />
|
||||
<MdPause style={{ width: "100%", height: "100%" }} />
|
||||
) : (
|
||||
<MdPlayArrow size="30px" />
|
||||
<MdPlayArrow style={{ width: "100%", height: "100%" }} />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Stop'}
|
||||
onClick={onStop}
|
||||
><MdStop size="30px" /></IconButton>
|
||||
variant="ghost"
|
||||
><MdStop style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Previous track'}
|
||||
onClick={onNavigatePrevious}
|
||||
marginLeft="auto"
|
||||
><MdNavigateBefore size="30px" /> </IconButton>
|
||||
variant="ghost"
|
||||
><MdNavigateBefore style={{ width: "100%", height: "100%" }} /> </IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'jump 15sec in past'}
|
||||
onClick={onFastRewind}
|
||||
><MdFastRewind size="30px" /></IconButton>
|
||||
variant="ghost"
|
||||
><MdFastRewind style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'jump 15sec in future'}
|
||||
onClick={onFastForward}
|
||||
variant="ghost"
|
||||
><MdFastForward style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'Next track'}
|
||||
marginRight="auto"
|
||||
onClick={onNavigateNext}
|
||||
variant="ghost"
|
||||
><MdNavigateNext style={{ width: "100%", height: "100%" }} /></IconButton>
|
||||
<IconButton
|
||||
{...configButton}
|
||||
aria-label={'continue to the end'}
|
||||
onClick={onTypePlay}
|
||||
variant="ghost"
|
||||
>{playModeIcon[playingMode]}</IconButton>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -29,6 +29,7 @@ export const Icon = forwardRef<HTMLDivElement, IconProps>(
|
||||
minWidth="100%"
|
||||
height="100%"
|
||||
color={color}
|
||||
asChild
|
||||
>
|
||||
{IconEl}
|
||||
</Box>
|
||||
|
@ -4,7 +4,7 @@ import { Flex, FlexProps } from '@chakra-ui/react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
import { colors } from '@/theme/foundations/colors';
|
||||
import { colors } from '@/theme/colors';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
|
||||
export type LayoutProps = FlexProps & {
|
||||
|
@ -7,10 +7,14 @@ import {
|
||||
IconButton,
|
||||
Text,
|
||||
useDisclosure,
|
||||
Button,
|
||||
ConditionalValue,
|
||||
Span,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
LuAlignJustify,
|
||||
LuArrowBigLeft,
|
||||
LuKeySquare,
|
||||
LuLogIn,
|
||||
LuLogOut,
|
||||
LuMoon,
|
||||
@ -21,8 +25,8 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { SessionState } from '@/service/SessionState';
|
||||
import { colors } from '@/theme/foundations/colors';
|
||||
import { requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
||||
import { colors } from '@/theme/colors';
|
||||
import { requestOpenSite, requestSignIn, requestSignOut, requestSignUp } from '@/utils/sso';
|
||||
import { useSessionService } from '@/service/session';
|
||||
import { MdHelp, MdHome, MdMore, MdOutlinePlaylistPlay, MdOutlineUploadFile, MdSupervisedUserCircle } from 'react-icons/md';
|
||||
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '@/components/ui/menu';
|
||||
@ -33,12 +37,14 @@ import {
|
||||
DrawerHeader,
|
||||
DrawerRoot,
|
||||
} from '@/components/ui/drawer';
|
||||
import { Button } from '../ui/themed';
|
||||
|
||||
export const TOP_BAR_HEIGHT = '50px';
|
||||
|
||||
export const BUTTON_TOP_BAR_PROPERTY = {
|
||||
theme: '@menu',
|
||||
variant: "ghost" as ConditionalValue<"ghost" | "outline" | "solid" | "subtle" | "surface" | "plain" | undefined>,
|
||||
//colorPalette: "brand",
|
||||
fontSize: '20px',
|
||||
textTransform: 'uppercase',
|
||||
height: TOP_BAR_HEIGHT,
|
||||
};
|
||||
|
||||
@ -70,6 +76,9 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
clearToken();
|
||||
requestSignOut();
|
||||
};
|
||||
const onKarso = (): void => {
|
||||
requestOpenSite();
|
||||
};
|
||||
const onSelectAdd = () => {
|
||||
navigate('/add');
|
||||
};
|
||||
@ -101,10 +110,12 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
zIndex={200}
|
||||
>
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onChangeTheme}>
|
||||
<LuAlignJustify />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Karusic
|
||||
</Text>
|
||||
<HStack>
|
||||
<LuAlignJustify />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Karusic
|
||||
</Text>
|
||||
</HStack>
|
||||
</Button>
|
||||
{title && (
|
||||
<Text
|
||||
@ -113,6 +124,7 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
textTransform="uppercase"
|
||||
marginRight="auto"
|
||||
userSelect="none"
|
||||
color="brand.500"
|
||||
>
|
||||
{title}
|
||||
</Text>
|
||||
@ -121,7 +133,7 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
<Flex right="0">
|
||||
{session?.state !== SessionState.CONNECTED && (
|
||||
<>
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} theme="@primary" onClick={onSignIn}>
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onSignIn}>
|
||||
<LuLogIn />
|
||||
<Text paddingLeft="3px" fontWeight="bold">
|
||||
Sign-in
|
||||
@ -143,14 +155,14 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
<MenuRoot>
|
||||
<MenuTrigger asChild>
|
||||
<IconButton
|
||||
as={IconButton}
|
||||
asChild
|
||||
aria-label="Options"
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
width={TOP_BAR_HEIGHT}
|
||||
><MdSupervisedUserCircle /></IconButton>
|
||||
</MenuTrigger>
|
||||
<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 />
|
||||
<Box flex="1">Sign in as {session?.login ?? 'Fail'}</Box>
|
||||
</MenuItem>
|
||||
@ -159,6 +171,9 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
<MenuItem value="Sign-out" valueText="Sign-out" onClick={onSignOut}>
|
||||
<LuLogOut /> Sign-out
|
||||
</MenuItem>
|
||||
<MenuItem value="karso" valueText="Karso" onClick={onKarso}>
|
||||
<LuKeySquare /> Karso (SSO)
|
||||
</MenuItem>
|
||||
{colorMode === 'light' ? (
|
||||
<MenuItem value="set-dark" valueText="set-dark" onClick={toggleColorMode}>
|
||||
<LuMoon /> Set dark mode
|
||||
@ -189,45 +204,50 @@ export const TopBar = ({ title, children }: TopBarProps) => {
|
||||
color={useColorModeValue('brand.900', 'brand.50')}
|
||||
textTransform="uppercase"
|
||||
>
|
||||
<HStack height={TOP_BAR_HEIGHT}>
|
||||
<HStack
|
||||
{...BUTTON_TOP_BAR_PROPERTY} cursor="pointer">
|
||||
<LuArrowBigLeft />
|
||||
<Text as="span" paddingLeft="3px">
|
||||
<Span paddingLeft="3px">
|
||||
Karusic
|
||||
</Text>
|
||||
</Span>
|
||||
</HStack>
|
||||
</DrawerHeader>
|
||||
<DrawerBody paddingX="0px">
|
||||
<Box marginY="3" />
|
||||
<Button
|
||||
background="#00000000"
|
||||
borderRadius="0px"
|
||||
onClick={onSelectHome}
|
||||
width="full"
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
>
|
||||
<MdHome />
|
||||
<MdHome style={{ width: "45px", height: "45px" }} />
|
||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||
Home
|
||||
</Text>
|
||||
</Button>
|
||||
<hr />
|
||||
<Box marginY="5" marginX="10" height="2px" background="brand.600" />
|
||||
<Button
|
||||
background="#00000000"
|
||||
borderRadius="0px"
|
||||
onClick={onSelectOnAir}
|
||||
width="full"
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
>
|
||||
<MdOutlinePlaylistPlay />
|
||||
<MdOutlinePlaylistPlay style={{ width: "45px", height: "45px" }} />
|
||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||
On air
|
||||
</Text>
|
||||
</Button>
|
||||
<hr />
|
||||
<Box marginY="5" marginX="10" height="2px" background="brand.600" />
|
||||
<Button
|
||||
background="#00000000"
|
||||
borderRadius="0px"
|
||||
onClick={onSelectAdd}
|
||||
width="full"
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
>
|
||||
<MdOutlineUploadFile />
|
||||
<MdOutlineUploadFile style={{ width: "45px", height: "45px" }} />
|
||||
<Text paddingLeft="3px" fontWeight="bold" marginRight="auto">
|
||||
Add Media
|
||||
</Text>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Flex, Text } from '@chakra-ui/react';
|
||||
import { Flex, Span, Text } from '@chakra-ui/react';
|
||||
import { LuDisc3 } from 'react-icons/lu';
|
||||
|
||||
import { Album } from '@/back-api';
|
||||
@ -25,7 +25,7 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
||||
data={dataAlbum?.covers}
|
||||
size={BASE_WRAP_ICON_SIZE}
|
||||
flex={1}
|
||||
// TODO: iconEmpty={LuDisc3}
|
||||
iconEmpty={<LuDisc3 />}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
@ -36,9 +36,8 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
||||
overflowX="hidden"
|
||||
flex={1}
|
||||
>
|
||||
<Text
|
||||
as="span"
|
||||
// align="left"
|
||||
<Span
|
||||
textAlign="left"
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
@ -47,10 +46,9 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
||||
// noOfLines={[1, 2]}
|
||||
>
|
||||
{dataAlbum?.name}
|
||||
</Text>
|
||||
<Text
|
||||
as="span"
|
||||
// align="left"
|
||||
</Span>
|
||||
<Span
|
||||
textAlign="left"
|
||||
fontSize="15px"
|
||||
userSelect="none"
|
||||
marginRight="auto"
|
||||
@ -58,7 +56,7 @@ export const DisplayAlbum = ({ dataAlbum }: DisplayAlbumProps) => {
|
||||
// noOfLines={1}
|
||||
>
|
||||
{countTracksOfAnAlbum} track{countTracksOfAnAlbum >= 1 && 's'}
|
||||
</Text>
|
||||
</Span>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
|
@ -1,14 +1,12 @@
|
||||
import { useState } from 'react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
|
||||
import {
|
||||
IconButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { LuMenu } from 'react-icons/lu';
|
||||
import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from '../ui/menu';
|
||||
import { Button } from '../ui/themed';
|
||||
import { customVariant } from '@/theme/recipes/button';
|
||||
import { Button } from '../ui/button';
|
||||
|
||||
|
||||
export type MenuElement = {
|
||||
icon?: ReactNode;
|
||||
name: string;
|
||||
onClick: () => void;
|
||||
};
|
||||
@ -17,21 +15,6 @@ export type ContextMenuProps = {
|
||||
elements?: MenuElement[];
|
||||
};
|
||||
|
||||
const theme = {
|
||||
plop: {
|
||||
bg: { _light: 'red', _dark: 'brand.300', _pink: "orange" },
|
||||
_hover: { bg: { _light: 'green', _dark: 'brand.400', _pink: "black" } },
|
||||
// bg: { _light: 'brand.600', _dark: 'brand.300' },
|
||||
// _hover: { bg: { _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'
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
export const ContextMenu = ({ elements }: ContextMenuProps) => {
|
||||
if (!elements) {
|
||||
return <></>;
|
||||
@ -40,17 +23,20 @@ export const ContextMenu = ({ elements }: ContextMenuProps) => {
|
||||
<MenuRoot
|
||||
data-testid="context-menu">
|
||||
<MenuTrigger asChild
|
||||
marginY="auto"
|
||||
marginRight="4px"
|
||||
data-testid="context-menu_trigger">
|
||||
{/* This is very stupid, we need to set as span to prevent a button in button... WTF */}
|
||||
<Button {...theme.plop} /*theme="@primary"*/>
|
||||
<Button variant="ghost" color="brand.500">
|
||||
<LuMenu />
|
||||
</Button>
|
||||
</MenuTrigger>
|
||||
<MenuContent
|
||||
data-testid="context-menu_content">
|
||||
{elements?.map((data) => (
|
||||
<MenuItem key={data.name} value={data.name} onClick={data.onClick}
|
||||
data-testid="context-menu_item">
|
||||
<MenuItem key={data.name} value={data.name} onClick={data.onClick} height="65px" fontSize="25px"
|
||||
data-test-id="context-menu_item" >
|
||||
{data.icon}
|
||||
{data.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
|
@ -104,14 +104,13 @@ export const CenterIcon = ({
|
||||
return (
|
||||
<Box position="relative" w={sizeIcon} h={sizeIcon} flex="none" {...rest}>
|
||||
<Box
|
||||
as={IconEl}
|
||||
w={sizeIcon}
|
||||
h={sizeIcon}
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
transform="translate(-50%, -50%)"
|
||||
/>
|
||||
>{IconEl}</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@ -150,14 +149,13 @@ export const FormCovers = ({
|
||||
<Box width="125px" height="125px" position="relative">
|
||||
<Box width="125px" height="125px" position="absolute">
|
||||
<CenterIcon
|
||||
icon={MdHighlightOff}
|
||||
width="125px"
|
||||
sizeIcon="100%"
|
||||
zIndex="+1"
|
||||
color="#00000020"
|
||||
_hover={{ color: 'red' }}
|
||||
onClick={() => onRemove && onRemove(index)}
|
||||
/>
|
||||
><MdHighlightOff /></CenterIcon>
|
||||
</Box>
|
||||
<Image loading="lazy" src={data} boxSize="full" />
|
||||
</Box>
|
||||
|
@ -1,14 +1,11 @@
|
||||
import { RefObject } from 'react';
|
||||
|
||||
import {
|
||||
NumberInput,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { FormGroup } from '@/components/form/FormGroup';
|
||||
import { UseFormidableReturn } from '@/components/form/Formidable';
|
||||
import { NumberInputField, NumberInputProps, NumberInputRoot } from '../ui/number-input';
|
||||
|
||||
export type FormNumberProps = Pick<
|
||||
NumberInput.RootProps,
|
||||
NumberInputProps,
|
||||
'step' | 'defaultValue' | 'min' | 'max'
|
||||
> & {
|
||||
form: UseFormidableReturn;
|
||||
@ -30,23 +27,26 @@ export const FormNumber = ({
|
||||
defaultValue,
|
||||
...rest
|
||||
}: FormNumberProps) => {
|
||||
const onEvent = (value) => {
|
||||
form.setValues({ [variableName]: value.value });
|
||||
}
|
||||
return (
|
||||
<FormGroup
|
||||
isModify={form.isModify[variableName]}
|
||||
onRestore={() => form.restoreValue({ [variableName]: true })}
|
||||
{...rest}
|
||||
>
|
||||
<NumberInput.Root
|
||||
<NumberInputRoot
|
||||
ref={ref}
|
||||
value={form.values[variableName]}
|
||||
onValueChange={(value) => form.setValues({ [variableName]: value })}
|
||||
onValueChange={onEvent}
|
||||
step={step}
|
||||
defaultValue={defaultValue}
|
||||
min={min}
|
||||
max={max}
|
||||
>
|
||||
<NumberInput.Input />
|
||||
</NumberInput.Root>
|
||||
<NumberInputField />
|
||||
</NumberInputRoot>
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ export const DisplayGender = ({ dataGender }: DisplayGenderProps) => {
|
||||
data={dataGender?.covers}
|
||||
size="100"
|
||||
height="full"
|
||||
//TODO: iconEmpty={LuDisc3}
|
||||
iconEmpty={<LuDisc3 />}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
|
@ -25,7 +25,8 @@ import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { useCountTracksWithAlbumId } from '@/service/Track';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||
import { Button } from '../ui/themed';
|
||||
import { Button } from '../ui/button';
|
||||
|
||||
|
||||
export type AlbumEditPopUpProps = {};
|
||||
|
||||
@ -139,7 +140,7 @@ export const AlbumEditPopUp = ({ }: AlbumEditPopUpProps) => {
|
||||
//initialFocusRef={initialRef}
|
||||
//finalFocusRef={finalRef}
|
||||
//closeOnOverlayClick={false}
|
||||
onOpenChange={onClose}
|
||||
//onOpenChange={onClose}
|
||||
open={true}
|
||||
data-testid="album-edit-pop-up"
|
||||
>
|
||||
@ -168,7 +169,7 @@ export const AlbumEditPopUp = ({ }: AlbumEditPopUpProps) => {
|
||||
<Button
|
||||
onClick={disclosure.onOpen}
|
||||
marginRight="auto"
|
||||
theme="@danger"
|
||||
colorPalette="@danger"
|
||||
disabled={countTracksOfAnAlbum !== 0}
|
||||
>
|
||||
<MdDeleteForever /> Remove Media
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
Flex,
|
||||
Text,
|
||||
useDisclosure,
|
||||
Button,
|
||||
} from '@chakra-ui/react';
|
||||
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||
import {
|
||||
@ -25,7 +26,6 @@ import { useArtistService, useSpecificArtist } from '@/service/Artist';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { useCountTracksOfAnArtist } from '@/service/Track';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
import { Button } from '../ui/themed';
|
||||
|
||||
export type ArtistEditPopUpProps = {};
|
||||
|
||||
@ -166,7 +166,7 @@ export const ArtistEditPopUp = ({ }: ArtistEditPopUpProps) => {
|
||||
<Button
|
||||
onClick={disclosure.onOpen}
|
||||
marginRight="auto"
|
||||
theme="@danger"
|
||||
colorPalette="@danger"
|
||||
disabled={countTracksOnAnArtist !== 0}
|
||||
>
|
||||
<MdDeleteForever /> Remove Media
|
||||
@ -217,6 +217,7 @@ export const ArtistEditPopUp = ({ }: ArtistEditPopUpProps) => {
|
||||
<Button
|
||||
onClick={() => setAdmin((value) => !value)}
|
||||
marginRight="auto"
|
||||
colorPalette={admin ? undefined : "@danger"}
|
||||
>
|
||||
{admin ? (
|
||||
<>
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
Flex,
|
||||
Text,
|
||||
useDisclosure,
|
||||
Button
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
MdAdminPanelSettings,
|
||||
@ -26,7 +27,6 @@ import { useGenderService, useSpecificGender } from '@/service/Gender';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { useCountTracksOfAGender } from '@/service/Track';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
import { Button } from '../ui/themed';
|
||||
|
||||
export type GenderEditPopUpProps = {};
|
||||
|
||||
@ -166,7 +166,7 @@ export const GenderEditPopUp = ({ }: GenderEditPopUpProps) => {
|
||||
<Button
|
||||
onClick={disclosure.onOpen}
|
||||
marginRight="auto"
|
||||
theme="@danger"
|
||||
colorPalette="@danger"
|
||||
disabled={countTracksOnAGender !== 0}
|
||||
>
|
||||
<MdDeleteForever /> Remove gender
|
||||
|
@ -4,10 +4,11 @@ import {
|
||||
Flex,
|
||||
Progress,
|
||||
Text,
|
||||
Button,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
import { DialogBody, DialogContent, DialogFooter, DialogHeader, DialogRoot } from '@/components/ui/dialog';
|
||||
import { Button } from '../ui/themed';
|
||||
|
||||
|
||||
export type PopUpUploadProgressProps = {
|
||||
title: string;
|
||||
@ -88,7 +89,7 @@ export const PopUpUploadProgress = ({
|
||||
</DialogBody>
|
||||
<DialogFooter>
|
||||
{isFinished ? (
|
||||
<Button onClick={onClose} theme="@success">
|
||||
<Button onClick={onClose} colorPalette="green">
|
||||
Ok
|
||||
</Button>
|
||||
) : (
|
||||
|
@ -3,6 +3,7 @@ import { useRef, useState } from 'react';
|
||||
import {
|
||||
Text,
|
||||
useDisclosure,
|
||||
Button,
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
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 { useSpecificTrack, useTrackService } from '@/service/Track';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
import { Button } from '../ui/themed';
|
||||
|
||||
export type TrackEditPopUpProps = {};
|
||||
|
||||
@ -113,7 +113,7 @@ export const TrackEditPopUp = ({ }: TrackEditPopUpProps) => {
|
||||
<Button
|
||||
onClick={disclosure.onOpen}
|
||||
marginRight="auto"
|
||||
theme="@danger"
|
||||
colorPalette="@danger"
|
||||
>
|
||||
<MdDeleteForever /> Remove Media
|
||||
</Button>
|
||||
|
@ -109,18 +109,17 @@ export const SelectMultiple = ({
|
||||
return (
|
||||
<Flex direction="column" width="full" gap="0px">
|
||||
{selectedOptions && (
|
||||
<HStack wrap="wrap" /*spacing="5px"*/ justify="left" width="full" marginBottom="2px">
|
||||
<HStack wrap="wrap" gap="5px" justify="left" width="full" marginBottom="2px">
|
||||
{selectedOptions.map((data) => (
|
||||
<Flex align="flex-start" key={data[keyKey]}>
|
||||
<Tag.Root
|
||||
size="md"
|
||||
key="md"
|
||||
size="xl"
|
||||
borderRadius="5px"
|
||||
variant="solid"
|
||||
backgroundColor="green.500"
|
||||
variant="surface"
|
||||
backgroundColor="green.800"
|
||||
>
|
||||
<Tag.Label>{data[keyValue] ?? `id=${data[keyKey]}`}</Tag.Label>
|
||||
<Tag.CloseTrigger onClick={() => selectValue(data)} />
|
||||
<Tag.CloseTrigger boxSize="5" onClick={() => selectValue(data)} />
|
||||
</Tag.Root>
|
||||
</Flex>
|
||||
))}
|
||||
|
@ -113,7 +113,7 @@ export const SelectSingle = ({
|
||||
showList ? (currentSearch ?? '') : (selectedOptions?.name ?? (hasSuggestion ? `suggest: ${currentSearch}` : ''))
|
||||
}
|
||||
backgroundColor={
|
||||
showList || !selectedOptions ? undefined : 'green.500'
|
||||
showList || !selectedOptions ? undefined : 'green.800'
|
||||
}
|
||||
borderRadius="5px 0 0 5px"
|
||||
/>
|
||||
|
@ -23,9 +23,9 @@ export const DisplayTrack = ({
|
||||
data={track?.covers}
|
||||
size="50"
|
||||
height="full"
|
||||
/* TODO: iconEmpty={
|
||||
trackActive?.id === track.id ? LuPlay : LuMusic2
|
||||
} */
|
||||
iconEmpty={
|
||||
trackActive?.id === track.id ? <LuPlay /> : <LuMusic2 />
|
||||
}
|
||||
onClick={onClick}
|
||||
/>
|
||||
<Flex
|
||||
|
@ -30,11 +30,11 @@ export const DisplayTrackFull = ({
|
||||
data-testid="display-track-full">
|
||||
<Covers
|
||||
data={track?.covers}
|
||||
size="50"
|
||||
size="60px"
|
||||
marginY="auto"
|
||||
/* TODO: iconEmpty={
|
||||
trackActive?.id === track.id ? LuPlay : LuMusic2
|
||||
} */
|
||||
iconEmpty={
|
||||
trackActive?.id === track.id ? <LuPlay /> : <LuMusic2 />
|
||||
}
|
||||
onClick={onClick}
|
||||
/>
|
||||
<Flex
|
||||
|
@ -20,7 +20,7 @@ export const DisplayTrackSkeleton = () => {
|
||||
{/* <SkeletonText
|
||||
skeletonHeight="20px"
|
||||
noOfLines={1}
|
||||
spacing={0}
|
||||
gap={0}
|
||||
width="50%"
|
||||
marginY="auto"
|
||||
/> */}
|
||||
|
74
front/src/components/ui/avatar.tsx
Normal file
74
front/src/components/ui/avatar.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
"use client"
|
||||
|
||||
import type { GroupProps, SlotRecipeProps } from "@chakra-ui/react"
|
||||
import { Avatar as ChakraAvatar, Group } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
type ImageProps = React.ImgHTMLAttributes<HTMLImageElement>
|
||||
|
||||
export interface AvatarProps extends ChakraAvatar.RootProps {
|
||||
name?: string
|
||||
src?: string
|
||||
srcSet?: string
|
||||
loading?: ImageProps["loading"]
|
||||
icon?: React.ReactElement
|
||||
fallback?: React.ReactNode
|
||||
}
|
||||
|
||||
export const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
|
||||
function Avatar(props, ref) {
|
||||
const { name, src, srcSet, loading, icon, fallback, children, ...rest } =
|
||||
props
|
||||
return (
|
||||
<ChakraAvatar.Root ref={ref} {...rest}>
|
||||
<AvatarFallback name={name} icon={icon}>
|
||||
{fallback}
|
||||
</AvatarFallback>
|
||||
<ChakraAvatar.Image src={src} srcSet={srcSet} loading={loading} />
|
||||
{children}
|
||||
</ChakraAvatar.Root>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
interface AvatarFallbackProps extends ChakraAvatar.FallbackProps {
|
||||
name?: string
|
||||
icon?: React.ReactElement
|
||||
}
|
||||
|
||||
const AvatarFallback = React.forwardRef<HTMLDivElement, AvatarFallbackProps>(
|
||||
function AvatarFallback(props, ref) {
|
||||
const { name, icon, children, ...rest } = props
|
||||
return (
|
||||
<ChakraAvatar.Fallback ref={ref} {...rest}>
|
||||
{children}
|
||||
{name != null && children == null && <>{getInitials(name)}</>}
|
||||
{name == null && children == null && (
|
||||
<ChakraAvatar.Icon asChild={!!icon}>{icon}</ChakraAvatar.Icon>
|
||||
)}
|
||||
</ChakraAvatar.Fallback>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
function getInitials(name: string) {
|
||||
const names = name.trim().split(" ")
|
||||
const firstName = names[0] != null ? names[0] : ""
|
||||
const lastName = names.length > 1 ? names[names.length - 1] : ""
|
||||
return firstName && lastName
|
||||
? `${firstName.charAt(0)}${lastName.charAt(0)}`
|
||||
: firstName.charAt(0)
|
||||
}
|
||||
|
||||
interface AvatarGroupProps extends GroupProps, SlotRecipeProps<"avatar"> {}
|
||||
|
||||
export const AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(
|
||||
function AvatarGroup(props, ref) {
|
||||
const { size, variant, borderless, ...rest } = props
|
||||
return (
|
||||
<ChakraAvatar.PropsProvider value={{ size, variant, borderless }}>
|
||||
<Group gap="0" spaceX="-3" ref={ref} {...rest} />
|
||||
</ChakraAvatar.PropsProvider>
|
||||
)
|
||||
},
|
||||
)
|
40
front/src/components/ui/button.tsx
Normal file
40
front/src/components/ui/button.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import type { ButtonProps as ChakraButtonProps } from "@chakra-ui/react"
|
||||
import {
|
||||
AbsoluteCenter,
|
||||
Button as ChakraButton,
|
||||
Span,
|
||||
Spinner,
|
||||
} from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
interface ButtonLoadingProps {
|
||||
loading?: boolean
|
||||
loadingText?: React.ReactNode
|
||||
}
|
||||
|
||||
export interface ButtonProps extends ChakraButtonProps, ButtonLoadingProps {}
|
||||
|
||||
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
function Button(props, ref) {
|
||||
const { loading, disabled, loadingText, children, ...rest } = props
|
||||
return (
|
||||
<ChakraButton disabled={loading || disabled} ref={ref} {...rest}>
|
||||
{loading && !loadingText ? (
|
||||
<>
|
||||
<AbsoluteCenter display="inline-flex">
|
||||
<Spinner size="inherit" color="inherit" />
|
||||
</AbsoluteCenter>
|
||||
<Span opacity={0}>{children}</Span>
|
||||
</>
|
||||
) : loading && loadingText ? (
|
||||
<>
|
||||
<Spinner size="inherit" color="inherit" />
|
||||
{loadingText}
|
||||
</>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</ChakraButton>
|
||||
)
|
||||
},
|
||||
)
|
25
front/src/components/ui/checkbox.tsx
Normal file
25
front/src/components/ui/checkbox.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { Checkbox as ChakraCheckbox } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
export interface CheckboxProps extends ChakraCheckbox.RootProps {
|
||||
icon?: React.ReactNode
|
||||
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
|
||||
rootRef?: React.Ref<HTMLLabelElement>
|
||||
}
|
||||
|
||||
export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
|
||||
function Checkbox(props, ref) {
|
||||
const { icon, children, inputProps, rootRef, ...rest } = props
|
||||
return (
|
||||
<ChakraCheckbox.Root ref={rootRef} {...rest}>
|
||||
<ChakraCheckbox.HiddenInput ref={ref} {...inputProps} />
|
||||
<ChakraCheckbox.Control>
|
||||
{icon || <ChakraCheckbox.Indicator />}
|
||||
</ChakraCheckbox.Control>
|
||||
{children != null && (
|
||||
<ChakraCheckbox.Label>{children}</ChakraCheckbox.Label>
|
||||
)}
|
||||
</ChakraCheckbox.Root>
|
||||
)
|
||||
},
|
||||
)
|
@ -1,23 +1,35 @@
|
||||
"use client"
|
||||
|
||||
import { ThemeProvider, useTheme, ThemeProviderProps } from "next-themes"
|
||||
import type { IconButtonProps } from "@chakra-ui/react"
|
||||
import { ClientOnly, IconButton, Skeleton } from "@chakra-ui/react"
|
||||
import { ThemeProvider, useTheme } from "next-themes"
|
||||
import type { ThemeProviderProps } from "next-themes"
|
||||
import * as React from "react"
|
||||
import { LuMoon, LuSun } from "react-icons/lu"
|
||||
|
||||
export interface ColorModeProviderProps extends ThemeProviderProps { }
|
||||
export interface ColorModeProviderProps extends ThemeProviderProps {}
|
||||
|
||||
export function ColorModeProvider(props: ColorModeProviderProps) {
|
||||
return (
|
||||
<ThemeProvider attribute="class" themes={['pink', 'dark', 'light']} disableTransitionOnChange {...props} />
|
||||
<ThemeProvider attribute="class" disableTransitionOnChange {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
export function useColorMode() {
|
||||
export type ColorMode = "light" | "dark"
|
||||
|
||||
export interface UseColorModeReturn {
|
||||
colorMode: ColorMode
|
||||
setColorMode: (colorMode: ColorMode) => void
|
||||
toggleColorMode: () => void
|
||||
}
|
||||
|
||||
export function useColorMode(): UseColorModeReturn {
|
||||
const { resolvedTheme, setTheme } = useTheme()
|
||||
const toggleColorMode = () => {
|
||||
console.log(`plop: ${resolvedTheme}`);
|
||||
setTheme(resolvedTheme === "light" ? "pink" : resolvedTheme === "pink" ? "dark" : "light")
|
||||
setTheme(resolvedTheme === "light" ? "dark" : "light")
|
||||
}
|
||||
return {
|
||||
colorMode: resolvedTheme,
|
||||
colorMode: resolvedTheme as ColorMode,
|
||||
setColorMode: setTheme,
|
||||
toggleColorMode,
|
||||
}
|
||||
@ -25,5 +37,39 @@ export function useColorMode() {
|
||||
|
||||
export function useColorModeValue<T>(light: T, dark: T) {
|
||||
const { colorMode } = useColorMode()
|
||||
return colorMode === "light" ? light : dark
|
||||
return colorMode === "dark" ? dark : light
|
||||
}
|
||||
|
||||
export function ColorModeIcon() {
|
||||
const { colorMode } = useColorMode()
|
||||
return colorMode === "dark" ? <LuMoon /> : <LuSun />
|
||||
}
|
||||
|
||||
interface ColorModeButtonProps extends Omit<IconButtonProps, "aria-label"> {}
|
||||
|
||||
export const ColorModeButton = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
ColorModeButtonProps
|
||||
>(function ColorModeButton(props, ref) {
|
||||
const { toggleColorMode } = useColorMode()
|
||||
return (
|
||||
<ClientOnly fallback={<Skeleton boxSize="8" />}>
|
||||
<IconButton
|
||||
onClick={toggleColorMode}
|
||||
variant="ghost"
|
||||
aria-label="Toggle color mode"
|
||||
size="sm"
|
||||
ref={ref}
|
||||
{...props}
|
||||
css={{
|
||||
_icon: {
|
||||
width: "5",
|
||||
height: "5",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ColorModeIcon />
|
||||
</IconButton>
|
||||
</ClientOnly>
|
||||
)
|
||||
})
|
||||
|
33
front/src/components/ui/field.tsx
Normal file
33
front/src/components/ui/field.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { Field as ChakraField } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
export interface FieldProps extends Omit<ChakraField.RootProps, "label"> {
|
||||
label?: React.ReactNode
|
||||
helperText?: React.ReactNode
|
||||
errorText?: React.ReactNode
|
||||
optionalText?: React.ReactNode
|
||||
}
|
||||
|
||||
export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
||||
function Field(props, ref) {
|
||||
const { label, children, helperText, errorText, optionalText, ...rest } =
|
||||
props
|
||||
return (
|
||||
<ChakraField.Root ref={ref} {...rest}>
|
||||
{label && (
|
||||
<ChakraField.Label>
|
||||
{label}
|
||||
<ChakraField.RequiredIndicator fallback={optionalText} />
|
||||
</ChakraField.Label>
|
||||
)}
|
||||
{children}
|
||||
{helperText && (
|
||||
<ChakraField.HelperText>{helperText}</ChakraField.HelperText>
|
||||
)}
|
||||
{errorText && (
|
||||
<ChakraField.ErrorText>{errorText}</ChakraField.ErrorText>
|
||||
)}
|
||||
</ChakraField.Root>
|
||||
)
|
||||
},
|
||||
)
|
53
front/src/components/ui/input-group.tsx
Normal file
53
front/src/components/ui/input-group.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import type { BoxProps, InputElementProps } from "@chakra-ui/react"
|
||||
import { Group, InputElement } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
export interface InputGroupProps extends BoxProps {
|
||||
startElementProps?: InputElementProps
|
||||
endElementProps?: InputElementProps
|
||||
startElement?: React.ReactNode
|
||||
endElement?: React.ReactNode
|
||||
children: React.ReactElement<InputElementProps>
|
||||
startOffset?: InputElementProps["paddingStart"]
|
||||
endOffset?: InputElementProps["paddingEnd"]
|
||||
}
|
||||
|
||||
export const InputGroup = React.forwardRef<HTMLDivElement, InputGroupProps>(
|
||||
function InputGroup(props, ref) {
|
||||
const {
|
||||
startElement,
|
||||
startElementProps,
|
||||
endElement,
|
||||
endElementProps,
|
||||
children,
|
||||
startOffset = "6px",
|
||||
endOffset = "6px",
|
||||
...rest
|
||||
} = props
|
||||
|
||||
const child =
|
||||
React.Children.only<React.ReactElement<InputElementProps>>(children)
|
||||
|
||||
return (
|
||||
<Group ref={ref} {...rest}>
|
||||
{startElement && (
|
||||
<InputElement pointerEvents="none" {...startElementProps}>
|
||||
{startElement}
|
||||
</InputElement>
|
||||
)}
|
||||
{React.cloneElement(child, {
|
||||
...(startElement && {
|
||||
ps: `calc(var(--input-height) - ${startOffset})`,
|
||||
}),
|
||||
...(endElement && { pe: `calc(var(--input-height) - ${endOffset})` }),
|
||||
...children.props,
|
||||
})}
|
||||
{endElement && (
|
||||
<InputElement placement="end" {...endElementProps}>
|
||||
{endElement}
|
||||
</InputElement>
|
||||
)}
|
||||
</Group>
|
||||
)
|
||||
},
|
||||
)
|
24
front/src/components/ui/number-input.tsx
Normal file
24
front/src/components/ui/number-input.tsx
Normal 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
|
59
front/src/components/ui/popover.tsx
Normal file
59
front/src/components/ui/popover.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { Popover as ChakraPopover, Portal } from "@chakra-ui/react"
|
||||
import { CloseButton } from "./close-button"
|
||||
import * as React from "react"
|
||||
|
||||
interface PopoverContentProps extends ChakraPopover.ContentProps {
|
||||
portalled?: boolean
|
||||
portalRef?: React.RefObject<HTMLElement>
|
||||
}
|
||||
|
||||
export const PopoverContent = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
PopoverContentProps
|
||||
>(function PopoverContent(props, ref) {
|
||||
const { portalled = true, portalRef, ...rest } = props
|
||||
return (
|
||||
<Portal disabled={!portalled} container={portalRef}>
|
||||
<ChakraPopover.Positioner>
|
||||
<ChakraPopover.Content ref={ref} {...rest} />
|
||||
</ChakraPopover.Positioner>
|
||||
</Portal>
|
||||
)
|
||||
})
|
||||
|
||||
export const PopoverArrow = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
ChakraPopover.ArrowProps
|
||||
>(function PopoverArrow(props, ref) {
|
||||
return (
|
||||
<ChakraPopover.Arrow {...props} ref={ref}>
|
||||
<ChakraPopover.ArrowTip />
|
||||
</ChakraPopover.Arrow>
|
||||
)
|
||||
})
|
||||
|
||||
export const PopoverCloseTrigger = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
ChakraPopover.CloseTriggerProps
|
||||
>(function PopoverCloseTrigger(props, ref) {
|
||||
return (
|
||||
<ChakraPopover.CloseTrigger
|
||||
position="absolute"
|
||||
top="1"
|
||||
insetEnd="1"
|
||||
{...props}
|
||||
asChild
|
||||
ref={ref}
|
||||
>
|
||||
<CloseButton size="sm" />
|
||||
</ChakraPopover.CloseTrigger>
|
||||
)
|
||||
})
|
||||
|
||||
export const PopoverTitle = ChakraPopover.Title
|
||||
export const PopoverDescription = ChakraPopover.Description
|
||||
export const PopoverFooter = ChakraPopover.Footer
|
||||
export const PopoverHeader = ChakraPopover.Header
|
||||
export const PopoverRoot = ChakraPopover.Root
|
||||
export const PopoverBody = ChakraPopover.Body
|
||||
export const PopoverTrigger = ChakraPopover.Trigger
|
15
front/src/components/ui/provider.tsx
Normal file
15
front/src/components/ui/provider.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
"use client"
|
||||
|
||||
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
|
||||
import {
|
||||
ColorModeProvider,
|
||||
type ColorModeProviderProps,
|
||||
} from "./color-mode"
|
||||
|
||||
export function Provider(props: ColorModeProviderProps) {
|
||||
return (
|
||||
<ChakraProvider value={defaultSystem}>
|
||||
<ColorModeProvider {...props} />
|
||||
</ChakraProvider>
|
||||
)
|
||||
}
|
24
front/src/components/ui/radio.tsx
Normal file
24
front/src/components/ui/radio.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { RadioGroup as ChakraRadioGroup } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
export interface RadioProps extends ChakraRadioGroup.ItemProps {
|
||||
rootRef?: React.Ref<HTMLDivElement>
|
||||
inputProps?: React.InputHTMLAttributes<HTMLInputElement>
|
||||
}
|
||||
|
||||
export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
|
||||
function Radio(props, ref) {
|
||||
const { children, inputProps, rootRef, ...rest } = props
|
||||
return (
|
||||
<ChakraRadioGroup.Item ref={rootRef} {...rest}>
|
||||
<ChakraRadioGroup.ItemHiddenInput ref={ref} {...inputProps} />
|
||||
<ChakraRadioGroup.ItemIndicator />
|
||||
{children && (
|
||||
<ChakraRadioGroup.ItemText>{children}</ChakraRadioGroup.ItemText>
|
||||
)}
|
||||
</ChakraRadioGroup.Item>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export const RadioGroup = ChakraRadioGroup.Root
|
82
front/src/components/ui/slider.tsx
Normal file
82
front/src/components/ui/slider.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import { Slider as ChakraSlider, For, HStack } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
export interface SliderProps extends ChakraSlider.RootProps {
|
||||
marks?: Array<number | { value: number; label: React.ReactNode }>
|
||||
label?: React.ReactNode
|
||||
showValue?: boolean
|
||||
}
|
||||
|
||||
export const Slider = React.forwardRef<HTMLDivElement, SliderProps>(
|
||||
function Slider(props, ref) {
|
||||
const { marks: marksProp, label, showValue, ...rest } = props
|
||||
const value = props.defaultValue ?? props.value
|
||||
|
||||
const marks = marksProp?.map((mark) => {
|
||||
if (typeof mark === "number") return { value: mark, label: undefined }
|
||||
return mark
|
||||
})
|
||||
|
||||
const hasMarkLabel = !!marks?.some((mark) => mark.label)
|
||||
|
||||
return (
|
||||
<ChakraSlider.Root ref={ref} thumbAlignment="center" {...rest}>
|
||||
{label && !showValue && (
|
||||
<ChakraSlider.Label>{label}</ChakraSlider.Label>
|
||||
)}
|
||||
{label && showValue && (
|
||||
<HStack justify="space-between">
|
||||
<ChakraSlider.Label>{label}</ChakraSlider.Label>
|
||||
<ChakraSlider.ValueText />
|
||||
</HStack>
|
||||
)}
|
||||
<ChakraSlider.Control data-has-mark-label={hasMarkLabel || undefined}>
|
||||
<ChakraSlider.Track>
|
||||
<ChakraSlider.Range />
|
||||
</ChakraSlider.Track>
|
||||
<SliderThumbs value={value} />
|
||||
<SliderMarks marks={marks} />
|
||||
</ChakraSlider.Control>
|
||||
</ChakraSlider.Root>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
function SliderThumbs(props: { value?: number[] }) {
|
||||
const { value } = props
|
||||
return (
|
||||
<For each={value}>
|
||||
{(_, index) => (
|
||||
<ChakraSlider.Thumb key={index} index={index}>
|
||||
<ChakraSlider.HiddenInput />
|
||||
</ChakraSlider.Thumb>
|
||||
)}
|
||||
</For>
|
||||
)
|
||||
}
|
||||
|
||||
interface SliderMarksProps {
|
||||
marks?: Array<number | { value: number; label: React.ReactNode }>
|
||||
}
|
||||
|
||||
const SliderMarks = React.forwardRef<HTMLDivElement, SliderMarksProps>(
|
||||
function SliderMarks(props, ref) {
|
||||
const { marks } = props
|
||||
if (!marks?.length) return null
|
||||
|
||||
return (
|
||||
<ChakraSlider.MarkerGroup ref={ref}>
|
||||
{marks.map((mark, index) => {
|
||||
const value = typeof mark === "number" ? mark : mark.value
|
||||
const label = typeof mark === "number" ? undefined : mark.label
|
||||
return (
|
||||
<ChakraSlider.Marker key={index} value={value}>
|
||||
<ChakraSlider.MarkerIndicator />
|
||||
{label}
|
||||
</ChakraSlider.Marker>
|
||||
)
|
||||
})}
|
||||
</ChakraSlider.MarkerGroup>
|
||||
)
|
||||
},
|
||||
)
|
@ -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)
|
46
front/src/components/ui/tooltip.tsx
Normal file
46
front/src/components/ui/tooltip.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { Tooltip as ChakraTooltip, Portal } from "@chakra-ui/react"
|
||||
import * as React from "react"
|
||||
|
||||
export interface TooltipProps extends ChakraTooltip.RootProps {
|
||||
showArrow?: boolean
|
||||
portalled?: boolean
|
||||
portalRef?: React.RefObject<HTMLElement>
|
||||
content: React.ReactNode
|
||||
contentProps?: ChakraTooltip.ContentProps
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
||||
function Tooltip(props, ref) {
|
||||
const {
|
||||
showArrow,
|
||||
children,
|
||||
disabled,
|
||||
portalled = true,
|
||||
content,
|
||||
contentProps,
|
||||
portalRef,
|
||||
...rest
|
||||
} = props
|
||||
|
||||
if (disabled) return children
|
||||
|
||||
return (
|
||||
<ChakraTooltip.Root {...rest}>
|
||||
<ChakraTooltip.Trigger asChild>{children}</ChakraTooltip.Trigger>
|
||||
<Portal disabled={!portalled} container={portalRef}>
|
||||
<ChakraTooltip.Positioner>
|
||||
<ChakraTooltip.Content ref={ref} {...contentProps}>
|
||||
{showArrow && (
|
||||
<ChakraTooltip.Arrow>
|
||||
<ChakraTooltip.ArrowTip />
|
||||
</ChakraTooltip.Arrow>
|
||||
)}
|
||||
{content}
|
||||
</ChakraTooltip.Content>
|
||||
</ChakraTooltip.Positioner>
|
||||
</Portal>
|
||||
</ChakraTooltip.Root>
|
||||
)
|
||||
},
|
||||
)
|
@ -10,7 +10,7 @@ export const Error401 = () => {
|
||||
<TopBar />
|
||||
<PageLayoutInfoCenter padding="25px">
|
||||
<Center>
|
||||
<MdControlCamera size="250px" color="red.600" />
|
||||
<MdControlCamera style={{ width: "250px", height: "250px", color: "orange" }} />
|
||||
</Center>
|
||||
<Box textAlign="center">
|
||||
<Heading>Erreur 401</Heading>
|
||||
|
@ -10,10 +10,10 @@ export const Error403 = () => {
|
||||
<TopBar />
|
||||
<PageLayoutInfoCenter padding="25px">
|
||||
<Center>
|
||||
<MdDangerous size="250px" color="orange.600" />
|
||||
<MdDangerous style={{ width: "250px", height: "250px", color: "red" }} />
|
||||
</Center>
|
||||
<Box textAlign="center">
|
||||
<Heading>Erreur 401</Heading>
|
||||
<Heading>Erreur 403</Heading>
|
||||
<Text color="orange.600">Cette page vous est interdite</Text>
|
||||
<Link href="/">
|
||||
Retour à l'accueil
|
||||
|
@ -10,7 +10,7 @@ export const Error404 = () => {
|
||||
<TopBar />
|
||||
<PageLayoutInfoCenter padding="25px">
|
||||
<Center>
|
||||
<MdSignpost size="250px" />
|
||||
<MdSignpost style={{ width: "250px", height: "250px", color: "aqua" }} />
|
||||
</Center>
|
||||
<Box textAlign="center">
|
||||
<Heading>Erreur 404</Heading>
|
||||
|
@ -2,46 +2,28 @@ import React, { FC } from 'react';
|
||||
|
||||
import {
|
||||
AlertDescription,
|
||||
AlertRoot,
|
||||
AlertTitle,
|
||||
Box,
|
||||
Collapsible,
|
||||
useDisclosure,
|
||||
Alert,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
FallbackProps,
|
||||
ErrorBoundary as ReactErrorBoundary,
|
||||
} from 'react-error-boundary';
|
||||
import { Button } from '@/components/ui/themed';
|
||||
import { LuChevronUp, LuChevronDown } from 'react-icons/lu';
|
||||
|
||||
const ErrorFallback = ({ error }: FallbackProps) => {
|
||||
const { open, onToggle } = useDisclosure();
|
||||
return (
|
||||
<Box p="4" m="auto">
|
||||
<AlertRoot status="error" borderRadius="md">
|
||||
{/* <AlertIcon /> */}
|
||||
<Box padding="8" margin="auto">
|
||||
<Alert.Root status="error" borderRadius="md">
|
||||
<Alert.Indicator height="75px" width="75px" />
|
||||
<Box flex="1">
|
||||
<AlertTitle>An unexpected error has occurred.</AlertTitle>
|
||||
<AlertDescription display="block" lineHeight="1.4">
|
||||
<Button
|
||||
theme="@secondary"
|
||||
color="red.800"
|
||||
//size="sm"
|
||||
onClick={onToggle}
|
||||
>
|
||||
Show details {open ? <LuChevronUp /> : <LuChevronDown />}
|
||||
</Button>
|
||||
<Collapsible.Root open={open}>
|
||||
<Collapsible.Content>
|
||||
<Box mt={4} fontFamily="monospace">
|
||||
{error.message}
|
||||
</Box>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
<AlertTitle fontWeight="bold" fontSize="35px">An unexpected error has occurred.</AlertTitle>
|
||||
<AlertDescription padding="5" marginTop="3" fontSize="20px" lineHeight="1.4">
|
||||
<br />
|
||||
{error.message}
|
||||
</AlertDescription>
|
||||
</Box>
|
||||
</AlertRoot>
|
||||
</Alert.Root>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||
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 { 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 { AlbumEditPopUp } from '@/components/popup/AlbumEditPopUp';
|
||||
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||
import { DisplayTrack } from '@/components/track/DisplayTrack';
|
||||
import { DisplayTrackFull } from '@/components/track/DisplayTrackFull';
|
||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||
import { useSpecificAlbum } from '@/service/Album';
|
||||
@ -76,7 +75,7 @@ export const AlbumDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataAlbum?.covers}
|
||||
// TODO: iconEmpty={LuDisc3}
|
||||
iconEmpty={<LuDisc3 />}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
@ -121,11 +120,16 @@ export const AlbumDetailPage = () => {
|
||||
contextMenu={[
|
||||
{
|
||||
name: 'Edit',
|
||||
icon: <MdEdit />,
|
||||
onClick: () => {
|
||||
navigate(`/album/${albumId}/edit-track/${data.id}`);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => { } },
|
||||
{
|
||||
icon: <MdAdd />,
|
||||
name: 'Add Playlist',
|
||||
onClick: () => { }
|
||||
},
|
||||
]}
|
||||
data-testid="Album-detail-page_display-detail"
|
||||
/>
|
||||
|
@ -36,7 +36,7 @@ export const AlbumsPage = () => {
|
||||
<SearchInput onChange={setFilterTitle} />
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<HStack wrap="wrap" /*spacing={BASE_WRAP_SPACING}*/ marginX="auto" padding="20px" justify="center">
|
||||
<HStack wrap="wrap" gap={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||
{dataAlbums.map((data) => (
|
||||
<Flex align="flex-start"
|
||||
width={BASE_WRAP_WIDTH}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||
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 { Covers } from '@/components/Cover';
|
||||
@ -67,7 +67,7 @@ export const ArtistAlbumDetailPage = () => {
|
||||
data={dataArtist?.covers}
|
||||
size="35px"
|
||||
borderRadius="full"
|
||||
// TODO: iconEmpty={MdPerson}
|
||||
iconEmpty={<MdPerson />}
|
||||
/>
|
||||
<Text fontSize="24px" fontWeight="bold">
|
||||
{dataArtist?.name}
|
||||
@ -94,7 +94,7 @@ export const ArtistAlbumDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataAlbum?.covers}
|
||||
// TODO: iconEmpty={LuDisc3}
|
||||
iconEmpty={<LuDisc3 />}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
@ -126,7 +126,6 @@ export const ArtistAlbumDetailPage = () => {
|
||||
backgroundColor={useColorModeValue('#FFFFFF88', '#00000088')}
|
||||
key={data.id}
|
||||
padding="5px"
|
||||
as="button"
|
||||
_hover={{
|
||||
boxShadow: 'outline-over',
|
||||
bgColor: useColorModeValue('#FFFFFFF7', '#000000F7'),
|
||||
@ -138,13 +137,18 @@ export const ArtistAlbumDetailPage = () => {
|
||||
contextMenu={[
|
||||
{
|
||||
name: 'Edit',
|
||||
icon: <MdEdit />,
|
||||
onClick: () => {
|
||||
navigate(
|
||||
`/artist/${artistIdInt}/album/${albumId}/edit-track/${data.id}`
|
||||
);
|
||||
},
|
||||
},
|
||||
{ name: 'Add Playlist', onClick: () => { } },
|
||||
{
|
||||
icon: <MdAdd />,
|
||||
name: 'Add Playlist',
|
||||
onClick: () => { }
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -70,7 +70,7 @@ export const ArtistDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataArtist?.covers}
|
||||
// TODO: iconEmpty={LuUser}
|
||||
iconEmpty={<LuUser />}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
@ -88,7 +88,7 @@ export const ArtistDetailPage = () => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
<HStack wrap="wrap" /*spacing={BASE_WRAP_SPACING}*/ marginX="auto" padding="20px" justify="center">
|
||||
<HStack wrap="wrap" gap={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||
{albumIdsOfAnArtist?.map((data) => (
|
||||
<Flex align="flex-start"
|
||||
width={BASE_WRAP_WIDTH}
|
||||
|
@ -59,12 +59,12 @@ export const ArtistsPage = () => {
|
||||
<SearchInput onChange={setFilterName} />
|
||||
<Tooltip.Root aria-label="Random play">
|
||||
<Button {...BUTTON_TOP_BAR_PROPERTY} onClick={onRandomPlay}>
|
||||
<MdOutlineForkRight />
|
||||
<MdOutlineForkRight style={{ width: "100%", height: "100%" }} />
|
||||
</Button>
|
||||
</Tooltip.Root>
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<HStack wrap="wrap" /*spacing={BASE_WRAP_SPACING}*/ marginX="auto" padding="20px" justify="center">
|
||||
<HStack wrap="wrap" gap={BASE_WRAP_SPACING} marginX="auto" padding="20px" justify="center">
|
||||
{dataArtist?.map((data) => (
|
||||
<Flex align="flex-start"
|
||||
width={BASE_WRAP_WIDTH}
|
||||
@ -86,7 +86,7 @@ export const ArtistsPage = () => {
|
||||
data={data.covers}
|
||||
size={BASE_WRAP_ICON_SIZE}
|
||||
height="full"
|
||||
// iconEmpty={LuUser}
|
||||
iconEmpty={<LuUser />}
|
||||
/>
|
||||
<Flex
|
||||
direction="column"
|
||||
@ -97,11 +97,10 @@ export const ArtistsPage = () => {
|
||||
overflowX="hidden"
|
||||
>
|
||||
<Text
|
||||
/*align="left"*/
|
||||
textAlign="left"
|
||||
/*noOfLines={[1, 2]}*/
|
||||
>
|
||||
<Span
|
||||
as="span"
|
||||
fontSize="20px"
|
||||
fontWeight="bold"
|
||||
userSelect="none"
|
||||
@ -118,7 +117,7 @@ export const ArtistsPage = () => {
|
||||
))}
|
||||
</HStack>
|
||||
<EmptyEnd />
|
||||
</PageLayout>
|
||||
</PageLayout >
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -75,7 +75,7 @@ export const GenderDetailPage = () => {
|
||||
>
|
||||
<Covers
|
||||
data={dataGender?.covers}
|
||||
// TODO: iconEmpty={LuDisc3}
|
||||
iconEmpty={<LuDisc3 />}
|
||||
slideshow
|
||||
/>
|
||||
<Flex direction="column" width="80%" marginRight="auto">
|
||||
@ -131,7 +131,7 @@ export const GenderDetailPage = () => {
|
||||
<Route path="edit-track/:trackId" element={<TrackEditPopUp />} />
|
||||
<Route path="edit-gender/:genderId" element={<GenderEditPopUp />} />
|
||||
</Routes>
|
||||
</PageLayout>
|
||||
</PageLayout >
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -35,7 +35,7 @@ export const GendersPage = () => {
|
||||
<SearchInput onChange={setFilterTitle} />
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<HStack wrap="wrap" /*spacing="20px"*/ marginX="auto" padding="20px" justify="center">
|
||||
<HStack wrap="wrap" gap="20px" marginX="auto" padding="20px" justify="center">
|
||||
{dataGenders.map((data) => (
|
||||
<Flex align="flex-start"
|
||||
width="270px"
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
Input,
|
||||
Table,
|
||||
Text,
|
||||
Button,
|
||||
} from '@chakra-ui/react';
|
||||
import { LuTrash } from 'react-icons/lu';
|
||||
import { MdCloudUpload } from 'react-icons/md';
|
||||
@ -31,7 +32,6 @@ import { useGenderService, useOrderedGenders } from '@/service/Gender';
|
||||
import { useServiceContext } from '@/service/ServiceContext';
|
||||
import { useTrackService } from '@/service/Track';
|
||||
import { isNullOrUndefined } from '@/utils/validator';
|
||||
import { Button } from '@/components/ui/themed';
|
||||
|
||||
export class ElementList {
|
||||
constructor(
|
||||
@ -540,7 +540,7 @@ export const AddPage = () => {
|
||||
</Table.Root>
|
||||
<Flex marginY="15px">
|
||||
<Button
|
||||
theme="@primary"
|
||||
colorPalette="brand"
|
||||
onClick={sendFile}
|
||||
disabled={!needSend}
|
||||
marginLeft="auto"
|
||||
|
@ -58,7 +58,7 @@ export const HomePage = () => {
|
||||
<>
|
||||
<TopBar title="Home" />
|
||||
<PageLayout>
|
||||
<HStack wrap="wrap" /*spacing="20px"*/ marginX="auto" padding="20px" justify="center">
|
||||
<HStack wrap="wrap" gap="20px" marginX="auto" padding="20px" justify="center">
|
||||
{homeList.map((data) => (
|
||||
<Flex align="flex-start"
|
||||
width="200px"
|
||||
|
@ -4,13 +4,16 @@ import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||
import { EmptyEnd } from '@/components/EmptyEnd';
|
||||
import { PageLayout } from '@/components/Layout/PageLayout';
|
||||
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 { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
|
||||
import { useActivePlaylistService } from '@/service/ActivePlaylist';
|
||||
import { useColorModeValue } from '@/components/ui/color-mode';
|
||||
import { BASE_WRAP_SPACING } from '@/constants/genericSpacing';
|
||||
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 = () => {
|
||||
const { playInList } = useActivePlaylistService();
|
||||
@ -53,9 +56,39 @@ export const OnAirPage = () => {
|
||||
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)}`);
|
||||
if (!playTrackList) {
|
||||
if (!playTrackList || playTrackList.length == 0) {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="Album detail" />
|
||||
@ -68,6 +101,12 @@ export const OnAirPage = () => {
|
||||
return (
|
||||
<>
|
||||
<TopBar title="On Air ...">
|
||||
<Button
|
||||
{...BUTTON_TOP_BAR_PROPERTY}
|
||||
onClick={clean}
|
||||
>
|
||||
<LuTrash2 />
|
||||
</Button>
|
||||
</TopBar>
|
||||
<PageLayout>
|
||||
<Flex
|
||||
@ -78,7 +117,7 @@ export const OnAirPage = () => {
|
||||
width="80%"
|
||||
>
|
||||
{!playTrackList && <>No playing</>}
|
||||
{playTrackList && playTrackList?.map((data) => (
|
||||
{playTrackList && playTrackList?.map((data, index) => (
|
||||
<Box
|
||||
minWidth="100%"
|
||||
//height="60px"
|
||||
@ -99,16 +138,32 @@ export const OnAirPage = () => {
|
||||
contextMenu={[
|
||||
{
|
||||
name: 'Edit',
|
||||
icon: <MdEdit />,
|
||||
onClick: () => {
|
||||
navigate(`edit-track/${data}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Remove from playlist',
|
||||
name: 'Remove previous',
|
||||
icon: <MdKeyboardDoubleArrowUp />,
|
||||
onClick: () => {
|
||||
removePreviousTrack(index);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Remove',
|
||||
icon: <MdRemove />,
|
||||
onClick: () => {
|
||||
removeTrack(data);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Remove After',
|
||||
icon: <MdKeyboardDoubleArrowDown />,
|
||||
onClick: () => {
|
||||
removeNextTrack(index);
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
@ -14,7 +14,7 @@ export type ActivePlaylistServiceProps = {
|
||||
trackActive?: Track;
|
||||
setNewPlaylist: (listIds: number[]) => void;
|
||||
setNewPlaylistShuffle: (listIds: number[]) => void;
|
||||
playInList: (id: number, listIds: number[]) => void;
|
||||
playInList: (id: number | undefined, listIds: number[]) => void;
|
||||
play: (id: number) => void;
|
||||
previous: () => void;
|
||||
next: () => void;
|
||||
@ -65,7 +65,7 @@ export const useActivePlaylistServiceWrapped = (
|
||||
[setPlayTrackList, setTrackOffset]
|
||||
);
|
||||
const playInList = useCallback(
|
||||
(id: number, listIds: number[]) => {
|
||||
(id: number | undefined, listIds: number[]) => {
|
||||
console.log(`Request paly in list: ${id} in ${listIds}`);
|
||||
setPlayTrackList(listIds);
|
||||
setTrackOffset(id);
|
||||
|
@ -1,17 +1,21 @@
|
||||
|
||||
type PandaColorModel = {
|
||||
value: string;
|
||||
}
|
||||
type ThemeModel = {
|
||||
50: string;
|
||||
100: string;
|
||||
200: string;
|
||||
300: string;
|
||||
400: string;
|
||||
500: string;
|
||||
600: string;
|
||||
700: string;
|
||||
800: string;
|
||||
900: string;
|
||||
50: PandaColorModel;
|
||||
100: PandaColorModel;
|
||||
200: PandaColorModel;
|
||||
300: PandaColorModel;
|
||||
400: PandaColorModel;
|
||||
500: PandaColorModel;
|
||||
600: PandaColorModel;
|
||||
700: PandaColorModel;
|
||||
800: PandaColorModel;
|
||||
900: PandaColorModel;
|
||||
};
|
||||
|
||||
const back = {
|
||||
const back: ThemeModel = {
|
||||
50: { value: '#ebf4fa' },
|
||||
100: { value: '#d1dbe0' },
|
||||
200: { value: '#b6c2c9' },
|
||||
@ -24,7 +28,7 @@ const back = {
|
||||
900: { value: '#020f12' },
|
||||
};
|
||||
|
||||
const brand = {
|
||||
const brand: ThemeModel = {
|
||||
50: { value: '#e3edff' },
|
||||
100: { value: '#b6c9fd' },
|
||||
200: { value: '#88a5f7' },
|
||||
@ -36,7 +40,7 @@ const brand = {
|
||||
800: { value: '#02164a' },
|
||||
900: { value: '#00071e' },
|
||||
};
|
||||
const normalText = {
|
||||
const normalText: ThemeModel = {
|
||||
50: { value: '#f2f2f2' },
|
||||
100: { value: '#d9d9d9' },
|
||||
200: { value: '#bfbfbf' },
|
||||
@ -49,7 +53,7 @@ const normalText = {
|
||||
900: { value: '#0d0d0d' },
|
||||
};
|
||||
|
||||
const green = {
|
||||
const green: ThemeModel = {
|
||||
50: { value: '#f0fdf4' },
|
||||
100: { value: '#dcfce7' },
|
||||
200: { value: '#bbf7d0' },
|
||||
@ -61,7 +65,7 @@ const green = {
|
||||
800: { value: '#166534' },
|
||||
900: { value: '#14532d' },
|
||||
};
|
||||
const blue = {
|
||||
const blue: ThemeModel = {
|
||||
50: { value: '#eff6ff' },
|
||||
100: { value: '#dbeafe' },
|
||||
200: { value: '#bfdbfe' },
|
||||
@ -74,7 +78,7 @@ const blue = {
|
||||
900: { value: '#1e3a8a' },
|
||||
};
|
||||
|
||||
const orange = {
|
||||
const orange: ThemeModel = {
|
||||
50: { value: '#fff7ed' },
|
||||
100: { value: '#ffedd5' },
|
||||
200: { value: '#fed7aa' },
|
||||
@ -86,7 +90,7 @@ const orange = {
|
||||
800: { value: '#9a3412' },
|
||||
900: { value: '#7c2d12' },
|
||||
};
|
||||
const red = {
|
||||
const red: ThemeModel = {
|
||||
50: { value: '#fef2f2' },
|
||||
100: { value: '#fee2e2' },
|
||||
200: { value: '#fecaca' },
|
||||
@ -107,4 +111,5 @@ export const colors = {
|
||||
success: green,
|
||||
error: red,
|
||||
warning: orange,
|
||||
danger: red,
|
||||
} as const;
|
10888
front/src/theme/config sample.ts
Normal file
10888
front/src/theme/config sample.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +0,0 @@
|
||||
import { colors } from './colors';
|
||||
import { shadows } from './shadows';
|
||||
|
||||
const foundations = {
|
||||
colors,
|
||||
shadows,
|
||||
};
|
||||
|
||||
export default foundations;
|
@ -1,21 +0,0 @@
|
||||
import { colors } from './colors';
|
||||
|
||||
const createOutline = (colorScheme = 'gray') =>
|
||||
`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)',
|
||||
};
|
@ -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',
|
||||
},
|
||||
};
|
@ -1,109 +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({
|
||||
variants: {
|
||||
theme: {
|
||||
"@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',
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default buttonRecipe;
|
@ -1,5 +0,0 @@
|
||||
export default {
|
||||
defaultProps: {
|
||||
colorScheme: 'brand',
|
||||
},
|
||||
};
|
@ -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;
|
@ -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;
|
@ -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';
|
@ -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;
|
@ -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;
|
@ -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,
|
||||
});
|
@ -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',
|
||||
},
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
export default {
|
||||
defaultProps: {
|
||||
colorScheme: 'brand',
|
||||
},
|
||||
};
|
@ -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;
|
@ -1,5 +0,0 @@
|
||||
export default {
|
||||
defaultProps: {
|
||||
colorScheme: 'brand',
|
||||
},
|
||||
};
|
@ -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;
|
@ -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>
|
||||
),
|
||||
};
|
@ -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
|
||||
// spacing="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" />,
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
import { Styles } from '@chakra-ui/theme-tools';
|
||||
|
||||
export const styles: Styles = {
|
||||
};
|
@ -1,6 +1,5 @@
|
||||
import * as recipes from './recipes';
|
||||
import { createSystem, defaultConfig, mergeConfigs, SystemConfig } from "@chakra-ui/react"
|
||||
import { colors } from "./foundations/colors"
|
||||
import { colors } from "./colors"
|
||||
|
||||
const baseTheme: SystemConfig = {
|
||||
globalCss: {
|
||||
@ -17,7 +16,30 @@ const baseTheme: SystemConfig = {
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
...recipes,
|
||||
slotRecipes: {
|
||||
dialog: {
|
||||
slots: [
|
||||
"header"
|
||||
],
|
||||
base: {
|
||||
header: {
|
||||
fontWeight: "bold",
|
||||
fontSize: "2xl",
|
||||
color: { _dark: "brand.400", _light: "brand.500" }
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
recipes: {
|
||||
button: {
|
||||
base: {
|
||||
borderRadius: 0,
|
||||
_hover: {
|
||||
//boxShadow: "3px 5px 8px gray"
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
tokens: {
|
||||
fonts: {
|
||||
heading: { value: `Roboto, Helvetica, Arial, "sans-serif"` },
|
||||
@ -27,10 +49,19 @@ const baseTheme: SystemConfig = {
|
||||
},
|
||||
semanticTokens: {
|
||||
colors: {
|
||||
brand: {
|
||||
"@danger": {
|
||||
solid: { value: "{colors.danger.500}" },
|
||||
contrast: { value: "{colors.danger.100}" },
|
||||
fg: { value: "{colors.danger.900}" },
|
||||
muted: { value: "{colors.danger.100}" },
|
||||
subtle: { value: "{colors.danger.200}" },
|
||||
emphasized: { value: "{colors.danger.300}" },
|
||||
focusRing: { value: "{colors.danger.500}" },
|
||||
},
|
||||
"brand": {
|
||||
solid: { value: "{colors.brand.500}" },
|
||||
contrast: { value: "{colors.brand.100}" },
|
||||
fg: { value: "{colors.brand.700}" },
|
||||
fg: { value: "{colors.brand.900}" },
|
||||
muted: { value: "{colors.brand.100}" },
|
||||
subtle: { value: "{colors.brand.200}" },
|
||||
emphasized: { value: "{colors.brand.300}" },
|
||||
@ -41,4 +72,5 @@ const baseTheme: SystemConfig = {
|
||||
},
|
||||
};
|
||||
const config = mergeConfigs(defaultConfig, baseTheme);
|
||||
export const systemTheme = createSystem(config);
|
||||
//console.log("defaultConfig: " + JSON.stringify(defaultConfig, null, 2));
|
||||
export const systemTheme = createSystem(config);
|
||||
|
2
front/src/types/theme.d.ts
vendored
2
front/src/types/theme.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user