diff --git a/README.md b/README.md index 873c499..27fd59a 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Able to render 1Lakh+ input boxes in react, A quick solution for web based sprea ## Getting Started Input data format -``` +```json [ [{value: 1},{value: 1},{value: "a"},{value: "b"},{value: "d"}] ] @@ -48,7 +48,7 @@ npm install react-spread-sheet-excel ![alt text](https://raw.githubusercontent.com/sojinantony01/react-spread-sheet/main/public/images/samplesheet.png) -``` +```js import React, { useRef, useState } from "react"; import Sheet, { SheetRef } from "./lib"; import packageConf from "../package.json"; diff --git a/src/lib/list/index.tsx b/src/lib/list/index.tsx index 44e3a7e..84f20aa 100644 --- a/src/lib/list/index.tsx +++ b/src/lib/list/index.tsx @@ -4,6 +4,7 @@ import Row from "./row"; import { addData, deleteSelectItems, selectAllCells, updateStyles } from "../reducer"; import SheetXAxis from "./sheet-x-axis"; import { generateDummyContent } from "./utils"; +import Tools from "./tools/tools" export interface Props { data?: any[][]; onChange?(i: number, j: number, value: string): void; @@ -13,10 +14,13 @@ export interface Props { headerValues?: string[]; readonly?: boolean; } - +const featureFlag = { + tools: false +} const List = (props: Props) => { const dispatch = useAppDispatch(); const itemLength = useAppSelector((store) => store.list.data.length); + const divRef = useRef(null); const parentDivRef = useRef(null); const [j, setJ] = useState(0); @@ -55,13 +59,41 @@ const List = (props: Props) => { if (e.code === "Backspace") { dispatch(deleteSelectItems()); } else if (e.code === "KeyB" && (e.ctrlKey || e.metaKey)) { - dispatch(updateStyles({ value: { key: "fontWeight", value: "bold" } })); + changeStyle("B"); } else if (e.code === "KeyU" && (e.ctrlKey || e.metaKey)) { - dispatch(updateStyles({ value: { key: "text-decoration", value: "underline" } })); + changeStyle("U"); } else if (e.code === "KeyI" && (e.ctrlKey || e.metaKey)) { - dispatch(updateStyles({ value: { key: "fontStyle", value: "italic" } })); + changeStyle("I"); } } + const getStyle = (key: string, value?:string) => { + switch (key) { + case "B": + return { value: { key: "fontWeight", value: "bold" } }; + case "U": + return { value: { key: "text-decoration", value: "underline" } }; + case "I": + return { value: { key: "fontStyle", value: "italic" } }; + case "ALIGN-LEFT": + return { value: { key: "textAlign", value: "left" } }; + case "ALIGN-CENTER": + return { value: { key: "textAlign", value: "center" } }; + case "ALIGN-RIGHT": + return { value: { key: "textAlign", value: "right" } }; + case "ALIGN-JUSTIFY": + return { value: { key: "textAlign", value: "justify" } }; + case "FONT": + return { value: { key: "fontSize", value: value ? value + "px" : "" }, replace: true }; + case "COLOR": + return { value: { key: "color", value: value }, replace: true }; + case "BACKGROUND": + return { value: { key: "background", value: value }, replace: true }; + } + }; + const changeStyle = (key: string, value?: string) => { + dispatch(updateStyles(getStyle(key, value))); + }; + return (
{ ref={parentDivRef} data-testid="sheet-table" > + {featureFlag.tools && }
{items.length && ( diff --git a/src/lib/list/input.tsx b/src/lib/list/input.tsx index 5e3001f..2c6a81d 100644 --- a/src/lib/list/input.tsx +++ b/src/lib/list/input.tsx @@ -96,12 +96,11 @@ const Input = (props: Prop) => { onKeyDown={keyDown} onMouseMoveCapture={onDrag} onMouseDown={onClick} - className={`${editMode ? "" : "view_mode"} ${selected ? "selected" : ""}`} + className={`input ${editMode ? "" : "view_mode"} ${selected ? "selected" : ""}`} onBlur={() => { setEdit(false); setFocus(false); }} - // onClick={onClick} onDoubleClick={() => setEdit(true)} onChange={change} /> diff --git a/src/lib/list/sheet-x-axis.tsx b/src/lib/list/sheet-x-axis.tsx index 2ec199f..9c8a58c 100644 --- a/src/lib/list/sheet-x-axis.tsx +++ b/src/lib/list/sheet-x-axis.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from "react"; import { useAppSelector, useAppDispatch } from "../store"; import { printToLetter } from "./utils"; -import { selectVerticalCells } from "../reducer"; +import { selectAllCells, selectVerticalCells } from "../reducer"; interface Props { resize?: boolean; headerValues?: string[]; @@ -26,6 +26,9 @@ const SheetXAxis = ({ resize, headerValues }: Props) => { key={`${i}-x-axis`} tabIndex={0} onClick={(e) => { + if(i === 0) { + dispatch(selectAllCells()); + } else dispatch(selectVerticalCells({ j: i - 1, ctrlPressed: e.metaKey || e.ctrlKey })); }} > diff --git a/src/lib/list/tools/tools.tsx b/src/lib/list/tools/tools.tsx new file mode 100644 index 0000000..ce798fb --- /dev/null +++ b/src/lib/list/tools/tools.tsx @@ -0,0 +1,60 @@ +import React from "react"; +import Icons from "../../svg/icons"; +import { useAppSelector } from "../../store"; +let timer: string | number | NodeJS.Timeout | undefined; +const Tools = ({ changeStyle }: {changeStyle: (type:string, val?:string)=> void}) => { + const selectedStyles = useAppSelector((store) => { + const index = store.list.selected[0]; + if (index) { + return store.list.data[index[0]][index[1]].styles || {}; + } + return {}; + }); + const changeStyleWithDebounce = (type: string, val: string) => { + clearTimeout(timer); + timer = setTimeout(() => {changeStyle(type, val)}, 200) + } + return ( +
+ + + + {/* */} + + changeStyle("FONT", e.target.value)} + onKeyDown={(e) => e.stopPropagation()} + /> + + {/* */} + + + + + + changeStyleWithDebounce("COLOR", e.target.value)} + /> + changeStyleWithDebounce("BACKGROUND", e.target.value)} + /> +
+ ); +}; +export default Tools; \ No newline at end of file diff --git a/src/lib/reducer.ts b/src/lib/reducer.ts index 866abd1..07ff389 100644 --- a/src/lib/reducer.ts +++ b/src/lib/reducer.ts @@ -29,6 +29,7 @@ const listSlice = createSlice({ updateStyles(state, action) { let add = true; if ( + !action.payload.replace && state.selected[0] && state.data[state.selected[0][0]][state.selected[0][1]]?.styles?.[action.payload.value.key] === action.payload.value.value @@ -95,8 +96,8 @@ const listSlice = createSlice({ selectCellsDrag(state, action) { const [startRow, startCol] = state.lastSelected || [0, 0]; const [endRow, endCol] = [action.payload.i, action.payload.j]; - const result:Selected[] = []; - if(startRow === endRow && startCol === endCol) { + const result: Selected[] = []; + if (startRow === endRow && startCol === endCol) { return; } // Determine iteration directions @@ -106,13 +107,16 @@ const listSlice = createSlice({ for (let row = startRow; row !== endRow + rowIncrement; row += rowIncrement) { for (let col = startCol; col !== endCol + colIncrement; col += colIncrement) { if (row >= 0 && row < matrix.length && col >= 0 && col < matrix[row].length) { - result.push([row,col]); + result.push([row, col]); } } } - + state.selected = result; - } + }, + updateFont(state, action) { + + }, }, }); diff --git a/src/lib/sheet.css b/src/lib/sheet.css index 25490bb..cb7ed8c 100644 --- a/src/lib/sheet.css +++ b/src/lib/sheet.css @@ -48,7 +48,7 @@ th { overflow: auto; } -.sheet-table input { +.sheet-table .input { margin: 0; box-sizing: border-box; border: none; @@ -86,4 +86,8 @@ th { .sheet-table tr td:first-child { position: sticky; left: -2px; +} + +.font-size-input { + width: 50px; } \ No newline at end of file diff --git a/src/lib/svg/icons.tsx b/src/lib/svg/icons.tsx new file mode 100644 index 0000000..523be01 --- /dev/null +++ b/src/lib/svg/icons.tsx @@ -0,0 +1,66 @@ +const Icons = ({type}: {type: string}) => { + switch (type) { + case "align-left": + return ( + + + + + + + + ); + case "align-right": + return ( + + + + + + + + ); + case "align-center": + return ( + + + + + + + + ); + case "align-justify": + return ( + + + + ); + } + return <> +} +export default Icons; \ No newline at end of file