diff --git a/packages/app/src/scenes/AppLayers.tsx b/packages/app/src/scenes/AppLayers.tsx index 9e9711709..fe451b948 100644 --- a/packages/app/src/scenes/AppLayers.tsx +++ b/packages/app/src/scenes/AppLayers.tsx @@ -2,6 +2,7 @@ import React, {FC, lazy} from 'react'; import {observer} from 'mobx-react-lite'; import {toast} from 'react-toastify'; import {useTheme} from 'styled-components'; +import {SectionedScreen} from '@momentum-xyz/ui-kit'; import {UnityControlContextProvider} from '@momentum-xyz/sdk'; import {useStore} from 'shared/hooks'; @@ -27,6 +28,9 @@ const AppLayers: FC = (props) => {
+
+ +
{renderUnity && }
{children}
diff --git a/packages/app/src/scenes/object/pages/ObjectPluginPage/ObjectPluginPage.tsx b/packages/app/src/scenes/object/pages/ObjectPluginPage/ObjectPluginPage.tsx index e34d7a392..38f2e27cf 100644 --- a/packages/app/src/scenes/object/pages/ObjectPluginPage/ObjectPluginPage.tsx +++ b/packages/app/src/scenes/object/pages/ObjectPluginPage/ObjectPluginPage.tsx @@ -3,7 +3,8 @@ import {observer} from 'mobx-react-lite'; import {useTheme} from 'styled-components'; import { ErrorBoundary, - // ObjectTopBar, SpacePage, + ScreenSectionsEnum, + SectionedScreenPortal, Text, WindowPanel } from '@momentum-xyz/ui-kit'; @@ -103,7 +104,13 @@ const PluginInnerWrapper = ({ const {content, objectView} = plugin.usePlugin(pluginProps); return !pluginLoader.isError ? ( - + {content ? ( {content} @@ -113,6 +120,8 @@ const PluginInnerWrapper = ({ title={objectView.title || ''} subtitle={objectView.subtitle} actions={objectView.actions} + initialIsExpanded={pluginLoader.isExpanded} + onToggleExpand={pluginProps.onToggleExpand} onClose={pluginProps.onClose} > {objectView.content} @@ -120,7 +129,7 @@ const PluginInnerWrapper = ({ ) : ( )} - + ) : ( ); diff --git a/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.styled.ts b/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.styled.ts deleted file mode 100644 index 1e50c608c..000000000 --- a/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.styled.ts +++ /dev/null @@ -1,76 +0,0 @@ -import styled from 'styled-components'; - -export const InnerContainer = styled.div` - display: flex; - height: 100%; - width: 100%; - min-height: 0; - flex-direction: column; - align-items: flex-start; -`; - -export const Container = styled.div` - border-radius: 10px; - pointer-events: all; - - padding-top: 60px; - width: 100%; - height: 100%; -`; - -export const Modal = styled.div` - position: absolute; - display: flex; - width: 96%; - height: 90%; - pointer-events: all; - top: 2%; - left: 2%; - margin: 0; - padding-top: 60px; - border-radius: 10px; - background: ${(props) => props.theme.bg}; - - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; - z-index: calc(var(--dialog-z-index) + 1); - - :not(&.expanded) { - position: absolute; - flex-direction: column; - width: 679px; - height: 348px; - margin: 0; - top: unset; - left: unset; - right: 20px; - bottom: 61px; - } -`; - -export const HeaderElement = styled.div` - display: flex; - align-items: center; - justify-content: center; - position: absolute; - padding: 5px 20px; - opacity: 0.8; - - height: 60px; - gap: 20px; - top: 0; - &.left { - left: 0; - gap: 5px; - } - &.right { - right: 0; - } -`; - -export const Title = styled.div` - min-width: 0; -`; -export const SubTitle = styled.div` - min-width: 0; -`; diff --git a/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.tsx b/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.tsx index 3b7bb4dbb..6ebe2603e 100644 --- a/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.tsx +++ b/packages/app/src/scenes/widgets/pages/ScreenShareWidget/ScreenShareWidget.tsx @@ -1,13 +1,11 @@ import React, {FC, useCallback, useEffect} from 'react'; import {observer} from 'mobx-react-lite'; -import cn from 'classnames'; -import {Portal, SvgButton, Text} from '@momentum-xyz/ui-kit'; +import {ScreenSectionsEnum, SectionedScreenPortal, WindowPanel} from '@momentum-xyz/ui-kit'; import {useTranslation} from 'react-i18next'; import {useStore} from 'shared/hooks'; import {ScreenChoice, ScreenVideo} from './components/templates'; -import * as styled from './ScreenShareWidget.styled'; const ScreenShareWidget: FC = () => { const {widgetsStore, agoraStore, sessionStore, unityStore} = useStore(); @@ -43,50 +41,31 @@ const ScreenShareWidget: FC = () => { }; return ( - - - - - - - - - - - - + + {!localVideoTrack && !remoteVideoTrack ? ( + - - - - - {!localVideoTrack && !remoteVideoTrack ? ( - - ) : ( - - )} - - - + ) : ( + + )} + + ); }; diff --git a/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.styled.ts b/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.styled.ts index d0f737978..7fac31589 100644 --- a/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.styled.ts +++ b/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.styled.ts @@ -1,20 +1,13 @@ import {rgba} from 'polished'; import styled from 'styled-components'; -export const Modal = styled.div` - display: flex; - position: absolute; - right: 0; - top: 0; - margin: 20px; -`; - export const Container = styled.div` background: ${(props) => props.theme.bg && rgba(props.theme.bg, 0.75)}; border-radius: 10px; overflow: hidden; width: 280px; - height: 584px; + max-height: 584px; + height: 100%; display: flex; flex-direction: column; diff --git a/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.tsx b/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.tsx index 430cb756d..810512fe3 100644 --- a/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.tsx +++ b/packages/app/src/scenes/widgets/pages/TextChatWidget/TextChatWidget.tsx @@ -1,5 +1,11 @@ import React, {FC, useEffect} from 'react'; -import {Heading, IconSvg, Portal, SvgButton} from '@momentum-xyz/ui-kit'; +import { + Heading, + IconSvg, + ScreenSectionsEnum, + SectionedScreenPortal, + SvgButton +} from '@momentum-xyz/ui-kit'; import {observer} from 'mobx-react-lite'; import {useTranslation} from 'react-i18next'; @@ -11,7 +17,7 @@ import * as styled from './TextChatWidget.styled'; const TextChatWidget: FC = () => { const {widgetsStore, sessionStore, unityStore} = useStore(); const {unityInstanceStore} = unityStore; - const {textChatStore, voiceChatStore} = widgetsStore; + const {textChatStore} = widgetsStore; const {streamChat} = textChatStore; const {t} = useTranslation(); @@ -25,33 +31,30 @@ const TextChatWidget: FC = () => { }, [sessionStore.user, sessionStore.userId, streamChat, unityStore.worldId]); return ( - - {/* FIXME: Design discussion in order to avoid relation to VoiceChatStore */} - - - - - - - - - - - - - - {streamChat.client && streamChat.currentChannel && ( - unityInstanceStore.changeKeyboardControl(false)} - onBlur={() => unityInstanceStore.changeKeyboardControl(true)} - /> - )} - - - - + + + + + + + + + + + + + + {streamChat.client && streamChat.currentChannel && ( + unityInstanceStore.changeKeyboardControl(false)} + onBlur={() => unityInstanceStore.changeKeyboardControl(true)} + /> + )} + + + ); }; diff --git a/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.styled.ts b/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.styled.ts index d0f737978..7fac31589 100644 --- a/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.styled.ts +++ b/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.styled.ts @@ -1,20 +1,13 @@ import {rgba} from 'polished'; import styled from 'styled-components'; -export const Modal = styled.div` - display: flex; - position: absolute; - right: 0; - top: 0; - margin: 20px; -`; - export const Container = styled.div` background: ${(props) => props.theme.bg && rgba(props.theme.bg, 0.75)}; border-radius: 10px; overflow: hidden; width: 280px; - height: 584px; + max-height: 584px; + height: 100%; display: flex; flex-direction: column; diff --git a/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.tsx b/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.tsx index 16d4ee6ce..34b960368 100644 --- a/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.tsx +++ b/packages/app/src/scenes/widgets/pages/VoiceChatWidget/VoiceChatWidget.tsx @@ -1,7 +1,13 @@ import React, {FC, useCallback, useEffect} from 'react'; import {observer} from 'mobx-react-lite'; import {useTranslation} from 'react-i18next'; -import {Heading, IconSvg, Portal, SvgButton} from '@momentum-xyz/ui-kit'; +import { + Heading, + IconSvg, + ScreenSectionsEnum, + SectionedScreenPortal, + SvgButton +} from '@momentum-xyz/ui-kit'; import {useStore} from 'shared/hooks'; @@ -28,24 +34,22 @@ const VoiceChatWidget: FC = () => { }, [agoraStore, agoraVoiceChatStore.hasJoined, voiceChatStore.dialog]); return ( - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + ); }; diff --git a/packages/app/src/scenes/widgets/pages/VoiceChatWidget/components/VoiceChatPanel/VoiceChatPanel.styled.ts b/packages/app/src/scenes/widgets/pages/VoiceChatWidget/components/VoiceChatPanel/VoiceChatPanel.styled.ts index b0e09d503..0c92902b0 100644 --- a/packages/app/src/scenes/widgets/pages/VoiceChatWidget/components/VoiceChatPanel/VoiceChatPanel.styled.ts +++ b/packages/app/src/scenes/widgets/pages/VoiceChatWidget/components/VoiceChatPanel/VoiceChatPanel.styled.ts @@ -40,7 +40,7 @@ export const EnterLeaveButton = styled.button` export const VoiceActions = styled.div` display: flex; - height: 55px; + height: 48px; width: 100%; align-items: center; justify-content: center; diff --git a/packages/app/src/static/styles/main.scss b/packages/app/src/static/styles/main.scss index c9d9b0e26..d41f10f1d 100644 --- a/packages/app/src/static/styles/main.scss +++ b/packages/app/src/static/styles/main.scss @@ -25,6 +25,16 @@ body { z-index: 1; } +#sectioned-screen-container { + width: 100%; + height: calc(100% - 50px); + position: absolute; + top: 0; + left: 0; + z-index: var(--base-z-index); + pointer-events: none; +} + .noScrollIndicator { -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ diff --git a/packages/app/src/static/styles/variables.css b/packages/app/src/static/styles/variables.css index f9663b781..c9afea5ba 100644 --- a/packages/app/src/static/styles/variables.css +++ b/packages/app/src/static/styles/variables.css @@ -95,7 +95,6 @@ --overlay-z-index: 40; --base-z-index: 1; - /* MUSIC PLAYER */ --player-default-color: rgba(255, 255, 255, 0.5); diff --git a/packages/ui-kit/src/atoms/Portal/Portal.ts b/packages/ui-kit/src/atoms/Portal/Portal.ts index 27eb1a66b..00e52eec5 100644 --- a/packages/ui-kit/src/atoms/Portal/Portal.ts +++ b/packages/ui-kit/src/atoms/Portal/Portal.ts @@ -1,18 +1,34 @@ import {FC, useRef, useEffect} from 'react'; import {createPortal} from 'react-dom'; -const Portal: FC = ({children}) => { - const domBody: HTMLElement = document.body; +interface PropsInterface { + parentId?: string; + maximized?: boolean; + className?: string; +} + +const Portal: FC = ({children, className, parentId, maximized}) => { + const domParent: HTMLElement = (parentId && document.getElementById(parentId)) || document.body; + const domContainer = useRef(document.createElement('div')); domContainer.current.setAttribute('data-testid', 'Portal-test'); + domContainer.current.className = className || ''; + + if (maximized) { + // set flex-grow to 1 to make it fill the parent + domContainer.current.style.flexGrow = '1'; + } else { + // set flex-grow to 0 to make it not fill the parent + domContainer.current.style.flexGrow = '0'; + } useEffect(() => { const element = domContainer.current; - domBody.appendChild(element); + domParent.appendChild(element); return () => { - domBody.removeChild(element); + domParent.removeChild(element); }; - }, [domContainer, domBody]); + }, [domContainer, domParent]); return createPortal(children, domContainer.current); }; diff --git a/packages/ui-kit/src/atoms/SectionedScreen/SectionedScreen.styled.ts b/packages/ui-kit/src/atoms/SectionedScreen/SectionedScreen.styled.ts new file mode 100644 index 000000000..835766295 --- /dev/null +++ b/packages/ui-kit/src/atoms/SectionedScreen/SectionedScreen.styled.ts @@ -0,0 +1,71 @@ +import styled from 'styled-components'; + +// this should allow debugging the layout even on prod - just set DEBUG=1 in sessionStorage and refresh +const DEBUG = sessionStorage.getItem('DEBUG') !== null; + +export const Container = styled.div` + pointer-events: none; + + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + align-items: flex-start; + gap: 10px; + padding: 10px; + + width: 100%; + height: 100%; + + .sectioned-screen-top-left { + flex-grow: 1; + order: 1; + } + .sectioned-screen-top-right { + order: 2; + justify-content: end; + } + .sectioned-screen-bottom-right { + display: flex; + justify-content: end; + order: 10; + align-self: flex-end; + } + + .sectioned-screen-top-left, + .sectioned-screen-top-right { + height: 60%; + } + .sectioned-screen-bottom-right { + height: 38%; + } + + .sectioned-screen-section-break { + flex-basis: 100%; + height: ${() => (DEBUG ? '2px' : '0')}; + background: ${() => (DEBUG ? 'red' : 'transparent')}; + margin: -5px 0; + order: 5; + } + + @supports (selector(:has(div))) { + .sectioned-screen-section-break { + display: none; + } + .sectioned-screen-top-left, + .sectioned-screen-top-right { + height: 100%; + } + } + + // right now we're ok with having top-left and bottom-right sections on the same row but alighed differently + // if we have top-right and bottom-right, then we need to split them into two rows and limit the top row height + &:has(.sectioned-screen-bottom-right):has(.sectioned-screen-top-right) { + .sectioned-screen-section-break { + display: block; + } + .sectioned-screen-top-left, + .sectioned-screen-top-right { + height: 60%; + } + } +`; diff --git a/packages/ui-kit/src/atoms/SectionedScreen/SectionedScreen.tsx b/packages/ui-kit/src/atoms/SectionedScreen/SectionedScreen.tsx new file mode 100644 index 000000000..7ab27143c --- /dev/null +++ b/packages/ui-kit/src/atoms/SectionedScreen/SectionedScreen.tsx @@ -0,0 +1,17 @@ +import {FC} from 'react'; + +import * as styled from './SectionedScreen.styled'; + +export const SECTIONED_SCREEN_ID = 'sectioned-screen'; + +export interface PropsInterface { + id?: string; +} + +export const SectionedScreen: FC = ({id = SECTIONED_SCREEN_ID}) => { + return ( + +
+ + ); +}; diff --git a/packages/ui-kit/src/atoms/SectionedScreen/index.ts b/packages/ui-kit/src/atoms/SectionedScreen/index.ts new file mode 100644 index 000000000..b0c2ce73c --- /dev/null +++ b/packages/ui-kit/src/atoms/SectionedScreen/index.ts @@ -0,0 +1 @@ +export * from './SectionedScreen'; diff --git a/packages/ui-kit/src/atoms/SectionedScreenPortal/SectionedScreenPortal.tsx b/packages/ui-kit/src/atoms/SectionedScreenPortal/SectionedScreenPortal.tsx new file mode 100644 index 000000000..8a5632809 --- /dev/null +++ b/packages/ui-kit/src/atoms/SectionedScreenPortal/SectionedScreenPortal.tsx @@ -0,0 +1,25 @@ +import {FC} from 'react'; + +import {ScreenSectionsEnum} from '../../enums'; +import {Portal} from '../Portal'; +import {SECTIONED_SCREEN_ID} from '../SectionedScreen/SectionedScreen'; + +interface SectionedScreenPortalPropsInterface { + section: ScreenSectionsEnum; + maximized?: boolean; +} +export const SectionedScreenPortal: FC = ({ + children, + section, + maximized +}) => { + return ( + + {children} + + ); +}; diff --git a/packages/ui-kit/src/atoms/SectionedScreenPortal/index.ts b/packages/ui-kit/src/atoms/SectionedScreenPortal/index.ts new file mode 100644 index 000000000..4578e19b1 --- /dev/null +++ b/packages/ui-kit/src/atoms/SectionedScreenPortal/index.ts @@ -0,0 +1 @@ +export * from './SectionedScreenPortal'; diff --git a/packages/ui-kit/src/atoms/index.ts b/packages/ui-kit/src/atoms/index.ts index 05e87bd12..205d6baba 100644 --- a/packages/ui-kit/src/atoms/index.ts +++ b/packages/ui-kit/src/atoms/index.ts @@ -18,3 +18,5 @@ export * from './Tooltip'; export * from './ErrorBoundary'; export * from './SpacePage'; export * from './LoaderFallback'; +export * from './SectionedScreen'; +export * from './SectionedScreenPortal'; diff --git a/packages/ui-kit/src/enums/index.ts b/packages/ui-kit/src/enums/index.ts index 8f6cca7ec..0c0c26b56 100644 --- a/packages/ui-kit/src/enums/index.ts +++ b/packages/ui-kit/src/enums/index.ts @@ -1,3 +1,4 @@ export * from './userStatus.enum'; export * from './imageSize.enum'; export * from './errors.enum'; +export * from './screenSections.enum'; diff --git a/packages/ui-kit/src/enums/screenSections.enum.ts b/packages/ui-kit/src/enums/screenSections.enum.ts new file mode 100644 index 000000000..14883eefe --- /dev/null +++ b/packages/ui-kit/src/enums/screenSections.enum.ts @@ -0,0 +1,5 @@ +export enum ScreenSectionsEnum { + TOP_LEFT = 'top-left', + TOP_RIGHT = 'top-right', + BOTTOM_RIGHT = 'bottom-right' +} diff --git a/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.styled.ts b/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.styled.ts index a3a96481d..b66f65258 100644 --- a/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.styled.ts +++ b/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.styled.ts @@ -4,18 +4,18 @@ import styled from 'styled-components'; export const Container = styled.div` display: flex; flex-direction: column; - // flex: 1 0 auto; ?? overflow: hidden; pointer-events: auto; - width: 680px; - height: 420px; + background: ${(props) => props.theme.bg && rgba(props.theme.bg, 0.9)}; border-radius: 10px 10px 0px 0px; - &.expanded { - margin-top: 20px; - width: 100%; - height: calc(100% - 20px); + width: 100%; + height: 100%; + + &:not(.expanded) { + max-height: 420px; + aspect-ratio: 16 / 9; } transition: all 0.2s ease-in-out; diff --git a/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.tsx b/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.tsx index 55539867e..83e955358 100644 --- a/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.tsx +++ b/packages/ui-kit/src/molecules/WindowPanel/WindowPanel.tsx @@ -10,6 +10,8 @@ interface PropsInterface { subtitle?: string; actions?: React.ReactNode; onClose?: () => void; + initialIsExpanded?: boolean; + onToggleExpand?: (isExpanded: boolean) => void; } const WindowPanel: FC = ({ @@ -18,9 +20,11 @@ const WindowPanel: FC = ({ title, subtitle, actions, + initialIsExpanded = false, + onToggleExpand, onClose }) => { - const [isExpanded, setIsExpanded] = useState(false); + const [isExpanded, setIsExpanded] = useState(initialIsExpanded); return ( @@ -28,7 +32,11 @@ const WindowPanel: FC = ({ title={title} subtitle={subtitle} isExpanded={isExpanded} - onToggleExpand={() => setIsExpanded((isExpanded) => !isExpanded)} + onToggleExpand={() => { + const newVal = !isExpanded; + setIsExpanded(newVal); + onToggleExpand?.(newVal); + }} onClose={onClose} > {actions}