= (props) => {
const {
- id,
rowSum,
onMouseEnterPercent,
onMouseLeavePercent
@@ -20,7 +19,6 @@ const TableCellSum = (props) => {
return (
|
@@ -30,6 +28,4 @@ const TableCellSum = (props) => {
)
}
-TableCellSum.propTypes = TableCellSumShape.isRequired
-
export default React.memo(TableCellSum, areEqual)
diff --git a/src/components/TableRow/TableRow.jsx b/src/components/TableRow/TableRow.tsx
similarity index 62%
rename from src/components/TableRow/TableRow.jsx
rename to src/components/TableRow/TableRow.tsx
index 131d22f..57fffc6 100644
--- a/src/components/TableRow/TableRow.jsx
+++ b/src/components/TableRow/TableRow.tsx
@@ -1,30 +1,26 @@
import React, { useState } from 'react'
-// import { v4 as uuidv4 } from 'uuid'
-import { TableRowShape } from '../../helpers/shapes'
+import { TableRowProps } from '../../helpers/interface'
import TableCell from '../TableCell/TableCell'
import TableCellSum from '../TableCellSum/TableCellSum'
-const areEqual = (prevProps, nextProps) => {
- const { row, cells, lightArray } = prevProps
+const areEqual = (prevProps: TableRowProps, nextProps: TableRowProps): boolean => {
+ const { row, cells, lightList } = prevProps
- for (const id of row) {
- if (nextProps.cells[id].amount !== cells[id].amount) {
- return false
- }
- if (nextProps.lightArray[id] !== lightArray[id]) {
- return false
- }
- }
- return true
+ const equal = !row.some((id) => (
+ nextProps.cells[id].amount !== cells[id].amount ||
+ nextProps.lightList[id] !== lightList[id]
+ ))
+
+ return equal
}
-const TableRow = (props) => {
+const TableRow: React.FC = (props) => {
const {
id,
row,
cells,
rowSum,
- lightArray,
+ lightList,
onMouseEnterHandler,
onMouseLeaveHandler,
onClickIncrement
@@ -45,21 +41,21 @@ const TableRow = (props) => {
id={id}
>
{row.map(cellId => {
- let value = cells[cellId].amount
+ const value = cells[cellId].amount
+ const percent = (cells[cellId].amount / rowSum * 100).toFixed(2)
let styleString = ''
- if (lightArray[cellId]) { styleString += '#6c757d' }
+ if (lightList[cellId]) { styleString += '#6c757d' }
if (showPercent) {
- value = (cells[cellId].amount / rowSum * 100).toFixed(2)
- styleString += `linear-gradient(90deg, rgba(220,53,69,1) ${value}%, rgba(108,117,125,1) ${value}%)`
+ styleString += `linear-gradient(90deg, rgba(220,53,69,1) ${percent}%, rgba(108,117,125,1) ${percent}%)`
}
return (
{
)
}
-TableRow.propTypes = TableRowShape.isRequired
-
export default React.memo(TableRow, areEqual)
diff --git a/src/helpers/generate.js b/src/helpers/generate.js
deleted file mode 100644
index 2a4dc14..0000000
--- a/src/helpers/generate.js
+++ /dev/null
@@ -1,25 +0,0 @@
-import { v4 as uuidv4 } from 'uuid'
-
-export const generateTable = (rowsAmount, columnsAmount) => {
- const table = []
- const rows = {}
- const cells = {}
-
- for (let i = 0; i < rowsAmount; i++) {
- const rowId = uuidv4()
- table[i] = rowId
- rows[rowId] = []
-
- for (let j = 0; j < columnsAmount; j++) {
- const cellId = uuidv4()
- rows[rowId][j] = cellId
-
- cells[cellId] = {
- id: cellId,
- amount: Math.floor(Math.random() * 999)
- }
- }
- }
-
- return { table, rows, cells }
-}
diff --git a/src/helpers/generate.tsx b/src/helpers/generate.tsx
new file mode 100644
index 0000000..a17e858
--- /dev/null
+++ b/src/helpers/generate.tsx
@@ -0,0 +1,32 @@
+import { v4 as uuidv4 } from 'uuid'
+import { RowsParams, CellsParams } from '../store/actions'
+
+export const generateTable = (rowsAmount: string | number, columnsAmount: string | number) => {
+ const table: Array = new Array(+rowsAmount).fill(0).map((item: string) => item = uuidv4())
+
+ const preparedArray: Array = new Array(+rowsAmount * +columnsAmount).fill(0).map((item: string) => item = uuidv4())
+
+ const rows = table.reduce(
+ (acum: RowsParams, rowID, index) => (
+ (acum[rowID] = preparedArray.slice(
+ +columnsAmount * index,
+ +columnsAmount * index + +columnsAmount
+ )),
+ acum
+ ),
+ {}
+ )
+
+ const cells = preparedArray.reduce(
+ (acum: CellsParams, cellID: string) => (
+ (acum[cellID] = {
+ id: cellID,
+ amount: Math.floor(Math.random() * 999)
+ }),
+ acum
+ ),
+ {}
+ );
+
+ return { table, rows, cells };
+}
diff --git a/src/helpers/getLightClosest.js b/src/helpers/getLightClosest.js
deleted file mode 100644
index c4c08e5..0000000
--- a/src/helpers/getLightClosest.js
+++ /dev/null
@@ -1,16 +0,0 @@
-export const getLightClosest = (id, cells, lightsAmount) => {
- const arr = Object.values(cells).map(item => ({
- ...item,
- difference: Math.abs(item.amount - cells[id].amount)
- })).sort((a, b) => a.difference - b.difference)
- .slice(0, Number(lightsAmount))
- .map(item => item.id)
-
- const obj = {}
-
- for (const id of arr) {
- obj[id] = true
- }
-
- return obj
-}
diff --git a/src/helpers/getLightClosest.tsx b/src/helpers/getLightClosest.tsx
new file mode 100644
index 0000000..6a95e52
--- /dev/null
+++ b/src/helpers/getLightClosest.tsx
@@ -0,0 +1,14 @@
+import { CellsParams } from '../store/actions'
+
+export const getLightClosest = (id: string, cells: CellsParams, lightsAmount: string | number) => {
+ const arr = Object.values(cells).map(item => ({
+ ...item,
+ difference: Math.abs(item.amount - cells[id].amount)
+ })).sort((a, b) => a.difference - b.difference)
+ .slice(0, +lightsAmount)
+ .map(item => item.id)
+
+ const resObj: { [name: string]: boolean } = arr.reduce((prev, cur) => ( { ...prev, [cur]: true } ), {});
+
+ return resObj
+}
diff --git a/src/helpers/interface.tsx b/src/helpers/interface.tsx
new file mode 100644
index 0000000..009f710
--- /dev/null
+++ b/src/helpers/interface.tsx
@@ -0,0 +1,27 @@
+import { SyntheticEvent } from 'react';
+
+export interface TableRowProps {
+ id: string
+ row: Array
+ cells: { [name: string]: { id: string, amount: number } }
+ lightList: { [name: string]: boolean }
+ rowSum: number
+ onMouseEnterHandler: (event: SyntheticEvent) => void
+ onMouseLeaveHandler: () => void
+ onClickIncrement: (event: SyntheticEvent) => void
+}
+
+export interface TableCellProps {
+ id: string
+ amount: string | number
+ styleString: string
+ onMouseEnterHandler: (event: SyntheticEvent) => void
+ onMouseLeaveHandler: () => void
+ onClickIncrement: (event: SyntheticEvent) => void
+}
+
+export interface TableCellSumProps {
+ rowSum: number
+ onMouseEnterPercent: () => void
+ onMouseLeavePercent: () => void
+}
diff --git a/src/helpers/selectors.js b/src/helpers/selectors.js
deleted file mode 100644
index 8ae5bf0..0000000
--- a/src/helpers/selectors.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { createSelector } from 'reselect'
-
-export const getRows = state => state.rows
-
-export const getCells = state => state.cells
-
-export const getColumns = state => state.columns
-
-export const getTable = state => state.table
-
-export const getRowsAmount = state => state.params.rowsAmount
-
-export const getColumnsAmount = state => state.params.columnsAmount
-
-export const getLightsAmount = state => state.params.lightsAmount
-
-export const getRowSum = createSelector(
- [getRows, getCells],
- (rows, cells) => {
- const sumArr = Object.values(rows).map(row => (
- row.map(item => cells[item].amount).reduce((prev, cur) => prev + cur, 0)
- ))
-
- return sumArr
- }
-)
-
-export const getAverageRowSum = createSelector(
- [getRows, getCells],
- (rows, cells) => {
- const onlyAmount = Object.values(rows).map(row => (
- row.map(item => cells[item].amount)
- ))
-
- const res = []
- for (let i = 0; i < onlyAmount.length; i++) {
- for (let j = 0; j < onlyAmount[i].length; j++) {
- res[j] = (res[j] || 0) + Math.floor(onlyAmount[i][j] / onlyAmount.length)
- }
- }
-
- return res
- }
-)
diff --git a/src/helpers/selectors.tsx b/src/helpers/selectors.tsx
new file mode 100644
index 0000000..0c0fd08
--- /dev/null
+++ b/src/helpers/selectors.tsx
@@ -0,0 +1,41 @@
+import { createSelector } from 'reselect'
+import type { State } from '../store/actions'
+
+export const getRows = (state: State) => state.rows
+
+export const getCells = (state: State) => state.cells
+
+export const getTable = (state: State) => state.table
+
+export const getRowsAmount = (state: State) => state.params.rowsAmount
+
+export const getColumnsAmount = (state: State) => state.params.columnsAmount
+
+export const getLightsAmount = (state: State) => state.params.lightsAmount
+
+export const getShowButtons = (state: State) => state.buttons
+
+export const getRowSum = createSelector(
+ [getRows, getCells],
+ (rows, cells) => {
+ const sumArr = Object.values(rows).map(row => (
+ row.map(item => cells[item].amount).reduce((prev, cur) => prev + cur, 0)
+ ))
+
+ return sumArr
+ }
+)
+
+export const getAverageRowSum = createSelector(
+ [getRows, getCells, getColumnsAmount],
+ (rows, cells, columnsAmount) => {
+ const onlyAmount = Object.values(rows).map(row => (
+ row.map(item => cells[item].amount)
+ ))
+
+ const averageRow = onlyAmount.reduce((acum, cur) =>
+ cur.map((amount, index) => (acum[index] += amount)), new Array(+columnsAmount).fill(0)
+ )
+ return averageRow.map((item: number) => Math.floor(item / onlyAmount.length))
+ }
+)
diff --git a/src/helpers/shapes.js b/src/helpers/shapes.js
deleted file mode 100644
index b5f819c..0000000
--- a/src/helpers/shapes.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import PropTypes from 'prop-types'
-
-export const TableShape = PropTypes.shape({
- rows: PropTypes.number,
- columns: PropTypes.number,
- table: PropTypes.array,
- sumRowArray: PropTypes.number,
- lightsAmount: PropTypes.number
-})
-
-export const TableCellShape = PropTypes.shape({
- id: PropTypes.number,
- cell: PropTypes.number,
- amount: PropTypes.number,
- isLight: PropTypes.bool,
- showPercent: PropTypes.bool,
- percent: PropTypes.number,
- incrementCell: PropTypes.func,
- lightArray: PropTypes.array,
- onMouseEnterHandler: PropTypes.func,
- onMouseLeaveHandler: PropTypes.func
-})
-
-export const TableCellSumShape = PropTypes.shape({
- id: PropTypes.number,
- rowSum: PropTypes.number,
- onMouseEnterPercent: PropTypes.func,
- onMouseLeavePercent: PropTypes.func
-})
-
-export const TableRowShape = PropTypes.shape({
- id: PropTypes.number,
- row: PropTypes.array,
- cells: PropTypes.array,
- rowSum: PropTypes.number,
- lightArray: PropTypes.array,
- onMouseEnterHandler: PropTypes.func,
- onMouseLeaveHandler: PropTypes.func
-})
-
-export const AverageRowShape = PropTypes.shape({
- averageArray: PropTypes.array
-})
-
-export const FormShape = PropTypes.shape({
- setParamsData: PropTypes.func,
- setTableData: PropTypes.func,
- setRowsData: PropTypes.func,
- setCellsData: PropTypes.func,
- setShowButtonsBoll: PropTypes.func
-})
-
-export const ButtonsShape = PropTypes.shape({
- showButtons: PropTypes.bool,
- rows: PropTypes.number,
- columns: PropTypes.number,
- table: PropTypes.array
-})
-
-export const ButtonDeleteShape = PropTypes.shape({
- deleteRow: PropTypes.func
-})
-
-export const ButtonAddShape = PropTypes.shape({
- addRow: PropTypes.func
-})
diff --git a/src/index.jsx b/src/index.tsx
similarity index 100%
rename from src/index.jsx
rename to src/index.tsx
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
new file mode 100644
index 0000000..ece12df
--- /dev/null
+++ b/src/react-app-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/src/store/actions.js b/src/store/actions.js
deleted file mode 100644
index 6886ede..0000000
--- a/src/store/actions.js
+++ /dev/null
@@ -1,50 +0,0 @@
-export const ACTION_TYPES = {
- SET_PARAMS: 'SET::PARAMS',
- SET_TABLE: 'SET::TABLE',
- SET_ROWS: 'SET::ROWS',
- SET_CELLS: 'SET::CELLS',
- SHOW_BUTTONS: 'SHOW::BUTTONS',
- INCREMENT: 'INCREMENT',
- ADD_ROW: 'ADD_ROW',
- REMOVE_ROW: 'REMOVE::ROW'
-}
-
-export const setParams = params => ({
- payload: params,
- type: ACTION_TYPES.SET_PARAMS
-})
-
-export const setTable = params => ({
- payload: params,
- type: ACTION_TYPES.SET_TABLE
-})
-
-export const setRows = params => ({
- payload: params,
- type: ACTION_TYPES.SET_ROWS
-})
-
-export const setCells = params => ({
- payload: params,
- type: ACTION_TYPES.SET_CELLS
-})
-
-export const setShowButtons = params => ({
- payload: params,
- type: ACTION_TYPES.SHOW_BUTTONS
-})
-
-export const increment = params => ({
- type: ACTION_TYPES.INCREMENT,
- payload: params
-})
-
-export const setNewRow = params => ({
- type: ACTION_TYPES.ADD_ROW,
- payload: params
-})
-
-export const removeRow = params => ({
- type: ACTION_TYPES.REMOVE_ROW,
- payload: params
-})
diff --git a/src/store/actions.tsx b/src/store/actions.tsx
new file mode 100644
index 0000000..f60666e
--- /dev/null
+++ b/src/store/actions.tsx
@@ -0,0 +1,92 @@
+export const ACTION_TYPES = {
+ SET_PARAMS: 'SET::PARAMS',
+ SET_TABLE: 'SET::TABLE',
+ SET_ROWS: 'SET::ROWS',
+ SET_CELLS: 'SET::CELLS',
+ SHOW_BUTTONS: 'SHOW::BUTTONS',
+ INCREMENT: 'INCREMENT',
+ ADD_ROW: 'ADD::ROW',
+ REMOVE_ROW: 'REMOVE::ROW'
+}
+
+// TYPES
+
+export type State = {
+ table: Array
+ rows: RowsParams
+ cells: CellsParams
+ params: Params
+ buttons: boolean
+}
+
+export type Action = (
+ | ReturnType
+ | ReturnType
+ | ReturnType
+ | ReturnType
+ | ReturnType
+ | ReturnType
+ | ReturnType
+ | ReturnType
+)
+
+export type Params = {
+ [name: string]: string | number
+}
+
+export type RowsParams = {
+ [name: string]: Array
+}
+
+export type CellsParams = {
+ [name: string]: { id: string, amount: number }
+}
+
+
+export type NewRowsParams = {
+ table: Array
+ rows: RowsParams
+ cells: CellsParams
+}
+
+// ACTIONS
+
+export const setParams = (params: Params) => ({
+ payload: params,
+ type: ACTION_TYPES.SET_PARAMS
+})
+
+export const setTable = (params: Array) => ({
+ payload: params,
+ type: ACTION_TYPES.SET_TABLE
+})
+
+export const setRows = (params: RowsParams) => ({
+ payload: params,
+ type: ACTION_TYPES.SET_ROWS
+})
+
+export const setCells = (params: CellsParams) => ({
+ payload: params,
+ type: ACTION_TYPES.SET_CELLS
+})
+
+export const setShowButtons = (params: boolean) => ({
+ payload: params,
+ type: ACTION_TYPES.SHOW_BUTTONS
+})
+
+export const increment = (params: string) => ({
+ payload: params,
+ type: ACTION_TYPES.INCREMENT
+})
+
+export const setNewRow = (params: NewRowsParams) => ({
+ payload: params,
+ type: ACTION_TYPES.ADD_ROW
+})
+
+export const removeRow = (params: any) => ({
+ payload: params,
+ type: ACTION_TYPES.REMOVE_ROW
+})
diff --git a/src/store/reducers.js b/src/store/reducers.tsx
similarity index 58%
rename from src/store/reducers.js
rename to src/store/reducers.tsx
index 1885a30..0f056d0 100644
--- a/src/store/reducers.js
+++ b/src/store/reducers.tsx
@@ -1,7 +1,8 @@
import { combineReducers } from 'redux'
import { ACTION_TYPES } from './actions'
+import { Action, Params, RowsParams, CellsParams } from './actions'
-const paramsReducer = (state = {}, action) => {
+const paramsReducer = (state: Params = {}, action: Action): Params => {
const { SET_PARAMS } = ACTION_TYPES
switch (action.type) {
@@ -12,7 +13,7 @@ const paramsReducer = (state = {}, action) => {
}
}
-function tableReducer (state = [], action) {
+function tableReducer (state: Array = [], action: Action): Array {
const { SET_TABLE, ADD_ROW, REMOVE_ROW } = ACTION_TYPES
switch (action.type) {
@@ -21,13 +22,13 @@ function tableReducer (state = [], action) {
case ADD_ROW:
return [...state, action.payload.table]
case REMOVE_ROW:
- return state.filter(id => id !== action.payload.lastIndex)
+ return state.filter(id => id !== action.payload.lastRowKey)
default:
return state
}
}
-const rowsReducer = (state = {}, action) => {
+const rowsReducer = (state: RowsParams = {}, action: Action): RowsParams => {
const { SET_ROWS, ADD_ROW, REMOVE_ROW } = ACTION_TYPES
switch (action.type) {
@@ -36,21 +37,31 @@ const rowsReducer = (state = {}, action) => {
case ADD_ROW:
return { ...state, ...action.payload.rows }
case REMOVE_ROW:
- delete state[action.payload.lastIndex]
+ delete state[action.payload.lastRowKey]
return state
default:
return state
}
}
-const cellsReducer = (state = {}, action) => {
- const { SET_CELLS, INCREMENT, ADD_ROW } = ACTION_TYPES
+const cellsReducer = (state: CellsParams = {}, action: Action): CellsParams => {
+ const { SET_CELLS, INCREMENT, ADD_ROW, REMOVE_ROW } = ACTION_TYPES
switch (action.type) {
case SET_CELLS:
return action.payload
case ADD_ROW:
return { ...state, ...action.payload.cells }
+ case REMOVE_ROW:
+ const { columnsAmount } = action.payload
+ const notDeleted = Object.keys(state).slice(0, -columnsAmount)
+
+ const res = Object.values(state).filter((cellsItem, index) => (
+ cellsItem.id === notDeleted[index]
+ ))
+ const resObj: CellsParams = {}
+ res.map((item: { id: string, amount: number }) => resObj[item.id] = { ...item })
+ return resObj
case INCREMENT:
return {
...state,
@@ -64,7 +75,7 @@ const cellsReducer = (state = {}, action) => {
}
}
-const buttonsReducer = (state = false, action) => {
+const buttonsReducer = (state: boolean = false, action: Action): boolean => {
const { SHOW_BUTTONS } = ACTION_TYPES
switch (action.type) {
diff --git a/src/store/store.js b/src/store/store.js
deleted file mode 100644
index 1e38b93..0000000
--- a/src/store/store.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { createStore } from 'redux'
-import { rootReducer } from './reducers'
-
-const store = createStore(
- rootReducer,
- window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
-)
-
-export default store
diff --git a/src/store/store.tsx b/src/store/store.tsx
new file mode 100644
index 0000000..134f616
--- /dev/null
+++ b/src/store/store.tsx
@@ -0,0 +1,6 @@
+import { createStore } from 'redux'
+import { rootReducer } from './reducers'
+
+const store = createStore(rootReducer)
+
+export default store
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9a05724
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react"
+ },
+ "include": [
+ "src"
+ ]
+}
|