Added CSS prefixes
Added common styles for cells and single cells styles
This commit is contained in:
parent
e4c09a517d
commit
a37401a28d
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "modern_spreadsheet",
|
||||
"private": false,
|
||||
"version": "0.0.21",
|
||||
"version": "0.0.23",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/main.js",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Spreadsheet from "../main";
|
||||
import Spreadsheet, { CSS_PREFIX } from "../main";
|
||||
import { Position } from "../modules/cell";
|
||||
import { RenderBox } from "../modules/renderBox";
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ export class Editor {
|
|||
constructor(root: Spreadsheet) {
|
||||
this.root = root
|
||||
const element = document.createElement('input')
|
||||
element.classList.add('editor')
|
||||
element.classList.add(CSS_PREFIX + 'editor')
|
||||
this.element = element
|
||||
this.hide()
|
||||
}
|
||||
|
|
@ -19,11 +19,11 @@ export class Editor {
|
|||
this.element.blur()
|
||||
window.removeEventListener('click', this.handleClickOutside)
|
||||
this.element.removeEventListener('keydown', this.handleKeydown)
|
||||
|
||||
|
||||
this.root.focusTable()
|
||||
}
|
||||
|
||||
show(position: Position) {
|
||||
show(position: Position, initialString?: string) {
|
||||
const { height, width, x, y } = new RenderBox(this.root.config, position);
|
||||
const cell = this.root.getCell(position)
|
||||
this.element.classList.remove('hide')
|
||||
|
|
@ -36,15 +36,17 @@ export class Editor {
|
|||
|
||||
window.addEventListener('click', this.handleClickOutside)
|
||||
this.element.addEventListener('keydown', this.handleKeydown)
|
||||
this.element.value = cell.value
|
||||
this.element.value = initialString ? initialString : cell.value
|
||||
this.element.focus()
|
||||
this.element.select()
|
||||
if (!initialString) this.element.select()
|
||||
|
||||
|
||||
}
|
||||
|
||||
handleKeydown = (event: KeyboardEvent) => {
|
||||
const {key} = event
|
||||
|
||||
switch(key) {
|
||||
handleKeydown = (event: KeyboardEvent) => {
|
||||
const { key } = event
|
||||
|
||||
switch (key) {
|
||||
case 'Escape': {
|
||||
this.hide();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Spreadsheet from "../main"
|
||||
import Spreadsheet, { CSS_PREFIX } from "../main"
|
||||
|
||||
export interface ViewportRect {
|
||||
top: number
|
||||
|
|
@ -107,11 +107,15 @@ export class Scroller {
|
|||
}
|
||||
}
|
||||
}
|
||||
const keysRegex = /^([a-z]|[а-я])$/
|
||||
if (!event.metaKey && !event.ctrlKey) { //* Prevent handle shortcutrs
|
||||
if (event.key === 'F2' || /^([a-z]|[а-я])$/.test(event.key.toLowerCase())) { //* English and Russian keyboard. Or F2 button
|
||||
const isPressedLetterKey = keysRegex.test(event.key.toLowerCase())
|
||||
|
||||
if (event.key === 'F2' || isPressedLetterKey) { //* English and Russian keyboard. Or F2 button
|
||||
event.preventDefault()
|
||||
if (!this.root.selection.selectedCell) return;
|
||||
this.root.showEditor(this.root.selection.selectedCell)
|
||||
|
||||
this.root.showEditor(this.root.selection.selectedCell, isPressedLetterKey ? event.key : undefined)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +181,7 @@ export class Scroller {
|
|||
this.verticalScroller = verticalScroller
|
||||
this.horizontalScroller = horizontalScroller
|
||||
scroller.appendChild(groupScrollers)
|
||||
scroller.classList.add('scroller')
|
||||
scroller.classList.add(CSS_PREFIX + 'scroller')
|
||||
|
||||
return { scroller, verticalScroller, horizontalScroller }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Spreadsheet from "../main"
|
||||
import Spreadsheet, { CSS_PREFIX } from "../main"
|
||||
import { Position } from "../modules/cell"
|
||||
|
||||
/**
|
||||
|
|
@ -11,7 +11,7 @@ export class Sheet {
|
|||
constructor(root: Spreadsheet) {
|
||||
this.root = root
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.classList.add('sheet')
|
||||
canvas.classList.add(CSS_PREFIX + 'sheet')
|
||||
|
||||
//* Set up canvas sizes based on provided root config
|
||||
canvas.height = this.root.config.view.height
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Spreadsheet from "../main"
|
||||
import Spreadsheet, { CSS_PREFIX } from "../main"
|
||||
import { ViewProperties } from "../modules/config"
|
||||
|
||||
/** Base (root) component */
|
||||
|
|
@ -8,7 +8,7 @@ export class Table {
|
|||
constructor(root: Spreadsheet) {
|
||||
this.root = root
|
||||
const container = document.createElement('div')
|
||||
container.classList.add('spreadsheet_container')
|
||||
container.classList.add(CSS_PREFIX + 'spreadsheet_container')
|
||||
this.element = container
|
||||
|
||||
this.changeElementSizes(this.root.viewProps)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import Spreadsheet from "../main"
|
||||
import Spreadsheet, { CSS_PREFIX } from "../main"
|
||||
|
||||
export class Toolbar {
|
||||
element: HTMLDivElement
|
||||
|
|
@ -6,7 +6,7 @@ export class Toolbar {
|
|||
constructor(root: Spreadsheet) {
|
||||
this.root = root
|
||||
const toolbarElement = document.createElement('div')
|
||||
toolbarElement.classList.add('toolbar')
|
||||
toolbarElement.classList.add(CSS_PREFIX + 'toolbar')
|
||||
this.element = toolbarElement
|
||||
}
|
||||
}
|
||||
|
|
@ -24,3 +24,11 @@ function loadDataFromLS() {
|
|||
|
||||
saveButton.addEventListener('click', saveDataToLS)
|
||||
loadButton.addEventListener('click', loadDataFromLS)
|
||||
sheet.changeCellStyles({column: 1, row: 1}, {
|
||||
background: 'black',
|
||||
borderColor: 'white',
|
||||
fontColor: 'white',
|
||||
fontSize: 20,
|
||||
selectedBackground: 'green',
|
||||
selectedFontColor: 'black'
|
||||
})
|
||||
19
src/main.ts
19
src/main.ts
|
|
@ -4,7 +4,7 @@ import { Scroller } from "./components/scroller";
|
|||
import { Sheet } from "./components/sheet";
|
||||
import { Table } from "./components/table";
|
||||
import { Toolbar } from "./components/toolbar";
|
||||
import { Cell, CellConstructorProps, Position, SerializableCell } from "./modules/cell";
|
||||
import { Cell, CellConstructorProps, CellStyles, Position, SerializableCell } from "./modules/cell";
|
||||
import { Config, ViewProperties } from "./modules/config";
|
||||
import { RangeSelectionType, Selection } from "./modules/selection";
|
||||
import { Styles } from "./modules/styles";
|
||||
|
|
@ -32,6 +32,8 @@ interface SpreadsheetConstructorProperties {
|
|||
view?: ViewProperties
|
||||
}
|
||||
|
||||
export const CSS_PREFIX = "modern_sc_"
|
||||
|
||||
export default class Spreadsheet {
|
||||
private table: Table
|
||||
private scroller: Scroller
|
||||
|
|
@ -115,7 +117,7 @@ export default class Spreadsheet {
|
|||
content.appendChild(this.header.element)
|
||||
content.appendChild(this.sheet.element)
|
||||
|
||||
content.classList.add('content')
|
||||
content.classList.add(CSS_PREFIX + 'content')
|
||||
|
||||
this.table.element.appendChild(this.toolbar.element)
|
||||
this.table.element.appendChild(content)
|
||||
|
|
@ -175,6 +177,12 @@ export default class Spreadsheet {
|
|||
this.renderCell(row, column)
|
||||
}
|
||||
|
||||
changeCellStyles(position: Position, styles: CellStyles) {
|
||||
const { column, row } = position
|
||||
this.data[row][column].changeStyles(styles)
|
||||
this.renderCell(row, column)
|
||||
}
|
||||
|
||||
applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => any): void {
|
||||
const fromRow = Math.min(range.from.row, range.to.row)
|
||||
const toRow = Math.max(range.from.row, range.to.row)
|
||||
|
|
@ -211,8 +219,8 @@ export default class Spreadsheet {
|
|||
}
|
||||
}
|
||||
|
||||
showEditor(position: Position) {
|
||||
this.editor.show(position)
|
||||
showEditor(position: Position, initialString?: string) {
|
||||
this.editor.show(position, initialString)
|
||||
}
|
||||
|
||||
renderSheet() {
|
||||
|
|
@ -238,7 +246,8 @@ export default class Spreadsheet {
|
|||
displayValue: cell.displayValue,
|
||||
position: cell.position,
|
||||
resultValue: cell.resultValue,
|
||||
value: cell.value
|
||||
value: cell.value,
|
||||
style: cell.style
|
||||
}))
|
||||
}
|
||||
formattedData.push(innerRow)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export type CellConstructorProps = {
|
|||
displayValue: string
|
||||
resultValue: string
|
||||
position: Position
|
||||
style: CellStyles | null
|
||||
}
|
||||
|
||||
interface CellStylesConstructorProps {
|
||||
|
|
@ -48,7 +49,7 @@ export class SerializableCell {
|
|||
displayValue: string
|
||||
resultValue: string
|
||||
position: Position
|
||||
style: CellStyles
|
||||
style: CellStyles | null
|
||||
constructor(props: SerializableCell | SerializableCell) {
|
||||
this.value = props.value
|
||||
this.displayValue = props.displayValue
|
||||
|
|
@ -59,18 +60,21 @@ export class SerializableCell {
|
|||
}
|
||||
|
||||
export class Cell {
|
||||
/** True value (data) */
|
||||
value: string
|
||||
/** Value to render */
|
||||
displayValue: string
|
||||
/** This refers to the values that were obtained by calculations, for example, after calculating the formula */
|
||||
resultValue: string
|
||||
position: Position
|
||||
style: CellStyles = new CellStyles()
|
||||
style: CellStyles | null = null
|
||||
|
||||
constructor(props: CellConstructorProps) {
|
||||
this.value = props.value
|
||||
this.displayValue = props.displayValue
|
||||
this.resultValue = props.resultValue
|
||||
this.position = props.position
|
||||
this.style = props.style
|
||||
}
|
||||
|
||||
public getSerializableCell(): SerializableCell {
|
||||
|
|
@ -84,6 +88,10 @@ export class Cell {
|
|||
return cell
|
||||
}
|
||||
|
||||
changeStyles(styles: CellStyles) {
|
||||
this.style = styles
|
||||
}
|
||||
|
||||
changeValues(values: Partial<Omit<CellConstructorProps, 'position'>>) {
|
||||
Object.assign(this, values)
|
||||
}
|
||||
|
|
@ -109,15 +117,17 @@ export class Cell {
|
|||
y -= root.viewport.top
|
||||
x -= root.viewport.left
|
||||
|
||||
const styles = this.style ?? root.styles.cells
|
||||
|
||||
ctx.clearRect(x, y, width, height)
|
||||
ctx.fillStyle = isCellSelected || isCellInRange ? this.style.selectedBackground : this.style.background
|
||||
ctx.fillStyle = isCellSelected || isCellInRange ? styles.selectedBackground : styles.background
|
||||
ctx.strokeStyle = 'black'
|
||||
ctx.fillRect(x, y, width - 1, height - 1)
|
||||
ctx.strokeRect(x, y, width, height)
|
||||
|
||||
ctx.fillStyle = isCellSelected || isCellInRange ? this.style.selectedFontColor : this.style.fontColor
|
||||
ctx.fillStyle = isCellSelected || isCellInRange ? styles.selectedFontColor : styles.fontColor
|
||||
ctx.textAlign = 'left'
|
||||
ctx.font = `${this.style.fontSize}px Arial`
|
||||
ctx.font = `${styles.fontSize}px Arial`
|
||||
ctx.textBaseline = 'middle'
|
||||
ctx.fillText(this.displayValue, x + 2, y + height / 2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { CellStyles } from "./cell";
|
||||
|
||||
|
||||
export class Styles {
|
||||
cells: CellStyles
|
||||
constructor() {
|
||||
this.cells = new CellStyles()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
body {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
$css_prefix: "modern_sc_";
|
||||
|
||||
|
||||
.content {
|
||||
.#{$css_prefix}content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.spreadsheet_container {
|
||||
.#{$css_prefix}spreadsheet_container {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
border: 2px solid black;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.sheet{
|
||||
.#{$css_prefix}sheet{
|
||||
display: block;
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.scroller {
|
||||
.#{$css_prefix}scroller {
|
||||
overflow: scroll;
|
||||
box-sizing: border-box;
|
||||
transform: translateZ(0);
|
||||
|
|
@ -28,13 +26,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.editor {
|
||||
.#{$css_prefix}editor {
|
||||
position: absolute;
|
||||
box-sizing: border-box;
|
||||
font-size: 16px;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.hide {
|
||||
.#{$css_prefix}hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
Loading…
Reference in New Issue