diff --git a/frontend/src/bundles/auth/components/common/password-input/password-input.tsx b/frontend/src/bundles/auth/components/common/password-input/password-input.tsx index 6c7881d4a..974d79a9f 100644 --- a/frontend/src/bundles/auth/components/common/password-input/password-input.tsx +++ b/frontend/src/bundles/auth/components/common/password-input/password-input.tsx @@ -1,12 +1,12 @@ import { + Icon, IconButton, Input, InputGroup, InputRightElement, - ViewIcon, - ViewOffIcon, } from '~/bundles/common/components/components.js'; import { useCallback, useState } from '~/bundles/common/hooks/hooks.js'; +import { IconName } from '~/bundles/common/icons/icons.js'; type Properties = { label: string; @@ -44,7 +44,15 @@ const PasswordInput: React.FC = ({ aria-label={ isPasswordVisible ? 'Hide password' : 'Show password' } - icon={isPasswordVisible ? : } + icon={ + + } onClick={handlePasswordIconClick} variant="ghostIcon" /> diff --git a/frontend/src/bundles/common/components/components.ts b/frontend/src/bundles/common/components/components.ts index 1cfa1cd9f..4a87d8cc0 100644 --- a/frontend/src/bundles/common/components/components.ts +++ b/frontend/src/bundles/common/components/components.ts @@ -7,13 +7,13 @@ export { Overlay } from './overlay/overlay.js'; export { Player } from './player/player.js'; export { RouterProvider } from './router-provider/router-provider.js'; export { VideoModal } from './video-modal/video-modal.js'; -export { DownloadIcon } from '@chakra-ui/icons'; -export { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'; export { Badge, Box, Center, Circle, + CloseButton, + Collapse, ChakraProvider as ComponentsProvider, Flex, FormControl, @@ -28,6 +28,7 @@ export { Text, VStack, } from '@chakra-ui/react'; +export { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; export { FormikProvider as FormProvider } from 'formik'; export { Provider as StoreProvider } from 'react-redux'; export { Outlet as RouterOutlet } from 'react-router-dom'; diff --git a/frontend/src/bundles/common/icons/icon-name.ts b/frontend/src/bundles/common/icons/icon-name.ts index 89d7a2a1d..5b6e99ad7 100644 --- a/frontend/src/bundles/common/icons/icon-name.ts +++ b/frontend/src/bundles/common/icons/icon-name.ts @@ -1,8 +1,25 @@ -import { faEllipsisVertical, faPen } from '@fortawesome/free-solid-svg-icons'; +import { DownloadIcon, ViewIcon, ViewOffIcon } from '@chakra-ui/icons'; +import { + faCircleUser, + faCloudArrowUp, + faEllipsisVertical, + faFont, + faPen, + faT, + faTableColumns, +} from '@fortawesome/free-solid-svg-icons'; const IconName = { - OPTIONS_VERTICAL: faEllipsisVertical, PEN: faPen, + OPTIONS_VERTICAL: faEllipsisVertical, + DOWNLOAD: DownloadIcon, + VIEW: ViewIcon, + VIEW_OFF: ViewOffIcon, + AVATAR: faCircleUser, + UPLOAD: faCloudArrowUp, + TEMPLATE: faTableColumns, + SCRIPT: faFont, + TEXT: faT, } as const; export { IconName }; diff --git a/frontend/src/bundles/studio/pages/studio.tsx b/frontend/src/bundles/studio/pages/studio.tsx index fd0acd3d0..4bb9e35fc 100644 --- a/frontend/src/bundles/studio/pages/studio.tsx +++ b/frontend/src/bundles/studio/pages/studio.tsx @@ -1,9 +1,10 @@ import { Button, - DownloadIcon, Header, + Icon, IconButton, } from '~/bundles/common/components/components.js'; +import { IconName } from '~/bundles/common/icons/icons.js'; const Studio: React.FC = () => { return ( @@ -20,7 +21,7 @@ const Studio: React.FC = () => { } + icon={} /> } /> diff --git a/frontend/src/bundles/video-editor/components/components.ts b/frontend/src/bundles/video-editor/components/components.ts new file mode 100644 index 000000000..10b0aa452 --- /dev/null +++ b/frontend/src/bundles/video-editor/components/components.ts @@ -0,0 +1,2 @@ +export { Menu } from './menu/menu.js'; +export { MenuBody } from './menu-body/menu-body.js'; diff --git a/frontend/src/bundles/video-editor/components/menu-body/menu-body.tsx b/frontend/src/bundles/video-editor/components/menu-body/menu-body.tsx new file mode 100644 index 000000000..fa439a5ef --- /dev/null +++ b/frontend/src/bundles/video-editor/components/menu-body/menu-body.tsx @@ -0,0 +1,54 @@ +import { + Box, + CloseButton, + Flex, + Heading, + VStack, +} from '~/bundles/common/components/components.js'; + +type Properties = { + title: string | React.ReactNode; + children: React.ReactNode; + isOpen: boolean; + onClose: () => void; +}; + +const MenuBody: React.FC = ({ + title, + children, + isOpen, + onClose, +}) => { + return ( + <> + {isOpen && ( + + + {title} + + + + {children} + + + )} + + ); +}; + +export { MenuBody }; diff --git a/frontend/src/bundles/video-editor/components/menu/menu.tsx b/frontend/src/bundles/video-editor/components/menu/menu.tsx new file mode 100644 index 000000000..09e1c4a72 --- /dev/null +++ b/frontend/src/bundles/video-editor/components/menu/menu.tsx @@ -0,0 +1,93 @@ +import { Box, Flex, VStack } from '~/bundles/common/components/components.js'; +import { useCallback } from '~/bundles/common/hooks/hooks.js'; + +import { type MenuItem } from '../../types/types.js'; + +type Properties = { + items: MenuItem[]; + activeIndex: number | null; + onActiveIndexSet: (index: number) => void; +}; + +const Menu: React.FC = ({ + items, + activeIndex, + onActiveIndexSet, +}) => { + const handleClick = useCallback( + (index: number) => { + return () => { + if (!items || items.length === 0 || index >= items.length) { + return; + } + + const item = items[index]; + if (!item) { + return; + } + + onActiveIndexSet(index); + item.onClick(); + }; + }, + [onActiveIndexSet, items], + ); + return ( + + + {items.map((item, index) => ( + + + {item.icon} + + + {item.label} + + + ))} + + + ); +}; + +export { Menu }; diff --git a/frontend/src/bundles/video-editor/components/mock/menu-mock.tsx b/frontend/src/bundles/video-editor/components/mock/menu-mock.tsx new file mode 100644 index 000000000..84d1c00e9 --- /dev/null +++ b/frontend/src/bundles/video-editor/components/mock/menu-mock.tsx @@ -0,0 +1,31 @@ +import { + Flex, + FontAwesomeIcon, + Icon, +} from '~/bundles/common/components/components.js'; +import { IconName } from '~/bundles/common/icons/icons.js'; + +const TemplatesContent: React.FC = () => ( +
This is the Templates content.
+); +const AvatarsContent: React.FC = () =>
This is the Avatars content.
; +const ScriptHeader: React.FC = () => ( + +
Script
+
+ +
+
+); +const ScriptContent: React.FC = () =>
This is the Script content.
; +const TextContent: React.FC = () =>
This is the Text content.
; +const AssetsContent: React.FC = () =>
This is the Assets content.
; + +export { + AssetsContent, + AvatarsContent, + ScriptContent, + ScriptHeader, + TemplatesContent, + TextContent, +}; diff --git a/frontend/src/bundles/video-editor/pages/video-editor.tsx b/frontend/src/bundles/video-editor/pages/video-editor.tsx new file mode 100644 index 000000000..80bd0a70c --- /dev/null +++ b/frontend/src/bundles/video-editor/pages/video-editor.tsx @@ -0,0 +1,89 @@ +import { + FontAwesomeIcon, + Icon, +} from '~/bundles/common/components/components.js'; +import { useCallback, useState } from '~/bundles/common/hooks/hooks.js'; +import { IconName } from '~/bundles/common/icons/icons.js'; + +import { Menu, MenuBody } from '../components/components.js'; +import { + AssetsContent, + AvatarsContent, + ScriptContent, + ScriptHeader, + TemplatesContent, + TextContent, +} from '../components/mock/menu-mock.js'; +import { type MenuItem } from '../types/menu-item.type.js'; + +const VideoEditor: React.FC = () => { + const [activeIndex, setActiveIndex] = useState(null); + const [activeContent, setActiveContent] = useState( + null, + ); + const [activeTitle, setActiveTitle] = useState( + '', + ); + const [isOpen, setIsOpen] = useState(false); + + const handleMenuClick = ( + header: string | React.ReactNode, + content: React.ReactNode, + ): void => { + setActiveContent(content); + setActiveTitle(header); + setIsOpen(true); + }; + + const resetActiveItem = useCallback((): void => { + setIsOpen(false); + setActiveIndex(null); + }, []); + + const menuItems: MenuItem[] = [ + { + label: 'Templates', + icon: , + onClick: () => handleMenuClick('Templates', ), + }, + { + label: 'Avatars', + icon: , + onClick: () => handleMenuClick('Avatars', ), + }, + { + label: 'Script', + icon: , + onClick: () => handleMenuClick(, ), + }, + { + label: 'Text', + icon: , + onClick: () => handleMenuClick('Text', ), + }, + { + label: 'Assets', + icon: , + onClick: () => handleMenuClick('Assets', ), + }, + ]; + + return ( + <> + + + {activeContent} + + + ); +}; + +export { VideoEditor }; diff --git a/frontend/src/bundles/video-editor/types/menu-item.type.ts b/frontend/src/bundles/video-editor/types/menu-item.type.ts new file mode 100644 index 000000000..c40212a7a --- /dev/null +++ b/frontend/src/bundles/video-editor/types/menu-item.type.ts @@ -0,0 +1,7 @@ +type MenuItem = { + label: string; + icon: React.ReactNode; + onClick: () => void; +}; + +export { type MenuItem }; diff --git a/frontend/src/bundles/video-editor/types/types.ts b/frontend/src/bundles/video-editor/types/types.ts new file mode 100644 index 000000000..c650f58cd --- /dev/null +++ b/frontend/src/bundles/video-editor/types/types.ts @@ -0,0 +1 @@ +export { type MenuItem } from './menu-item.type.js';