diff --git a/src/Playroom/Playroom.less b/src/Playroom/Playroom.less index a9652a08..91baabd4 100644 --- a/src/Playroom/Playroom.less +++ b/src/Playroom/Playroom.less @@ -4,7 +4,7 @@ width: 100%; height: 100%; overflow: hidden; - background-color: @code-background; + background-color: @body; } :global(body) { @@ -30,16 +30,74 @@ right: 0; overflow: hidden; box-shadow: @shadow-small; + transition: @transition-slow; } .resizeableContainer_isRight { top: 0; max-width: 90vw; + + &.resizeableContainer_isHidden { + transform: translateX(100%); + } } .resizeableContainer_isBottom { left: 0; max-height: 90vh; + + &.resizeableContainer_isHidden { + transform: translateY(100%); + } +} + +.toggleEditorContainer { + position: absolute; + bottom: 0; + right: 0; + display: flex; + justify-content: center; + + &.isBottom { + width: @toolbar-item-size; + } +} + +.toggleEditorButton { + position: relative; + appearance: none; + background: none; + outline: none; + border: 0; + padding: 0; + border-radius: @radius-large; + min-width: @interaction-height; + width: 100%; + height: @interaction-height; + cursor: pointer; + + &:not(:hover):not(:focus) { + opacity: @preview-inactive-label-opacity; + } + + &:before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background-color: currentColor; + border-radius: @radius-large; + opacity: 0; + transition: @transition-slow; + pointer-events: none; + + :hover&, + :focus& { + opacity: 0.05; + } + } } .editorContainer { diff --git a/src/Playroom/Playroom.tsx b/src/Playroom/Playroom.tsx index 8d25811d..c3f98a72 100644 --- a/src/Playroom/Playroom.tsx +++ b/src/Playroom/Playroom.tsx @@ -7,6 +7,7 @@ import WindowPortal from './WindowPortal'; import { Snippets } from '../../utils'; import componentsToHints from '../utils/componentsToHints'; import Toolbar from './Toolbar/Toolbar'; +import ChevronSvg from './Toolbar/icons/ChevronSvg'; import { StoreContext, EditorPosition } from '../StoreContext/StoreContext'; // @ts-ignore @@ -53,6 +54,17 @@ const resizableConfig = (position: EditorPosition = 'bottom') => ({ topLeft: false }); +const resolveDirection = ( + editorPosition: EditorPosition, + editorHidden: boolean +) => { + if (editorPosition === 'right') { + return editorHidden ? 'left' : 'right'; + } + + return editorHidden ? 'up' : 'down'; +}; + export interface PlayroomProps { components: Record; themes: string[]; @@ -66,6 +78,7 @@ export default ({ components, themes, widths, snippets }: PlayroomProps) => { editorPosition, editorHeight, editorWidth, + editorHidden, visibleThemes, visibleWidths, code, @@ -142,7 +155,8 @@ export default ({ components, themes, widths, snippets }: PlayroomProps) => { {
{ visibleWidths && visibleWidths.length > 0 ? visibleWidths : widths } /> +
+ +
{editorContainer} diff --git a/src/Playroom/Preview/Preview.less b/src/Playroom/Preview/Preview.less index c12dc663..c1bd34f0 100644 --- a/src/Playroom/Preview/Preview.less +++ b/src/Playroom/Preview/Preview.less @@ -3,7 +3,6 @@ @frame-name-height: 30px; .root { - background-color: @preview-background; box-sizing: border-box; width: 100%; height: 100%; @@ -50,7 +49,7 @@ transition: @transition-medium; .frameContainer:not(:hover) & { - opacity: 0.3; + opacity: @preview-inactive-label-opacity; } } diff --git a/src/Playroom/Toolbar/Toolbar.less b/src/Playroom/Toolbar/Toolbar.less index 5fe5f6cc..95cd0557 100644 --- a/src/Playroom/Toolbar/Toolbar.less +++ b/src/Playroom/Toolbar/Toolbar.less @@ -7,7 +7,6 @@ display: flex; flex-direction: row-reverse; color: @toolbar-foregound; - overflow: hidden; &.isOpen { width: 100vw; @@ -26,6 +25,7 @@ pointer-events: none; height: 100%; flex-direction: row-reverse; + overflow: hidden; } .topButtons { diff --git a/src/Playroom/Toolbar/Toolbar.tsx b/src/Playroom/Toolbar/Toolbar.tsx index f6bfc56d..4b1da50b 100644 --- a/src/Playroom/Toolbar/Toolbar.tsx +++ b/src/Playroom/Toolbar/Toolbar.tsx @@ -1,4 +1,5 @@ -import React, { useContext, ReactChild, useEffect, useState } from 'react'; +import React, { useContext, ReactChild, useState, useCallback } from 'react'; +import { useTimeoutFn } from 'react-use'; import copy from 'copy-to-clipboard'; import classnames from 'classnames'; import { PlayroomProps } from '../Playroom'; @@ -54,13 +55,19 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => { dispatch ] = useContext(StoreContext); const [copying, setCopying] = useState(false); + const [isReady, cancel, reset] = useTimeoutFn(() => setCopying(false), 2000); - useEffect(() => { - if (copying) { - copy(window.location.href); - setTimeout(() => setCopying(false), 2000); + const copyHandler = useCallback(() => { + copy(window.location.href); + dispatch({ type: 'closeToolbar' }); + setCopying(true); + + if (isReady() === false) { + cancel(); } - }, [copying]); + + reset(); + }, [cancel, dispatch, isReady, reset]); const isSnippetsOpen = activeToolbarPanel === 'snippets'; const isThemeOpen = activeToolbarPanel === 'themes'; @@ -97,16 +104,19 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => { title={`Insert snippet (${ navigator.platform.match('Mac') ? '\u2318' : 'Ctrl + ' }K)`} - showStatus={!validCursorPosition} + showStatus={ + !copying && !validCursorPosition && !activeToolbarPanel + } statusMessage="Can't insert snippet at cursor" statusMessageTone="critical" data-testid="toggleSnippets" - onClick={() => + onClick={() => { + setCopying(false); dispatch({ type: 'toggleToolbar', payload: { panel: 'snippets' } - }) - } + }); + }} > @@ -120,12 +130,13 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => { ? `Showing ${visibleThemes.length} of ${allThemes.length} themes` : 'Configure themes' } - onClick={() => + onClick={() => { + setCopying(false); dispatch({ type: 'toggleToolbar', payload: { panel: 'themes' } - }) - } + }); + }} > @@ -138,24 +149,25 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => { ? `Showing ${visibleWidths.length} of ${allWidths.length} widths` : 'Configure widths' } - onClick={() => + onClick={() => { + setCopying(false); dispatch({ type: 'toggleToolbar', payload: { panel: 'widths' } - }) - } + }); + }} data-testid="toggleWidths" > setCopying(true)} + showStatus={copying && !activeToolbarPanel} + onClick={copyHandler} data-testid="copyToClipboard" > @@ -185,7 +197,7 @@ export default ({ themes: allThemes, widths: allWidths, snippets }: Props) => { .filter(pos => pos !== editorPosition) .map(pos => { const position = pos as EditorPosition; - return ( + return position === 'undocked' ? null : (