Skip to content

Commit

Permalink
Merge pull request #47 from walkframe/protection
Browse files Browse the repository at this point in the history
Protection
  • Loading branch information
righ authored Jan 28, 2024
2 parents 1c7cec7 + 0b83d33 commit e28a000
Show file tree
Hide file tree
Showing 26 changed files with 557 additions and 207 deletions.
61 changes: 61 additions & 0 deletions .storybook/examples/basic/protection.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from "react";
import { ComponentStory } from "@storybook/react";
import { generateInitial, GridSheet } from "../../../src";
import * as prevention from "../../../src/lib/prevention";

export default {
title: "Basic",
};

type Props = {
numRows: number;
numCols: number;
defaultWidth: number;
};

const Sheet = ({ numRows, numCols, defaultWidth }: Props) => {
return (
<>
<GridSheet
options={{
headerHeight: 50,
headerWidth: 150,
}}
initial={generateInitial({
cells: {
default: { width: defaultWidth },
4: {
prevention: prevention.DeleteRow,
},
1: {
prevention: prevention.Resize,
style: { backgroundColor: "#eeeeee" },
},
"A:B": {
prevention: prevention.AddCol | prevention.DeleteCol,
style: { backgroundColor: "#dddddd" },
},
A: {
prevention: prevention.Resize,
style: { backgroundColor: "#eeeeee" },
},
C: {
style: {backgroundColor: "#ffffff"}
},
B2: {
value: "READONLY",
prevention: prevention.ReadOnly,
style: { backgroundColor: "#aaaaaa" },
}
},
ensured: { numRows, numCols },
})}
/>
</>
);
};

const Template: ComponentStory<typeof Sheet> = (args) => <Sheet {...args} />;

export const Prevention = Template.bind({});
Prevention.args = { numRows: 50, numCols: 20, defaultWidth: 50 };
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- Context menu
- External data manipulation
- Autofill
- Protection

## Installation

Expand Down Expand Up @@ -82,5 +83,8 @@ $ npm install @gridsheet/react-core --save
- 0.12.x
- Renamed from https://www.npmjs.com/package/react-gridsheet

- 0.13.x
- Support Protection.

## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwalkframe%2Freact-gridsheet.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwalkframe%2Freact-gridsheet?ref=badge_large)
9 changes: 4 additions & 5 deletions src/components/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ export const Cell: React.FC<Props> = React.memo(
editingCell,
choosing,
selectingZone,
headerTopSelecting,
headerLeftSelecting,
verticalHeaderSelecting,
horizontalheaderSelecting,
copyingZone,
cutting,
searchQuery,
matchingCells,
matchingCellIndex,
Expand Down Expand Up @@ -179,11 +178,11 @@ export const Cell: React.FC<Props> = React.memo(
}
return false;
}
if (headerTopSelecting) {
if (verticalHeaderSelecting) {
dispatch(drag({ y: table.getNumRows(), x }));
return false;
}
if (headerLeftSelecting) {
if (horizontalheaderSelecting) {
dispatch(drag({ y, x: table.getNumCols() }));
return false;
}
Expand Down
94 changes: 58 additions & 36 deletions src/components/ContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { areaToZone, zoneShape, zoneToArea } from "../lib/structs";

import { Context } from "../store";
import * as prevention from "../lib/prevention";

export const ContextMenu: React.FC = () => {
const { store, dispatch } = React.useContext(Context);
Expand All @@ -21,8 +22,8 @@ export const ContextMenu: React.FC = () => {
table,
choosing,
selectingZone,
headerTopSelecting,
headerLeftSelecting,
verticalHeaderSelecting,
horizontalheaderSelecting,
editorRef,
contextMenuPosition,
} = store;
Expand All @@ -49,6 +50,10 @@ export const ContextMenu: React.FC = () => {
if (top === -1) {
return null;
}
const selectingTopCell = table.getByPoint({ y: selectingTop, x: 0 });
const selectingLeftCell = table.getByPoint({ y: 0, x: selectingLeft });
const selectingBottomCell = table.getByPoint({ y: selectingBottom, x: 0 });
const selectingRightCell = table.getByPoint({ y: 0, x: selectingRight });
const historyIndex = table.getHistoryIndex();

return (
Expand All @@ -69,7 +74,7 @@ export const ContextMenu: React.FC = () => {
>
<ul>
<li
className="enabled"
className="gs-enabled"
onClick={() => {
const area = clip(store);
dispatch(cut(areaToZone(area)));
Expand All @@ -81,7 +86,7 @@ export const ContextMenu: React.FC = () => {
</div>
</li>
<li
className="enabled"
className="gs-enabled"
onClick={() => {
const area = clip(store);
dispatch(copy(areaToZone(area)));
Expand All @@ -93,7 +98,7 @@ export const ContextMenu: React.FC = () => {
</div>
</li>
<li
className="enabled"
className="gs-enabled"
onClick={async () => {
const text = editorRef.current?.value || "";
dispatch(paste({ text }));
Expand All @@ -107,13 +112,13 @@ export const ContextMenu: React.FC = () => {

<li className="gs-menu-divider" />

{!headerTopSelecting && (
{!verticalHeaderSelecting && (
<li
className={
table.maxNumRows !== -1 &&
tableHeight + height > table.maxNumRows
? "disabled"
: "enabled"
(table.maxNumRows !== -1 && tableHeight + height > table.maxNumRows) ||
prevention.isPrevented(selectingTopCell?.prevention, prevention.AddRowAbove)
? "gs-disabled"
: "gs-enabled"
}
onClick={(e) => {
const newTable = table.addRows({
Expand All @@ -133,15 +138,18 @@ export const ContextMenu: React.FC = () => {
</div>
</li>
)}
{!headerTopSelecting && (
{!verticalHeaderSelecting && (
<li
className={
table.maxNumRows !== -1 &&
tableHeight + height > table.maxNumRows
? "disabled"
: "enabled"
(table.maxNumRows !== -1 && tableHeight + height > table.maxNumRows) ||
prevention.isPrevented(selectingBottomCell?.prevention, prevention.AddRowBelow)
? "gs-disabled"
: "gs-enabled"
}
onClick={(e) => {
if (e.currentTarget.classList.contains("gs-disabled")) {
return;
}
selectingZone.startY += height;
selectingZone.endY += height;
choosing.y += height;
Expand All @@ -163,14 +171,18 @@ export const ContextMenu: React.FC = () => {
</li>
)}

{!headerLeftSelecting && (
{!horizontalheaderSelecting && (
<li
className={
table.maxNumCols !== -1 && tableWidth + width > table.maxNumCols
? "disabled"
: "enabled"
(table.maxNumCols !== -1 && tableWidth + width > table.maxNumCols) ||
prevention.isPrevented(selectingLeftCell?.prevention, prevention.AddColLeft)
? "gs-disabled"
: "gs-enabled"
}
onClick={(e) => {
if (e.currentTarget.classList.contains("gs-disabled")) {
return;
}
const newTable = table.addCols({
x: selectingLeft,
numCols: width,
Expand All @@ -188,14 +200,18 @@ export const ContextMenu: React.FC = () => {
</div>
</li>
)}
{!headerLeftSelecting && (
{!horizontalheaderSelecting && (
<li
className={
table.maxNumCols !== -1 && tableWidth + width > table.maxNumCols
? "disabled"
: "enabled"
(table.maxNumCols !== -1 && tableWidth + width > table.maxNumCols) ||
prevention.isPrevented(selectingRightCell?.prevention, prevention.AddColRight)
? "gs-disabled"
: "gs-enabled"
}
onClick={(e) => {
if (e.currentTarget.classList.contains("gs-disabled")) {
return;
}
selectingZone.startX += width;
selectingZone.endX += width;
choosing.x += width;
Expand All @@ -217,18 +233,22 @@ export const ContextMenu: React.FC = () => {
</li>
)}

{!headerTopSelecting && (
{!verticalHeaderSelecting && (
<li
className={
table.minNumRows !== -1 &&
tableHeight - height < table.minNumRows
? "disabled"
: "enabled"
(table.minNumRows !== -1 && tableHeight - height < table.minNumRows) ||
prevention.isPrevented(selectingTopCell?.prevention, prevention.DeleteRow)
? "gs-disabled"
: "gs-enabled"
}
onClick={(e) => {
const newTable = table.removeRows({
if (e.currentTarget.classList.contains("gs-disabled")) {
return;
}
const newTable = table.deleteRows({
y: selectingTop,
numRows: height,
operator: "USER",
reflection: {
selectingZone,
choosing,
Expand All @@ -238,22 +258,24 @@ export const ContextMenu: React.FC = () => {
}}
>
<div className="gs-menu-name">
Remove {height} row{height > 0 && "s"}
Delete {height} row{height > 0 && "s"}
</div>
</li>
)}

{!headerLeftSelecting && (
{!horizontalheaderSelecting && (
<li
className={
table.minNumCols !== -1 && tableWidth - width < table.minNumCols
? "disabled"
: "enabled"
(table.minNumCols !== -1 && tableWidth - width < table.minNumCols) ||
prevention.isPrevented(selectingRightCell?.prevention, prevention.DeleteCol)
? "gs-disabled"
: "gs-enabled"
}
onClick={(e) => {
const newTable = table.removeCols({
const newTable = table.deleteCols({
x: selectingLeft,
numCols: width,
operator: "USER",
reflection: {
selectingZone,
choosing,
Expand All @@ -263,7 +285,7 @@ export const ContextMenu: React.FC = () => {
}}
>
<div className="gs-menu-name">
Remove {width} column{width > 0 && "s"}
Delete {width} column{width > 0 && "s"}
</div>
</li>
)}
Expand Down
9 changes: 9 additions & 0 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { Context } from "../store";
import { areaToZone } from "../lib/structs";
import {DEFAULT_HEIGHT} from "../constants";
import * as prevention from "../lib/prevention";


export const Editor: React.FC = () => {
Expand Down Expand Up @@ -310,6 +311,10 @@ export const Editor: React.FC = () => {
if (e.ctrlKey || e.metaKey) {
return false;
}
if (prevention.isPrevented(cell?.prevention, prevention.Write)) {
console.warn("This cell is protected from writing.");
return false;
}
dispatch(setEditingCell(address));
return false;
};
Expand All @@ -334,6 +339,10 @@ export const Editor: React.FC = () => {
e.currentTarget.value = "";
}}
onDoubleClick={(e) => {
if (prevention.isPrevented(cell?.prevention, prevention.Write)) {
console.warn("This cell is protected from writing.");
return;
}
const input = e.currentTarget;
if (!editing) {
input.value = table.stringify({ y, x }, value);
Expand Down
4 changes: 2 additions & 2 deletions src/components/GridSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export const GridSheet: React.FC<Props> = ({
selectingZone: { startY: -1, startX: -1, endY: -1, endX: -1 },
copyingZone: { startY: -1, startX: -1, endY: -1, endX: -1 },
autofillDraggingTo: null,
headerTopSelecting: false,
headerLeftSelecting: false,
verticalHeaderSelecting: false,
horizontalheaderSelecting: false,
editingCell: "",
editorRect: { y: 0, x: 0, height: 0, width: 0 },
resizingRect: { y: -1, x: -1, height: -1, width: -1 },
Expand Down
Loading

0 comments on commit e28a000

Please sign in to comment.