Added new component ColumnsBar
Added render of ColumnsBar methods Added selection condition render styling
This commit is contained in:
parent
c34d913619
commit
1ef3ae3de4
|
|
@ -36,4 +36,5 @@ function loadData() {
|
||||||
- Formulas support
|
- Formulas support
|
||||||
- Selected cell depends cells highlight
|
- Selected cell depends cells highlight
|
||||||
- Async formulas support
|
- Async formulas support
|
||||||
- Mutlisheets (?)
|
- Mutlisheets (?)
|
||||||
|
- Copy & Paste support
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="spreadsheet"></div>
|
<div id="spreadsheet"></div>
|
||||||
<div id="spreadsheet_2"></div>
|
|
||||||
<button id="save_button">Save sheet</button>
|
<button id="save_button">Save sheet</button>
|
||||||
<button id="load_button">Load sheet</button>
|
<button id="load_button">Load sheet</button>
|
||||||
<script type="module" src="/src/index.ts"></script>
|
<script type="module" src="/src/index.ts"></script>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import Spreadsheet, { RenderBox } from "../main"
|
||||||
|
|
||||||
|
export class ColumnsBar {
|
||||||
|
public element: HTMLCanvasElement
|
||||||
|
private root: Spreadsheet
|
||||||
|
public height: number = 32
|
||||||
|
public width: number
|
||||||
|
private resizerWidth = 2
|
||||||
|
ctx: CanvasRenderingContext2D
|
||||||
|
|
||||||
|
constructor(root: Spreadsheet) {
|
||||||
|
this.root = root
|
||||||
|
this.element = this.createElement()
|
||||||
|
const ctx = this.element.getContext('2d')
|
||||||
|
if (!ctx) throw new Error("Enable hardware acceleration");
|
||||||
|
this.ctx = ctx
|
||||||
|
|
||||||
|
this.width = this.root.viewProps.width
|
||||||
|
}
|
||||||
|
|
||||||
|
private createElement(): HTMLCanvasElement {
|
||||||
|
const element = document.createElement('canvas')
|
||||||
|
element.style.position = 'absolute'
|
||||||
|
element.style.top = '0'
|
||||||
|
element.style.left = '0'
|
||||||
|
element.style.height = this.height + 'px'
|
||||||
|
element.style.width = this.root.viewProps.width + 'px'
|
||||||
|
element.style.display = 'block'
|
||||||
|
|
||||||
|
element.width = this.root.viewProps.width
|
||||||
|
element.height = this.height
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
|
||||||
|
private isColumnSelected(column: number): boolean {
|
||||||
|
const { selectedCell, selectedRange } = this.root.selection
|
||||||
|
if (selectedCell && selectedCell.column === column) return true
|
||||||
|
if (selectedRange) {
|
||||||
|
const inRange =
|
||||||
|
column >= Math.min(selectedRange.from.column, selectedRange.to.column) &&
|
||||||
|
column <= Math.max(selectedRange.from.column, selectedRange.to.column)
|
||||||
|
|
||||||
|
return inRange
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderText(column: number, renderBox: RenderBox) {
|
||||||
|
const { width, x } = renderBox
|
||||||
|
|
||||||
|
this.ctx.fillStyle = 'black'
|
||||||
|
this.ctx.textAlign = 'center'
|
||||||
|
this.ctx.textBaseline = 'middle'
|
||||||
|
this.ctx.font = '16px Arial'
|
||||||
|
this.ctx.fillText(this.root.config.columns[column].title, x + (width / 2) - this.root.viewport.left, 0 + this.height / 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderRect(column: number, renderBox: RenderBox) {
|
||||||
|
const { width, x } = renderBox
|
||||||
|
|
||||||
|
const { selectedCell } = this.root.selection
|
||||||
|
|
||||||
|
const isColSelected = this.isColumnSelected(column)
|
||||||
|
|
||||||
|
console.log(selectedCell)
|
||||||
|
|
||||||
|
this.ctx.fillStyle = isColSelected ? this.root.styles.cells.selectedBackground : 'white'
|
||||||
|
this.ctx.strokeStyle = 'black'
|
||||||
|
this.ctx.lineWidth = this.resizerWidth
|
||||||
|
this.ctx.fillRect(x - this.root.viewport.left - 1, 1, width - 1, this.height - 1)
|
||||||
|
this.ctx.strokeRect(x - this.root.viewport.left, 0, width, this.height)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSingleColumn(column: number) {
|
||||||
|
const renderBox = new RenderBox(this.root.config, {
|
||||||
|
row: 0,
|
||||||
|
column: column
|
||||||
|
})
|
||||||
|
|
||||||
|
this.renderRect(column, renderBox)
|
||||||
|
this.renderText(column, renderBox)
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderBar() {
|
||||||
|
const lastColIdx = this.root.viewport.lastCol + 3;
|
||||||
|
const firstColIdx = this.root.viewport.firstCol;
|
||||||
|
|
||||||
|
for (let col = firstColIdx; col <= lastColIdx; col++) {
|
||||||
|
this.renderSingleColumn(col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -28,7 +28,7 @@ export class Editor {
|
||||||
const cell = this.root.getCell(position);
|
const cell = this.root.getCell(position);
|
||||||
this.element.classList.remove("hide");
|
this.element.classList.remove("hide");
|
||||||
|
|
||||||
this.element.style.top = y - this.root.viewport.top + "px";
|
this.element.style.top = y - this.root.viewport.top + this.root.columnsBarHeight + "px";
|
||||||
this.element.style.left = x - this.root.viewport.left + "px";
|
this.element.style.left = x - this.root.viewport.left + "px";
|
||||||
this.element.style.width = width + "px";
|
this.element.style.width = width + "px";
|
||||||
this.element.style.height = height + "px";
|
this.element.style.height = height + "px";
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
import Spreadsheet from "../main";
|
|
||||||
|
|
||||||
export class Header {
|
|
||||||
element: HTMLHeadElement;
|
|
||||||
root: Spreadsheet;
|
|
||||||
constructor(root: Spreadsheet) {
|
|
||||||
this.root = root;
|
|
||||||
const headerElement = document.createElement("header");
|
|
||||||
headerElement.classList.add();
|
|
||||||
this.element = headerElement;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -25,6 +25,7 @@ export class Scroller {
|
||||||
|
|
||||||
this.element.style.height = this.root.config.view.height + "px";
|
this.element.style.height = this.root.config.view.height + "px";
|
||||||
this.element.style.width = this.root.config.view.width + "px";
|
this.element.style.width = this.root.config.view.width + "px";
|
||||||
|
this.element.style.top = this.root.columnsBarHeight + 'px'
|
||||||
this.element.tabIndex = -1;
|
this.element.tabIndex = -1;
|
||||||
|
|
||||||
this.updateScrollerSize(); //* Init size set
|
this.updateScrollerSize(); //* Init size set
|
||||||
|
|
@ -47,6 +48,7 @@ export class Scroller {
|
||||||
this.root.selection.selectedRange.to = lastSelectedCell;
|
this.root.selection.selectedRange.to = lastSelectedCell;
|
||||||
}
|
}
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
|
this.root.renderColumnsBar();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleMouseUp = () => {
|
private handleMouseUp = () => {
|
||||||
|
|
@ -55,15 +57,16 @@ export class Scroller {
|
||||||
if (this.root.selection.selectedRange) {
|
if (this.root.selection.selectedRange) {
|
||||||
if (
|
if (
|
||||||
this.root.selection.selectedRange.from.row ===
|
this.root.selection.selectedRange.from.row ===
|
||||||
this.root.selection.selectedRange.to.row &&
|
this.root.selection.selectedRange.to.row &&
|
||||||
this.root.selection.selectedRange.from.column ===
|
this.root.selection.selectedRange.from.column ===
|
||||||
this.root.selection.selectedRange.to.column
|
this.root.selection.selectedRange.to.column
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedRange = null;
|
this.root.selection.selectedRange = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
|
this.root.renderColumnsBar();
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleDoubleClick = (event: MouseEvent) => {
|
private handleDoubleClick = (event: MouseEvent) => {
|
||||||
|
|
@ -96,7 +99,7 @@ export class Scroller {
|
||||||
if (
|
if (
|
||||||
this.root.selection.selectedCell &&
|
this.root.selection.selectedCell &&
|
||||||
this.root.selection.selectedCell.column <
|
this.root.selection.selectedCell.column <
|
||||||
this.root.config.columns.length - 1
|
this.root.config.columns.length - 1
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedCell.column += 1;
|
this.root.selection.selectedCell.column += 1;
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
|
|
@ -117,7 +120,7 @@ export class Scroller {
|
||||||
if (
|
if (
|
||||||
this.root.selection.selectedCell &&
|
this.root.selection.selectedCell &&
|
||||||
this.root.selection.selectedCell.row <
|
this.root.selection.selectedCell.row <
|
||||||
this.root.config.rows.length - 1
|
this.root.config.rows.length - 1
|
||||||
) {
|
) {
|
||||||
this.root.selection.selectedCell.row += 1;
|
this.root.selection.selectedCell.row += 1;
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
|
|
@ -162,6 +165,7 @@ export class Scroller {
|
||||||
this.root.selection.selectedCell = clickedCell;
|
this.root.selection.selectedCell = clickedCell;
|
||||||
|
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
|
this.root.renderColumnsBar()
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleScroll = () => {
|
private handleScroll = () => {
|
||||||
|
|
@ -169,6 +173,7 @@ export class Scroller {
|
||||||
this.root.viewport.updateValues(rect);
|
this.root.viewport.updateValues(rect);
|
||||||
|
|
||||||
this.root.renderSheet();
|
this.root.renderSheet();
|
||||||
|
this.root.renderColumnsBar()
|
||||||
};
|
};
|
||||||
|
|
||||||
public getViewportBoundlingRect(): ViewportRect {
|
public getViewportBoundlingRect(): ViewportRect {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export class Sheet {
|
||||||
canvas.width = this.root.config.view.width;
|
canvas.width = this.root.config.view.width;
|
||||||
canvas.style.width = this.root.config.view.width + "px";
|
canvas.style.width = this.root.config.view.width + "px";
|
||||||
canvas.style.height = this.root.config.view.height + "px";
|
canvas.style.height = this.root.config.view.height + "px";
|
||||||
|
canvas.style.left = '0px'
|
||||||
|
|
||||||
this.element = canvas;
|
this.element = canvas;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,6 @@ export class Table {
|
||||||
changeElementSizes(sizes: ViewProperties) {
|
changeElementSizes(sizes: ViewProperties) {
|
||||||
const { height, width } = sizes;
|
const { height, width } = sizes;
|
||||||
this.element.style.width = width + "px";
|
this.element.style.width = width + "px";
|
||||||
this.element.style.height = height + "px";
|
this.element.style.height = height + this.root.columnsBarHeight + "px";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
src/index.ts
19
src/index.ts
|
|
@ -6,9 +6,6 @@ const loadButton = document.querySelector("#load_button");
|
||||||
if (!saveButton || !loadButton) throw new Error("LOST");
|
if (!saveButton || !loadButton) throw new Error("LOST");
|
||||||
|
|
||||||
const sheet = new Spreadsheet("#spreadsheet");
|
const sheet = new Spreadsheet("#spreadsheet");
|
||||||
const sheet2 = new Spreadsheet("#spreadsheet_2");
|
|
||||||
|
|
||||||
console.log(sheet2);
|
|
||||||
|
|
||||||
function saveDataToLS() {
|
function saveDataToLS() {
|
||||||
const serializableData = sheet.serializeData();
|
const serializableData = sheet.serializeData();
|
||||||
|
|
@ -35,3 +32,19 @@ sheet.changeCellStyles(
|
||||||
selectedFontColor: "black",
|
selectedFontColor: "black",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const text = 'Test123'
|
||||||
|
|
||||||
|
const cellPosition = {
|
||||||
|
column: 1,
|
||||||
|
row: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sheet.changeCellValues(cellPosition, {
|
||||||
|
displayValue: text,
|
||||||
|
resultValue: text,
|
||||||
|
value: text
|
||||||
|
})
|
||||||
|
}, 4000)
|
||||||
21
src/main.ts
21
src/main.ts
|
|
@ -1,5 +1,4 @@
|
||||||
import { Editor } from "./components/editor";
|
import { Editor } from "./components/editor";
|
||||||
import { Header } from "./components/header";
|
|
||||||
import { Scroller } from "./components/scroller";
|
import { Scroller } from "./components/scroller";
|
||||||
import { Sheet } from "./components/sheet";
|
import { Sheet } from "./components/sheet";
|
||||||
import { Table } from "./components/table";
|
import { Table } from "./components/table";
|
||||||
|
|
@ -20,6 +19,7 @@ import { createSampleData } from "./utils/createData";
|
||||||
import { Cache, CachedColumn, CachedRow } from "./modules/cache";
|
import { Cache, CachedColumn, CachedRow } from "./modules/cache";
|
||||||
import { Row } from "./modules/row";
|
import { Row } from "./modules/row";
|
||||||
import { Column } from "./modules/column";
|
import { Column } from "./modules/column";
|
||||||
|
import { ColumnsBar } from "./components/columnsBar";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
! Component structure
|
! Component structure
|
||||||
|
|
@ -44,7 +44,7 @@ export default class Spreadsheet {
|
||||||
private table: Table;
|
private table: Table;
|
||||||
private scroller: Scroller;
|
private scroller: Scroller;
|
||||||
private toolbar: Toolbar;
|
private toolbar: Toolbar;
|
||||||
private header: Header;
|
private columnsBar: ColumnsBar
|
||||||
private sheet: Sheet;
|
private sheet: Sheet;
|
||||||
private editor: Editor;
|
private editor: Editor;
|
||||||
public styles: Styles;
|
public styles: Styles;
|
||||||
|
|
@ -68,12 +68,12 @@ export default class Spreadsheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.config = new Config(config);
|
this.config = new Config(config);
|
||||||
this.sheet = new Sheet(this);
|
|
||||||
|
|
||||||
|
this.columnsBar = new ColumnsBar(this)
|
||||||
|
this.sheet = new Sheet(this);
|
||||||
this.table = new Table(this);
|
this.table = new Table(this);
|
||||||
this.scroller = new Scroller(this);
|
this.scroller = new Scroller(this);
|
||||||
this.toolbar = new Toolbar(this);
|
this.toolbar = new Toolbar(this);
|
||||||
this.header = new Header(this);
|
|
||||||
this.editor = new Editor(this);
|
this.editor = new Editor(this);
|
||||||
this.cache = this.getInitialCache();
|
this.cache = this.getInitialCache();
|
||||||
this.viewport = new Viewport(
|
this.viewport = new Viewport(
|
||||||
|
|
@ -87,6 +87,7 @@ export default class Spreadsheet {
|
||||||
this.buildComponent();
|
this.buildComponent();
|
||||||
this.appendTableToTarget(target);
|
this.appendTableToTarget(target);
|
||||||
this.renderSheet();
|
this.renderSheet();
|
||||||
|
this.renderColumnsBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
private getInitialCache(): Cache {
|
private getInitialCache(): Cache {
|
||||||
|
|
@ -126,12 +127,14 @@ export default class Spreadsheet {
|
||||||
|
|
||||||
private buildComponent(): void {
|
private buildComponent(): void {
|
||||||
const content = document.createElement("div"); //* Abstract
|
const content = document.createElement("div"); //* Abstract
|
||||||
content.appendChild(this.header.element);
|
content.style.top = this.columnsBarHeight + 'px'
|
||||||
|
|
||||||
content.appendChild(this.sheet.element);
|
content.appendChild(this.sheet.element);
|
||||||
|
|
||||||
content.classList.add(CSS_PREFIX + "content");
|
content.classList.add(CSS_PREFIX + "content");
|
||||||
|
|
||||||
this.table.element.appendChild(this.toolbar.element);
|
this.table.element.appendChild(this.toolbar.element);
|
||||||
|
this.table.element.appendChild(this.columnsBar.element)
|
||||||
this.table.element.appendChild(content);
|
this.table.element.appendChild(content);
|
||||||
this.table.element.appendChild(this.scroller.element);
|
this.table.element.appendChild(this.scroller.element);
|
||||||
this.table.element.append(this.editor.element);
|
this.table.element.append(this.editor.element);
|
||||||
|
|
@ -171,6 +174,10 @@ export default class Spreadsheet {
|
||||||
return this.config.view;
|
return this.config.view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get columnsBarHeight() {
|
||||||
|
return this.columnsBar.height
|
||||||
|
}
|
||||||
|
|
||||||
/** Focusing on interactive part of spreadsheet */
|
/** Focusing on interactive part of spreadsheet */
|
||||||
focusTable() {
|
focusTable() {
|
||||||
this.scroller.element.focus();
|
this.scroller.element.focus();
|
||||||
|
|
@ -246,6 +253,10 @@ export default class Spreadsheet {
|
||||||
this.sheet.renderSheet();
|
this.sheet.renderSheet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderColumnsBar() {
|
||||||
|
this.columnsBar.renderBar()
|
||||||
|
}
|
||||||
|
|
||||||
renderCell(row: number, col: number) {
|
renderCell(row: number, col: number) {
|
||||||
this.data[row][col].render(this);
|
this.data[row][col].render(this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ $css_prefix: "modern_sc_";
|
||||||
|
|
||||||
.#{$css_prefix}content {
|
.#{$css_prefix}content {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$css_prefix}spreadsheet_container {
|
.#{$css_prefix}spreadsheet_container {
|
||||||
|
|
@ -18,6 +16,7 @@ $css_prefix: "modern_sc_";
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$css_prefix}scroller {
|
.#{$css_prefix}scroller {
|
||||||
|
position: absolute;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue