├── examples ├── src │ ├── react-app-env.d.ts │ ├── components │ │ ├── FixedSizeHolder.css │ │ ├── EventLog.css │ │ ├── FixedSizeHolder.tsx │ │ ├── ControlsForm.css │ │ ├── EventLog.tsx │ │ └── ControlsForm.tsx │ ├── assets │ │ ├── autofill.png │ │ ├── inline-edit.png │ │ ├── custom-render.png │ │ ├── frozen-rows-cols.png │ │ ├── selection-range.png │ │ ├── feature-icon-gradient.svg │ │ ├── structure.svg │ │ ├── rocket-launch-lines.svg │ │ ├── logo.svg │ │ └── react-js.svg │ ├── index.tsx │ ├── examples │ │ ├── ExamplePage.css │ │ ├── Everything.css │ │ ├── SmallGrid.text.tsx │ │ ├── CustomText.text.tsx │ │ ├── FocusColumn.text.tsx │ │ ├── CustomTitle.text.tsx │ │ ├── FrozenCells.text.tsx │ │ ├── CustomBackground.text.tsx │ │ ├── Simple.grid.tsx │ │ ├── Index.tsx │ │ ├── FrozenCells.grid.tsx │ │ ├── SelectionEvents.text.tsx │ │ ├── SmallGrid.grid.tsx │ │ ├── Simple.text.tsx │ │ ├── CustomTitle.grid.tsx │ │ ├── CustomBackground.grid.tsx │ │ ├── KeyboardEvents.text.tsx │ │ ├── CustomText.grid.tsx │ │ ├── Resize.text.tsx │ │ ├── EditEvents.text.tsx │ │ ├── DynamicData.text.tsx │ │ ├── Autofill.data.ts │ │ ├── Resize.grid.tsx │ │ ├── KeyboardEvents.grid.tsx │ │ ├── Editable.text.tsx │ │ ├── EditEvents.grid.tsx │ │ ├── Autofill.text.tsx │ │ ├── Editable.grid.tsx │ │ ├── SelectionEvents.grid.tsx │ │ ├── ExamplePage.tsx │ │ ├── FocusColumn.grid.tsx │ │ └── Autofill.grid.tsx │ ├── App.css │ ├── index.css │ ├── App.tsx │ ├── data │ │ └── dataAndColumns.ts │ ├── Examples.tsx │ ├── Examples.css │ └── Home.css ├── .env ├── .gitignore ├── tsconfig.json ├── package.json └── public │ └── index.html ├── .gitignore ├── cypress.json ├── setupTests.js ├── jest.config.js ├── cypress ├── snapshots │ ├── small-grid.tsx │ │ └── small-grid.snap.png │ ├── autofill.tsx │ │ ├── autofill-complete.snap.png │ │ ├── autofill-drag-outline.snap.png │ │ ├── multi-autofill-handle.snap.png │ │ ├── single-autofill-handle.snap.png │ │ ├── autofill-hover-highlight.snap.png │ │ ├── none-no-autofill-handle.snap.png │ │ └── single-multi-no-autofill-handle.snap.png │ ├── scrollbars.tsx │ │ ├── scrollbar-hover.snap.png │ │ ├── scrollbar-drag-off-bar.snap.png │ │ └── scrollbar-release-drag-off-bar.snap.png │ ├── edit-inline.tsx │ │ ├── editing-updates-grid.snap.png │ │ ├── inline-editor-shown-on-dblclick.snap.png │ │ └── inline-editor-arrows-dont-change-selection.snap.png │ ├── custom-rendering.tsx │ │ ├── custom-render-text.snap.png │ │ └── custom-render-background.snap.png │ ├── focused-columns.tsx │ │ ├── focused-col-to-left.snap.png │ │ ├── focused-col-to-right.snap.png │ │ └── focused-col-to-left-with-frozen-cols.snap.png │ ├── data-resizing.tsx │ │ ├── reduce-number-of-columns.snap.png │ │ ├── increase-number-of-columns.snap.png │ │ ├── clear-selection-when-cols-change.snap.png │ │ ├── truncate-scroll-when-shrinking-data.snap.png │ │ ├── clear-selection-after-col-num-change.snap.png │ │ ├── clear-selection-when-num-rows-changes.snap.png │ │ └── keep-selection-when-only-data-values-change.snap.png │ ├── selection-highlight.tsx │ │ ├── simple-grid-drag-up.snap.png │ │ ├── simple-grid-drag-down.snap.png │ │ ├── simple-grid-drag-left.snap.png │ │ ├── simple-grid-after-click.snap.png │ │ ├── simple-grid-drag-right.snap.png │ │ ├── simple-grid-drag-release-move.snap.png │ │ ├── simple-grid-drag-down-and-right.snap.png │ │ ├── simple-grid-drag-right-and-release.snap.png │ │ ├── simple-grid-after-click-then-scroll.snap.png │ │ └── selection-highlight-click-shift-click.snap.png │ ├── simple-rendering.tsx │ │ ├── simple-grid-in-scroll.snap.png │ │ └── scrolled-grid-in-scroll.snap.png │ ├── grid-resizing.tsx │ │ └── resize-grid-small-to-large.snap.png │ └── frozen-cells.tsx │ │ └── scrolled-grid-with-frozen-cells.snap.png ├── support │ ├── imageSnapshot.d.ts │ ├── index.js │ └── commands.js ├── integration │ ├── small-grid.tsx │ ├── grid-resizing.tsx │ ├── frozen-cells.tsx │ ├── simple-rendering.tsx │ ├── custom-rendering.tsx │ ├── keyboard-events.tsx │ ├── focused-columns.tsx │ ├── scrollbars.tsx │ ├── edit-inline.tsx │ ├── edit-events.tsx │ ├── data-resizing.tsx │ └── autofill.tsx ├── tsconfig.json ├── plugins │ └── index.js └── data │ └── dataAndColumns.ts ├── tslint.json ├── webpack.config.js ├── src ├── index.ts ├── selectionState │ ├── selectionState.ts │ ├── selectionTypes.ts │ ├── focusOffset.ts │ ├── selectionStateFactory.ts │ ├── noSelection.ts │ ├── allGridSelection.test.ts │ ├── allGridSelection.ts │ ├── colSelection.test.ts │ ├── rowsSelection.test.ts │ └── cellsSelectionBuilder.ts ├── rafTestHelper.ts ├── baseGridOffsetRenderer.ts ├── utils.ts ├── eventHandlers │ ├── scrolling.ts │ ├── mouseCellAndRegionCalc.ts │ ├── scrolling.test.ts │ ├── keyboardEvents.ts │ ├── scrollingTimer.ts │ ├── autofillMouseEvents.ts │ ├── scrollbarMouseEvents.ts │ └── mouseEvents.ts ├── HighlightedGridCanvas.tsx ├── cellRenderer.ts ├── FrozenCanvas.tsx ├── FrozenCornerCanvas.tsx ├── scrollbars │ ├── ScrollbarCanvas.tsx │ ├── cornerScrollbarRenderer.ts │ ├── baseScrollbarRenderer.ts │ ├── verticalScrollbarRenderer.ts │ ├── horizontalScrollbarRenderer.ts │ ├── VerticalScrollbarCanvas.tsx │ ├── HorizontalScrollbarCanvas.tsx │ └── CornerScrollbarCanvas.tsx ├── InlineEditor.tsx ├── commonCanvasRenderer.test.ts ├── MainCanvas.tsx ├── commonCanvasRenderer.ts ├── FrozenRowsCanvas.tsx ├── autofill.ts ├── FrozenColsCanvas.tsx ├── GridCanvas.test.tsx ├── types.ts ├── HighlightCanvas.tsx ├── ReactCanvasGrid.test.tsx ├── GridCanvas.tsx └── autofill.test.ts ├── tsconfig.json ├── README.md └── package.json /examples/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | __diff_output__ 4 | coverage 5 | cypress/screenshots -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "video": false, 3 | "baseUrl": "http://localhost:3000" 4 | } -------------------------------------------------------------------------------- /examples/src/components/FixedSizeHolder.css: -------------------------------------------------------------------------------- 1 | .fixed-size-holder { 2 | width: 500px; 3 | height: 400px; 4 | } -------------------------------------------------------------------------------- /examples/src/assets/autofill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/examples/src/assets/autofill.png -------------------------------------------------------------------------------- /examples/src/assets/inline-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/examples/src/assets/inline-edit.png -------------------------------------------------------------------------------- /examples/src/assets/custom-render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/examples/src/assets/custom-render.png -------------------------------------------------------------------------------- /examples/src/assets/frozen-rows-cols.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/examples/src/assets/frozen-rows-cols.png -------------------------------------------------------------------------------- /examples/src/assets/selection-range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/examples/src/assets/selection-range.png -------------------------------------------------------------------------------- /examples/src/components/EventLog.css: -------------------------------------------------------------------------------- 1 | .event-log { 2 | width: 100%; 3 | height: 6em; 4 | font-family: 'Courier New', Courier, monospace; 5 | } -------------------------------------------------------------------------------- /setupTests.js: -------------------------------------------------------------------------------- 1 | var enzyme = require('enzyme'); 2 | var Adapter = require('enzyme-adapter-react-16'); 3 | 4 | enzyme.configure({ adapter: new Adapter() }); -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'jsdom', 4 | setupFilesAfterEnv: ["setupTests.js"] 5 | }; -------------------------------------------------------------------------------- /cypress/snapshots/small-grid.tsx/small-grid.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/small-grid.tsx/small-grid.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/autofill-complete.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/autofill-complete.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/scrollbars.tsx/scrollbar-hover.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/scrollbars.tsx/scrollbar-hover.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/autofill-drag-outline.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/autofill-drag-outline.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/multi-autofill-handle.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/multi-autofill-handle.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/single-autofill-handle.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/single-autofill-handle.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/autofill-hover-highlight.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/autofill-hover-highlight.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/none-no-autofill-handle.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/none-no-autofill-handle.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/edit-inline.tsx/editing-updates-grid.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/edit-inline.tsx/editing-updates-grid.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/scrollbars.tsx/scrollbar-drag-off-bar.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/scrollbars.tsx/scrollbar-drag-off-bar.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/custom-rendering.tsx/custom-render-text.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/custom-rendering.tsx/custom-render-text.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/focused-columns.tsx/focused-col-to-left.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/focused-columns.tsx/focused-col-to-left.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/focused-columns.tsx/focused-col-to-right.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/focused-columns.tsx/focused-col-to-right.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/reduce-number-of-columns.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/reduce-number-of-columns.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-up.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-up.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/simple-rendering.tsx/simple-grid-in-scroll.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/simple-rendering.tsx/simple-grid-in-scroll.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/autofill.tsx/single-multi-no-autofill-handle.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/autofill.tsx/single-multi-no-autofill-handle.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/custom-rendering.tsx/custom-render-background.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/custom-rendering.tsx/custom-render-background.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/increase-number-of-columns.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/increase-number-of-columns.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/grid-resizing.tsx/resize-grid-small-to-large.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/grid-resizing.tsx/resize-grid-small-to-large.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/scrollbars.tsx/scrollbar-release-drag-off-bar.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/scrollbars.tsx/scrollbar-release-drag-off-bar.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-down.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-down.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-left.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-left.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/simple-rendering.tsx/scrolled-grid-in-scroll.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/simple-rendering.tsx/scrolled-grid-in-scroll.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/edit-inline.tsx/inline-editor-shown-on-dblclick.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/edit-inline.tsx/inline-editor-shown-on-dblclick.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/frozen-cells.tsx/scrolled-grid-with-frozen-cells.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/frozen-cells.tsx/scrolled-grid-with-frozen-cells.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-after-click.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-after-click.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-right.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-right.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/clear-selection-when-cols-change.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/clear-selection-when-cols-change.snap.png -------------------------------------------------------------------------------- /examples/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/truncate-scroll-when-shrinking-data.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/truncate-scroll-when-shrinking-data.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-release-move.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-release-move.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/clear-selection-after-col-num-change.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/clear-selection-after-col-num-change.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/clear-selection-when-num-rows-changes.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/clear-selection-when-num-rows-changes.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-down-and-right.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-down-and-right.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/edit-inline.tsx/inline-editor-arrows-dont-change-selection.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/edit-inline.tsx/inline-editor-arrows-dont-change-selection.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/focused-columns.tsx/focused-col-to-left-with-frozen-cols.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/focused-columns.tsx/focused-col-to-left-with-frozen-cols.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-drag-right-and-release.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-drag-right-and-release.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/simple-grid-after-click-then-scroll.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/simple-grid-after-click-then-scroll.snap.png -------------------------------------------------------------------------------- /cypress/support/imageSnapshot.d.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable-next-line:no-namespace 2 | declare namespace Cypress { 3 | interface Chainable { 4 | matchImageSnapshot: (name?: string) => Chainable; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/src/examples/ExamplePage.css: -------------------------------------------------------------------------------- 1 | button.code-toggle { 2 | margin-top: 2em; 3 | font-size: 0.8em; 4 | text-transform: uppercase; 5 | } 6 | 7 | .code-example { 8 | padding: 1em; 9 | margin-top: 2em; 10 | } -------------------------------------------------------------------------------- /cypress/snapshots/data-resizing.tsx/keep-selection-when-only-data-values-change.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/data-resizing.tsx/keep-selection-when-only-data-values-change.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/selection-highlight.tsx/selection-highlight-click-shift-click.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanhill/react-canvas-grid/HEAD/cypress/snapshots/selection-highlight.tsx/selection-highlight-click-shift-click.snap.png -------------------------------------------------------------------------------- /examples/.env: -------------------------------------------------------------------------------- 1 | # The package.json in the parent folder may hold conflicting versions of dependencies, 2 | # which create-react-app reports as an error / source of subtle bugs. It doesn't seem 3 | # to be a problem in practice, however, so the following disables that check. 4 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /examples/src/components/FixedSizeHolder.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import './FixedSizeHolder.css'; 3 | 4 | export const FixedSizeHolder = (props: {children?: React.ReactNode}) => { 5 | return ( 6 |
7 | {props.children} 8 |
9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /examples/src/examples/Everything.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-header { 6 | background-color: #282c34; 7 | min-height: 10vh; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | justify-content: center; 12 | font-size: calc(10px + 2vmin); 13 | color: white; 14 | } 15 | -------------------------------------------------------------------------------- /cypress/integration/small-grid.tsx: -------------------------------------------------------------------------------- 1 | describe('ReactCanvasGrid with very little data', () => { 2 | beforeEach(() => { 3 | cy.visit('/#/examples/small'); 4 | }); 5 | 6 | it('renders a grid of data', () => { 7 | cy.get('div.react-canvas-grid > canvas:first-of-type').matchImageSnapshot('small-grid'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "interface-name": [true, "never-prefix"], 9 | "quotemark": [true, "single", "jsx-double"], 10 | "object-literal-sort-keys": false 11 | }, 12 | "rulesDirectory": [] 13 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resolve: { 3 | extensions: ['.ts', '.tsx', '.js'], 4 | }, 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.tsx?$/, 9 | exclude: [/node_modules/, /examples/], 10 | use: [{ 11 | loader: 'ts-loader', 12 | }], 13 | }, 14 | ], 15 | }, 16 | } -------------------------------------------------------------------------------- /examples/src/examples/SmallGrid.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const SmallGridText = () => { 4 | return ( 5 | <> 6 |

Small Grid

7 |

8 | This is a minimal usage of ReactCanvasGrid: a small, read-only grid of 9 | static values. 10 |

11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "baseUrl": "../node_modules", 5 | "target": "es5", 6 | "lib": ["es5", "dom"], 7 | "types": ["cypress"], 8 | "jsx": "react", 9 | "esModuleInterop": true, 10 | }, 11 | "include": [ 12 | "**/*.ts", "**/*.tsx" 13 | ], 14 | "exclude": [ 15 | "node_modules" 16 | ] 17 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactCanvasGrid, ReactCanvasGridProps, CellDataChangeEvent } from './ReactCanvasGrid'; 2 | export { SelectRange } from './selectionState/selectionTypes'; 3 | export { 4 | CellDef, CustomDrawCallbackMetadata, DataRow, ColumnDef, Coord, Size, 5 | getCellText, cellHasTextFunction, cellIsEditable, 6 | } from './types'; 7 | export { AutofillContext, repeatSelectionIntoFill } from './autofill'; 8 | -------------------------------------------------------------------------------- /src/selectionState/selectionState.ts: -------------------------------------------------------------------------------- 1 | import { GridState } from '../gridState'; 2 | import { Coord } from '../types'; 3 | 4 | export abstract class BaseSelectionState { 5 | public readonly isSelectionInProgress: boolean; 6 | 7 | public abstract getFocusGridOffset: (gridState: GridState) => Coord | null; 8 | 9 | constructor( 10 | isSelectionInProgress: boolean, 11 | ) { 12 | this.isSelectionInProgress = isSelectionInProgress; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/selectionState/selectionTypes.ts: -------------------------------------------------------------------------------- 1 | import { Coord } from '../types'; 2 | 3 | export type GridClickRegion = 'frozen-rows' | 'frozen-cols' | 'frozen-corner' | 'cells'; 4 | 5 | export interface ClickMeta { 6 | region: GridClickRegion; 7 | } 8 | 9 | export interface CellCoordBounds { 10 | frozenRows: number; 11 | frozenCols: number; 12 | numRows: number; 13 | numCols: number; 14 | } 15 | 16 | export interface SelectRange { 17 | topLeft: Coord; 18 | bottomRight: Coord; 19 | } 20 | -------------------------------------------------------------------------------- /examples/src/assets/feature-icon-gradient.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # Ignore the .grid.tsx files in public/examples - they're copied over at build time 26 | /public/examples/*.grid.tsx -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "jsx": "react", 6 | "declaration": true, 7 | "outDir": "./dist", 8 | "strict": true, 9 | "types": [ // cypress includes types with global definitions which conflict with jest, so we limit to jest here 10 | "jest" 11 | ], 12 | "esModuleInterop": true 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "dist", 17 | "examples", 18 | "cypress", 19 | "**/*.test.tsx", 20 | "**/*.test.ts", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/src/components/ControlsForm.css: -------------------------------------------------------------------------------- 1 | form.controls { 2 | display: inline-block; 3 | margin-bottom: 1em; 4 | padding: 0.2em; 5 | background-color: hsl(220, 75%, 90%); 6 | border: solid 1px hsl(220, 100%, 40%); 7 | border-radius: 2px; 8 | } 9 | 10 | form.controls .inline-controls-group { 11 | margin-right: 1em; 12 | } 13 | form.controls .inline-controls-group:last-of-type { 14 | margin-right: 0; 15 | } 16 | 17 | form.controls input[type="number"] { 18 | width: 4em; 19 | } 20 | 21 | form.controls label { 22 | margin-right: 0.5em; 23 | } -------------------------------------------------------------------------------- /examples/src/examples/CustomText.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const CustomTextText = () => { 4 | return ( 5 | <> 6 |

Custom Text Renderer

7 |

8 | When specifying a cell definition, you can provide a renderText function 9 | to customise drawing the cell's text. 10 |

11 |

12 | Here, all cells use the same text renderer in order to draw the text as red. 13 |

14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/rafTestHelper.ts: -------------------------------------------------------------------------------- 1 | const rafCallbacks: FrameRequestCallback[] = []; 2 | 3 | let rafId = 0; 4 | 5 | export function execRaf() { 6 | const cb = rafCallbacks.shift(); 7 | if (cb) { 8 | cb(performance.now() + 7); 9 | } 10 | } 11 | 12 | export function mockRaf() { 13 | jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { 14 | rafCallbacks.push(cb); 15 | rafId++; 16 | return rafId; 17 | }); 18 | } 19 | 20 | export function resetRaf() { 21 | (window.requestAnimationFrame as jest.Mock).mockRestore(); 22 | } 23 | -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /examples/src/examples/FocusColumn.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const FocusColumnText = () => { 4 | return ( 5 | <> 6 |

Focused Columns

7 |

8 | Updates to the focusedColIndex cause the grid to automatically scroll 9 | to ensure the indicated column is displayed. The scrolling behaviour is aware of 10 | frozen columns. 11 |

12 |

13 | This can be useful for building a 'search' feature. 14 |

15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /examples/src/examples/CustomTitle.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const CustomTitleText = () => { 4 | return ( 5 | <> 6 |

Custom Title Text

7 |

8 | When specifying a cell definition, you can provide either a getTitle function 9 | or a title property to specify the title text shown when hovering over that cell. 10 |

11 |

12 | The title text is displayed via the browser's native title text mechanism. 13 |

14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /examples/src/examples/FrozenCells.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const FrozenCellsText = () => { 4 | return ( 5 | <> 6 |

Frozen Rows & Columns

7 |

8 | By setting the frozenRows and frozenCols props, rows 9 | and columns of cells can be 'frozen' - i.e. fixed in place, even as the rest of 10 | the grid scrolls. 11 |

12 |

13 | This can be useful for creating column or row headers. 14 |

15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /examples/src/examples/CustomBackground.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const CustomBackgroundText = () => { 4 | return ( 5 | <> 6 |

Custom Background Renderer

7 |

8 | When specifying a cell definition, you can provide a renderBackground function 9 | to customise drawing the cell's background. 10 |

11 |

12 | Here, all cells use the same background renderer in order to draw the background as 13 | light green. 14 |

15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /examples/src/examples/Simple.grid.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ReactCanvasGrid } from 'react-canvas-grid'; 3 | import { FixedSizeHolder } from '../components/FixedSizeHolder'; 4 | import { createFakeDataAndColumns } from '../data/dataAndColumns'; 5 | 6 | export const SimpleGrid = () => { 7 | const { columns, rows: data } = createFakeDataAndColumns(100, 20, () => {/* no op */}); 8 | 9 | return ( 10 | 11 | 12 | columns={columns} 13 | data={data} 14 | rowHeight={20} 15 | /> 16 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /examples/src/examples/Index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import exampleMeta from './exampleMeta'; 4 | 5 | export const Index = () => { 6 | return ( 7 | 8 |

Examples

9 |
    10 | {exampleMeta.map((meta) => ( 11 |
  • 12 |

    {meta.name}

    13 |

    {meta.description}

    14 |
  • 15 | ))} 16 |
17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /cypress/integration/grid-resizing.tsx: -------------------------------------------------------------------------------- 1 | describe('ReactCanvasGrid with resizable cssWidth / cssHeight', () => { 2 | beforeEach(() => { 3 | cy.visit('/#/examples/resize'); 4 | cy.get('.react-canvas-grid').as('Root'); 5 | cy.get('.react-canvas-grid canvas').eq(1).as('Canvas'); 6 | 7 | cy.get('select').as('SizeSelect'); 8 | 9 | cy.get('Canvas').invoke('width').should('be.greaterThan', 0); 10 | }); 11 | 12 | it('redraws when cssWidth / cssHeight are increased', () => { 13 | cy.get('@SizeSelect').select('big'); 14 | 15 | cy.get('@Canvas') 16 | .matchImageSnapshot('resize-grid-small-to-large'); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/src/App.css: -------------------------------------------------------------------------------- 1 | .app-container { 2 | display: flex; 3 | flex: 1; 4 | flex-direction: column; 5 | } 6 | 7 | nav.top-nav { 8 | height: 3em; 9 | line-height: 3em; 10 | display: inline-flex; 11 | flex-direction: row; 12 | background-color: var(--primary-colour); 13 | } 14 | 15 | .top-nav .top-nav-item { 16 | margin-left: 1em; 17 | } 18 | 19 | .top-nav .top-nav-item a { 20 | color: white; 21 | } 22 | .top-nav .top-nav-item a.active { 23 | color: var(--secondary-colour); 24 | } 25 | .top-nav .top-nav-item a:hover { 26 | filter: drop-shadow(0 3px 3px rgba(0, 0, 0, 0.3)); 27 | } 28 | 29 | .top-nav .logo-svg { 30 | height: 100%; 31 | padding: 5px; 32 | } -------------------------------------------------------------------------------- /src/baseGridOffsetRenderer.ts: -------------------------------------------------------------------------------- 1 | import { CommonCanvasRenderer } from './commonCanvasRenderer'; 2 | import { Coord } from './types'; 3 | 4 | export interface CanvasRendererPosition { 5 | gridOffset: Coord; 6 | visibleRect: ClientRect; 7 | } 8 | 9 | const defaultPosProps = { 10 | gridOffset: { x: 0, y: 0 }, 11 | visibleRect: { left: 0, top: 0, right: 0, bottom: 0, height: 0, width: 0 }, 12 | }; 13 | 14 | export class BaseGridOffsetRenderer extends CommonCanvasRenderer { 15 | protected posProps: CanvasRendererPosition = defaultPosProps; 16 | 17 | public translate() { 18 | this.context.translate(-this.posProps.gridOffset.x, -this.posProps.gridOffset.y); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cypress/integration/frozen-cells.tsx: -------------------------------------------------------------------------------- 1 | describe('ReactCanvasGrid with frozen rows & cells', () => { 2 | beforeEach(() => { 3 | cy.visit('/#/examples/frozen'); 4 | cy.get('.fixed-size-holder').as('Holder'); 5 | cy.get('.fixed-size-holder .react-canvas-grid').as('Root'); 6 | cy.get('.fixed-size-holder canvas').eq(1).as('Canvas'); 7 | 8 | cy.get('Canvas').invoke('width').should('be.greaterThan', 0); 9 | }); 10 | 11 | it('keeps the frozen rows and columns shown on the grid (and fixes the top-left cells in place)', () => { 12 | cy.get('@Root') 13 | .trigger('wheel', { deltaX: 300, deltaY: 300 }) 14 | .matchImageSnapshot('scrolled-grid-with-frozen-cells'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /examples/src/examples/FrozenCells.grid.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ReactCanvasGrid } from 'react-canvas-grid'; 3 | import { FixedSizeHolder } from '../components/FixedSizeHolder'; 4 | import { createFakeDataAndColumns } from '../data/dataAndColumns'; 5 | 6 | export const FrozenCellsGrid = () => { 7 | const { columns, rows: data } = createFakeDataAndColumns(100, 20, () => {/* no op */}); 8 | 9 | return ( 10 | 11 | 12 | columns={columns} 13 | data={data} 14 | rowHeight={20} 15 | frozenRows={1} 16 | frozenCols={1} 17 | /> 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /examples/src/examples/SelectionEvents.text.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export const SelectionEventsText = () => { 4 | return ( 5 | <> 6 |

Selection Events

7 |

8 | The three callbacks onSelectionChange[Start|Update|End] allow consumers 9 | of react-canvas-grid to take action in response to the user changing the selected area. 10 | This can be useful for keeping track of the selection, in order to act upon the data. 11 |

12 |

13 | Note that clicking / dragging on frozen headers allows the user to select entire rows / columns. 14 |

15 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /src/selectionState/focusOffset.ts: -------------------------------------------------------------------------------- 1 | import { GridGeometry } from '../gridGeometry'; 2 | import { GridState } from '../gridState'; 3 | import { Coord } from '../types'; 4 | 5 | export function calculateGridOffsetForTargetCell(gridState: GridState, focusCell: Coord) { 6 | return GridGeometry.calculateGridOffsetForTargetCell( 7 | gridState.gridOffset(), 8 | gridState.canvasSize(), 9 | gridState.frozenColsWidth(), 10 | gridState.frozenRowsHeight(), 11 | focusCell, 12 | gridState.columnBoundaries(), 13 | gridState.rowHeight(), 14 | gridState.borderWidth(), 15 | gridState.data().length, 16 | gridState.verticalGutterBounds(), 17 | gridState.horizontalGutterBounds(), 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /examples/src/components/EventLog.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import './EventLog.css'; 3 | 4 | interface EventLogProps { 5 | log: string; 6 | } 7 | 8 | export class EventLog extends React.PureComponent { 9 | private logRef: React.RefObject; 10 | 11 | constructor(props: EventLogProps) { 12 | super(props); 13 | this.logRef = React.createRef(); 14 | } 15 | 16 | public render() { 17 | return (