From c74081112b2a74a5675e195310af5a4e00b263d7 Mon Sep 17 00:00:00 2001 From: Yannis Barlas <yannisbarlas@gmail.com> Date: Tue, 8 Sep 2020 17:19:07 +0300 Subject: [PATCH] grid tool backup --- .eslintrc.js | 2 +- stories/tables/Grid.stories.js | 20 + stories/tables/GridCell.stories.js | 13 + stories/tables/TableTool.stories.js | 11 + .../src/ui/tables/Grid.js | 274 ++++++++++++ .../src/ui/tables/GridCell.js | 44 ++ .../src/ui/tables/TableTool.js | 399 ++++++++++++++++++ 7 files changed, 762 insertions(+), 1 deletion(-) create mode 100644 stories/tables/Grid.stories.js create mode 100644 stories/tables/GridCell.stories.js create mode 100644 stories/tables/TableTool.stories.js create mode 100644 wax-prosemirror-components/src/ui/tables/Grid.js create mode 100644 wax-prosemirror-components/src/ui/tables/GridCell.js create mode 100644 wax-prosemirror-components/src/ui/tables/TableTool.js diff --git a/.eslintrc.js b/.eslintrc.js index c7f8ffb61..22426f434 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,7 +30,7 @@ eslint.rules = { 'react/prop-types': [ 2, - { ignore: ['children', 'className', 'onClick', 'theme'] }, + { ignore: ['children', 'className', 'onClick', 'onMouseEnter', 'theme'] }, ], // 'import/no-named-as-default': 0, }; diff --git a/stories/tables/Grid.stories.js b/stories/tables/Grid.stories.js new file mode 100644 index 000000000..e8d157b59 --- /dev/null +++ b/stories/tables/Grid.stories.js @@ -0,0 +1,20 @@ +import React from 'react'; +import styled from 'styled-components'; +// import { lorem } from 'faker' + +import Grid from '../../wax-prosemirror-components/src/ui/tables/Grid'; + +const Wrapper = styled.div` + height: 400px; +`; + +export const Base = () => ( + <Wrapper> + <Grid intialRows={5} initialColumns={5} />; + </Wrapper> +); + +export default { + component: Grid, + title: 'Tables/Grid', +}; diff --git a/stories/tables/GridCell.stories.js b/stories/tables/GridCell.stories.js new file mode 100644 index 000000000..308db746a --- /dev/null +++ b/stories/tables/GridCell.stories.js @@ -0,0 +1,13 @@ +import React from 'react'; +// import { lorem } from 'faker' + +import GridCell from '../../wax-prosemirror-components/src/ui/tables/GridCell'; + +export const Base = () => <GridCell />; + +export const Active = () => <GridCell active />; + +export default { + component: GridCell, + title: 'Tables/Grid Cell', +}; diff --git a/stories/tables/TableTool.stories.js b/stories/tables/TableTool.stories.js new file mode 100644 index 000000000..3c5d69c75 --- /dev/null +++ b/stories/tables/TableTool.stories.js @@ -0,0 +1,11 @@ +import React from 'react'; +// import { lorem } from 'faker' + +import TableTool from '../../wax-prosemirror-components/src/ui/tables/TableTool'; + +export const Base = () => <TableTool />; + +export default { + component: TableTool, + title: 'Tables/Table Tool', +}; diff --git a/wax-prosemirror-components/src/ui/tables/Grid.js b/wax-prosemirror-components/src/ui/tables/Grid.js new file mode 100644 index 000000000..8a54ec75a --- /dev/null +++ b/wax-prosemirror-components/src/ui/tables/Grid.js @@ -0,0 +1,274 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import styled, { css } from 'styled-components'; +import { range } from 'lodash'; + +import GridCell from './GridCell'; + +const INITIAL_GRID_SIZE = 5; +const CELL_SIZE = 12; +const GUTTER = 4; + +const Wrapper = styled.div` + /* flex-direction: column; */ + position: relative; + /* height: 100px; + width: 100px; */ + /* background: turquoise; */ + + border: 1px solid gray; + border-radius: 5px; + + box-sizing: border-box; + + height: ${props => `${16 + props.rows * (CELL_SIZE + GUTTER)}px`}; + width: ${props => `${props.columns * (CELL_SIZE + GUTTER)}px`}; + padding: 8px; +`; + +const StyledRow = styled.div` + display: flex; + flex-direction: row; +`; + +const StyledGridCell = styled(GridCell)` + margin: 1px; +`; + +// .attrs(props => ({ +// style: { +// background: props.active && 'skyblue', +// }, +// })) +const StyledCell = styled.div.attrs(({ top, left }) => ({ + style: { + top, + left, + }, +}))` + height: 12px; + width: 12px; + border: 1px solid gray; + display: inline-block; + /* transition: background 0.1s ease-in; */ + + position: absolute; +`; +// top: ${props => props.top}; +// left: ${props => props.left}; + +/* ${props => props.active && `background: skyblue;`} */ +// const Cell = props => { +// const { active, className, onMouseEnter, rowIndex, columnIndex } = props; + +// return ( +// <StyledCell +// row={rowIndex} +// column={columnIndex} +// active={active} +// className={className} +// onMouseEnter={onMouseEnter} +// /> +// ); +// }; + +class Cell extends React.PureComponent { + render() { + // console.log('render!'); + const { + active, + // className, + onMouseEnter, + // rowIndex, + // columnIndex, + top, + left, + } = this.props; + + // if (rowIndex === 0 && columnIndex === 0) { + // console.log(this.props); + // } + + return ( + <StyledCell + style={{ + background: active && 'skyblue', + }} + // row={rowIndex} + // column={columnIndex} + active={active} + // className={className} + onMouseEnter={onMouseEnter} + top={top} + left={left} + /> + ); + } +} + +const Row = props => { + const { activeRows, activeColumns, columns, rowIndex, updateActive } = props; + + return ( + <StyledRow> + {range(columns).map(columnIndex => ( + <StyledGridCell + active={rowIndex < activeRows && columnIndex < activeColumns} + onMouseEnter={() => updateActive(rowIndex + 1, columnIndex + 1)} + key={columnIndex} + /> + ))} + </StyledRow> + ); +}; + +const Grid = props => { + const { className, initialRows, initialColumns } = props; + + const [rows, setRows] = useState(INITIAL_GRID_SIZE); + const [columns, setColumns] = useState(INITIAL_GRID_SIZE); + + const [activeRows, setActiveRows] = useState(1); + const [activeColumns, setActiveColumns] = useState(1); + + // const [state, setState] = useState({ + // rows: 5, + // columns: 5, + // activeRows: 1, + // activeColumns: 1, + // }); + + const updateActive = (rowPosition, columnPosition) => { + console.log('run!'); + // console.log(rowPosition, columnPosition); + // console.log(rowPosition, rows); + // expand + // const newRowCount = + // rowPosition === rows && rowPosition < 20 ? rows + 1 : rows; + // setRows(newRowCount); + // rowPosition === rows && rowPosition < 20 && setRows(rows + 1); + // const newColumnCount = + // columnPosition === columns && columnPosition < 20 ? columns + 1 : columns; + // setColumns(newColumnCount); + // columnPosition === columns && + // columnPosition < 20 && + // setColumns(columns + 1); + setActiveRows(rowPosition); + // setActiveColumns(columnPosition); + // contract + // rowPosition < rows && rows < 20 && rowPosition > 5 && setRows(rows - 1); + // setState({ + // activeRows: rowPosition, + // activeColumns: columnPosition, + // rows: rowPosition === rows && rowPosition < 20 ? rows + 1 : rows, + // columns, + // }); + }; + + // console.log('render me'); + + // const { rows, columns, activeRows, activeColumns } = state; + + // return ( + // <Wrapper className={className}> + // {range(rows).map(rowIndex => { + // return ( + // <Row + // activeRows={activeRows} + // activeColumns={activeColumns} + // columns={columns} + // rowIndex={rowIndex} + // key={rowIndex} + // updateActive={updateActive} + // /> + // ); + // })} + + // <span> + // {activeColumns} x {activeRows} + // </span> + // </Wrapper> + // ); + + const onMouseMove = e => { + // console.log(e.nativeEvent.offsetX); + // const { offsetX, offsetY } = e.nativeEvent; + // console.log(offsetX, offsetY); + + // console.log(e.clientX, e.clientY); + + // console.log(e_offsetX, e_offsetY); + + // get position of our Wrapper within page + const container = e.currentTarget.getBoundingClientRect(); + const containerX = e.pageX - container.left; + const containerY = e.pageY - container.top; + + const overRow = Math.ceil(containerY / (CELL_SIZE + GUTTER)); + // console.log('offsetY', offsetY, 'overRow', overRow); + + const overColumn = Math.ceil(containerX / (CELL_SIZE + GUTTER)); + + setActiveRows(overRow); + setActiveColumns(overColumn); + + // overRow === rows && overRow < 20 && setRows(rows + 1); + // overRow + 1 < rows && overRow > 3 && setRows(rows - 1); + + if (overColumn < 5) { + setColumns(5); + } else if (overColumn >= 5 && overColumn <= 20) { + setColumns(overColumn + 1); + } + // overColumn < 5 && setColumns(5); + // overColumn >= 5 && overColumn <= 20 && setColumns(overColumn + 1); + + if (overRow < 5) { + setRows(5); + } else if (overRow >= 5 && overRow <= 20) { + setRows(overRow + 1); + } + + // overRow < 5 && setRows(5); + // overRow >= 5 && overRow <= 20 && setRows(overRow + 1); + + // overColumn === columns && overColumn < 20 && setColumns(columns + 1); + // overColumn + 1 < columns && overColumn > 3 && setColumns(columns - 1); + }; + + return ( + <Wrapper + className={className} + onMouseMove={onMouseMove} + columns={columns} + rows={rows} + > + {range(rows).map(rowIndex => + range(columns).map(columnIndex => { + // console.log(rowIndex, columnIndex); + return ( + <Cell + active={rowIndex < activeRows && columnIndex < activeColumns} + key={`${rowIndex}:${columnIndex}`} + rowIndex={rowIndex} + columnIndex={columnIndex} + // onMouseEnter={() => updateActive(rowIndex, columnIndex)} + top={`${rowIndex * CELL_SIZE + rowIndex * GUTTER}px`} + left={`${columnIndex * 12 + columnIndex * GUTTER}px`} + /> + ); + }), + )} + + {/* <span> + {activeColumns} x {activeRows} + </span> */} + </Wrapper> + ); +}; + +Grid.propTypes = {}; + +Grid.defaultProps = {}; + +export default Grid; diff --git a/wax-prosemirror-components/src/ui/tables/GridCell.js b/wax-prosemirror-components/src/ui/tables/GridCell.js new file mode 100644 index 000000000..13cd1a3ff --- /dev/null +++ b/wax-prosemirror-components/src/ui/tables/GridCell.js @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; + +const Wrapper = styled.div` + height: 12px; + width: 12px; + border: 1px solid gray; + transition: background 0.1s ease-in; + + ${props => props.active && `background: skyblue;`} +`; + +// const GridCell = props => { +// const { active, className, onMouseEnter } = props; + +// return ( +// <Wrapper +// active={active} +// className={className} +// onMouseEnter={onMouseEnter} +// /> +// ); +// }; + +class GridCell extends React.PureComponent { + render() { + const { active, className, onMouseEnter } = this.props; + + return ( + <Wrapper + active={active} + className={className} + onMouseEnter={onMouseEnter} + /> + ); + } +} + +GridCell.propTypes = {}; + +GridCell.defaultProps = {}; + +export default GridCell; diff --git a/wax-prosemirror-components/src/ui/tables/TableTool.js b/wax-prosemirror-components/src/ui/tables/TableTool.js new file mode 100644 index 000000000..97fd04316 --- /dev/null +++ b/wax-prosemirror-components/src/ui/tables/TableTool.js @@ -0,0 +1,399 @@ +/* eslint-disable react/prop-types */ + +// import cx from 'classnames'; +import React, { useState } from 'react'; +// import ReactDOM from 'react-dom'; +import styled from 'styled-components'; + +// import clamp from './clamp'; +// import htmlElementToRect from './htmlElementToRect'; +// import { fromHTMlElement, fromXY, isIntersected } from './rects'; + +// import './czi-table-grid-size-editor.css'; + +// export type TableGridSizeEditorValue = { +// cols: number, +// rows: number, +// }; + +const GUTTER_SIZE = 5; +const CELL_SIZE = 16; +const MAX_SIZE = 20; + +// class GridCell extends React.PureComponent<any, any, any> { +// render(): React.Element<any> { +// const { x, y, selected } = this.props; +// const style = { +// left: x + 'px', +// top: y + 'px', +// width: CELL_SIZE + 'px', +// height: CELL_SIZE + 'px', +// }; +// const className = cx('czi-table-grid-size-editor-cell', { +// selected, +// }); +// return <div className={className} style={style} />; +// } +// } + +const StyledGridCell = styled.div` + left: ${props => `${props.x}px`}; + top: ${props => `${props.y}px`}; + width: ${props => `${CELL_SIZE}px`}; + height: ${props => `${CELL_SIZE}px`}; +`; + +const GridCell = props => { + const { className, x, y, selected } = props; + + // const className = cx('czi-table-grid-size-editor-cell', { + // selected, + // }); + + return <StyledGridCell x={x} y={y} className={className} />; +}; + +// class TableGridSizeEditor extends React.PureComponent<any, any, any> { +// _ex = 0; +// _ey = 0; +// _mx = 0; +// _my = 0; +// _rafID = 0; +// _ref = null; +// _entered = false; + +// props: { +// close: (val: TableGridSizeEditorValue) => void, +// }; + +// state: TableGridSizeEditorValue = { +// rows: 1, +// cols: 1, +// }; + +// componentWillUnmount(): void { +// if (this._entered) { +// document.removeEventListener('mousemove', this._onMouseMove, true); +// } +// this._rafID && cancelAnimationFrame(this._rafID); +// } + +// render(): React.Element<any> { +// const { rows, cols } = this.state; +// let rr = Math.max(5, rows); +// let cc = Math.max(5, cols); +// if (rr === rows) { +// rr = Math.min(MAX_SIZE, rr + 1); +// } +// if (cc === cols) { +// cc = Math.min(MAX_SIZE, cc + 1); +// } +// const cells = []; +// let ii = 0; +// let y = 0; +// let w = 0; +// let h = 0; +// while (ii < rr) { +// y += GUTTER_SIZE; +// let jj = 0; +// let x = 0; +// while (jj < cc) { +// x += GUTTER_SIZE; +// const selected = ii < rows && jj < cols; +// cells.push( +// <GridCell +// key={`${String(ii)}-${String(jj)}`} +// selected={selected} +// x={x} +// y={y} +// />, +// ); +// x += CELL_SIZE; +// w = x + GUTTER_SIZE; +// jj++; +// } +// y += CELL_SIZE; +// h = y + GUTTER_SIZE; +// ii++; +// } +// const bodyStyle = { width: w + 'px', height: h + 'px' }; + +// return ( +// <div className="czi-table-grid-size-editor" ref={this._onRef}> +// <div +// className="czi-table-grid-size-editor-body" +// onMouseDown={this._onMouseDown} +// onMouseEnter={this._onMouseEnter} +// style={bodyStyle} +// > +// {cells} +// </div> +// <div className="czi-table-grid-size-editor-footer"> +// {rows} X {cols} +// </div> +// </div> +// ); +// } + +// _onRef = (ref: any): void => { +// this._ref = ref; +// }; + +// _onMouseEnter = (e: MouseEvent): void => { +// const node = e.currentTarget; +// if (node instanceof HTMLElement) { +// const rect = fromHTMlElement(node); +// const mx = Math.round(e.clientX); +// const my = Math.round(e.clientY); +// this._ex = rect.x; +// this._ey = rect.y; +// this._mx = mx; +// this._my = my; +// if (!this._entered) { +// this._entered = true; +// document.addEventListener('mousemove', this._onMouseMove, true); +// } +// } +// }; + +// _onMouseMove = (e: MouseEvent): void => { +// const el = this._ref && ReactDOM.findDOMNode(this._ref); +// const elRect = el ? htmlElementToRect(el) : null; +// const mouseRect = fromXY(e.screenX, e.screenY, 10); + +// if (elRect && mouseRect && isIntersected(elRect, mouseRect, 50)) { +// // This prevents `PopUpManager` from collapsing the editor. +// e.preventDefault(); +// e.stopImmediatePropagation(); +// } + +// const mx = Math.round(e.clientX); +// const my = Math.round(e.clientY); +// if (mx !== this._mx || my !== this._my) { +// this._mx = mx; +// this._my = my; +// this._rafID && cancelAnimationFrame(this._rafID); +// this._rafID = requestAnimationFrame(this._updateGridSize); +// } +// }; + +// _updateGridSize = (): void => { +// this._rafID = 0; +// const mx = this._mx; +// const my = this._my; +// const x = mx - this._ex; +// const y = my - this._ey; +// const rr = clamp(1, Math.ceil(y / (CELL_SIZE + GUTTER_SIZE)), MAX_SIZE); +// const cc = clamp(1, Math.ceil(x / (CELL_SIZE + GUTTER_SIZE)), MAX_SIZE); +// const { rows, cols } = this.state; +// if (rows !== rr || cols !== cc) { +// this.setState({ rows: rr, cols: cc }); +// } +// }; + +// _onMouseDown = (e: SyntheticEvent): void => { +// e.preventDefault(); +// this.props.close(this.state); +// }; +// } + +const TableGridSizeEditor = props => { + const [rows, setRows] = useState(1); + const [columns, setColumns] = useState(1); + + let rr = Math.max(5, rows); + let cc = Math.max(5, columns); + if (rr === rows) { + rr = Math.min(MAX_SIZE, rr + 1); + } + if (cc === columns) { + cc = Math.min(MAX_SIZE, cc + 1); + } + const cells = []; + let ii = 0; + let y = 0; + let w = 0; + let h = 0; + while (ii < rr) { + y += GUTTER_SIZE; + let jj = 0; + let x = 0; + while (jj < cc) { + x += GUTTER_SIZE; + const selected = ii < rows && jj < columns; + cells.push( + <GridCell + key={`${String(ii)}-${String(jj)}`} + selected={selected} + x={x} + y={y} + />, + ); + x += CELL_SIZE; + w = x + GUTTER_SIZE; + jj++; + } + y += CELL_SIZE; + h = y + GUTTER_SIZE; + ii++; + } + const bodyStyle = { width: w + 'px', height: h + 'px' }; + + return ( + <div + // className="czi-table-grid-size-editor" + // ref={this._onRef} + > + <div + // className="czi-table-grid-size-editor-body" + // onMouseDown={this._onMouseDown} + // onMouseEnter={this._onMouseEnter} + // style={bodyStyle} + > + {cells} + </div> + <div className="czi-table-grid-size-editor-footer"> + {rows} X {columns} + </div> + </div> + ); + + // _ex = 0; + // _ey = 0; + // _mx = 0; + // _my = 0; + // _rafID = 0; + // _ref = null; + // _entered = false; + + // props: { + // close: (val: TableGridSizeEditorValue) => void, + // }; + + // componentWillUnmount(): void { + // if (this._entered) { + // document.removeEventListener('mousemove', this._onMouseMove, true); + // } + // this._rafID && cancelAnimationFrame(this._rafID); + // } + + // render(): React.Element<any> { + // const { rows, cols } = this.state; + // let rr = Math.max(5, rows); + // let cc = Math.max(5, cols); + // if (rr === rows) { + // rr = Math.min(MAX_SIZE, rr + 1); + // } + // if (cc === cols) { + // cc = Math.min(MAX_SIZE, cc + 1); + // } + // const cells = []; + // let ii = 0; + // let y = 0; + // let w = 0; + // let h = 0; + // while (ii < rr) { + // y += GUTTER_SIZE; + // let jj = 0; + // let x = 0; + // while (jj < cc) { + // x += GUTTER_SIZE; + // const selected = ii < rows && jj < cols; + // cells.push( + // <GridCell + // key={`${String(ii)}-${String(jj)}`} + // selected={selected} + // x={x} + // y={y} + // />, + // ); + // x += CELL_SIZE; + // w = x + GUTTER_SIZE; + // jj++; + // } + // y += CELL_SIZE; + // h = y + GUTTER_SIZE; + // ii++; + // } + // const bodyStyle = { width: w + 'px', height: h + 'px' }; + + // return ( + // <div className="czi-table-grid-size-editor" ref={this._onRef}> + // <div + // className="czi-table-grid-size-editor-body" + // onMouseDown={this._onMouseDown} + // onMouseEnter={this._onMouseEnter} + // style={bodyStyle} + // > + // {cells} + // </div> + // <div className="czi-table-grid-size-editor-footer"> + // {rows} X {cols} + // </div> + // </div> + // ); + // } + + // _onRef = (ref: any): void => { + // this._ref = ref; + // }; + + // _onMouseEnter = (e: MouseEvent): void => { + // const node = e.currentTarget; + // if (node instanceof HTMLElement) { + // const rect = fromHTMlElement(node); + // const mx = Math.round(e.clientX); + // const my = Math.round(e.clientY); + // this._ex = rect.x; + // this._ey = rect.y; + // this._mx = mx; + // this._my = my; + // if (!this._entered) { + // this._entered = true; + // document.addEventListener('mousemove', this._onMouseMove, true); + // } + // } + // }; + + // _onMouseMove = (e: MouseEvent): void => { + // const el = this._ref && ReactDOM.findDOMNode(this._ref); + // const elRect = el ? htmlElementToRect(el) : null; + // const mouseRect = fromXY(e.screenX, e.screenY, 10); + + // if (elRect && mouseRect && isIntersected(elRect, mouseRect, 50)) { + // // This prevents `PopUpManager` from collapsing the editor. + // e.preventDefault(); + // e.stopImmediatePropagation(); + // } + + // const mx = Math.round(e.clientX); + // const my = Math.round(e.clientY); + // if (mx !== this._mx || my !== this._my) { + // this._mx = mx; + // this._my = my; + // this._rafID && cancelAnimationFrame(this._rafID); + // this._rafID = requestAnimationFrame(this._updateGridSize); + // } + // }; + + // _updateGridSize = (): void => { + // this._rafID = 0; + // const mx = this._mx; + // const my = this._my; + // const x = mx - this._ex; + // const y = my - this._ey; + // const rr = clamp(1, Math.ceil(y / (CELL_SIZE + GUTTER_SIZE)), MAX_SIZE); + // const cc = clamp(1, Math.ceil(x / (CELL_SIZE + GUTTER_SIZE)), MAX_SIZE); + // const { rows, cols } = this.state; + // if (rows !== rr || cols !== cc) { + // this.setState({ rows: rr, cols: cc }); + // } + // }; + + // _onMouseDown = (e: SyntheticEvent): void => { + // e.preventDefault(); + // this.props.close(this.state); + // }; +}; + +export default TableGridSizeEditor; -- GitLab