From ae1d7a19a2bbace9940f8668ac4cd3db0a884428 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 22 Dec 2022 00:14:24 +0100 Subject: [PATCH 001/421] making changes --- code/ui/manager/src/app.tsx | 140 ++---- .../src/components/test/Layout.stories.ts | 73 +++ .../ui/manager/src/components/test/Layout.tsx | 455 ++++++++++++++++++ 3 files changed, 569 insertions(+), 99 deletions(-) create mode 100644 code/ui/manager/src/components/test/Layout.stories.ts create mode 100644 code/ui/manager/src/components/test/Layout.tsx diff --git a/code/ui/manager/src/app.tsx b/code/ui/manager/src/app.tsx index 666c93752b42..fa9f4c9472a1 100644 --- a/code/ui/manager/src/app.tsx +++ b/code/ui/manager/src/app.tsx @@ -1,118 +1,60 @@ import type { FC } from 'react'; import React, { useMemo } from 'react'; -import ResizeObserver from 'react-resize-detector'; import { type State } from '@storybook/manager-api'; -import { Symbols } from '@storybook/components'; import { Route } from '@storybook/router'; -import { Global, createGlobal, styled } from '@storybook/theming'; -import { Mobile } from './components/layout/mobile'; -import { Desktop } from './components/layout/desktop'; import Sidebar from './containers/sidebar'; import Preview from './containers/preview'; import Panel from './containers/panel'; import Notifications from './containers/notifications'; import SettingsPages from './settings'; - -const View = styled.div({ - position: 'fixed', - overflow: 'hidden', - height: '100vh', - width: '100vw', -}); +import { Layout } from './components/test/Layout'; export interface AppProps { viewMode: State['viewMode']; layout: State['layout']; panelCount: number; - width: number; - height: number; } -const App = React.memo( - ({ viewMode, layout, panelCount, width, height }) => { - let content; - - const props = useMemo( - () => ({ - Sidebar, - Preview, - Panel, - Notifications, - pages: [ - { - key: 'settings', - render: () => , - route: (({ children }) => ( - - {children} - - )) as FC, - }, - ], - }), - [] - ); - - if (!width || !height) { - content =
; - } else if (width < 600) { - content = ; - } else { - content = ( - - ); - } - - return ( - - - - {content} - - ); - }, - // This is the default shallowEqual implementation, but with custom behavior for the `size` prop. - (prevProps: any, nextProps: any) => { - if (Object.is(prevProps, nextProps)) return true; - if (typeof prevProps !== 'object' || prevProps === null) return false; - if (typeof nextProps !== 'object' || nextProps === null) return false; - - const keysA = Object.keys(prevProps); - const keysB = Object.keys(nextProps); - if (keysA.length !== keysB.length) return false; - - // eslint-disable-next-line no-restricted-syntax - for (const key of keysA) { - if (key === 'size') { - // SizeMe injects a new `size` object every time, even if the width/height doesn't change, - // so we chech that one manually. - if (prevProps[key].width !== nextProps[key].width) return false; - if (prevProps[key].height !== nextProps[key].height) return false; - } else { - if (!Object.prototype.hasOwnProperty.call(nextProps, key)) return false; - if (!Object.is(prevProps[key], nextProps[key])) return false; - } - } - - return true; - } -); - -const SizedApp = (props: Omit) => ( - - {({ width, height }) => } - -); - -App.displayName = 'App'; - -export default SizedApp; +const App = ({ viewMode, layout }: AppProps) => { + const props = useMemo( + () => ({ + Sidebar, + Preview, + Panel, + Notifications, + pages: [ + { + key: 'settings', + render: () => , + route: (({ children }) => ( + + {children} + + )) as FC, + }, + ], + }), + [] + ); + + return ( + } + sidebarContent={} + panelContent={} + customContent={props.pages.map(({ key, route: RouteX, render: Content }) => ( + + + + ))} + /> + ); +}; + +export default App; diff --git a/code/ui/manager/src/components/test/Layout.stories.ts b/code/ui/manager/src/components/test/Layout.stories.ts new file mode 100644 index 000000000000..b66e406a4de8 --- /dev/null +++ b/code/ui/manager/src/components/test/Layout.stories.ts @@ -0,0 +1,73 @@ +import { Layout } from './Layout'; + +export default { + title: 'Layout/New', + component: Layout, + args: { + sidebar: true, + panel: 'bottom', + mainContent: 'Main Content', + sidebarContent: 'Sidebar Content', + panelContent: 'Panel Content', + customContent: 'Custom Content', + }, +}; + +export const Desktop = {}; +export const DesktopHorizontal = { + args: { + panel: 'right', + }, +}; + +export const DesktopDocs = { + args: { + viewMode: 'docs', + }, +}; + +export const DesktopCustom = { + args: { + viewMode: 'custom', + }, +}; + +export const Mobile = { + parameters: { + viewport: { + defaultViewport: 'mobile1', + }, + }, +}; +export const MobileHorizontal = { + args: { + panel: 'right', + }, + parameters: { + viewport: { + defaultViewport: 'mobile1', + }, + }, +}; + +export const MobileDocs = { + args: { + viewMode: 'docs', + }, + parameters: { + viewport: { + defaultViewport: 'mobile1', + }, + }, +}; + +export const MobileCustom = { + args: { + viewMode: 'custom', + }, + parameters: { + viewport: { + defaultViewport: 'mobile1', + }, + }, +}; diff --git a/code/ui/manager/src/components/test/Layout.tsx b/code/ui/manager/src/components/test/Layout.tsx new file mode 100644 index 000000000000..bd19acb1b3b0 --- /dev/null +++ b/code/ui/manager/src/components/test/Layout.tsx @@ -0,0 +1,455 @@ +import type { ViewMode } from '@storybook/types'; +import React, { useEffect, useRef, useState } from 'react'; + +const getGridTemplate = ({ + panel, + viewMode, +}: { + panel: 'bottom' | 'right' | false; + viewMode: ViewMode; +}) => { + if (viewMode !== 'story' && viewMode !== 'docs') { + return ` + .sb-layout { + grid-template-areas: + "a sSidebar o o o" + "a sSidebar o o o" + "a sSidebar o o o"; + }`; + } + + if (viewMode !== 'story') { + return ` + .sb-layout { + grid-template-areas: + "a sSidebar c c c" + "a sSidebar c c c" + "a sSidebar c c c"; + }`; + } + + if (panel === 'right') { + return ` + .sb-layout { + grid-template-areas: + "a sSidebar c sHorizontal b" + "a sSidebar c sHorizontal b" + "a sSidebar c sHorizontal b"; + }`; + } + + return ` + .sb-layout { + grid-template-areas: + "a sSidebar c c c" + "a sSidebar sVertical sVertical sVertical" + "a sSidebar b b b"; + }`; +}; + +const MARGIN = 10; + +const DESKTOP = ` +body{ + margin: 0; + + padding: 0; +} + +.sb-layout { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + + display: grid; + gap: 0; +} + +.sb-aside { + grid-area: a; + background: blue; + position: relative; + z-index: 3; +} +.sb-content { + grid-area: c; + background: green; + position: relative; + z-index: 1; +} +.sb-custom { + grid-area: o; + background: red; + position: relative; + z-index: 1; +} +.sb-panel { + grid-area: b; + background: yellow; + position: relative; + z-index: 2; +} + +.sb-sizer { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + z-index: 4; +} + +.sb-sizer:hover .sb-shade { + opacity: 1; +} + +.sb-mobile-control { + display: none; +} + +.sb-sHorizontal { + grid-area: sHorizontal; + width: ${MARGIN * 2}px; + margin-left: -${MARGIN}px; + cursor: col-resize; + top: 100px; + bottom: 100px; +} +.sb-sHorizontal .sb-shade { + transform: rotate(180deg); + left: 0; +} +.sb-sVertical .sb-shade { + // transform: rotate(90deg); + left: 0; + width: 100%; + transform: rotate(180deg); + height: ${MARGIN}px; + background-size: 100% ${MARGIN * 2}px; + background-position: 50% ${MARGIN}px; + +} + +.sb-sVertical { + grid-area: sVertical; + height: ${MARGIN * 2}px; + margin-top: -${MARGIN}px; + cursor: row-resize; + left: 100px; + right: 100px; +} +.sb-sSidebar { + grid-area: sSidebar; + width: ${MARGIN * 2}px; + margin-left: -${MARGIN}px; + cursor: col-resize; + top: 100px; + bottom: 100px; +} + +.sb-shade { + position: absolute; + right: 0; + bottom: 0; + top: 0; + width: ${MARGIN}px; + background: radial-gradient(at center center,rgba(0,0,0,1) 0%,transparent 70%,transparent 100%); + background-size: ${MARGIN * 2}px 100%; + background-position: ${MARGIN}px 50%; + opacity: 0; + transition: opacity 0.2s; +} +`; + +const MOBILE = ` +body{ + margin: 0; + padding: 0; +} + +.sb-layout { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 1; +} + +.sb-aside { + position: absolute; + top: 0; + left: 0; + bottom: 50px; + right: 30%; + background: blue; + transform: translateX(0%); + transition: transform 0.3s; + z-index: 3; +} +.sb-aside[hidden] { + display: block; + transform: translateX(-100%); +} + +.sb-content { + position: absolute; + top: 0; + left: 0; + bottom: 50px; + right: 0; + background: green; + z-index: 1; +} + +.sb-custom { + position: absolute; + top: 0; + left: 0; + bottom: 50px; + right: 0; + background: orange; + z-index: 1; +} + + +.sb-aside:not([hidden]) + .sb-panel { + display: block; + transform: translateX(100%); +} +.sb-panel { + position: absolute; + top: 0; + left: 30%; + bottom: 50px; + right: 0; + background: yellow; + transform: translateX(0%); + transition: transform 0.3s; + z-index: 2; +} +.sb-panel[hidden] { + display: block; + transform: translateX(100%); +} +.sb-mobile-control { + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: 50px; + background: red; + z-index: 4; + + display: flex; +} +.sb-mobile-control > * { + flex: 1; +} + +`; + +interface Props { + panel?: 'bottom' | 'right' | false; + sidebar?: boolean; + mainContent?: React.ReactNode; + sidebarContent?: React.ReactNode; + panelContent?: React.ReactNode; + customContent?: React.ReactNode; + viewMode?: ViewMode; +} + +export const Layout = ({ + panel, + sidebar, + mainContent, + sidebarContent, + panelContent, + customContent, + viewMode = 'story', +}: Props) => { + const resizer0 = useRef(null); + const resizer1 = useRef(null); + const resizer2 = useRef(null); + + const [sidebarWidth, setSidebarWidth] = useState(sidebar ? 20 : 0); + const [bottomHeight, setBottomHeight] = useState(panel === 'bottom' ? 20 : 0); + const [columnWidth, setColumnWidth] = useState(panel === 'right' ? 20 : 0); + + useEffect(() => { + const sHorizontal = resizer0.current; + const sVertical = resizer1.current; + const sSidebar = resizer2.current; + let current: typeof sHorizontal | typeof sVertical | typeof sSidebar | null = null; + + const onDragStart = (e: MouseEvent) => { + e.preventDefault(); + if (e.currentTarget === sHorizontal) { + current = sHorizontal; + } else if (e.currentTarget === sVertical) { + current = sVertical; + } else if (e.currentTarget === sSidebar) { + current = sSidebar; + } + window.addEventListener('mousemove', onDrag); + window.addEventListener('mouseup', onDragEnd); + }; + + const onDragEnd = (e: MouseEvent) => { + window.removeEventListener('mousemove', onDrag); + window.removeEventListener('mouseup', onDragEnd); + }; + + const onDrag = (e: MouseEvent) => { + if (e.buttons === 0) { + onDragEnd(e); + return; + } + + if (current === sSidebar) { + const value = Math.round((e.clientX / e.view.innerWidth) * 100); + if (value + columnWidth > 70) { + // preserve space for content + return; + } + + if (value < 5) { + setSidebarWidth(0); + return; + } + setSidebarWidth(value); + } else if (current === sHorizontal) { + const value = 100 - Math.round((e.clientX / e.view.innerWidth) * 100); + if (value + sidebarWidth > 70) { + // preserve space for content + return; + } + + if (value < 5) { + setColumnWidth(0); + return; + } + + setColumnWidth(value); + } else if (current === sVertical) { + const value = 100 - Math.round((e.clientY / e.view.innerHeight) * 100); + if (value > 70) { + return; + } + if (value < 5) { + setBottomHeight(0); + return; + } + setBottomHeight(value); + } + }; + + sHorizontal?.addEventListener('mousedown', onDragStart); + sVertical?.addEventListener('mousedown', onDragStart); + sSidebar?.addEventListener('mousedown', onDragStart); + + return () => { + sHorizontal?.removeEventListener('mousedown', onDragStart); + sVertical?.removeEventListener('mousedown', onDragStart); + sSidebar?.removeEventListener('mousedown', onDragStart); + }; + }); + + const k = panel === 'bottom' ? bottomHeight : columnWidth; + const mobileNavShown = sidebarWidth !== 0; + const mobilePanelShown = k !== 0 && mobileNavShown === false; + + const setMobileNavShown = () => { + setSidebarWidth(30); + setColumnWidth(0); + setBottomHeight(0); + }; + + const setMobilePanelShown = () => { + setColumnWidth(30); + setBottomHeight(30); + setSidebarWidth(0); + }; + const setMobileContentShown = () => { + setColumnWidth(0); + setBottomHeight(0); + setSidebarWidth(0); + }; + + return ( + <> +