From ca2c1d9b1d424f57b431509f8ed184f2492d8afa Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Mon, 23 Apr 2018 08:27:33 +0200 Subject: [PATCH 01/32] feat(component): update styling of layout --- src/component/container/app.tsx | 8 +++---- src/lsg/patterns/chrome/index.tsx | 16 +++++++------ src/lsg/patterns/layout/index.tsx | 3 ++- src/lsg/patterns/panes/element-pane/index.tsx | 24 ++++++++++++++++--- src/lsg/patterns/panes/preview-pane/index.tsx | 8 +++---- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index 252bac6bd..4a8153e5b 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -150,19 +150,19 @@ export class App extends React.Component { {project && [ - + - - - , , + + + ]} diff --git a/src/lsg/patterns/chrome/index.tsx b/src/lsg/patterns/chrome/index.tsx index 379c67105..1a1b9bc70 100644 --- a/src/lsg/patterns/chrome/index.tsx +++ b/src/lsg/patterns/chrome/index.tsx @@ -20,12 +20,15 @@ const StyledChrome = styled.div` display: flex; align-items: center; width: 100%; - height: 54px; + height: 40px; padding: ${getSpace(SpaceSize.XS)}px ${getSpace(SpaceSize.XXL) * 3}px; + border-bottom: 0.5px solid ${colors.grey90.toString()}; + background: ${colors.white.toString()}; font-family: ${fonts().NORMAL_FONT}; -webkit-app-region: drag; -webkit-user-select: none; user-select: none; + -webkit-font-smoothing: antialiased; `; const StyledChromeTitle = styled.div` @@ -38,13 +41,12 @@ const StyledChromeTitle = styled.div` const StyledTitleWrapper = styled.div` position: relative; - margin: 0 ${getSpace(SpaceSize.XS)}px; + margin: -${getSpace(SpaceSize.XXS)}px ${getSpace(SpaceSize.XS)}px 0; white-space: nowrap; - text-overflow: ellipsis; + text-overflow: ellipsis; overflow: hidden; text-align: center; width: 130px; - } `; interface StyledIconWrapperProps { @@ -53,7 +55,7 @@ interface StyledIconWrapperProps { const StyledIconWrapper = styled.div` margin: ${getSpace(SpaceSize.XS)}px; - padding: ${getSpace(SpaceSize.XS)}px; + padding: ${getSpace(SpaceSize.XXS)}px; border-radius: ${getSpace(SpaceSize.XXS)}px; &:hover { @@ -77,13 +79,13 @@ const Chrome: React.StatelessComponent = props => ( - + {props.title} - + {props.children} diff --git a/src/lsg/patterns/layout/index.tsx b/src/lsg/patterns/layout/index.tsx index ca7d3938a..d4eee9226 100644 --- a/src/lsg/patterns/layout/index.tsx +++ b/src/lsg/patterns/layout/index.tsx @@ -18,7 +18,8 @@ const StyledLayout = styled.div` const StyledMainArea = styled(StyledLayout)` box-sizing: border-box; height: 100vh; - padding-top: 54px; + padding-top: 40px; + -webkit-font-smoothing: antialiased; `; const StyledSideBar = styled(StyledLayout)` diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index 68839c19a..d76aa0ef7 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import styled from 'styled-components'; +import { colors } from '../../colors'; const StyledElementPane = styled.div` position: relative; @@ -8,6 +9,19 @@ const StyledElementPane = styled.div` flex-basis: 60%; overflow: scroll; + &::before { + content: ''; + position: absolute; + top: 0; + right: 0; + display: block; + width: 1px; + height: 100%; + border-right: 0.5px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + box-sizing: border-box; + z-index: 10; + } + /*FadeOut*/ &::after { content: ''; @@ -16,13 +30,17 @@ const StyledElementPane = styled.div` display: block; width: 100%; height: 40px; - background: linear-gradient(to bottom, + background: linear-gradient( + to bottom, rgba(247, 247, 247, 0) 0%, rgba(247, 247, 247, 0.5) 15%, - rgba(247, 247, 247, 1) 100%); + rgba(247, 247, 247, 1) 100% + ); } `; -const ElementPane: React.StatelessComponent = props => {props.children}; +const ElementPane: React.StatelessComponent = props => ( + {props.children} +); export default ElementPane; diff --git a/src/lsg/patterns/panes/preview-pane/index.tsx b/src/lsg/patterns/panes/preview-pane/index.tsx index b104675f7..fca23a11d 100644 --- a/src/lsg/patterns/panes/preview-pane/index.tsx +++ b/src/lsg/patterns/panes/preview-pane/index.tsx @@ -24,6 +24,7 @@ const StyledPreviewResizer = styled.div` width: 12px; height: 100%; cursor: ew-resize; + display: none; &::after { content: ''; position: absolute; @@ -41,15 +42,14 @@ const BaseStyledPreviewPane = styled.div` flex-grow: 1; overflow: hidden; background: ${colors.white.toString()}; - border-radius: 6px 6px 0 0; - box-shadow: 0 3px 9px 0 ${colors.black.toRGBString(0.15)}; + border-right: 0.5px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; `; const StyledPreviewPane = BaseStyledPreviewPane.extend.attrs({ style: (props: PreviewPaneProps) => ({ maxWidth: `${props.width}px` || 'none' }) -}) `${(props: PreviewPaneProps) => ({})}`; +})`${(props: PreviewPaneProps) => ({})}`; export default class PreviewPane extends React.Component { private previewPane: HTMLElement; @@ -84,7 +84,7 @@ export default class PreviewPane extends React.Component { ` }} /> From b0cc9435feabcb9a8c94999e87ff2eb1899274d1 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Mon, 23 Apr 2018 08:54:27 +0200 Subject: [PATCH 02/32] feat(component): update styling of element pane --- src/lsg/patterns/colors/index.tsx | 4 + src/lsg/patterns/element/index.tsx | 97 ++++++++++++++----- src/lsg/patterns/panes/element-pane/index.tsx | 2 + 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/lsg/patterns/colors/index.tsx b/src/lsg/patterns/colors/index.tsx index df1a1576a..af3b0571d 100644 --- a/src/lsg/patterns/colors/index.tsx +++ b/src/lsg/patterns/colors/index.tsx @@ -71,6 +71,10 @@ export const colors = { displayName: 'Blue 40', rgb: [102, 169, 230] }), + blue80: new Color({ + displayName: 'Blue 80', + rgb: [212, 226, 242] + }), grey20: new Color({ displayName: 'Grey 20', rgb: [52, 61, 69] diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index bce1a9377..b564f292c 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -45,32 +45,56 @@ export interface StyledPlaceholder { } const StyledElement = styled.div` - cursor: default; position: relative; + z-index: 1; `; const StyledElementLabel = styled.div` position: relative; display: flex; - padding: 9px ${getSpace(Size.L)}px 9px ${getSpace(Size.XL)}px; - border-radius: 3px; - cursor: pointer; + padding: ${getSpace(Size.XS)}px ${getSpace(Size.L)}px ${getSpace(Size.XS)}px ${getSpace(Size.XXL)}px; align-items: center; - color: ${colors.black.toString()}; + color: ${colors.grey20.toString()}; position: relative; + font-size: 15px; + line-height: 21px; + z-index: 1; + + &::before { + content: ''; + display: block; + position: absolute; + height: 100%; + width: 240px; + left: 0; + top: 0; + margin-left: -240px; + } &:hover { - background: ${colors.grey90.toString()}; + background ${colors.black.toString('rgb', { alpha: 0.05 })}; + + &::before { + background: ${colors.black.toString('rgb', { alpha: 0.05 })}; + } } ${(props: StyledElementLabelProps) => props.active ? ` - color: ${colors.white.toString()}; - background: ${colors.blue40.toString()}; + color: ${colors.blue.toString()}; + background: ${colors.blue80.toString()}; + + &::before { + background: ${colors.blue80.toString()}; + } &:hover { - background: ${colors.blue40.toString()}; + background: ${colors.blue80.toString()}; + + &::before { + background: ${colors.blue80.toString()}; + } } ` : ''}; @@ -78,36 +102,59 @@ const StyledElementLabel = styled.div` props.highlight ? ` background: ${colors.grey90.toString()}; + + &::before { + background: ${colors.grey90.toString()}; + } ` : ''}; `; const StyledPlaceholder = styled.div` position: relative; - height: 10px; + height: ${getSpace(Size.S)}; width: 100%; - margin-top: -5px; - margin-bottom: -5px; - border-radius: 3px; + margin-top: -${getSpace(Size.XS)}; + margin-bottom: -${getSpace(Size.XS)}; + z-index: 10; + + &::before { + content: ''; + display: block; + position: absolute; + height: 6px; + width: 6px; + left: 6px; + top: 3px; + border-radius: 3px; + background: ${colors.blue40.toString()}; + transform: scale(0); + transition: transform 0.2s; + z-index: 20; + } &::after { content: ''; display: block; position: absolute; - height: 100%; - width: 100%; - left: 0; - top: 0; - background: ${colors.grey90.toString()}; + height: 2px; + width: calc(100% - 6px); + left: ${getSpace(Size.XS)}; + top: 5px; + background: ${colors.blue40.toString()}; transform: scaleY(0); transition: transform 0.2s; - z-index: 50; + z-index: 20; } ${(props: StyledPlaceholder) => props.highlightPlaceholder ? ` - &:after { + &::before { + transform: scale(1); + } + + &::after { transform: scaleY(1); } ` @@ -122,15 +169,15 @@ const StyledElementChild = styled.div` const StyledIcon = styled(Icon)` position: absolute; - left: 0; + left: ${getSpace(Size.XS) + getSpace(Size.XXS)}px; fill: ${colors.grey60.toString()}; - width: 12px; - height: 12px; + width: ${getSpace(Size.S)}px; + height: ${getSpace(Size.S)}px; padding: ${getSpace(Size.XS)}px; transition: transform 0.2s; ${(props: StyledIconProps) => (props.open ? 'transform: rotate(90deg)' : '')}; - ${(props: StyledIconProps) => (props.active ? 'fill: white' : '')}; + ${(props: StyledIconProps) => (props.active ? 'fill: #0070D6' : '')}; `; const Element: React.StatelessComponent = props => { @@ -155,7 +202,7 @@ const Element: React.StatelessComponent = props => { } = props; return ( - + ) => { diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index d76aa0ef7..cfd77090d 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import styled from 'styled-components'; import { colors } from '../../colors'; +import { getSpace, Size } from '../../space'; const StyledElementPane = styled.div` position: relative; @@ -8,6 +9,7 @@ const StyledElementPane = styled.div` flex-shrink: 0; flex-basis: 60%; overflow: scroll; + padding-top: ${getSpace(Size.M)}px; &::before { content: ''; From 551ec59ba695cd90d7b78930c559f321bc29472d Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 18:35:11 +0200 Subject: [PATCH 03/32] feat: unselect items when sidebar root receives click --- src/component/container/app.tsx | 11 +++++++---- src/lsg/patterns/element/index.tsx | 8 +++++--- src/lsg/patterns/layout/index.tsx | 5 +++-- src/lsg/patterns/panes/element-pane/index.tsx | 4 ++-- src/store/store.ts | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index 4a8153e5b..70a9fcc3f 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -4,6 +4,7 @@ import { colors } from '../../lsg/patterns/colors'; import Copy, { Size as CopySize } from '../../lsg/patterns/copy'; import { remote } from 'electron'; import { ElementList } from '../../component/container/element-list'; +// import { ElementButton } from '../../component/container/element-button'; import ElementPane from '../../lsg/patterns/panes/element-pane'; import * as FileExtraUtils from 'fs-extra'; import globalStyles from '../../lsg/patterns/global-styles'; @@ -42,7 +43,6 @@ export class App extends React.Component { public constructor(props: {}) { super(props); this.handleTabNaviagtionClick = this.handleTabNaviagtionClick.bind(this); - this.handleMainWindowClick = this.handleMainWindowClick.bind(this); this.handleCreateNewSpaceClick = this.handleCreateNewSpaceClick.bind(this); this.handleOpenSpaceClick = this.handleOpenSpaceClick.bind(this); } @@ -145,12 +145,15 @@ export class App extends React.Component { const DevTools = this.getDevTools(); return ( - + - {project && [ - + Store.getInstance().setSelectedElement()} + > diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index b564f292c..77d57faa3 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -52,14 +52,16 @@ const StyledElement = styled.div` const StyledElementLabel = styled.div` position: relative; display: flex; - padding: ${getSpace(Size.XS)}px ${getSpace(Size.L)}px ${getSpace(Size.XS)}px ${getSpace(Size.XXL)}px; + padding: ${getSpace(Size.XS)}px ${getSpace(Size.L)}px ${getSpace(Size.XS)}px ${getSpace( + Size.XXL +)}px; align-items: center; color: ${colors.grey20.toString()}; position: relative; font-size: 15px; line-height: 21px; z-index: 1; - + &::before { content: ''; display: block; @@ -73,7 +75,7 @@ const StyledElementLabel = styled.div` &:hover { background ${colors.black.toString('rgb', { alpha: 0.05 })}; - + &::before { background: ${colors.black.toString('rgb', { alpha: 0.05 })}; } diff --git a/src/lsg/patterns/layout/index.tsx b/src/lsg/patterns/layout/index.tsx index d4eee9226..117314f70 100644 --- a/src/lsg/patterns/layout/index.tsx +++ b/src/lsg/patterns/layout/index.tsx @@ -5,8 +5,8 @@ import styled from 'styled-components'; export interface LayoutProps { className?: string; directionVertical?: boolean; - handleClick?: React.MouseEventHandler; hasPaddings?: boolean; + onClick?: React.MouseEventHandler; } const StyledLayout = styled.div` @@ -42,6 +42,7 @@ export const SideBar: React.StatelessComponent = props => ( className={props.className} directionVertical={props.directionVertical} hasPaddings={props.hasPaddings} + onClick={props.onClick} > {props.children} @@ -52,7 +53,7 @@ const Layout: React.StatelessComponent = props => ( className={props.className} directionVertical={props.directionVertical} hasPaddings={props.hasPaddings} - onClick={props.handleClick} + onClick={props.onClick} > {props.children} diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index cfd77090d..20058905c 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -1,7 +1,7 @@ -import * as React from 'react'; -import styled from 'styled-components'; import { colors } from '../../colors'; +import * as React from 'react'; import { getSpace, Size } from '../../space'; +import styled from 'styled-components'; const StyledElementPane = styled.div` position: relative; diff --git a/src/store/store.ts b/src/store/store.ts index b565a8b29..418918737 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -813,7 +813,7 @@ export class Store { * @param selectedElement The selected element or undefined. * @see setElementFocussed */ - public setSelectedElement(selectedElement: PageElement | undefined): void { + public setSelectedElement(selectedElement?: PageElement): void { this.selectedElement = selectedElement; } From 71d38c973a4ffde6bf038cb5df1a6410b0d3d3c0 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 19:01:13 +0200 Subject: [PATCH 04/32] refactor: simplify app view by moving out splashscreen --- src/component/container/app.tsx | 117 +++++++++------------- src/component/container/splash-screen.tsx | 40 ++++++++ 2 files changed, 86 insertions(+), 71 deletions(-) create mode 100644 src/component/container/splash-screen.tsx diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index 70a9fcc3f..9245d27d6 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -1,17 +1,12 @@ -import Button, { Order } from '../../lsg/patterns/button'; import { ChromeContainer } from '../chrome/chrome-container'; -import { colors } from '../../lsg/patterns/colors'; -import Copy, { Size as CopySize } from '../../lsg/patterns/copy'; import { remote } from 'electron'; import { ElementList } from '../../component/container/element-list'; // import { ElementButton } from '../../component/container/element-button'; import ElementPane from '../../lsg/patterns/panes/element-pane'; import * as FileExtraUtils from 'fs-extra'; import globalStyles from '../../lsg/patterns/global-styles'; -import { Headline } from '../../lsg/patterns/headline'; import { IconName, IconRegistry } from '../../lsg/patterns/icons'; import Layout, { MainArea, SideBar } from '../../lsg/patterns/layout'; -import Link from '../../lsg/patterns/link'; import { createMenu } from '../../electron/menu'; import * as MobX from 'mobx'; import { observer } from 'mobx-react'; @@ -23,8 +18,7 @@ import * as ProcessUtils from 'process'; import { PropertyList } from './property-list'; import PropertyPane from '../../lsg/patterns/panes/property-pane'; import * as React from 'react'; -import Space, { Size as SpaceSize } from '../../lsg/patterns/space'; -import SplashScreen from '../../lsg/patterns/splash-screen'; +import { SplashScreen } from './splash-screen'; import { Store } from '../../store/store'; globalStyles(); @@ -43,8 +37,6 @@ export class App extends React.Component { public constructor(props: {}) { super(props); this.handleTabNaviagtionClick = this.handleTabNaviagtionClick.bind(this); - this.handleCreateNewSpaceClick = this.handleCreateNewSpaceClick.bind(this); - this.handleOpenSpaceClick = this.handleOpenSpaceClick.bind(this); } public componentDidMount(): void { @@ -52,16 +44,8 @@ export class App extends React.Component { this.redirectUndoRedo(); } - private getDevTools(): React.StatelessComponent | null { - try { - const DevToolsExports = require('mobx-react-devtools'); - return DevToolsExports ? DevToolsExports.default : undefined; - } catch (error) { - return null; - } - } - - protected handleCreateNewSpaceClick(): void { + // TODO: Should move to store + protected createNewSpace(): void { let appPath: string = remote.app.getAppPath().replace('.asar', '.asar.unpacked'); if (appPath.indexOf('node_modules') >= 0) { appPath = ProcessUtils.cwd(); @@ -82,18 +66,20 @@ export class App extends React.Component { ); } + private getDevTools(): React.StatelessComponent | null { + try { + const DevToolsExports = require('mobx-react-devtools'); + return DevToolsExports ? DevToolsExports.default : undefined; + } catch (error) { + return null; + } + } + private handleMainWindowClick(): void { Store.getInstance().setElementFocussed(false); createMenu(); } - protected handleOpenSpaceClick(): void { - remote.dialog.showOpenDialog({ properties: ['openDirectory'] }, filePaths => { - store.openStyleguide(filePaths[0]); - store.openFirstPage(); - }); - } - protected handleTabNaviagtionClick(evt: React.MouseEvent, id: string): void { this.activeTab = id; } @@ -106,6 +92,14 @@ export class App extends React.Component { return this.activeTab === App.PROPERTIES_LIST_ID; } + // TODO: Should move to store + protected openSpace(): void { + remote.dialog.showOpenDialog({ properties: ['openDirectory'] }, filePaths => { + store.openStyleguide(filePaths[0]); + store.openFirstPage(); + }); + } + private redirectUndoRedo(): void { document.body.onkeydown = event => { if (event.keyCode === 16) { @@ -148,54 +142,35 @@ export class App extends React.Component { - {project && [ - Store.getInstance().setSelectedElement()} - > - - - - , - , - - - - - - - - - ]} - - {!project && ( - - - - Getting started with Alva - - - - - You can open an existing Alva space or create a new one based on our - designkit including some basic components to kickstart your project. - - - - - - - or open existing Alva space - - + {project ? ( + + Store.getInstance().setSelectedElement()} + > + + + + + + + + + + + + + + + ) : ( + this.createNewSpace()} + onSecondaryButtonClick={() => this.openSpace()} + /> )} - - {DevTools ? : null} ); diff --git a/src/component/container/splash-screen.tsx b/src/component/container/splash-screen.tsx new file mode 100644 index 000000000..805b4fe24 --- /dev/null +++ b/src/component/container/splash-screen.tsx @@ -0,0 +1,40 @@ +import Button, { Order } from '../../lsg/patterns/button'; +import { colors } from '../../lsg/patterns/colors'; +import Copy, { Size as CopySize } from '../../lsg/patterns/copy'; +import { Headline } from '../../lsg/patterns/headline'; +import Link from '../../lsg/patterns/link'; +import * as React from 'react'; +import Space, { Size as SpaceSize } from '../../lsg/patterns/space'; +// tslint:disable-next-line +import SplashScreenContainer from '../../lsg/patterns/splash-screen'; + +export interface SplashScreenProps { + onPrimaryButtonClick?: React.MouseEventHandler; + onSecondaryButtonClick?: React.MouseEventHandler; +} + +export function SplashScreen(props: SplashScreenProps): JSX.Element { + return ( + + + + Getting started with Alva + + + + + You can open an existing Alva space or create a new one based on our designkit + including some basic components to kickstart your project. + + + + + + + or open existing Alva space + + + ); +} From 76ac1aeb5e197b44c913c00974febf671c35c56e Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 19:04:33 +0200 Subject: [PATCH 05/32] style: use tabs for indentation --- src/lsg/patterns/element/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index 77d57faa3..ee76f9bb5 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -91,13 +91,13 @@ const StyledElementLabel = styled.div` background: ${colors.blue80.toString()}; } - &:hover { - background: ${colors.blue80.toString()}; + &:hover { + background: ${colors.blue80.toString()}; &::before { background: ${colors.blue80.toString()}; } - } + } ` : ''}; ${(props: StyledElementLabelProps) => From 71c298d81a3a4fa0c4f046c6a159f704028ce5b9 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Mon, 23 Apr 2018 21:09:00 +0200 Subject: [PATCH 06/32] feat(component): add drag image styling --- src/component/container/element-list.tsx | 10 ++++++++++ src/lsg/patterns/element/index.tsx | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index db1e65050..d467a3f96 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -60,6 +60,16 @@ export class ElementList extends React.Component { onContextMenu: () => elementMenu(element), handleDragStart: (e: React.DragEvent) => { Store.getInstance().setDraggedElement(element); + e.dataTransfer.effectAllowed = 'move'; + + // restyle the drag image and move it somewhere invisible + var dragImg = e.currentTarget.querySelector('div').cloneNode(true); + dragImg.setAttribute( + 'style', + 'position: absolute; background-color: #fff; color: #000; padding: 1px 6px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' + ); + document.body.appendChild(dragImg); + e.dataTransfer.setDragImage(dragImg, 75, 10); }, handleDragDropForChild: (e: React.DragEvent) => { const patternId = e.dataTransfer.getData('patternId'); diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index ee76f9bb5..e86cc530e 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -238,7 +238,7 @@ const Element: React.StatelessComponent = props => { active={active} /> )} - {title} +
{title}
{children && {children}}
From 42284b6e7473ee1564e665860ef0827708f37ed6 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Mon, 23 Apr 2018 21:25:54 +0200 Subject: [PATCH 07/32] fixup! increase element drag size --- src/component/container/element-list.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index d467a3f96..e9ccca2ca 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -66,10 +66,10 @@ export class ElementList extends React.Component { var dragImg = e.currentTarget.querySelector('div').cloneNode(true); dragImg.setAttribute( 'style', - 'position: absolute; background-color: #fff; color: #000; padding: 1px 6px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' + 'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' ); document.body.appendChild(dragImg); - e.dataTransfer.setDragImage(dragImg, 75, 10); + e.dataTransfer.setDragImage(dragImg, 75, 15); }, handleDragDropForChild: (e: React.DragEvent) => { const patternId = e.dataTransfer.getData('patternId'); From c6c52ff26356d0ec270ae09f46a50c849b0e2949 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Mon, 23 Apr 2018 21:59:16 +0200 Subject: [PATCH 08/32] fixup! add check for null on drag element --- src/component/container/element-list.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index e9ccca2ca..6ed039d57 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -62,14 +62,18 @@ export class ElementList extends React.Component { Store.getInstance().setDraggedElement(element); e.dataTransfer.effectAllowed = 'move'; + let dragElement = e.currentTarget.querySelector('div'); + // restyle the drag image and move it somewhere invisible - var dragImg = e.currentTarget.querySelector('div').cloneNode(true); - dragImg.setAttribute( - 'style', - 'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' - ); - document.body.appendChild(dragImg); - e.dataTransfer.setDragImage(dragImg, 75, 15); + if (dragElement) { + let dragImg = dragElement.cloneNode(true) as HTMLElement; + dragImg.setAttribute( + 'style', + 'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' + ); + document.body.appendChild(dragImg); + e.dataTransfer.setDragImage(dragImg, 75, 15); + } }, handleDragDropForChild: (e: React.DragEvent) => { const patternId = e.dataTransfer.getData('patternId'); From 4f6f148350c75f79e54f325ca1264b79b0c18ba4 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 21:41:04 +0200 Subject: [PATCH 09/32] fix: avoid side effect froms drop areas on click interaction --- src/component/container/element-list.tsx | 251 +++++++++++++------- src/component/container/element-wrapper.tsx | 84 +++---- src/lsg/patterns/element/demo.tsx | 11 +- src/lsg/patterns/element/index.tsx | 94 ++++---- src/lsg/patterns/list/index.tsx | 13 +- 5 files changed, 263 insertions(+), 190 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index 6ed039d57..e061fbdf3 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -10,9 +10,18 @@ import { Pattern } from '../../store/styleguide/pattern'; import { PropertyValue } from '../../store/page/property-value'; import * as React from 'react'; import { Store } from '../../store/store'; +import * as uuid from 'uuid'; + +export interface ElementListState { + dragging: boolean; +} @observer -export class ElementList extends React.Component { +export class ElementList extends React.Component<{}, ElementListState> { + public state = { + dragging: false + }; + public componentDidMount(): void { createMenu(); } @@ -25,57 +34,26 @@ export class ElementList extends React.Component { key: string, element: PageElement, selectedElement?: PageElement - ): ListItemProps { + ): ElementNodeProps { const pattern: Pattern | undefined = element.getPattern(); + if (!pattern) { return { label: key, - value: '(invalid)', - children: [] + title: '(invalid)', + id: uuid.v4(), + children: [], + dragging: this.state.dragging }; } - const items: ListItemProps[] = []; - const children: PageElement[] = element.getChildren() || []; - children.forEach((value: PageElement, index: number) => { - items.push( - this.createItemFromProperty( - children.length > 1 ? `Child ${index + 1}` : 'Child', - value, - selectedElement - ) - ); - }); - - const updatePageElement: React.MouseEventHandler = event => { - event.stopPropagation(); - Store.getInstance().setSelectedElement(element); - Store.getInstance().setElementFocussed(true); - }; - return { label: key, - value: element.getName(), - onClick: updatePageElement, - onContextMenu: () => elementMenu(element), - handleDragStart: (e: React.DragEvent) => { - Store.getInstance().setDraggedElement(element); - e.dataTransfer.effectAllowed = 'move'; - - let dragElement = e.currentTarget.querySelector('div'); - - // restyle the drag image and move it somewhere invisible - if (dragElement) { - let dragImg = dragElement.cloneNode(true) as HTMLElement; - dragImg.setAttribute( - 'style', - 'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' - ); - document.body.appendChild(dragImg); - e.dataTransfer.setDragImage(dragImg, 75, 15); - } - }, - handleDragDropForChild: (e: React.DragEvent) => { + title: element.getName(), + dragging: this.state.dragging, + id: element.getId(), + onDragDropForChild: (e: React.DragEvent) => { + this.handleDragEnd(e); const patternId = e.dataTransfer.getData('patternId'); const newParent = element.getParent(); @@ -114,7 +92,8 @@ export class ElementList extends React.Component { store.execute(ElementLocationCommand.addChild(newParent, draggedElement, newIndex)); store.setSelectedElement(draggedElement); }, - handleDragDrop: (e: React.DragEvent) => { + onDragDrop: (e: React.DragEvent) => { + this.handleDragEnd(e); const patternId = e.dataTransfer.getData('patternId'); let draggedElement: PageElement | undefined; @@ -142,7 +121,15 @@ export class ElementList extends React.Component { store.execute(ElementLocationCommand.addChild(element, draggedElement)); store.setSelectedElement(draggedElement); }, - children: items, + children: element + .getChildren() + .map((child, index, items) => + this.createItemFromProperty( + items.length > 1 ? `Child ${index + 1}` : 'Child', + child, + selectedElement + ) + ), active: element === selectedElement }; } @@ -151,65 +138,163 @@ export class ElementList extends React.Component { key: string, value: PropertyValue, selectedElement?: PageElement - ): ListItemProps { - if (value instanceof Array) { - const items: ListItemProps[] = []; - (value as (string | number)[]).forEach((child, index: number) => { - items.push(this.createItemFromProperty(String(index + 1), child)); - }); - return { value: key, children: items }; + ): ElementNodeProps { + if (Array.isArray(value)) { + return { + title: key, + children: (value as (number | string)[]).map((child, index) => + this.createItemFromProperty(String(index + 1), child) + ), + dragging: this.state.dragging, + id: uuid.v4() + }; } if (value === undefined || value === null || typeof value !== 'object') { - return { label: key, value: String(value) }; + return { label: key, title: String(value), dragging: this.state.dragging, id: uuid.v4() }; } if (value instanceof PageElement) { return this.createItemFromElement(key, value, selectedElement); - } else { - const items: ListItemProps[] = []; - Object.keys(value).forEach((childKey: string) => { - // tslint:disable-next-line:no-any - items.push(this.createItemFromProperty(childKey, (value as any)[childKey])); - }); - return { value: key, children: items }; + } + + return { + title: key, + children: Object.entries(value).map(entry => + this.createItemFromProperty(entry[0], entry[1]) + ), + dragging: this.state.dragging, + id: uuid.v4() + }; + } + + private handleClick(e: React.MouseEvent): void { + const element = elementFromTarget(e.target); + e.stopPropagation(); + Store.getInstance().setSelectedElement(element); + Store.getInstance().setElementFocussed(true); + } + + private handleContextMenu(e: React.MouseEvent): void { + const element = elementFromTarget(e.target); + if (element) { + elementMenu(element); + } + } + + private handleDragEnd(e: React.DragEvent): void { + this.setState({ dragging: false }); + } + + private handleDragStart(e: React.DragEvent): void { + this.setState({ dragging: true }); + const element = elementFromTarget(e.target); + + if (element) { + Store.getInstance().setDraggedElement(element); + } + + const dragElement = e.currentTarget.querySelector('div'); + + // restyle the drag image and move it somewhere invisible + if (dragElement) { + const dragImg = dragElement.cloneNode(true) as HTMLElement; + dragImg.setAttribute( + 'style', + 'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' + ); + document.body.appendChild(dragImg); + e.dataTransfer.setDragImage(dragImg, 75, 15); } } public render(): JSX.Element | null { const store = Store.getInstance(); const page: Page | undefined = store.getCurrentPage(); - if (page) { - const rootElement = page.getRoot(); - if (!rootElement) { - return null; - } + if (!page) { + return null; + } - const selectedElement = store.getSelectedElement(); + const rootElement = page.getRoot(); - return this.renderList(this.createItemFromElement('Root', rootElement, selectedElement)); - } else { + if (!rootElement) { return null; } - } - public renderList(item: ListItemProps, key?: number): JSX.Element { + const selectedElement = store.getSelectedElement(); + const item = this.createItemFromElement('Root', rootElement, selectedElement); + return ( - this.handleClick(e)} + onContextMenu={e => this.handleContextMenu(e)} + onDragStart={e => this.handleDragStart(e)} + onDragEnd={e => this.handleDragEnd(e)} > - {item.children && - item.children.length > 0 && - item.children.map((child, index) => this.renderList(child, index))} - + + ); } } + +export interface ElementNodeProps extends ListItemProps { + children?: ElementNodeProps[]; + dragging: boolean; + id: string; +} + +function ElementTree(props: ElementNodeProps): JSX.Element { + const children = Array.isArray(props.children) ? props.children : []; + + return ( + + {children.map(child => ( + + ))} + + ); +} + +function above(node: EventTarget, selector: string): HTMLElement | null { + let el = node as HTMLElement; + let ended = false; + + while (el && !ended) { + if (el.matches(selector)) { + break; + } + + if (el.parentElement !== null) { + el = el.parentElement; + } else { + ended = true; + break; + } + } + + return ended ? null : el; +} + +function elementFromTarget(target: EventTarget): PageElement | undefined { + const el = above(target, '[data-id]'); + + if (!el) { + return; + } + + const id = el.getAttribute('data-id'); + + if (typeof id !== 'string') { + return; + } + + const store = Store.getInstance(); + const page = store.getCurrentPage(); + + if (!page) { + return; + } + + return page.getElementById(id); +} diff --git a/src/component/container/element-wrapper.tsx b/src/component/container/element-wrapper.tsx index eb0a6096b..aaf19efd3 100644 --- a/src/component/container/element-wrapper.tsx +++ b/src/component/container/element-wrapper.tsx @@ -9,40 +9,40 @@ export interface ElementWrapperState { export interface ElementWrapperProps { active?: boolean; - handleClick?: React.MouseEventHandler; - handleContextMenu?: React.MouseEventHandler; - handleDragDrop?: React.DragEventHandler; - handleDragDropForChild?: React.DragEventHandler; - handleDragStart?: React.DragEventHandler; + dragging: boolean; + id: string; + onClick?: React.MouseEventHandler; + onContextMenu?: React.MouseEventHandler; + onDragDrop?: React.DragEventHandler; + onDragDropForChild?: React.DragEventHandler; + onDragStart?: React.DragEventHandler; open?: boolean; title: string; } export class ElementWrapper extends React.Component { - public constructor(props: ElementWrapperProps) { - super(props); + public state = { + open: this.props.open, + highlightPlaceholder: false, + highlight: false + }; - this.state = { - open: this.props.open, - highlight: false - }; + private handleClick(e: React.MouseEvent): void { + const target = e.target as HTMLElement; - this.handleIconClick = this.handleIconClick.bind(this); - this.handleDragStart = this.handleDragStart.bind(this); - this.handleDragEnter = this.handleDragEnter.bind(this); - this.handleDragLeave = this.handleDragLeave.bind(this); - this.handleDragDrop = this.handleDragDrop.bind(this); - this.handleDragEnterForChild = this.handleDragEnterForChild.bind(this); - this.handleDragLeaveForChild = this.handleDragLeaveForChild.bind(this); - this.handleDragDropForChild = this.handleDragDropForChild.bind(this); + if (target.getAttribute('data-element-icon')) { + this.setState({ + open: !this.state.open + }); + } } private handleDragDrop(e: React.DragEvent): void { this.setState({ highlight: false }); - if (typeof this.props.handleDragDrop === 'function') { - this.props.handleDragDrop(e); + if (typeof this.props.onDragDrop === 'function') { + this.props.onDragDrop(e); } } @@ -50,8 +50,8 @@ export class ElementWrapper extends React.Component this.handleClick(e)} + onDragDrop={e => this.handleDragDrop(e)} + onDragDropForChild={e => this.handleDragDropForChild(e)} + onDragEnter={e => this.handleDragEnter(e)} + onDragEnterForChild={e => this.handleDragEnterForChild(e)} + onDragLeave={e => this.handleDragLeave(e)} + onDragLeaveForChild={e => this.handleDragLeaveForChild(e)} + onDragStart={e => this.handleDragStart(e)} highlight={this.state.highlight} highlightPlaceholder={this.state.highlightPlaceholder} - handleClick={handleClick} - handleContextMenu={handleContextMenu} - draggable - handleIconClick={this.handleIconClick} - handleDragStart={this.handleDragStart} - handleDragEnter={this.handleDragEnter} - handleDragLeave={this.handleDragLeave} - handleDragDrop={this.handleDragDrop} - handleDragEnterForChild={this.handleDragEnterForChild} - handleDragLeaveForChild={this.handleDragLeaveForChild} - handleDragDropForChild={this.handleDragDropForChild} + id={this.props.id} + open={!this.state.open} + title={title} > {children} diff --git a/src/lsg/patterns/element/demo.tsx b/src/lsg/patterns/element/demo.tsx index ad92988ab..28c62f390 100644 --- a/src/lsg/patterns/element/demo.tsx +++ b/src/lsg/patterns/element/demo.tsx @@ -16,11 +16,11 @@ const ElementDemo: React.StatelessComponent = (): JSX.Element => ( Default - + Active - + @@ -31,25 +31,26 @@ const ElementDemo: React.StatelessComponent = (): JSX.Element => ( }} handleIconClick={NOOP} title="Element" + dragging > Child With Child and open - + Child With child and active - + Child With child, active and open - + Child diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index e86cc530e..9a8f29996 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -7,18 +7,19 @@ import styled from 'styled-components'; export interface ElementProps { active?: boolean; draggable?: boolean; - handleClick?: React.MouseEventHandler; - handleContextMenu?: React.MouseEventHandler; - handleDragDrop?: React.DragEventHandler; - handleDragDropForChild?: React.DragEventHandler; - handleDragEnter?: React.DragEventHandler; - handleDragEnterForChild?: React.DragEventHandler; - handleDragLeave?: React.DragEventHandler; - handleDragLeaveForChild?: React.DragEventHandler; - handleDragStart?: React.DragEventHandler; - handleIconClick?: React.MouseEventHandler; + dragging: boolean; highlight?: boolean; highlightPlaceholder?: boolean; + id?: string; + onClick?: React.MouseEventHandler; + onContextMenu?: React.MouseEventHandler; + onDragDrop?: React.DragEventHandler; + onDragDropForChild?: React.DragEventHandler; + onDragEnter?: React.DragEventHandler; + onDragEnterForChild?: React.DragEventHandler; + onDragLeave?: React.DragEventHandler; + onDragLeaveForChild?: React.DragEventHandler; + onDragStart?: React.DragEventHandler; open?: boolean; title: string; } @@ -190,55 +191,48 @@ const Element: React.StatelessComponent = props => { open, highlight, draggable, - handleClick, - handleContextMenu, - handleIconClick, - handleDragStart, - handleDragEnter, - handleDragLeave, - handleDragDrop, - handleDragEnterForChild, - handleDragLeaveForChild, - handleDragDropForChild, + dragging, + onClick, + onContextMenu, + onDragEnterForChild, + onDragLeaveForChild, + onDragDropForChild, highlightPlaceholder } = props; return ( - - ) => { - e.preventDefault(); - }} - onDragEnter={handleDragEnterForChild} - onDragLeave={handleDragLeaveForChild} - onDrop={handleDragDropForChild} - /> + + {dragging && ( + ) => { + e.preventDefault(); + }} + onDragEnter={onDragEnterForChild} + onDragLeave={onDragLeaveForChild} + onDrop={onDragDropForChild} + /> + )} ) => { - e.preventDefault(); - }} + data-element-label draggable={draggable} - onDragStart={handleDragStart} - onDragEnter={handleDragEnter} - onDragLeave={handleDragLeave} - onDrop={handleDragDrop} active={active} highlight={highlight} - onClick={handleClick} - onContextMenu={handleContextMenu} + onContextMenu={onContextMenu} > - {children && ( - - )} -
{title}
+ {Array.isArray(children) && + children.length > 0 && ( + + )} + {title}
{children && {children}}
diff --git a/src/lsg/patterns/list/index.tsx b/src/lsg/patterns/list/index.tsx index 5ca97b348..f55beb607 100644 --- a/src/lsg/patterns/list/index.tsx +++ b/src/lsg/patterns/list/index.tsx @@ -11,13 +11,13 @@ export interface ListItemProps { active?: boolean; children?: ListItemProps[]; draggable?: boolean; - handleDragDrop?: React.DragEventHandler; - handleDragDropForChild?: React.DragEventHandler; - handleDragStart?: React.DragEventHandler; label?: string; onClick?: React.MouseEventHandler; onContextMenu?: React.MouseEventHandler; - value: string; + onDragDrop?: React.DragEventHandler; + onDragDropForChild?: React.DragEventHandler; + onDragStart?: React.DragEventHandler; + title: string; } interface StyledListItemProps { @@ -44,9 +44,8 @@ const StyledUl = styled.ul` const StyledLi = styled.li` line-height: 25px; list-style: none; - ${(props: StyledListItemProps) => (props.onClick ? 'cursor: pointer;' : '')} ${( - props: StyledListItemProps - ) => (props.active ? 'background: #def' : '')}; + ${(props: StyledListItemProps) => (props.onClick ? 'cursor: pointer;' : '')}; + ${(props: StyledListItemProps) => (props.active ? 'background: #def' : '')}; `; const StyledLabel = styled.span` From 686f26467a7ecea8a330bb839efaa153808e28c4 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 22:15:23 +0200 Subject: [PATCH 10/32] fix: remove obsolete handler --- src/lsg/patterns/element/demo.tsx | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/lsg/patterns/element/demo.tsx b/src/lsg/patterns/element/demo.tsx index 28c62f390..237c46197 100644 --- a/src/lsg/patterns/element/demo.tsx +++ b/src/lsg/patterns/element/demo.tsx @@ -4,8 +4,6 @@ import Element from './index'; import * as React from 'react'; import styled from 'styled-components'; -const NOOP = () => {}; // tslint:disable-line no-empty - const StyledTestDiv = styled.div` flex-grow: 1; max-width: 200px; @@ -25,14 +23,7 @@ const ElementDemo: React.StatelessComponent = (): JSX.Element => ( With Child and handleIconClick - { - e.stopPropagation(); - }} - handleIconClick={NOOP} - title="Element" - dragging - > + e.stopPropagation()} title="Element" dragging> Child From 643d5381bbd833aa216b070d42f0f81697a89898 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Mon, 23 Apr 2018 22:20:51 +0200 Subject: [PATCH 11/32] fix: select right drag element target --- src/component/container/element-list.tsx | 2 +- src/lsg/patterns/element/index.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index e061fbdf3..aabc7f0ce 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -194,7 +194,7 @@ export class ElementList extends React.Component<{}, ElementListState> { Store.getInstance().setDraggedElement(element); } - const dragElement = e.currentTarget.querySelector('div'); + const dragElement = e.target.querySelector('div'); // restyle the drag image and move it somewhere invisible if (dragElement) { diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index 9a8f29996..e1fd187ae 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -232,7 +232,7 @@ const Element: React.StatelessComponent = props => { active={active} /> )} - {title} +
{title}
{children && {children}}
From 8904d0a1aed09f54d1ac642d1b82cfd02f4fab1c Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 22:26:49 +0200 Subject: [PATCH 12/32] fix: adapt to new interface --- src/component/container/property-list.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/component/container/property-list.tsx b/src/component/container/property-list.tsx index fcc86c5e0..ff4519972 100644 --- a/src/component/container/property-list.tsx +++ b/src/component/container/property-list.tsx @@ -98,7 +98,12 @@ class PropertyTree extends React.Component { const { property } = context; return ( - + {this.renderItems()} ); From 5500cbfd254465f71ce76fd1fa309eac4b4a952b Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 22:28:49 +0200 Subject: [PATCH 13/32] fix: make ts happy --- src/component/container/element-list.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index aabc7f0ce..c059c7572 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -194,7 +194,8 @@ export class ElementList extends React.Component<{}, ElementListState> { Store.getInstance().setDraggedElement(element); } - const dragElement = e.target.querySelector('div'); + const target = e.target as HTMLElement; + const dragElement = target.querySelector('div'); // restyle the drag image and move it somewhere invisible if (dragElement) { From 4fe6cbf198d5050520d4225d6fa73ddb0ce92700 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 22:54:59 +0200 Subject: [PATCH 14/32] fix: restore icon click functionality --- src/component/container/element-wrapper.tsx | 24 ++++++++++++++++++++- src/lsg/patterns/element/index.tsx | 3 ++- src/lsg/patterns/icons/index.tsx | 2 ++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/component/container/element-wrapper.tsx b/src/component/container/element-wrapper.tsx index aaf19efd3..3cc22457c 100644 --- a/src/component/container/element-wrapper.tsx +++ b/src/component/container/element-wrapper.tsx @@ -29,8 +29,10 @@ export class ElementWrapper extends React.Component): void { const target = e.target as HTMLElement; + const icon = above(target, 'svg[data-icon]'); - if (target.getAttribute('data-element-icon')) { + if (icon) { + e.stopPropagation(); this.setState({ open: !this.state.open }); @@ -114,3 +116,23 @@ export class ElementWrapper extends React.Component = props => { {Array.isArray(children) && children.length > 0 && ( ; name: IconName | null; size?: Size; @@ -104,6 +105,7 @@ export const Icon: React.StatelessComponent = (props): JSX.Element => className={props.className} iconColor={props.color} size={props.size} + data-icon={props.dataIcon} > {icon !== null && } From 9c223fc79768af0dff661fd09b89094e40fd9e97 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 23:04:56 +0200 Subject: [PATCH 15/32] feat: show either properties or patterns pane --- src/component/container/app.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index 9245d27d6..22e84aa94 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -155,12 +155,15 @@ export class App extends React.Component {
- - - - - - + {store.getSelectedElement() ? ( + + + + ) : ( + + + + )} ) : ( From 57208e6b3d007ff32d2d0b89c74b506f60bff3f0 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Mon, 23 Apr 2018 23:16:28 +0200 Subject: [PATCH 16/32] fix: restore pattern drag --- src/lsg/patterns/element/index.tsx | 100 +++++++++++++---------------- 1 file changed, 45 insertions(+), 55 deletions(-) diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index 83ba33c23..2570a7d8a 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -184,60 +184,50 @@ const StyledIcon = styled(Icon)` ${(props: StyledIconProps) => (props.active ? 'fill: #0070D6' : '')}; `; -const Element: React.StatelessComponent = props => { - const { - children, - title, - active, - open, - highlight, - draggable, - dragging, - onClick, - onContextMenu, - onDragEnterForChild, - onDragLeaveForChild, - onDragDropForChild, - highlightPlaceholder - } = props; - - return ( - - {dragging && ( - ) => { - e.preventDefault(); - }} - onDragEnter={onDragEnterForChild} - onDragLeave={onDragLeaveForChild} - onDrop={onDragDropForChild} - /> - )} - - {Array.isArray(children) && - children.length > 0 && ( - - )} -
{title}
-
- {children && {children}} -
- ); -}; +const Element: React.StatelessComponent = props => ( + + {props.dragging && ( + ) => { + e.preventDefault(); + }} + onDragEnter={props.onDragEnterForChild} + onDragLeave={props.onDragLeaveForChild} + onDrop={props.onDragDropForChild} + /> + )} + ) => { + e.preventDefault(); + }} + onDragEnter={props.onDragEnter} + onDragLeave={props.onDragLeave} + onDrop={props.onDragDrop} + > + {Array.isArray(props.children) && + props.children.length > 0 && ( + + )} +
{props.title}
+
+ {props.children && ( + {props.children} + )} +
+); export default Element; From dbe48a10658d8f1833bc6b5d09836af8be9fcc58 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Tue, 24 Apr 2018 08:37:07 +0200 Subject: [PATCH 17/32] fix: set backgroundColor during startup --- src/electron/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/electron/main.ts b/src/electron/main.ts index 39d9d97b5..5c2303c95 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -17,6 +17,7 @@ function createWindow(): void { minWidth: 780, minHeight: 380, titleBarStyle: 'hiddenInset', + backgroundColor: '#f7f7f7', title: 'Alva' }); From c7de980ef7a7994cfa303bdf985048e73f19d192 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Tue, 24 Apr 2018 13:39:37 +0200 Subject: [PATCH 18/32] fix: handle draggable states via unified state --- src/component/container/element-list.tsx | 22 ++++++++++++++++----- src/component/container/element-wrapper.tsx | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index c059c7572..25726b137 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -16,10 +16,22 @@ export interface ElementListState { dragging: boolean; } +const DRAG_IMG_STYLE = ` + position: absolute; + background-color: #fff; + color: #000; + padding: 6px 18px; + border-radius: 3px; + font-size: 12px; + opacity: 1; + top: 0; + left: -500px; +`; + @observer export class ElementList extends React.Component<{}, ElementListState> { public state = { - dragging: false + dragging: true }; public componentDidMount(): void { @@ -200,10 +212,7 @@ export class ElementList extends React.Component<{}, ElementListState> { // restyle the drag image and move it somewhere invisible if (dragElement) { const dragImg = dragElement.cloneNode(true) as HTMLElement; - dragImg.setAttribute( - 'style', - 'position: absolute; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; top: 0; left: -500px;' - ); + dragImg.setAttribute('style', DRAG_IMG_STYLE); document.body.appendChild(dragImg); e.dataTransfer.setDragImage(dragImg, 75, 15); } @@ -228,10 +237,13 @@ export class ElementList extends React.Component<{}, ElementListState> { return (
this.handleClick(e)} onContextMenu={e => this.handleContextMenu(e)} onDragStart={e => this.handleDragStart(e)} onDragEnd={e => this.handleDragEnd(e)} + onMouseOver={e => this.setState({ dragging: false })} + onMouseLeave={e => this.setState({ dragging: true })} >
diff --git a/src/component/container/element-wrapper.tsx b/src/component/container/element-wrapper.tsx index 3cc22457c..8aca40fc1 100644 --- a/src/component/container/element-wrapper.tsx +++ b/src/component/container/element-wrapper.tsx @@ -95,8 +95,8 @@ export class ElementWrapper extends React.Component this.handleClick(e)} onDragDrop={e => this.handleDragDrop(e)} onDragDropForChild={e => this.handleDragDropForChild(e)} From dd1a6c4fcb95608903735510717a644592918ab2 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Tue, 24 Apr 2018 14:40:23 +0200 Subject: [PATCH 19/32] feat: add button to show pattern pane --- src/component/container/app.tsx | 22 ++++++++++----- src/component/container/element-button.tsx | 9 +++++++ src/store/store.ts | 31 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/component/container/element-button.tsx diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index 22e84aa94..1d986286b 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -1,7 +1,7 @@ import { ChromeContainer } from '../chrome/chrome-container'; import { remote } from 'electron'; +import { ElementButton } from './element-button'; import { ElementList } from '../../component/container/element-list'; -// import { ElementButton } from '../../component/container/element-button'; import ElementPane from '../../lsg/patterns/panes/element-pane'; import * as FileExtraUtils from 'fs-extra'; import globalStyles from '../../lsg/patterns/global-styles'; @@ -11,7 +11,7 @@ import { createMenu } from '../../electron/menu'; import * as MobX from 'mobx'; import { observer } from 'mobx-react'; import * as PathUtils from 'path'; -import { PatternListContainer } from '../../component/container/pattern-list'; +import { PatternListContainer } from './pattern-list'; import PatternsPane from '../../lsg/patterns/panes/patterns-pane'; import { PreviewPaneWrapper } from '../../component/container/preview-pane-wrapper'; import * as ProcessUtils from 'process'; @@ -19,7 +19,7 @@ import { PropertyList } from './property-list'; import PropertyPane from '../../lsg/patterns/panes/property-pane'; import * as React from 'react'; import { SplashScreen } from './splash-screen'; -import { Store } from '../../store/store'; +import { RightPane, Store } from '../../store/store'; globalStyles(); @@ -31,6 +31,7 @@ export class App extends React.Component { private static PROPERTIES_LIST_ID = 'propertieslist'; @MobX.observable protected activeTab: string = App.PATTERN_LIST_ID; + private ctrlDown: boolean = false; private shiftDown: boolean = false; @@ -147,19 +148,28 @@ export class App extends React.Component { Store.getInstance().setSelectedElement()} + onClick={() => store.setSelectedElement()} > + {store.getRightPane() === RightPane.Properties && ( + { + e.stopPropagation(); + store.setRightPane(RightPane.Patterns); + }} + /> + )} - {store.getSelectedElement() ? ( + {store.getRightPane() === RightPane.Properties && ( - ) : ( + )} + {store.getRightPane() === RightPane.Patterns && ( diff --git a/src/component/container/element-button.tsx b/src/component/container/element-button.tsx new file mode 100644 index 000000000..450f8cb71 --- /dev/null +++ b/src/component/container/element-button.tsx @@ -0,0 +1,9 @@ +import * as React from 'react'; + +export interface ElementButtonProps { + onClick: React.MouseEventHandler; +} + +export const ElementButton: React.SFC = props => ( + +); diff --git a/src/store/store.ts b/src/store/store.ts index 418918737..44fbe5544 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -14,6 +14,11 @@ import { Preferences } from './preferences'; import { Project } from './project'; import { Styleguide } from './styleguide/styleguide'; +export enum RightPane { + Patterns = 'Patterns', + Properties = 'Properties' +} + /** * The central entry-point for all application state, managed by MobX. * Use this object and its properties in your React components, @@ -93,6 +98,12 @@ export class Store { */ @MobX.observable private relativePatternsPath: string; + /** + * The well-known enum name of content that should be visible in + * the right-hand sidebar/pane. + */ + @MobX.observable private rightPane: RightPane | null = null; + /** * The currently selected element in the element list. * The properties pane shows the properties of this element, @@ -411,6 +422,17 @@ export class Store { return this.projects; } + /** + * @return The content id to show in the right-hand sidebar + * @see isElementFocussed + */ + public getRightPane(): RightPane { + if (this.rightPane === null) { + return this.selectedElement ? RightPane.Properties : RightPane.Patterns; + } + return this.rightPane; + } + /** * Returns the currently selected element in the element list. * The properties pane shows the properties of this element, @@ -805,6 +827,14 @@ export class Store { this.patternSearchTerm = patternSearchTerm; } + /** + * @return The content id to show in the right-hand sidebar + * @see rightPane + */ + public setRightPane(pane: RightPane | null): void { + this.rightPane = pane; + } + /** * Sets the currently selected element in the element list. * The properties pane shows the properties of this element, @@ -814,6 +844,7 @@ export class Store { * @see setElementFocussed */ public setSelectedElement(selectedElement?: PageElement): void { + this.rightPane = null; this.selectedElement = selectedElement; } From f6212c47c68f13934c11fb85d25365c3b61d6383 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Wed, 25 Apr 2018 13:07:40 +0200 Subject: [PATCH 20/32] feat: add button styling --- src/component/container/app.tsx | 11 +-- src/component/container/element-button.tsx | 9 --- src/lsg/patterns/add-button/index.tsx | 80 +++++++++++++++++++ src/lsg/patterns/add-button/pattern.json | 6 ++ src/lsg/patterns/panes/element-pane/index.tsx | 16 ---- 5 files changed, 92 insertions(+), 30 deletions(-) delete mode 100644 src/component/container/element-button.tsx create mode 100644 src/lsg/patterns/add-button/index.tsx create mode 100644 src/lsg/patterns/add-button/pattern.json diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index 1d986286b..b6741b1c6 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -1,6 +1,6 @@ +import AddButton from '../../lsg/patterns/add-button'; import { ChromeContainer } from '../chrome/chrome-container'; import { remote } from 'electron'; -import { ElementButton } from './element-button'; import { ElementList } from '../../component/container/element-list'; import ElementPane from '../../lsg/patterns/panes/element-pane'; import * as FileExtraUtils from 'fs-extra'; @@ -152,15 +152,16 @@ export class App extends React.Component { > - - {store.getRightPane() === RightPane.Properties && ( - { e.stopPropagation(); store.setRightPane(RightPane.Patterns); + store.setSelectedElement(); }} /> - )} + diff --git a/src/component/container/element-button.tsx b/src/component/container/element-button.tsx deleted file mode 100644 index 450f8cb71..000000000 --- a/src/component/container/element-button.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import * as React from 'react'; - -export interface ElementButtonProps { - onClick: React.MouseEventHandler; -} - -export const ElementButton: React.SFC = props => ( - -); diff --git a/src/lsg/patterns/add-button/index.tsx b/src/lsg/patterns/add-button/index.tsx new file mode 100644 index 000000000..d54035910 --- /dev/null +++ b/src/lsg/patterns/add-button/index.tsx @@ -0,0 +1,80 @@ +import { colors } from '../colors'; +import { Icon, IconName, Size as IconSize } from '../icons'; +import * as React from 'react'; +import { getSpace, Size as SpaceSize } from '../space'; +import styled from 'styled-components'; + +export interface AddButtonProps { + onClick?: React.MouseEventHandler; + label?: string; + active?: boolean; +} + +interface StyledAddButtonProps { + active?: boolean; +} + +interface StyledIconProps { + active?: boolean; +} + +const StyledAddButton = styled.div` + position: absolute; + bottom: 0; + width: 100%; + border-top: 0.5px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + cursor: default; + display: flex; + height: 40px; + box-sizing: border-box; + padding: ${getSpace(SpaceSize.XXS)}px ${getSpace(SpaceSize.XS)}px ${getSpace(SpaceSize.XXS)}px + ${getSpace(SpaceSize.L)}px; + justify-content: space-between; + color: ${colors.grey36.toString()}; + + &:hover { + background: ${colors.grey90.toString()}; + } + + ${(props: StyledAddButtonProps) => + props.active + ? ` + border-top: 0.5px solid ${colors.blue.toString('rgb', { alpha: 0.1 })}; + background: ${colors.blue80.toString()}; + color: ${colors.blue.toString()}; + + &:hover { + background: ${colors.blue80.toString()}; + } + ` + : ''}; +`; + +const StyledLabelWrapper = styled.div` + font-size: 15px; + padding-top: ${getSpace(SpaceSize.XS)}px; +`; + +const StyledIconWrapper = styled.div` + margin: ${getSpace(SpaceSize.XS)}px; + padding: ${getSpace(SpaceSize.XXS)}px; + border-radius: ${getSpace(SpaceSize.XXS)}px; +`; + +const StyledIcon = styled(Icon)` + fill: ${colors.grey60.toString()}; + + ${(props: StyledIconProps) => (props.active ? `fill: ${colors.blue.toString()};` : '')}; +`; + +const AddButton: React.StatelessComponent = props => ( + + {props.label} + + + + + +); + +export default AddButton; diff --git a/src/lsg/patterns/add-button/pattern.json b/src/lsg/patterns/add-button/pattern.json new file mode 100644 index 000000000..86d08b971 --- /dev/null +++ b/src/lsg/patterns/add-button/pattern.json @@ -0,0 +1,6 @@ +{ + "name": "add-button", + "displayName": "Add Button", + "version": "1.0.0", + "flag": "alpha" +} diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index 20058905c..da07fcb56 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -23,22 +23,6 @@ const StyledElementPane = styled.div` box-sizing: border-box; z-index: 10; } - - /*FadeOut*/ - &::after { - content: ''; - position: sticky; - bottom: 0; - display: block; - width: 100%; - height: 40px; - background: linear-gradient( - to bottom, - rgba(247, 247, 247, 0) 0%, - rgba(247, 247, 247, 0.5) 15%, - rgba(247, 247, 247, 1) 100% - ); - } `; const ElementPane: React.StatelessComponent = props => ( From 40a6397e044aa3d318a554a891d92e4e1a456294 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Thu, 26 Apr 2018 14:45:15 +0200 Subject: [PATCH 21/32] fixup! change requests --- src/electron/main.ts | 3 ++- src/lsg/patterns/add-button/index.tsx | 11 +++++++++-- src/lsg/patterns/chrome/index.tsx | 5 ++++- src/lsg/patterns/colors/index.tsx | 4 ++++ src/lsg/patterns/element/index.tsx | 4 ++-- src/lsg/patterns/global-styles/index.tsx | 5 +++-- src/lsg/patterns/panes/element-pane/index.tsx | 5 ++++- src/lsg/patterns/panes/patterns-pane/index.tsx | 9 +++++++-- src/lsg/patterns/panes/preview-pane/index.tsx | 6 ++---- src/lsg/patterns/panes/property-pane/index.tsx | 9 ++++++++- 10 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/electron/main.ts b/src/electron/main.ts index 5c2303c95..1a7885545 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -1,4 +1,5 @@ import { checkForUpdates } from './auto-updater'; +import { colors } from '../lsg/patterns/colors'; import { app, BrowserWindow, ipcMain, screen } from 'electron'; import * as PathUtils from 'path'; import * as url from 'url'; @@ -17,7 +18,7 @@ function createWindow(): void { minWidth: 780, minHeight: 380, titleBarStyle: 'hiddenInset', - backgroundColor: '#f7f7f7', + backgroundColor: colors.grey97.toString(), title: 'Alva' }); diff --git a/src/lsg/patterns/add-button/index.tsx b/src/lsg/patterns/add-button/index.tsx index d54035910..1b8361f97 100644 --- a/src/lsg/patterns/add-button/index.tsx +++ b/src/lsg/patterns/add-button/index.tsx @@ -22,8 +22,12 @@ const StyledAddButton = styled.div` position: absolute; bottom: 0; width: 100%; - border-top: 0.5px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + border-top: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-top-width: 0.5px; + } cursor: default; + user-select: none; display: flex; height: 40px; box-sizing: border-box; @@ -39,7 +43,10 @@ const StyledAddButton = styled.div` ${(props: StyledAddButtonProps) => props.active ? ` - border-top: 0.5px solid ${colors.blue.toString('rgb', { alpha: 0.1 })}; + border-top: 1px solid ${colors.blue.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-top-width: 0.5px; + } background: ${colors.blue80.toString()}; color: ${colors.blue.toString()}; diff --git a/src/lsg/patterns/chrome/index.tsx b/src/lsg/patterns/chrome/index.tsx index 1a1b9bc70..09815f1eb 100644 --- a/src/lsg/patterns/chrome/index.tsx +++ b/src/lsg/patterns/chrome/index.tsx @@ -22,7 +22,10 @@ const StyledChrome = styled.div` width: 100%; height: 40px; padding: ${getSpace(SpaceSize.XS)}px ${getSpace(SpaceSize.XXL) * 3}px; - border-bottom: 0.5px solid ${colors.grey90.toString()}; + border-bottom: 1px solid ${colors.grey90.toString()}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-bottom-width: 0.5px; + } background: ${colors.white.toString()}; font-family: ${fonts().NORMAL_FONT}; -webkit-app-region: drag; diff --git a/src/lsg/patterns/colors/index.tsx b/src/lsg/patterns/colors/index.tsx index af3b0571d..3130251df 100644 --- a/src/lsg/patterns/colors/index.tsx +++ b/src/lsg/patterns/colors/index.tsx @@ -99,6 +99,10 @@ export const colors = { displayName: 'Grey 90', rgb: [229, 230, 231] }), + grey97: new Color({ + displayName: 'Grey 97', + rgb: [247, 247, 247] + }), white: new Color({ displayName: 'White', rgb: [255, 255, 255] diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index 2570a7d8a..5a4fb8476 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -180,8 +180,8 @@ const StyledIcon = styled(Icon)` padding: ${getSpace(Size.XS)}px; transition: transform 0.2s; - ${(props: StyledIconProps) => (props.open ? 'transform: rotate(90deg)' : '')}; - ${(props: StyledIconProps) => (props.active ? 'fill: #0070D6' : '')}; + ${(props: StyledIconProps) => (props.open ? `transform: rotate(90deg)` : '')}; + ${(props: StyledIconProps) => (props.active ? `fill: ${colors.blue20.toString()}` : '')}; `; const Element: React.StatelessComponent = props => ( diff --git a/src/lsg/patterns/global-styles/index.tsx b/src/lsg/patterns/global-styles/index.tsx index f95fd92d6..d19dd3973 100644 --- a/src/lsg/patterns/global-styles/index.tsx +++ b/src/lsg/patterns/global-styles/index.tsx @@ -1,12 +1,13 @@ +import { colors } from '../colors'; import { fonts } from '../fonts'; -import {injectGlobal} from 'styled-components'; +import { injectGlobal } from 'styled-components'; export default function globalStyles(): void { // tslint:disable-next-line return injectGlobal` body { margin: 0; - background-color: #f7f7f7; + background-color: ${colors.grey97.toString()};; font-family: ${fonts().NORMAL_FONT}; font-size: 12px; } diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index da07fcb56..fe74af47d 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -19,7 +19,10 @@ const StyledElementPane = styled.div` display: block; width: 1px; height: 100%; - border-right: 0.5px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + border-right: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-right-width: 0.5px; + } box-sizing: border-box; z-index: 10; } diff --git a/src/lsg/patterns/panes/patterns-pane/index.tsx b/src/lsg/patterns/panes/patterns-pane/index.tsx index 44d9ed1f1..0ba078a1e 100644 --- a/src/lsg/patterns/panes/patterns-pane/index.tsx +++ b/src/lsg/patterns/panes/patterns-pane/index.tsx @@ -9,12 +9,17 @@ const StyledPatternsPane = styled.div` flex-shrink: 0; flex-basis: 40%; padding: ${getSpace(Size.M)}px 0; - border-top: 1px solid ${colors.grey90.toString()}; overflow: scroll; margin-left: -${getSpace(Size.L)}px; margin-right: -${getSpace(Size.L)}px; + border-left: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-left-width: 0.5px; + } `; -const PatternsPane: React.StatelessComponent = props => {props.children}; +const PatternsPane: React.StatelessComponent = props => ( + {props.children} +); export default PatternsPane; diff --git a/src/lsg/patterns/panes/preview-pane/index.tsx b/src/lsg/patterns/panes/preview-pane/index.tsx index fca23a11d..07d54ad6c 100644 --- a/src/lsg/patterns/panes/preview-pane/index.tsx +++ b/src/lsg/patterns/panes/preview-pane/index.tsx @@ -24,17 +24,16 @@ const StyledPreviewResizer = styled.div` width: 12px; height: 100%; cursor: ew-resize; - display: none; &::after { content: ''; position: absolute; top: 50%; transform: translateY(-50%); height: 36px; - width: 6px; + width: 3px; margin: 3px; border-radius: 5px; - background: grey; + background: ${colors.grey80.toString()}; } `; @@ -42,7 +41,6 @@ const BaseStyledPreviewPane = styled.div` flex-grow: 1; overflow: hidden; background: ${colors.white.toString()}; - border-right: 0.5px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; `; const StyledPreviewPane = BaseStyledPreviewPane.extend.attrs({ diff --git a/src/lsg/patterns/panes/property-pane/index.tsx b/src/lsg/patterns/panes/property-pane/index.tsx index ecebd737b..12b3fef2c 100644 --- a/src/lsg/patterns/panes/property-pane/index.tsx +++ b/src/lsg/patterns/panes/property-pane/index.tsx @@ -1,3 +1,4 @@ +import { colors } from '../../colors'; import * as React from 'react'; import styled from 'styled-components'; @@ -5,8 +6,14 @@ const StyledPropertyPane = styled.div` flex-grow: 1; flex-shrink: 0; flex-basis: 40%; + border-left: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-left-width: 0.5px; + } `; -const PropertyPane: React.StatelessComponent = props => {props.children}; +const PropertyPane: React.StatelessComponent = props => ( + {props.children} +); export default PropertyPane; From 05dd8a211887e04570beb7c7195c095f2e33a30d Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Thu, 26 Apr 2018 15:23:57 +0200 Subject: [PATCH 22/32] fix: pass hex color to BrowserWindow --- src/electron/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/electron/main.ts b/src/electron/main.ts index 1a7885545..6e4cdf40b 100644 --- a/src/electron/main.ts +++ b/src/electron/main.ts @@ -18,7 +18,7 @@ function createWindow(): void { minWidth: 780, minHeight: 380, titleBarStyle: 'hiddenInset', - backgroundColor: colors.grey97.toString(), + backgroundColor: colors.grey97.toString('hex'), title: 'Alva' }); From ed39ced07d198ecbffdb724415b9e9b950613865 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Thu, 26 Apr 2018 15:26:47 +0200 Subject: [PATCH 23/32] refactor: use 100vh to move element outside of viewport --- src/component/container/element-list.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index ef6782fe2..829c3563b 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -17,15 +17,14 @@ export interface ElementListState { } const DRAG_IMG_STYLE = ` - position: absolute; + position: fixed; + top: 100vh; background-color: #fff; color: #000; padding: 6px 18px; border-radius: 3px; font-size: 12px; opacity: 1; - top: 0; - left: -500px; `; @observer From 70d4708e1a589725be2d765c98f860a11e844575 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Thu, 26 Apr 2018 15:29:58 +0200 Subject: [PATCH 24/32] fix: clean up dragimg nodes after dragging --- src/component/container/element-list.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index 829c3563b..dff657d3e 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -29,6 +29,8 @@ const DRAG_IMG_STYLE = ` @observer export class ElementList extends React.Component<{}, ElementListState> { + private dragImg?: HTMLElement; + public state = { dragging: true }; @@ -241,6 +243,10 @@ export class ElementList extends React.Component<{}, ElementListState> { private handleDragEnd(e: React.DragEvent): void { this.setState({ dragging: false }); + + if (this.dragImg && this.dragImg.parentNode) { + this.dragImg.parentNode.removeChild(this.dragImg); + } } private handleDragStart(e: React.DragEvent): void { @@ -260,6 +266,7 @@ export class ElementList extends React.Component<{}, ElementListState> { dragImg.setAttribute('style', DRAG_IMG_STYLE); document.body.appendChild(dragImg); e.dataTransfer.setDragImage(dragImg, 75, 15); + this.dragImg = dragImg; } } From d8f8408412e0403bfd20ffb193772af683bd4242 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Thu, 26 Apr 2018 16:18:56 +0200 Subject: [PATCH 25/32] fix: get color from styleguide --- src/lsg/patterns/list/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lsg/patterns/list/index.tsx b/src/lsg/patterns/list/index.tsx index f55beb607..205e0bf8f 100644 --- a/src/lsg/patterns/list/index.tsx +++ b/src/lsg/patterns/list/index.tsx @@ -45,7 +45,8 @@ const StyledLi = styled.li` line-height: 25px; list-style: none; ${(props: StyledListItemProps) => (props.onClick ? 'cursor: pointer;' : '')}; - ${(props: StyledListItemProps) => (props.active ? 'background: #def' : '')}; + ${(props: StyledListItemProps) => + props.active ? `background: ${colors.blue80.toString()}` : ''}; `; const StyledLabel = styled.span` From 0a20c6ea5c237758363f82c441dd718b0c815bf9 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Thu, 26 Apr 2018 16:24:08 +0200 Subject: [PATCH 26/32] fix: read drag image colors from styleguide --- src/component/container/element-list.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index dff657d3e..cbe51a71f 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -1,3 +1,4 @@ +import { colors } from '../../lsg/patterns/colors'; import { elementMenu } from '../../electron/context-menus'; import { ElementLocationCommand } from '../../store/command/element-location-command'; import { ElementWrapper } from './element-wrapper'; @@ -19,8 +20,8 @@ export interface ElementListState { const DRAG_IMG_STYLE = ` position: fixed; top: 100vh; - background-color: #fff; - color: #000; + background-color: ${colors.white.toString()}; + color: ${colors.black.toString()}; padding: 6px 18px; border-radius: 3px; font-size: 12px; From c42f0e2940e2169e385698c65f0c20fd407d68ca Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Fri, 27 Apr 2018 08:53:31 +0200 Subject: [PATCH 27/32] fix: update add button position and side bar borders --- src/component/container/app.tsx | 23 +++++++------- src/lsg/patterns/add-button/index.tsx | 3 +- src/lsg/patterns/layout/demo.tsx | 2 +- src/lsg/patterns/layout/index.tsx | 31 +++++++++++++++---- src/lsg/patterns/panes/element-pane/index.tsx | 22 ++----------- .../patterns/panes/patterns-pane/index.tsx | 11 ++----- src/lsg/patterns/panes/preview-pane/index.tsx | 4 +-- .../patterns/panes/property-pane/index.tsx | 8 ++--- 8 files changed, 48 insertions(+), 56 deletions(-) diff --git a/src/component/container/app.tsx b/src/component/container/app.tsx index b6741b1c6..f508df978 100644 --- a/src/component/container/app.tsx +++ b/src/component/container/app.tsx @@ -146,25 +146,26 @@ export class App extends React.Component { {project ? ( store.setSelectedElement()} + hasBorder > - { - e.stopPropagation(); - store.setRightPane(RightPane.Patterns); - store.setSelectedElement(); - }} - /> + { + e.stopPropagation(); + store.setRightPane(RightPane.Patterns); + store.setSelectedElement(); + }} + /> - + {store.getRightPane() === RightPane.Properties && ( diff --git a/src/lsg/patterns/add-button/index.tsx b/src/lsg/patterns/add-button/index.tsx index 1b8361f97..43d66600c 100644 --- a/src/lsg/patterns/add-button/index.tsx +++ b/src/lsg/patterns/add-button/index.tsx @@ -19,8 +19,6 @@ interface StyledIconProps { } const StyledAddButton = styled.div` - position: absolute; - bottom: 0; width: 100%; border-top: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; @media screen and (-webkit-min-device-pixel-ratio: 2) { @@ -29,6 +27,7 @@ const StyledAddButton = styled.div` cursor: default; user-select: none; display: flex; + flex: none; height: 40px; box-sizing: border-box; padding: ${getSpace(SpaceSize.XXS)}px ${getSpace(SpaceSize.XS)}px ${getSpace(SpaceSize.XXS)}px diff --git a/src/lsg/patterns/layout/demo.tsx b/src/lsg/patterns/layout/demo.tsx index b3cd97df7..3fb3da39b 100644 --- a/src/lsg/patterns/layout/demo.tsx +++ b/src/lsg/patterns/layout/demo.tsx @@ -26,7 +26,7 @@ const LayoutDemo: React.StatelessComponent = (): JSX.Element => ( Horizontal Horizontal - + Vertical with margins Vertical with margins Vertical with margins diff --git a/src/lsg/patterns/layout/index.tsx b/src/lsg/patterns/layout/index.tsx index 117314f70..213cbaf0c 100644 --- a/src/lsg/patterns/layout/index.tsx +++ b/src/lsg/patterns/layout/index.tsx @@ -1,18 +1,36 @@ +import { colors } from '../colors'; import * as React from 'react'; -import { getSpace, Size } from '../space'; import styled from 'styled-components'; export interface LayoutProps { className?: string; directionVertical?: boolean; - hasPaddings?: boolean; + hasBorder?: boolean; onClick?: React.MouseEventHandler; + side?: string; } const StyledLayout = styled.div` display: flex; ${(props: LayoutProps) => (props.directionVertical ? 'flex-direction: column;' : '')}; - ${(props: LayoutProps) => (props.hasPaddings ? `padding: 0 ${getSpace(Size.L)}px` : '')}; + ${(props: LayoutProps) => + props.hasBorder && props.side == 'left' + ? ` + border-right: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-right-width: 0.5px; + } + ` + : ''}; + ${(props: LayoutProps) => + props.hasBorder && props.side == 'right' + ? ` + border-left: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; + @media screen and (-webkit-min-device-pixel-ratio: 2) { + border-left-width: 0.5px; + } + ` + : ''}; `; const StyledMainArea = styled(StyledLayout)` @@ -31,7 +49,7 @@ export const MainArea: React.StatelessComponent = props => ( {props.children} @@ -41,8 +59,9 @@ export const SideBar: React.StatelessComponent = props => ( {props.children} @@ -52,7 +71,7 @@ const Layout: React.StatelessComponent = props => ( {props.children} diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index fe74af47d..0fc836c6d 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -1,31 +1,13 @@ -import { colors } from '../../colors'; import * as React from 'react'; import { getSpace, Size } from '../../space'; import styled from 'styled-components'; const StyledElementPane = styled.div` position: relative; - flex-grow: 3; - flex-shrink: 0; - flex-basis: 60%; + flex: 1; overflow: scroll; padding-top: ${getSpace(Size.M)}px; - - &::before { - content: ''; - position: absolute; - top: 0; - right: 0; - display: block; - width: 1px; - height: 100%; - border-right: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; - @media screen and (-webkit-min-device-pixel-ratio: 2) { - border-right-width: 0.5px; - } - box-sizing: border-box; - z-index: 10; - } + padding-bottom: ${getSpace(Size.XL)}px; `; const ElementPane: React.StatelessComponent = props => ( diff --git a/src/lsg/patterns/panes/patterns-pane/index.tsx b/src/lsg/patterns/panes/patterns-pane/index.tsx index 0ba078a1e..0245213bc 100644 --- a/src/lsg/patterns/panes/patterns-pane/index.tsx +++ b/src/lsg/patterns/panes/patterns-pane/index.tsx @@ -1,21 +1,14 @@ -import { colors } from '../../colors'; import * as React from 'react'; import { getSpace, Size } from '../../space'; import styled from 'styled-components'; const StyledPatternsPane = styled.div` box-sizing: border-box; - flex-grow: 2; - flex-shrink: 0; - flex-basis: 40%; - padding: ${getSpace(Size.M)}px 0; + flex: 1; + padding: ${getSpace(Size.M)}px; overflow: scroll; margin-left: -${getSpace(Size.L)}px; margin-right: -${getSpace(Size.L)}px; - border-left: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; - @media screen and (-webkit-min-device-pixel-ratio: 2) { - border-left-width: 0.5px; - } `; const PatternsPane: React.StatelessComponent = props => ( diff --git a/src/lsg/patterns/panes/preview-pane/index.tsx b/src/lsg/patterns/panes/preview-pane/index.tsx index 07d54ad6c..c5a85cbea 100644 --- a/src/lsg/patterns/panes/preview-pane/index.tsx +++ b/src/lsg/patterns/panes/preview-pane/index.tsx @@ -21,7 +21,7 @@ const StyledPreviewWrapper = styled.div` `; const StyledPreviewResizer = styled.div` - width: 12px; + width: 9px; height: 100%; cursor: ew-resize; &::after { @@ -33,7 +33,7 @@ const StyledPreviewResizer = styled.div` width: 3px; margin: 3px; border-radius: 5px; - background: ${colors.grey80.toString()}; + background: ${colors.grey60.toString()}; } `; diff --git a/src/lsg/patterns/panes/property-pane/index.tsx b/src/lsg/patterns/panes/property-pane/index.tsx index 12b3fef2c..8a285c7a3 100644 --- a/src/lsg/patterns/panes/property-pane/index.tsx +++ b/src/lsg/patterns/panes/property-pane/index.tsx @@ -1,15 +1,13 @@ -import { colors } from '../../colors'; import * as React from 'react'; +import { getSpace, Size } from '../../space'; import styled from 'styled-components'; const StyledPropertyPane = styled.div` flex-grow: 1; flex-shrink: 0; flex-basis: 40%; - border-left: 1px solid ${colors.black.toString('rgb', { alpha: 0.1 })}; - @media screen and (-webkit-min-device-pixel-ratio: 2) { - border-left-width: 0.5px; - } + padding: ${getSpace(Size.M)}px; + overflow: scroll; `; const PropertyPane: React.StatelessComponent = props => ( From a1929197b767e8d4c2adb6545f8b264fc0eb8a9a Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Fri, 27 Apr 2018 09:15:55 +0200 Subject: [PATCH 28/32] fix: set hover and active styling to preview resizer --- src/lsg/patterns/panes/preview-pane/index.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/lsg/patterns/panes/preview-pane/index.tsx b/src/lsg/patterns/panes/preview-pane/index.tsx index c5a85cbea..a96ab814a 100644 --- a/src/lsg/patterns/panes/preview-pane/index.tsx +++ b/src/lsg/patterns/panes/preview-pane/index.tsx @@ -32,8 +32,19 @@ const StyledPreviewResizer = styled.div` height: 36px; width: 3px; margin: 3px; - border-radius: 5px; - background: ${colors.grey60.toString()}; + border-radius: 2px; + background: ${colors.grey80.toString()}; + } + + &:hover { + &::after { + background: ${colors.grey60.toString()}; + } + } + &:active { + &::after { + background: ${colors.blue40.toString()}; + } } `; From 344f2cb95d66933e87fd0b3eda2372f8c19637d7 Mon Sep 17 00:00:00 2001 From: Tilman Frick Date: Fri, 27 Apr 2018 12:40:09 +0200 Subject: [PATCH 29/32] fix: add element overflow gradient --- src/lsg/patterns/panes/element-pane/index.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lsg/patterns/panes/element-pane/index.tsx b/src/lsg/patterns/panes/element-pane/index.tsx index 0fc836c6d..578f14e89 100644 --- a/src/lsg/patterns/panes/element-pane/index.tsx +++ b/src/lsg/patterns/panes/element-pane/index.tsx @@ -1,3 +1,4 @@ +import { colors } from '../../colors'; import * as React from 'react'; import { getSpace, Size } from '../../space'; import styled from 'styled-components'; @@ -8,6 +9,21 @@ const StyledElementPane = styled.div` overflow: scroll; padding-top: ${getSpace(Size.M)}px; padding-bottom: ${getSpace(Size.XL)}px; + + &::after { + content: ''; + position: sticky; + bottom: 0; + display: block; + width: 100%; + height: ${getSpace(Size.XXXL)}px; + background: linear-gradient( + to bottom, + ${colors.grey97.toString('rgb', { alpha: 0 })}, + ${colors.grey97.toString('rgb', { alpha: 1 })} + ); + z-index: 15; + } `; const ElementPane: React.StatelessComponent = props => ( From a3b2dc8452161b8506b6feca55cc823aa0399379 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Fri, 27 Apr 2018 21:51:26 +0200 Subject: [PATCH 30/32] fix: make entire tree element draggable --- src/component/container/element-list.tsx | 25 ++++++++++++++---------- src/lsg/patterns/element/index.tsx | 11 ++++++++--- src/store/store.ts | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index cbe51a71f..bd9a05441 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -256,14 +256,8 @@ export class ElementList extends React.Component<{}, ElementListState> { if (element) { Store.getInstance().setDraggedElement(element); - } - - const target = e.target as HTMLElement; - const dragElement = target.querySelector('div'); - - // restyle the drag image and move it somewhere invisible - if (dragElement) { - const dragImg = dragElement.cloneNode(true) as HTMLElement; + const dragImg = document.createElement('div'); + dragImg.textContent = element.getName(); dragImg.setAttribute('style', DRAG_IMG_STYLE); document.body.appendChild(dragImg); e.dataTransfer.setDragImage(dragImg, 75, 15); @@ -271,6 +265,17 @@ export class ElementList extends React.Component<{}, ElementListState> { } } + private handleMouseLeave(e: React.MouseEvent): void { + const element = elementFromTarget(e.target); + if (element) { + this.setState({ dragging: true }); + } + } + + private handleMouseOver(e: React.MouseEvent): void { + this.setState({ dragging: false }); + } + public render(): JSX.Element | null { const store = Store.getInstance(); const page: Page | undefined = store.getCurrentPage(); @@ -295,8 +300,8 @@ export class ElementList extends React.Component<{}, ElementListState> { onContextMenu={e => this.handleContextMenu(e)} onDragStart={e => this.handleDragStart(e)} onDragEnd={e => this.handleDragEnd(e)} - onMouseOver={e => this.setState({ dragging: false })} - onMouseLeave={e => this.setState({ dragging: true })} + onMouseOver={e => this.handleMouseOver(e)} + onMouseLeave={e => this.handleMouseLeave(e)} > diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index 5a4fb8476..3ae7651b7 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -180,12 +180,18 @@ const StyledIcon = styled(Icon)` padding: ${getSpace(Size.XS)}px; transition: transform 0.2s; - ${(props: StyledIconProps) => (props.open ? `transform: rotate(90deg)` : '')}; + ${(props: StyledIconProps) => (props.open ? 'transform: rotate(90deg)' : '')}; ${(props: StyledIconProps) => (props.active ? `fill: ${colors.blue20.toString()}` : '')}; `; const Element: React.StatelessComponent = props => ( - + e.stopPropagation()} + onMouseLeave={e => e.stopPropagation()} + > {props.dragging && ( = props => ( )} Date: Fri, 27 Apr 2018 21:54:14 +0200 Subject: [PATCH 31/32] fix: reenable placeholder targets for off-list payloads --- src/component/container/element-list.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/component/container/element-list.tsx b/src/component/container/element-list.tsx index bd9a05441..edd8f9ae3 100644 --- a/src/component/container/element-list.tsx +++ b/src/component/container/element-list.tsx @@ -266,10 +266,7 @@ export class ElementList extends React.Component<{}, ElementListState> { } private handleMouseLeave(e: React.MouseEvent): void { - const element = elementFromTarget(e.target); - if (element) { - this.setState({ dragging: true }); - } + this.setState({ dragging: true }); } private handleMouseOver(e: React.MouseEvent): void { From e5ea5f0882211f5fcec22f6a1de1ab2d8ea7cfc9 Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Fri, 27 Apr 2018 22:39:22 +0200 Subject: [PATCH 32/32] fix: do not write bogus dom attributes --- package-lock.json | 3 ++- src/lsg/patterns/element/index.tsx | 11 ++++++++--- src/lsg/patterns/tag.tsx | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 src/lsg/patterns/tag.tsx diff --git a/package-lock.json b/package-lock.json index 9ea077b8e..cca61b495 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6775,7 +6775,8 @@ }, "jsbn": { "version": "0.1.1", - "bundled": true + "bundled": true, + "optional": true }, "json-schema": { "version": "0.2.3", diff --git a/src/lsg/patterns/element/index.tsx b/src/lsg/patterns/element/index.tsx index 3ae7651b7..35d02c042 100644 --- a/src/lsg/patterns/element/index.tsx +++ b/src/lsg/patterns/element/index.tsx @@ -3,6 +3,7 @@ import { Icon, IconName, Size as IconSize } from '../icons'; import * as React from 'react'; import { getSpace, Size } from '../space'; import styled from 'styled-components'; +import { tag } from '../tag'; export interface ElementProps { active?: boolean; @@ -51,7 +52,9 @@ const StyledElement = styled.div` z-index: 1; `; -const StyledElementLabel = styled.div` +const div = tag('div').omit(['active', 'highlight']); + +const StyledElementLabel = styled(div)` position: relative; display: flex; padding: ${getSpace(Size.XS)}px ${getSpace(Size.L)}px ${getSpace(Size.XS)}px ${getSpace( @@ -114,7 +117,8 @@ const StyledElementLabel = styled.div` : ''}; `; -const StyledPlaceholder = styled.div` +const placeholderDiv = tag('div').omit(['highlightPlaceholder']); +const StyledPlaceholder = styled(placeholderDiv)` position: relative; height: ${getSpace(Size.S)}; width: 100%; @@ -165,7 +169,8 @@ const StyledPlaceholder = styled.div` : ''}; `; -const StyledElementChild = styled.div` +const elementDiv = tag('div').omit(['open']); +const StyledElementChild = styled(elementDiv)` flex-basis: 100%; padding-left: ${getSpace(Size.L)}px; ${(props: StyledElementChildProps) => (props.open ? 'display: block;' : 'display: none;')}; diff --git a/src/lsg/patterns/tag.tsx b/src/lsg/patterns/tag.tsx new file mode 100644 index 000000000..979bfb469 --- /dev/null +++ b/src/lsg/patterns/tag.tsx @@ -0,0 +1,24 @@ +import { omit, pick } from 'lodash'; +import * as React from 'react'; + +export interface Tag { + omit(whitelist: string[]): React.SFC>; + pick(blacklist: string[]): React.SFC>; +} + +export function tag(TagName: string): Tag { + return { + omit(blacklist: string[]): React.SFC<{}> { + return props => { + const p = omit(props, blacklist); + return {p.children}; + }; + }, + pick(whitelist: string[]): React.SFC<{}> { + return props => { + const p = pick(props, whitelist); + return {p.children}; + }; + } + }; +}