Skip to content

Commit

Permalink
migrated to typescript
Browse files Browse the repository at this point in the history
WARNING: broken build atm because eslint-typescript is broken / out of date in create-react-app (facebook/create-react-app#9583)
  • Loading branch information
JaninaWibker committed Sep 6, 2020
1 parent 6145a45 commit 38e5568
Show file tree
Hide file tree
Showing 18 changed files with 2,259 additions and 3,471 deletions.
4,591 changes: 1,590 additions & 3,001 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@
"private": true,
"dependencies": {
"@types/jest": "^26.0.13",
"@types/moo": "^0.5.3",
"@types/nearley": "^2.11.1",
"@types/node": "^14.6.4",
"@types/react": "^16.9.49",
"@types/react-dom": "^16.9.8",
"moo": "^0.5.1",
"nearley": "^2.19.1",
"nearley": "^2.19.6",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "^3.4.1",
"react-scripts": "^3.4.3",
"typescript": "^4.0.2"
},
"scripts": {
Expand Down
37 changes: 18 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { Component } from 'react'
import SpreadsheetComp from './components/Spreadsheet.js'
import SpreadsheetComp from './components/SpreadsheetComp'
import './css/index.css'

import type { Spreadsheet } from './types/Spreadsheet'
import { CellType } from './types/CellTypes'

import { /*range, createCell, createEmptyCell, createStringCell, createNumberCell, createRow, createCol, createTable,*/ fillTableEmpty, fillTableIds, CELL_TYPE } from './util/helpers.js'
import parse_file from './util/file-parser.js'
import { /*range, createCell, createEmptyCell, createStringCell, createNumberCell, createRow, createCol, createTable,*/ fillTableEmpty, fillTableIds } from './util/helpers'
import parse_file from './util/file-parser'

const file_demo_spreadsheet = parse_file(`
---
Expand All @@ -18,7 +19,7 @@ index_cell.height: 25
[{ "tp": "S", "vl": "**test2**", "style": { "fontFamily": "Menlo" } }, { "tp": "N", "vl": 5, "name": "thisIsSomeName" } ]
[{ "tp": "S", "vl": "\`123\`: blub" }, { "tp": "N", "vl": "=A1:A1" } ]
[ { "tp": "S", "vl": "=pi" }, { "tp": "N", "vl": "=thisIsSomeName" } ]
[ { "tp": "E" }, { "tp": "S", "vl": "=IF(B1 > 5, \\"true\\", \\"false\\")", "name": "blub" } ]`)
[ { "tp": "E" }, { "tp": "S", "vl": "=IF(B1 > 5, \\"true\\", \\"false\\")", "name": "blub" } ]`, 'file_demo_spreadsheet')

const demo_spreadsheet: Spreadsheet = {
options: {
Expand All @@ -30,16 +31,16 @@ const demo_spreadsheet: Spreadsheet = {
},
// data: createTable(0, 0, 26, 28, createStringCell([null, null], "")),
data: fillTableIds(4, 2, fillTableEmpty(4, 2, [
[{tp: CELL_TYPE.STRING, vl: '**test**', style: {fontFamily: 'Menlo'}}, {tp: CELL_TYPE.NUMBER, vl: 5, name: 'thisIsSomeName'}],
[{tp: CELL_TYPE.STRING, vl: '`123`: blub'}, {tp: CELL_TYPE.NUMBER, vl: '=A1:A1'}],
[{tp: CELL_TYPE.STRING, vl: '=pi'}],
[{tp: CELL_TYPE.EMPTY}, {tp: CELL_TYPE.STRING, vl: '=IF(B1 > 5, "true", "false")', name: 'blub'}],
[{tp: CellType.STRING, vl: '**test**', style: {fontFamily: 'Menlo'}}, {tp: CellType.NUMBER, vl: 5, name: 'thisIsSomeName'}],
[{tp: CellType.STRING, vl: '`123`: blub'}, {tp: CellType.NUMBER, vl: '=A1:A1'}],
[{tp: CellType.STRING, vl: '=pi'}],
[{tp: CellType.EMPTY, vl: ''}, {tp: CellType.STRING, vl: '=IF(B1 > 5, "true", "false")', name: 'blub'}],
])),
// data: fillTableIds(4, 2, fillTableEmpty(4, 2, [
// [{tp: CELL_TYPE.NUMBER, vl: 5}, {tp: CELL_TYPE.NUMBER, vl: '=A1'}],
// [{tp: CELL_TYPE.NUMBER, vl: '=B1'}, {tp: CELL_TYPE.NUMBER, vl: '=B3'}],
// [{tp: CELL_TYPE.NUMBER, vl: '=A3'}, {tp: CELL_TYPE.NUMBER, vl: 0}],
// [{tp: CELL_TYPE.NUMBER, vl: 0}, {tp: CELL_TYPE.STRING, vl: '=IF(B1 > 5, "true", "false")', name: 'blub'}],
// [{tp: CellType.NUMBER, vl: 5}, {tp: CellType.NUMBER, vl: '=A1'}],
// [{tp: CellType.NUMBER, vl: '=B1'}, {tp: CellType.NUMBER, vl: '=B3'}],
// [{tp: CellType.NUMBER, vl: '=A3'}, {tp: CellType.NUMBER, vl: 0}],
// [{tp: CellType.NUMBER, vl: 0}, {tp: CellType.STRING, vl: '=IF(B1 > 5, "true", "false")', name: 'blub'}],
// ])),
name: 'demo_spreadsheet'
}
Expand All @@ -49,7 +50,7 @@ interface IProps {
}

interface IState {
spreadsheet: any,
spreadsheet: Spreadsheet,
update?: any,
cb: any
}
Expand All @@ -71,12 +72,10 @@ class App extends Component<IProps, IState> {
}
}

loadSpreadsheet(options: any, data: any, name: any) {
console.log({
spreadsheet: { options, data, name }
})
loadSpreadsheet(spreadsheet: Spreadsheet) {
console.log(spreadsheet)
this.setState({
spreadsheet: { options, data, name }
spreadsheet: spreadsheet
})
}

Expand All @@ -90,7 +89,7 @@ class App extends Component<IProps, IState> {
name={this.state.spreadsheet.name}
cb={this.state.cb} />
</div>
<button onClick={() => this.loadSpreadsheet(file_demo_spreadsheet.options, file_demo_spreadsheet.data, 'file_demo_spreadsheet')}>load other spreadsheet</button>
<button onClick={() => this.loadSpreadsheet(file_demo_spreadsheet)}>load other spreadsheet</button>
</div>
)
}
Expand Down
33 changes: 27 additions & 6 deletions src/components/Cell.js → src/components/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
import React from 'react'
import Editable from './Editable.js'
import Editable from './Editable'
import '../css/cell.css'

import { CELL_TYPE } from '../util/helpers.js'
import { CellType } from '../types/CellTypes'

type BorderCellProps = {
id: string, content: string, className: string
}
type CellProps = {
id: string,
content: number | string,
style: object,
editable: boolean,
onValueChange: (value: string) => any,
onMouseEvent: (event: React.MouseEvent<HTMLDivElement, MouseEvent>, id: string) => any,
onArrowKeyEvent: (key: "ArrowLeft" | "ArrowUp" | "ArrowRight" | "ArrowDown", shift: boolean, alt: boolean, ctrl: boolean, preventDefault: () => any) => any,
raw_data: any,
tp: CellType,
isFocused: boolean
}

type GenericCellProps = CellProps & BorderCellProps & {
isBorder: boolean
}

// TODO: make clicks on border cells select the whole row / column (maybe some special interaction with holding shift as well?)
const BorderCell = ({id, content, className}) => (
const BorderCell = ({id, content, className}: BorderCellProps) => (
<th
className={'border-cell border' + (className ? ' ' + className : '')}
onMouseDown={e => console.log(e.target, id)}
Expand All @@ -15,7 +36,7 @@ const BorderCell = ({id, content, className}) => (
</th>
)

const Cell = ({id, content, style, editable=false, onValueChange, onMouseEvent, onArrowKeyEvent, raw_data, tp, isFocused=false}) => (
const Cell = ({id, content, style, editable=false, onValueChange, onMouseEvent, onArrowKeyEvent, raw_data, tp, isFocused=false}: CellProps) => (
<td id={id}>
<div
onMouseDown={e => onMouseEvent ? onMouseEvent(e, id) : null}
Expand All @@ -25,7 +46,7 @@ const Cell = ({id, content, style, editable=false, onValueChange, onMouseEvent,
{!editable
? <span>{String(content)}</span>
: <Editable
setInnerHTML={tp === CELL_TYPE.STRING}
setInnerHTML={tp === CellType.STRING}
onArrowKeyEvent={onArrowKeyEvent}
raw_data={String(raw_data)}
cb={onValueChange}
Expand All @@ -37,7 +58,7 @@ const Cell = ({id, content, style, editable=false, onValueChange, onMouseEvent,
</td>
)

const GenericCell = ({id, content, className, style, editable, isBorder=false, onValueChange, onMouseEvent, onArrowKeyEvent, raw_data, tp, isFocused}) =>
const GenericCell = ({id, content, className, style, editable, isBorder=false, onValueChange, onMouseEvent, onArrowKeyEvent, raw_data, tp, isFocused}: GenericCellProps) =>
isBorder
? (<BorderCell id={id} content={content} className={className} />)
: (<Cell id={id} content={content} style={style} editable={editable} onValueChange={onValueChange} onMouseEvent={onMouseEvent} onArrowKeyEvent={onArrowKeyEvent} raw_data={raw_data} tp={tp} isFocused={isFocused} />)
Expand Down
54 changes: 37 additions & 17 deletions src/components/Editable.js → src/components/Editable.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
import React, { Component } from 'react'
import '../css/editable.css'

export default class Editable extends Component {
constructor(props) {
interface IProps {
raw_data: string,
text?: string,
pretty_text?: string,
setInnerHTML: boolean,
isFocused: boolean, // TODO: this is not being used?
cb: (value: string) => any,
onArrowKeyEvent?: (key: "ArrowLeft" | "ArrowUp" | "ArrowRight" | "ArrowDown", shift: boolean, alt: boolean, ctrl: boolean, preventDefault: () => any) => any
}

interface IState {
editing: boolean,
old_text: string,
text: string,
pretty_text: string
}

export default class Editable extends Component<IProps, IState> {

el: HTMLInputElement | null = null

constructor(props: Readonly<IProps> & Readonly<{ children?: React.ReactNode }>) {
super(props)

this.state = {
editing: false,
old_text: this.props.raw_data || this.props.text || this.props.children,
text: this.props.raw_data || this.props.text || this.props.children,
pretty_text: this.props.text || this.props.children
old_text: this.props.raw_data || this.props.text || this.props.children!.toString(),
text: this.props.raw_data || this.props.text || this.props.children!.toString(),
pretty_text: this.props.text || this.props.children!.toString()
}


Expand Down Expand Up @@ -40,10 +60,10 @@ export default class Editable extends Component {
// }
// }

UNSAFE_componentWillReceiveProps(nextProps) { // TODO: replace componentWillReceiveProps with getDerivedStateFromProps (https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607)
UNSAFE_componentWillReceiveProps(nextProps: Readonly<IProps> & Readonly<{ children?: React.ReactNode }>) { // TODO: replace componentWillReceiveProps with getDerivedStateFromProps (https://hackernoon.com/replacing-componentwillreceiveprops-with-getderivedstatefromprops-c3956f7ce607)
// console.log(nextProps)
//console.log(nextProps, this.state)
const newText = nextProps.raw_data || nextProps.text || nextProps.children
const newText = nextProps.raw_data || nextProps.text || nextProps.children!.toString()
// console.log('idk', newText, nextProps, this.state)
if(newText !== this.state.text || this.state.editing || nextProps.pretty_text !== this.state.pretty_text) {
console.log('text has changed: ', newText, this.state.text)
// console.log({
Expand All @@ -54,15 +74,15 @@ export default class Editable extends Component {
this.setState({
old_text: newText,
text: this.state.editing ? this.state.text : newText,
pretty_text: nextProps.text || nextProps.children
pretty_text: nextProps.text || nextProps.children!.toString()
})
}
}

startEdit(e) {
startEdit(_e?: React.MouseEvent<HTMLSpanElement, MouseEvent>) {
this.setState({editing: true, old_text: this.state.text}, () => {
this.el.focus()
this.el.setSelectionRange(0, this.el.value.length)
this.el!.focus()
this.el!.setSelectionRange(0, this.el!.value.length)
})
}

Expand All @@ -75,22 +95,22 @@ export default class Editable extends Component {
this.setState({editing: false, text: this.state.old_text})
}

onKeyDown(e) {
onKeyDown(e: React.KeyboardEvent<HTMLInputElement> & { target: HTMLInputElement }) {
if(e.target.nodeName === 'INPUT') {
if(e.key === 'Enter' || e.key === 'Tab') this.finishEdit()
else if(e.key === 'Escape') this.cancelEdit()
} else if(e.target.nodeName === 'SPAN') {
if((e.key === 'ArrowLeft' || e.key === 'ArrowUp' || e.key === 'ArrowRight' || e.key === 'ArrowDown') && this.props.onArrowKeyEvent) {
this.props.onArrowKeyEvent(e.key, e.shiftKey, e.altKey, e.ctrlKey || e.metaKey, e.preventDefault.bind(e))
} else if(e.key === 'Backspace') {
this.setState({text: '', pretty_text: '', old_text: ''}, x => this.props.cb(''))
this.setState({text: '', pretty_text: '', old_text: ''}, () => this.props.cb(''))
} else if(e.altKey || e.metaKey || e.shiftKey) {
// nothing
} else this.startEdit()
}
}

onChange(e) {
onChange(e: React.ChangeEvent<HTMLInputElement> & { target: HTMLInputElement }) {
this.setState({text: e.target.value.trim()})
}

Expand All @@ -105,12 +125,12 @@ export default class Editable extends Component {
ref={el => this.el = el} />
: this.props.setInnerHTML
? <span
tabIndex="0"
tabIndex={0}
onKeyDown={this.onKeyDown}
onDoubleClick={this.startEdit}
dangerouslySetInnerHTML={{__html: this.state.pretty_text}} />
: <span
tabIndex="0"
tabIndex={0}
onKeyDown={this.onKeyDown}
onDoubleClick={this.startEdit}>
{this.state.pretty_text}
Expand Down
14 changes: 13 additions & 1 deletion src/components/Selection.js → src/components/Selection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import React from 'react'

const Selection = ({start, end, ref, constants: {cell_width, cell_height, index_cell_width, index_cell_height}}) => (
type SelectionProps = {
start: {x: number, y: number},
end: {x: number, y: number},
ref?: (instance: HTMLDivElement | null) => void,
constants: {
cell_width: number,
cell_height: number,
index_cell_width: number,
index_cell_height: number
}
}

const Selection = ({start, end, ref, constants: {cell_width, cell_height, index_cell_width, index_cell_height}}: SelectionProps) => (
<div className="selection" ref={ref} style={{
width: ((Math.abs(start.x - end.x) * cell_width) + cell_width) + 'px',
height: ((Math.abs(start.y - end.y) * cell_height) + cell_height) + 'px',
Expand Down
Loading

0 comments on commit 38e5568

Please sign in to comment.