Added click on cell event

Added selection change event
This commit is contained in:
Eugene 2023-07-26 14:29:51 +03:00
parent 9dd64b9a77
commit 8596220e85
6 changed files with 175 additions and 39 deletions

View File

@ -1,4 +1,6 @@
import Spreadsheet, { CSS_PREFIX } from "../main";
import { EventTypes } from "../modules/events";
import { checkEqualCellSelections } from "../utils/position";
export interface ViewportRect {
top: number;
@ -12,7 +14,6 @@ export class Scroller {
private verticalScroller: HTMLDivElement;
private horizontalScroller: HTMLDivElement;
private root: Spreadsheet;
private isSelecting = false;
constructor(root: Spreadsheet) {
@ -41,29 +42,46 @@ export class Scroller {
this.element.addEventListener("keydown", this.handleKeydown);
}
public setSelectingMode(mode: boolean) {
this.isSelecting = mode
}
private handleMouseMove = (event: MouseEvent) => {
if (!this.isSelecting) return;
const { offsetX, offsetY } = event;
const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY);
let isRangeChanged = false
if (this.root.selection.selectedRange) {
isRangeChanged = !checkEqualCellSelections(this.root.selection.selectedRange.to, lastSelectedCell)
if (isRangeChanged) {
this.root.selection.selectedRange.to = lastSelectedCell;
this.root.events.dispatch({
type: EventTypes.CHANGE_SELECTION,
selection: this.root.selection,
enableCallback: true
})
}
this.root.renderSheet();
this.root.renderColumnsBar();
this.root.renderRowsBar();
}
};
private handleMouseUp = () => {
this.isSelecting = false;
const newSelection = {...this.root.selection}
if (this.root.selection.selectedRange) {
if (
this.root.selection.selectedRange.from.row ===
this.root.selection.selectedRange.to.row &&
this.root.selection.selectedRange.from.column ===
this.root.selection.selectedRange.to.column
checkEqualCellSelections(this.root.selection.selectedRange.from, this.root.selection.selectedRange.to)
) {
this.root.selection.selectedRange = null;
newSelection.selectedRange = null;
this.root.events.dispatch({
type: EventTypes.CHANGE_SELECTION,
selection: newSelection,
enableCallback: false
})
}
}
@ -79,7 +97,6 @@ export class Scroller {
};
private handleKeydown = (event: KeyboardEvent) => {
console.log(event);
//* Navigation
if (
["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)
@ -92,9 +109,8 @@ export class Scroller {
this.root.selection.selectedCell &&
this.root.selection.selectedCell.column > 0
) {
console.log("tick");
this.root.selection.selectedCell.column -= 1;
this.root.renderSheet();
// this.root.renderSheet();
}
break;
}
@ -105,7 +121,7 @@ export class Scroller {
this.root.config.columns.length - 1
) {
this.root.selection.selectedCell.column += 1;
this.root.renderSheet();
// this.root.renderSheet();
}
break;
}
@ -115,7 +131,7 @@ export class Scroller {
this.root.selection.selectedCell.row > 0
) {
this.root.selection.selectedCell.row -= 1;
this.root.renderSheet();
// this.root.renderSheet();
}
break;
}
@ -126,12 +142,19 @@ export class Scroller {
this.root.config.rows.length - 1
) {
this.root.selection.selectedCell.row += 1;
this.root.renderSheet();
// this.root.renderSheet();
}
break;
}
}
this.root.events.dispatch({
type: EventTypes.CHANGE_SELECTION,
selection: this.root.selection,
enableCallback: true
})
}
//* Start typings
const keysRegex = /^([a-z]|[а-я])$/;
if (!event.metaKey && !event.ctrlKey) {
//* Prevent handle shortcutrs
@ -157,20 +180,11 @@ export class Scroller {
};
private handleClick = (event: MouseEvent) => {
if (event.button !== 0) return; // Left mouse button
const { offsetX, offsetY } = event;
const clickedCell = this.root.getCellByCoords(offsetX, offsetY);
this.isSelecting = true;
this.root.selection.selectedRange = {
from: clickedCell,
to: clickedCell,
};
this.root.selection.selectedCell = clickedCell;
this.root.renderSheet();
this.root.renderColumnsBar();
this.root.renderRowsBar();
this.root.events.dispatch({
type: EventTypes.CELL_CLICK,
event,
scroller: this
})
};
private handleScroll = () => {

View File

@ -1,11 +1,16 @@
import Spreadsheet from "./main";
import Spreadsheet, { SpreadsheetConstructorProperties } from "./main";
const saveButton = document.querySelector("#save_button");
const loadButton = document.querySelector("#load_button");
const options: SpreadsheetConstructorProperties = {
onCellClick: (event, cell) => {
console.log('Callback from instance', event, cell)
},
onSelectionChange: (selection) => {
console.log("Changed selection: ", selection)
}
}
if (!saveButton || !loadButton) throw new Error("LOST");
const sheet = new Spreadsheet("#spreadsheet", options);
const sheet = new Spreadsheet("#spreadsheet");
function saveDataToLS() {
const serializableData = sheet.serializeData();
@ -19,5 +24,10 @@ function loadDataFromLS() {
sheet.loadData(json);
}
const saveButton = document.querySelector("#save_button");
const loadButton = document.querySelector("#load_button");
if (!saveButton || !loadButton) throw new Error("LOST");
saveButton.addEventListener("click", saveDataToLS);
loadButton.addEventListener("click", loadDataFromLS);

View File

@ -21,6 +21,7 @@ import { Row } from "./modules/row";
import { Column } from "./modules/column";
import { ColumnsBar } from "./components/columnsBar";
import { RowsBar } from "./components/rowsBar";
import { Events } from "./modules/events";
/*
! Component structure
@ -34,9 +35,10 @@ import { RowsBar } from "./components/rowsBar";
</Table>
*/
interface SpreadsheetConstructorProperties {
config?: Omit<Config, "view">; // Not optional.
export interface SpreadsheetConstructorProperties {
view?: ViewProperties;
onCellClick?: ((event: MouseEvent, cell: Cell) => void) | null
onSelectionChange?: ((selection: Selection) => void) | null
}
export const CSS_PREFIX = "modern_sc_";
@ -55,6 +57,7 @@ export default class Spreadsheet {
public viewport: Viewport;
public selection: Selection;
public cache: Cache;
public events: Events
constructor(
target: string | HTMLElement,
@ -71,6 +74,9 @@ export default class Spreadsheet {
this.config = new Config(config);
this.config.onCellClick = props?.onCellClick ?? null
this.config.onSelectonChange = props?.onSelectionChange ?? null
this.rowsBar = new RowsBar(this);
this.columnsBar = new ColumnsBar(this);
this.sheet = new Sheet(this);
@ -84,6 +90,8 @@ export default class Spreadsheet {
this.scroller.getViewportBoundlingRect(),
);
this.selection = new Selection();
this.events = new Events(this)
this.data = data;
this.styles = new Styles();
@ -366,6 +374,7 @@ export default class Spreadsheet {
view,
rows,
columns,
onCellClick: null
});
return config;

View File

@ -1,5 +1,7 @@
import { Cell } from "./cell";
import { Column } from "./column";
import { Row } from "./row";
import { Selection } from "./selection";
export interface ViewProperties {
width: number;
@ -16,6 +18,8 @@ export type ConfigProperties = {
rows: Row[];
columns: Column[];
view: ViewProperties;
onCellClick?: ((event: MouseEvent, cell: Cell) => void) | null
onSelectionChange?: ((selection: Selection) => void) | null
};
export type SheetConfigConstructorProps = {
@ -30,9 +34,16 @@ export class Config {
width: 800,
height: 600,
};
onCellClick: ( (event: MouseEvent, cell: Cell) => void ) | null = null
onSelectonChange: ((selection: Selection) => void) | null = null
constructor(props: ConfigProperties) {
this.columns = props.columns;
this.rows = props.rows;
this.view = props.view;
this.onCellClick = props.onCellClick ?? null
this.onSelectonChange = props.onSelectionChange ?? null
}
}

80
src/modules/events.ts Normal file
View File

@ -0,0 +1,80 @@
import { Scroller } from "../components/scroller";
import Spreadsheet, { Selection } from "../main";
export enum EventTypes {
CELL_CLICK = "CELL_CLICK",
CHANGE_SELECTION = "CHANGE_SELECTION"
}
export type CellClickEvent = {
type: EventTypes.CELL_CLICK
event: MouseEvent
scroller: Scroller
}
export type ChangeSelectionEvent = {
type: EventTypes.CHANGE_SELECTION,
selection: Selection
enableCallback?: boolean
}
export type ActionTypes = CellClickEvent | ChangeSelectionEvent
export class Events {
root: Spreadsheet
constructor(root: Spreadsheet) {
this.root = root
}
dispatch(action: ActionTypes) {
switch (action.type) {
case EventTypes.CELL_CLICK: {
const { event, scroller } = action
this.cellClick(event, scroller)
break;
}
case EventTypes.CHANGE_SELECTION: {
const { selection, enableCallback } = action
this.changeSelection(selection, enableCallback)
break;
}
default: {
break;
}
}
}
private cellClick = (event: MouseEvent, scroller: Scroller) => {
if (event.button !== 0) return; // Left mouse button
const { offsetX, offsetY } = event;
const clickedCell = this.root.getCellByCoords(offsetX, offsetY);
const cell = this.root.getCell(clickedCell)
const selection = new Selection()
selection.selectedCell = clickedCell
selection.selectedRange = {
from: clickedCell,
to: clickedCell,
};
scroller.setSelectingMode(true);
this.changeSelection(selection, true)
this.root.config.onCellClick?.(event, cell)
}
private changeSelection = (selection: Selection, enableCallback = false) => {
this.root.selection = selection
if (enableCallback) this.root.config.onSelectonChange?.(selection)
this.root.renderSheet();
this.root.renderColumnsBar();
this.root.renderRowsBar();
}
}

12
src/utils/position.ts Normal file
View File

@ -0,0 +1,12 @@
import { BaseSelectionType, RangeSelectionType } from "../main";
export function checkEqualRanges(range1: RangeSelectionType, range2: RangeSelectionType) {
const equalRows = range1.from.row === range2.to.row
const equalColumns = range1.from.column === range2.to.column
return equalRows && equalColumns
}
export function checkEqualCellSelections(selection1: BaseSelectionType, selection2: BaseSelectionType) {
return selection1.column === selection2.column && selection1.row === selection2.row
}