diff --git a/src/components/common/Dock/Dock.tsx b/src/components/common/Dock/Dock.tsx index 5e52cc6..8071773 100644 --- a/src/components/common/Dock/Dock.tsx +++ b/src/components/common/Dock/Dock.tsx @@ -4,86 +4,22 @@ // // @jsxImportSource @emotion/react -import React, { PropsWithChildren, ReactNode } from 'react'; -import DockContainer from './DockContainer'; -import DockItem, { DockItemProps } from './DockItem'; -import { dockAttachedDelayProps, dockAttachedDirectionProps } from './types'; +import React from 'react'; +import DockManager, { DockedReactNode } from './DockManager'; +import { DockDirection, dockDirectionPropName } from './types'; -export default ({ children }: PropsWithChildren) => { - let topLineCount = 1; - let bottomLineCount = 1; - let leftLineCount = 1; - let rightLineCount = 1; +export default ({ children }: React.PropsWithChildren) => { + const dockedNodes: DockedReactNode[] = React.Children.toArray(children).filter(child => child).map(child => { + let dockDirection: DockDirection = 'Fill'; - const mapChild = (child: ReactNode, index: number) => { - if (child && typeof child === 'object' && 'props' in child) { - let props: DockItemProps = { - rowStart: `top-${topLineCount}`, - rowEnd: `bottom-${bottomLineCount}`, - columnStart: `left-${leftLineCount}`, - columnEnd: `right-${rightLineCount}`, - dock: 'Fill', - }; - - dockAttachedDelayProps.filter(prop => prop in child.props).forEach(prop => { - props = { ...props, ...{ [prop.substring('dock-'.length)]: child.props[prop] } }; - }); - - switch (dockAttachedDirectionProps.find(propName => propName in child.props)) { - case 'dock-top': { - ++topLineCount; - props.rowEnd = `top-${topLineCount}`; - props.dock = 'Top'; - break; - } - - case 'dock-bottom': { - ++bottomLineCount; - props.rowStart = `bottom-${bottomLineCount}`; - props.dock = 'Bottom'; - break; - } - - case 'dock-left': { - ++leftLineCount; - props.columnEnd = `left-${leftLineCount}`; - props.dock = 'Left'; - break; - } - - case 'dock-right': { - ++rightLineCount; - props.columnStart = `right-${rightLineCount}`; - props.dock = 'Right'; - break; - } - - case 'dock-fill': { - break; - } - - default: { - return child; - } - } - - const key = child.key ?? index.toString(36); - return {child}; + if (child && typeof child === 'object' && 'props' in child && dockDirectionPropName in child.props) { + dockDirection = child.props[dockDirectionPropName]; } - return child; - } - - const wrappedChildren = React.Children.map(children, mapChild); - - const topLines = [...Array(topLineCount + 1).keys()].slice(1).map(num => `[top-${num}]`); - const bottomLines = [...Array(bottomLineCount + 1).keys()].slice(1).reverse().map(num => `[bottom-${num}]`); - const leftLines = [...Array(leftLineCount + 1).keys()].slice(1).map(num => `[left-${num}]`); - const rightLines = [...Array(rightLineCount + 1).keys()].slice(1).reverse().map(num => `[right-${num}]`); + return [dockDirection, child]; + }); return ( - - {wrappedChildren} - + ); }; diff --git a/src/components/common/Dock/DockContainer.tsx b/src/components/common/Dock/DockContainer.tsx index 443d890..0436782 100644 --- a/src/components/common/Dock/DockContainer.tsx +++ b/src/components/common/Dock/DockContainer.tsx @@ -5,7 +5,7 @@ // @jsxImportSource @emotion/react import { CSSObject } from '@emotion/react' -import { PropsWithChildren } from 'react' +import React from 'react' import { Fill } from '../../../styles/layout'; import { mergeStyles } from '../../../styles/mergeStyles'; @@ -16,7 +16,7 @@ export interface DockContainerProps { rightLines: string[], } -export default ({ topLines, bottomLines, leftLines, rightLines, children }: PropsWithChildren) => { +export default ({ topLines, bottomLines, leftLines, rightLines, children }: React.PropsWithChildren) => { const gridTemplateRows = `${topLines.join(' max-content ')} 1fr ${bottomLines.join(' max-content ')}`; const gridTemplateColumns = `${leftLines.join(' max-content ')} 1fr ${rightLines.join(' max-content ')}`; diff --git a/src/components/common/Dock/DockItem.tsx b/src/components/common/Dock/DockItem.tsx index c1fbc82..7c99370 100644 --- a/src/components/common/Dock/DockItem.tsx +++ b/src/components/common/Dock/DockItem.tsx @@ -5,9 +5,7 @@ // @jsxImportSource @emotion/react import { CSSObject } from '@emotion/react' -import { PropsWithChildren } from 'react' -import useThrottledState from '../../../hooks/useThrottledState' -import { mergeStyles } from '../../../styles/mergeStyles' +import React from 'react' import { DockDirection } from './types' export interface DockItemProps { @@ -16,12 +14,10 @@ export interface DockItemProps { rowStart: string, rowEnd: string, dock: DockDirection, - showDelay?: number, - hideDelay?: number, } -export default ({ columnStart, columnEnd, rowStart, rowEnd, dock, showDelay, hideDelay, children }: PropsWithChildren) => { - const baseStyle: CSSObject = { +export default ({ columnStart, columnEnd, rowStart, rowEnd, dock, children }: React.PropsWithChildren) => { + const style: CSSObject = { gridColumnStart: columnStart, gridColumnEnd: columnEnd, gridRowStart: rowStart, @@ -29,32 +25,8 @@ export default ({ columnStart, columnEnd, rowStart, rowEnd, dock, showDelay, hid label: `Dock-${dock}`, } - const autoHide = showDelay !== undefined || hideDelay !== undefined; - const showDelayWithDefault = showDelay ?? 0; - const hideDelayWithDefault = hideDelay ?? 0; - - const calculateTimeout = (isCurrentlyHidden: boolean) => (isCurrentlyHidden ? showDelayWithDefault : hideDelayWithDefault); - const [isHidden, setIsHidden] = useThrottledState(autoHide, calculateTimeout); - - let style = baseStyle; - if (autoHide && isHidden) { - if (dock === 'Top' || dock === 'Bottom') { - style = mergeStyles(baseStyle, { height: '10px' }); - } else { - style = mergeStyles(baseStyle, { width: '10px' }); - } - } - - const onMouseEnter = () => { - setIsHidden(false); - }; - - const onMouseLeave = () => { - setIsHidden(true); - }; - return ( -
+
{children}
); diff --git a/src/components/common/Dock/DockManager.tsx b/src/components/common/Dock/DockManager.tsx new file mode 100644 index 0000000..2c0112f --- /dev/null +++ b/src/components/common/Dock/DockManager.tsx @@ -0,0 +1,92 @@ +// Copyright (c) Hubert Bukowski. All rights reserved. +// Licensed under the MIT License. +// See LICENSE file in the project root for full license information. +// +// @jsxImportSource @emotion/react + +import React from 'react' +import DockContainer from './DockContainer'; +import DockItem, { DockItemProps } from './DockItem'; +import { DockDirection } from './types'; + +export type DockedReactNode = [ DockDirection, React.ReactNode ]; +export interface RelocationInfo { + index: number, + dock: DockDirection, +} + +export interface DockManagerProps { + dockedNodes: DockedReactNode[], +} + +export default ({ dockedNodes }: DockManagerProps) => { + let topLineCount = 1; + let bottomLineCount = 1; + let leftLineCount = 1; + let rightLineCount = 1; + + const mapChild = (dockedNode: DockedReactNode, index: number) => { + const [dock, child] = dockedNode; + + const props: DockItemProps = { + rowStart: `top-${topLineCount}`, + rowEnd: `bottom-${bottomLineCount}`, + columnStart: `left-${leftLineCount}`, + columnEnd: `right-${rightLineCount}`, + dock, + }; + + let key: React.Key = index.toString(36); + + if (child && typeof child === 'object' && 'props' in child) { + switch (dock) { + case 'Top': { + ++topLineCount; + props.rowEnd = `top-${topLineCount}`; + break; + } + + case 'Bottom': { + ++bottomLineCount; + props.rowStart = `bottom-${bottomLineCount}`; + break; + } + + case 'Left': { + ++leftLineCount; + props.columnEnd = `left-${leftLineCount}`; + break; + } + + case 'Right': { + ++rightLineCount; + props.columnStart = `right-${rightLineCount}`; + break; + } + + default: { + break; + } + } + + if (child.key) { + key = child.key; + } + } + + return {child} + } + + const wrappedChildren = dockedNodes.map(mapChild); + + const topLines = [...Array(topLineCount + 1).keys()].slice(1).map(num => `[top-${num}]`); + const bottomLines = [...Array(bottomLineCount + 1).keys()].slice(1).reverse().map(num => `[bottom-${num}]`); + const leftLines = [...Array(leftLineCount + 1).keys()].slice(1).map(num => `[left-${num}]`); + const rightLines = [...Array(rightLineCount + 1).keys()].slice(1).reverse().map(num => `[right-${num}]`); + + return ( + + {wrappedChildren} + + ); +} diff --git a/src/components/common/Dock/DockWrapper.tsx b/src/components/common/Dock/DockWrapper.tsx index 1f9f5ba..002e6d6 100644 --- a/src/components/common/Dock/DockWrapper.tsx +++ b/src/components/common/Dock/DockWrapper.tsx @@ -4,8 +4,8 @@ // // @jsxImportSource @emotion/react -import { PropsWithChildren } from 'react'; +import React from 'react'; -export default ({ children }: PropsWithChildren) => ( +export default ({ children }: React.PropsWithChildren) => ( <>{children} ); diff --git a/src/components/common/Dock/index.ts b/src/components/common/Dock/index.ts index a9fd34b..f81195a 100644 --- a/src/components/common/Dock/index.ts +++ b/src/components/common/Dock/index.ts @@ -6,4 +6,4 @@ export { default } from './Dock'; export { default as DockWrapper } from './DockWrapper'; -export type { DockAttachedDirectionProps, DockAttachedDelayProps, DockAttachedProps } from './types'; +export type { DockAttachedProps } from './types'; diff --git a/src/components/common/Dock/types.ts b/src/components/common/Dock/types.ts index ee08e77..f454503 100644 --- a/src/components/common/Dock/types.ts +++ b/src/components/common/Dock/types.ts @@ -6,39 +6,16 @@ export type DockDirection = 'Top' | 'Bottom' | 'Left' | 'Right' | 'Fill'; -export interface DockAttachedDirectionProps { - 'dock-top'?: boolean, - 'dock-bottom'?: boolean, - 'dock-left'?: boolean, - 'dock-right'?: boolean, - 'dock-fill'?: boolean, -} - -export interface DockAttachedDelayProps { - 'dock-showDelay'?: number, - 'dock-hideDelay'?: number, -} +export const dockDirectionPropName = 'dock-direction'; -export interface DockAttachedProps extends DockAttachedDirectionProps, DockAttachedDelayProps { +export interface DockAttachedProps { + 'dock-direction'?: DockDirection, } -export type DockAttachedDirectionProp = keyof DockAttachedDirectionProps; -export type DockAttachedDelayProp = keyof DockAttachedDelayProps; export type DockAttachedProp = keyof DockAttachedProps; -export const defaultDockAttachedDirectionProps: Required = { - 'dock-top': false, - 'dock-bottom': false, - 'dock-left': false, - 'dock-right': false, - 'dock-fill': false, -} - -export const dockAttachedDirectionProps = Object.getOwnPropertyNames(defaultDockAttachedDirectionProps) as DockAttachedDirectionProp[]; - -export const defaultDockAttachedDelayProps: Required = { - 'dock-showDelay': 0, - 'dock-hideDelay': 0, +const fullDockAttachedProps: DockAttachedProps = { + 'dock-direction': undefined, } -export const dockAttachedDelayProps = Object.getOwnPropertyNames(defaultDockAttachedDelayProps) as DockAttachedDelayProp[]; +export const dockAttachedProps = Object.getOwnPropertyNames(fullDockAttachedProps) as DockAttachedProps[]; diff --git a/src/components/common/FullscreenViewport.tsx b/src/components/common/FullscreenViewport.tsx index b0f35c8..1f1407e 100644 --- a/src/components/common/FullscreenViewport.tsx +++ b/src/components/common/FullscreenViewport.tsx @@ -4,7 +4,7 @@ // // @jsxImportSource @emotion/react -import { PropsWithChildren } from 'react'; +import React from 'react'; import { CSSObject } from '@emotion/react'; import { Fill, FullScreen } from '../../styles/layout'; import { mergeStyles } from '../../styles/mergeStyles'; @@ -20,7 +20,7 @@ const fitContentStyle: CSSObject = mergeStyles(Fill, { minHeight: 'fit-content', }); -export default ({ children }: PropsWithChildren) => ( +export default ({ children }: React.PropsWithChildren) => (
{children} diff --git a/src/components/common/ModalDialog.tsx b/src/components/common/ModalDialog.tsx index 10ad449..904c36a 100644 --- a/src/components/common/ModalDialog.tsx +++ b/src/components/common/ModalDialog.tsx @@ -6,7 +6,7 @@ import { CSSObject } from '@emotion/react'; import FocusTrap from 'focus-trap-react'; -import { PropsWithChildren, useEffect } from 'react'; +import React from 'react'; import { Fill } from '../../styles/layout'; import { mergeStyles } from '../../styles/mergeStyles'; import { BackgroundColor, DimerColor } from '../../styles/themes'; @@ -48,7 +48,7 @@ const contentStyle: CSSObject = { margin: '0.5rem', } -export default ({ onClose, children }: PropsWithChildren) => { +export default ({ onClose, children }: React.PropsWithChildren) => { const closeOnEscapeKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && onClose) { onClose(); @@ -64,7 +64,7 @@ export default ({ onClose, children }: PropsWithChildren) => { window.removeEventListener('keydown', closeOnEscapeKey); }; - useEffect(() => { + React.useEffect(() => { addListener(); return removeListener; }, []); diff --git a/src/components/common/ModalDialogLink.tsx b/src/components/common/ModalDialogLink.tsx index 328881c..6afb9d4 100644 --- a/src/components/common/ModalDialogLink.tsx +++ b/src/components/common/ModalDialogLink.tsx @@ -4,16 +4,16 @@ // // @jsxImportSource @emotion/react -import { PropsWithChildren, ReactNode } from 'react'; +import React from 'react'; import { Link, Outlet, Route, Routes } from 'react-router-dom'; import RoutedModalDialog from './RoutedModalDialog'; export interface ModalDialogLinkProps { - content: ReactNode, + content: React.ReactNode, to: string, } -export default ({ content, to, children }: PropsWithChildren) => { +export default ({ content, to, children }: React.PropsWithChildren) => { const dialog = {content}; return ( diff --git a/src/components/common/RoutedModalDialog.tsx b/src/components/common/RoutedModalDialog.tsx index c975ab7..194ccd2 100644 --- a/src/components/common/RoutedModalDialog.tsx +++ b/src/components/common/RoutedModalDialog.tsx @@ -4,11 +4,11 @@ // // @jsxImportSource @emotion/react -import { PropsWithChildren } from 'react'; +import React from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import ModalDialog from './ModalDialog'; -export default ({ children }: PropsWithChildren) => { +export default ({ children }: React.PropsWithChildren) => { const navigate = useNavigate(); const location = useLocation(); const navigateBack = () => { diff --git a/src/components/common/Toolbar.tsx b/src/components/common/Toolbar.tsx index a0ee5f6..1e9d815 100644 --- a/src/components/common/Toolbar.tsx +++ b/src/components/common/Toolbar.tsx @@ -5,7 +5,7 @@ // @jsxImportSource @emotion/react import { CSSObject } from '@emotion/react'; -import { PropsWithChildren } from 'react'; +import React from 'react'; const style: CSSObject = { label: 'Toolbar-Main', @@ -13,7 +13,7 @@ const style: CSSObject = { flexDirection: 'row', }; -export default ({ children }: PropsWithChildren) => ( +export default ({ children }: React.PropsWithChildren) => (
{children}
diff --git a/src/components/views/main/Footer.tsx b/src/components/views/main/Footer.tsx index a212515..2a56a33 100644 --- a/src/components/views/main/Footer.tsx +++ b/src/components/views/main/Footer.tsx @@ -11,8 +11,8 @@ import VersionLabel from '../../specialized/VersionLabel'; export default () => (
- - + +
); diff --git a/src/components/views/main/MainView.tsx b/src/components/views/main/MainView.tsx index dd064fd..c5d0371 100644 --- a/src/components/views/main/MainView.tsx +++ b/src/components/views/main/MainView.tsx @@ -5,15 +5,14 @@ // @jsxImportSource @emotion/react import { CSSObject, Global } from '@emotion/react'; +import React from 'react'; import FullscreenViewport from '../../common/FullscreenViewport'; import Footer from './Footer'; import Header from './Header'; -import { Fill } from '../../../styles/layout'; -import { mergeStyles } from '../../../styles/mergeStyles'; import Toolbox from './Toolbox'; -import TileGalleryView from '../tileGallery/TileGalleryView'; -import Dock from '../../common/Dock'; -import DockWrapper from '../../common/Dock/DockWrapper'; +import DockManager from '../../common/Dock/DockManager'; +import Workspace from './Workspace'; +import { DockDirection } from '../../common/Dock/types'; const globalStyle : CSSObject = { body: { @@ -21,24 +20,22 @@ const globalStyle : CSSObject = { }, }; -const MainStyle: CSSObject = mergeStyles(Fill, { - label: 'MainView-Main', -}); +type DockElement = [ DockDirection, React.ReactNode ]; -export default () => ( - <> - - - -
-