diff --git a/app/actions/columns.ts b/app/actions/columns.ts new file mode 100644 index 0000000..2823f4d --- /dev/null +++ b/app/actions/columns.ts @@ -0,0 +1,14 @@ +import { IColumn } from '../components/column/IColumn'; + +export const ADD_COLUMN = 'ADD_COLUMN'; +export const REMOVE_COLUMN = 'REMOVE_COLUMN'; + +export const addColumn = (grid: IColumn) => ({ + type: ADD_COLUMN, + payload: grid +}); + +export const removeColumn = (grid: IColumn) => ({ + type: REMOVE_COLUMN, + payload: grid +}); diff --git a/app/components/App.tsx b/app/components/App.tsx index fdbad58..7e05c51 100644 --- a/app/components/App.tsx +++ b/app/components/App.tsx @@ -3,22 +3,25 @@ import * as R from 'ramda'; import * as React from 'react'; import { connect } from 'react-redux'; import styled from 'styled-components'; +import { addColumn, removeColumn } from '../actions/columns'; import { addGrid, removeGrid } from '../actions/grids'; import { addGuide, removeGuide } from '../actions/guides'; import { toggleVisibility as toggleHelpVisibility } from '../actions/help'; import { addOnion, removeOnion } from '../actions/onions'; import { addRuler, removeRuler } from '../actions/rulers'; import { toggleVisibility as toggleToolsVisibility } from '../actions/tools'; +import { Column } from '../components/column/Column'; import { Grid } from '../components/grid/Grid'; -import Guide from '../components/guide/Guide'; -import OnionImage from '../components/onionImage/OnionImage'; -import Ruler from '../components/ruler/Ruler'; +import { Guide } from '../components/guide/Guide'; +import { OnionImage } from '../components/onionImage/OnionImage'; +import { Ruler } from '../components/ruler/Ruler'; import { Toolbox } from '../components/toolbox/Toolbox'; import { AppStore } from '../reducers'; import { HelpStore } from '../reducers/help'; import { ToolsStore } from '../reducers/tools'; import { initializeAnalytics, track } from './core/analytics'; import { factory as GridFactory } from './grid/factory'; +import { factory as ColumnFactory } from './column/factory'; import { IGrid } from './grid/IGrid'; import { factory as GuideFactory } from './guide/factory'; import { IGuide } from './guide/IGuide'; @@ -28,16 +31,20 @@ import { IOnionImage } from './onionImage/IOnionImage'; import { factory as RulerFactory } from './ruler/factory'; import { IRuler } from './ruler/IRuler'; import { Tool } from './toolbox/Tool'; +import { IColumn } from './column/IColumn'; interface Props { help: HelpStore; tools: ToolsStore; + columns: IColumn[]; grids: IGrid[]; guides: IGuide[]; onions: IOnionImage[]; rulers: IRuler[]; addGrid: (grid: IGrid) => void; removeGrid: (grid: IGrid) => void; + addColumn: (column: IColumn) => void; + removeColumn: (column: IColumn) => void; addGuide: (guide: IGuide) => void; removeGuide: (guide: IGuide) => void; addOnion: (onion: IOnionImage) => void; @@ -84,10 +91,11 @@ class AppView extends React.Component { }; render() { - const { grids, guides, onions, rulers, tools, help } = this.props; + const { columns, grids, guides, onions, rulers, tools, help } = this.props; const isToolsVisible = tools.visible; const isHelpVisible = help.visible; const isGridVisible = R.gt(R.length(grids), 0); + const isColumnVisible = R.gt(R.length(columns), 0); return ( <> @@ -95,6 +103,9 @@ class AppView extends React.Component { {grids.map((grid: IGrid) => ( ))} + {columns.map((column: IColumn) => ( + + ))} {rulers.map((ruler: IRuler) => ( { y={10} toggleVisibility={this._setVisible} isStuffVisible={isToolsVisible} + isColumnVisible={isColumnVisible} isGridVisible={isGridVisible} create={this.create} - toggle={this._toggleGrid} + toggle={this._toggleTool} toggleHelp={this._toggleHelp} /> ); } - private _toggleGrid = () => { - const isGridVisible = R.gt(R.length(this.props.grids), 0); - if (isGridVisible) { - this.props.removeGrid(this.props.grids[0]); - track('tool', 'grid', 'remove'); - } else { - this.props.addGrid(GridFactory()); - track('tool', 'grid', 'add'); + private _toggleTool = (tool: Tool) => { + switch (tool) { + case Tool.GRID: + const isGridVisible = R.gt(R.length(this.props.grids), 0); + if (isGridVisible) { + this.props.removeGrid(this.props.grids[0]); + track('tool', 'grid', 'remove'); + } else { + this.props.addGrid(GridFactory()); + track('tool', 'grid', 'add'); + } + break; + case Tool.COLUMN: + const isColumnVisible = R.gt(R.length(this.props.columns), 0); + if (isColumnVisible) { + this.props.removeColumn(this.props.columns[0]); + track('tool', 'grid', 'remove'); + } else { + this.props.addColumn(ColumnFactory()); + track('tool', 'grid', 'add'); + } + break; } }; @@ -169,6 +195,8 @@ const App = connect( { addGrid, removeGrid, + addColumn, + removeColumn, addGuide, removeGuide, addOnion, diff --git a/app/components/column/Column.tsx b/app/components/column/Column.tsx new file mode 100644 index 0000000..89f170d --- /dev/null +++ b/app/components/column/Column.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import styled from 'styled-components'; +import { IColumn } from './IColumn'; +import { createColumn } from './utils'; + +export const Column = ({ width, height, color, opacity }: IColumn) => ( + +); + +interface Props { + data: string; + opacity: number; +} + +const Element = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-image: url(${({ data }) => data}); + background-repeat: repeat; + opacity: ${({ opacity }) => opacity}; +`; diff --git a/app/components/column/IColumn.ts b/app/components/column/IColumn.ts new file mode 100644 index 0000000..27a3946 --- /dev/null +++ b/app/components/column/IColumn.ts @@ -0,0 +1,10 @@ +import { Color } from '../../utils/Color'; + +export interface IColumn { + className?: string; + id: string; + color: Color; + width: number; + height: number; + opacity: number; +} diff --git a/app/components/column/factory.ts b/app/components/column/factory.ts new file mode 100644 index 0000000..296a617 --- /dev/null +++ b/app/components/column/factory.ts @@ -0,0 +1,12 @@ +import * as uuid from 'uuid/v1'; +import { Color } from '../../utils/Color'; +import { IColumn } from './IColumn'; + +export const factory = (id: string = uuid(), props = {}): IColumn => ({ + id, + width: 100, + height: 100, + color: Color.RED, + opacity: 0.1, + ...props +}); diff --git a/app/components/column/utils.ts b/app/components/column/utils.ts new file mode 100644 index 0000000..35b2edf --- /dev/null +++ b/app/components/column/utils.ts @@ -0,0 +1,17 @@ +export const createColumn = ( + width: number, + height: number, + gap: number, + color: string +): string => { + const canvas: HTMLCanvasElement = document.createElement('canvas'); + const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; + + canvas.width = width + gap; + canvas.height = height; + + ctx.fillStyle = color; + ctx.fillRect(0, 0, width, height); + + return canvas.toDataURL(); +}; diff --git a/app/components/guide/Guide.tsx b/app/components/guide/Guide.tsx index 24a552b..e665b47 100644 --- a/app/components/guide/Guide.tsx +++ b/app/components/guide/Guide.tsx @@ -37,7 +37,7 @@ interface Props { remove: () => void; } -export default class Guide extends React.Component { +export class Guide extends React.Component { private el: React.RefObject = React.createRef(); static getDerivedStateFromProps(nextProps, prevState) { diff --git a/app/components/onionImage/OnionImage.tsx b/app/components/onionImage/OnionImage.tsx index eee35c5..ad63fee 100644 --- a/app/components/onionImage/OnionImage.tsx +++ b/app/components/onionImage/OnionImage.tsx @@ -80,10 +80,7 @@ interface Props { remove: () => void; } -export default class OnionImage extends React.Component< - IOnionImage & Props, - State -> { +export class OnionImage extends React.Component { private el: React.RefObject = React.createRef(); private image: React.RefObject = React.createRef(); diff --git a/app/components/ruler/Ruler.tsx b/app/components/ruler/Ruler.tsx index 6a005a0..21d2807 100644 --- a/app/components/ruler/Ruler.tsx +++ b/app/components/ruler/Ruler.tsx @@ -36,7 +36,7 @@ interface Props { remove: () => void; } -export default class Ruler extends React.Component { +export class Ruler extends React.Component { private el: React.RefObject = React.createRef(); private ruler: React.RefObject = React.createRef(); diff --git a/app/components/toolbox/Tool.ts b/app/components/toolbox/Tool.ts index ee608b6..abd4756 100644 --- a/app/components/toolbox/Tool.ts +++ b/app/components/toolbox/Tool.ts @@ -1,4 +1,5 @@ export enum Tool { + COLUMN = 'column', GUIDE = 'guide', RULER = 'ruler', ONION = 'onion', diff --git a/app/components/toolbox/Toolbox.tsx b/app/components/toolbox/Toolbox.tsx index 9903cd2..5f32bc1 100644 --- a/app/components/toolbox/Toolbox.tsx +++ b/app/components/toolbox/Toolbox.tsx @@ -15,6 +15,7 @@ interface Props { x: number; y: number; isStuffVisible: boolean; + isColumnVisible: boolean; isGridVisible: boolean; toggleVisibility: () => void; create: (tool: Tool) => void; @@ -62,6 +63,7 @@ export class Toolbox extends React.Component { render() { const { isStuffVisible, + isColumnVisible, isGridVisible, x, y, @@ -82,7 +84,10 @@ export class Toolbox extends React.Component { title={`${isStuffVisible ? 'Hide' : 'Show'} all`} onClick={() => toggleVisibility()} > - + create(Tool.GUIDE)}> @@ -101,7 +106,13 @@ export class Toolbox extends React.Component { title={isGridVisible ? 'Hide grid' : 'Show grid'} onClick={() => toggle(Tool.GRID)} > - + + + toggle(Tool.COLUMN)} + > + diff --git a/app/reducers/columns.ts b/app/reducers/columns.ts new file mode 100644 index 0000000..cc2682f --- /dev/null +++ b/app/reducers/columns.ts @@ -0,0 +1,20 @@ +import * as R from 'ramda'; +import { AnyAction } from 'redux'; +import { ADD_COLUMN, REMOVE_COLUMN } from '../actions/columns'; +import { IColumn } from '../components/column/IColumn'; + +const initialStore: IColumn[] = []; + +export const columns = ( + store = initialStore, + { type, payload }: AnyAction +): IColumn[] => { + switch (type) { + case ADD_COLUMN: + return R.append(payload, store); + case REMOVE_COLUMN: + return R.reject(R.equals(payload), store); + } + + return store; +}; diff --git a/app/reducers/index.ts b/app/reducers/index.ts index 96ca73f..5c90ca1 100644 --- a/app/reducers/index.ts +++ b/app/reducers/index.ts @@ -3,14 +3,17 @@ import { IGrid } from '../components/grid/IGrid'; import { IGuide } from '../components/guide/IGuide'; import { IOnionImage } from '../components/onionImage/IOnionImage'; import { IRuler } from '../components/ruler/IRuler'; +import { columns } from './columns'; import { grids } from './grids'; import { guides } from './guides'; import { onions } from './onions'; import { rulers } from './rulers'; import { tools, ToolsStore } from './tools'; import { help, HelpStore } from './help'; +import { IColumn } from '../components/column/IColumn'; export interface AppStore { + columns: IColumn[]; grids: IGrid[]; guides: IGuide[]; onions: IOnionImage[]; @@ -20,6 +23,7 @@ export interface AppStore { } const rootReducer = combineReducers({ + columns, grids, guides, onions,