Added CSS prefixes

Added common styles for cells and single cells styles
This commit is contained in:
Eugene 2023-07-25 15:26:43 +03:00
parent e4c09a517d
commit a37401a28d
12 changed files with 77 additions and 41 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "modern_spreadsheet", "name": "modern_spreadsheet",
"private": false, "private": false,
"version": "0.0.21", "version": "0.0.23",
"exports": { "exports": {
".": { ".": {
"import": "./dist/main.js", "import": "./dist/main.js",

View File

@ -1,4 +1,4 @@
import Spreadsheet from "../main"; import Spreadsheet, { CSS_PREFIX } from "../main";
import { Position } from "../modules/cell"; import { Position } from "../modules/cell";
import { RenderBox } from "../modules/renderBox"; import { RenderBox } from "../modules/renderBox";
@ -8,7 +8,7 @@ export class Editor {
constructor(root: Spreadsheet) { constructor(root: Spreadsheet) {
this.root = root this.root = root
const element = document.createElement('input') const element = document.createElement('input')
element.classList.add('editor') element.classList.add(CSS_PREFIX + 'editor')
this.element = element this.element = element
this.hide() this.hide()
} }
@ -23,7 +23,7 @@ export class Editor {
this.root.focusTable() this.root.focusTable()
} }
show(position: Position) { show(position: Position, initialString?: string) {
const { height, width, x, y } = new RenderBox(this.root.config, position); const { height, width, x, y } = new RenderBox(this.root.config, position);
const cell = this.root.getCell(position) const cell = this.root.getCell(position)
this.element.classList.remove('hide') this.element.classList.remove('hide')
@ -36,9 +36,11 @@ export class Editor {
window.addEventListener('click', this.handleClickOutside) window.addEventListener('click', this.handleClickOutside)
this.element.addEventListener('keydown', this.handleKeydown) this.element.addEventListener('keydown', this.handleKeydown)
this.element.value = cell.value this.element.value = initialString ? initialString : cell.value
this.element.focus() this.element.focus()
this.element.select() if (!initialString) this.element.select()
} }
handleKeydown = (event: KeyboardEvent) => { handleKeydown = (event: KeyboardEvent) => {

View File

@ -1,4 +1,4 @@
import Spreadsheet from "../main" import Spreadsheet, { CSS_PREFIX } from "../main"
export interface ViewportRect { export interface ViewportRect {
top: number top: number
@ -107,11 +107,15 @@ export class Scroller {
} }
} }
} }
const keysRegex = /^([a-z]|[а-я])$/
if (!event.metaKey && !event.ctrlKey) { //* Prevent handle shortcutrs 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() event.preventDefault()
if (!this.root.selection.selectedCell) return; 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.verticalScroller = verticalScroller
this.horizontalScroller = horizontalScroller this.horizontalScroller = horizontalScroller
scroller.appendChild(groupScrollers) scroller.appendChild(groupScrollers)
scroller.classList.add('scroller') scroller.classList.add(CSS_PREFIX + 'scroller')
return { scroller, verticalScroller, horizontalScroller } return { scroller, verticalScroller, horizontalScroller }
} }

View File

@ -1,4 +1,4 @@
import Spreadsheet from "../main" import Spreadsheet, { CSS_PREFIX } from "../main"
import { Position } from "../modules/cell" import { Position } from "../modules/cell"
/** /**
@ -11,7 +11,7 @@ export class Sheet {
constructor(root: Spreadsheet) { constructor(root: Spreadsheet) {
this.root = root this.root = root
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
canvas.classList.add('sheet') canvas.classList.add(CSS_PREFIX + 'sheet')
//* Set up canvas sizes based on provided root config //* Set up canvas sizes based on provided root config
canvas.height = this.root.config.view.height canvas.height = this.root.config.view.height

View File

@ -1,4 +1,4 @@
import Spreadsheet from "../main" import Spreadsheet, { CSS_PREFIX } from "../main"
import { ViewProperties } from "../modules/config" import { ViewProperties } from "../modules/config"
/** Base (root) component */ /** Base (root) component */
@ -8,7 +8,7 @@ export class Table {
constructor(root: Spreadsheet) { constructor(root: Spreadsheet) {
this.root = root this.root = root
const container = document.createElement('div') const container = document.createElement('div')
container.classList.add('spreadsheet_container') container.classList.add(CSS_PREFIX + 'spreadsheet_container')
this.element = container this.element = container
this.changeElementSizes(this.root.viewProps) this.changeElementSizes(this.root.viewProps)

View File

@ -1,4 +1,4 @@
import Spreadsheet from "../main" import Spreadsheet, { CSS_PREFIX } from "../main"
export class Toolbar { export class Toolbar {
element: HTMLDivElement element: HTMLDivElement
@ -6,7 +6,7 @@ export class Toolbar {
constructor(root: Spreadsheet) { constructor(root: Spreadsheet) {
this.root = root this.root = root
const toolbarElement = document.createElement('div') const toolbarElement = document.createElement('div')
toolbarElement.classList.add('toolbar') toolbarElement.classList.add(CSS_PREFIX + 'toolbar')
this.element = toolbarElement this.element = toolbarElement
} }
} }

View File

@ -24,3 +24,11 @@ function loadDataFromLS() {
saveButton.addEventListener('click', saveDataToLS) saveButton.addEventListener('click', saveDataToLS)
loadButton.addEventListener('click', loadDataFromLS) loadButton.addEventListener('click', loadDataFromLS)
sheet.changeCellStyles({column: 1, row: 1}, {
background: 'black',
borderColor: 'white',
fontColor: 'white',
fontSize: 20,
selectedBackground: 'green',
selectedFontColor: 'black'
})

View File

@ -4,7 +4,7 @@ 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";
import { Toolbar } from "./components/toolbar"; 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 { Config, ViewProperties } from "./modules/config";
import { RangeSelectionType, Selection } from "./modules/selection"; import { RangeSelectionType, Selection } from "./modules/selection";
import { Styles } from "./modules/styles"; import { Styles } from "./modules/styles";
@ -32,6 +32,8 @@ interface SpreadsheetConstructorProperties {
view?: ViewProperties view?: ViewProperties
} }
export const CSS_PREFIX = "modern_sc_"
export default class Spreadsheet { export default class Spreadsheet {
private table: Table private table: Table
private scroller: Scroller private scroller: Scroller
@ -115,7 +117,7 @@ export default class Spreadsheet {
content.appendChild(this.header.element) content.appendChild(this.header.element)
content.appendChild(this.sheet.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(this.toolbar.element)
this.table.element.appendChild(content) this.table.element.appendChild(content)
@ -175,6 +177,12 @@ export default class Spreadsheet {
this.renderCell(row, column) 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 { applyActionToRange(range: RangeSelectionType, callback: (cell: Cell) => any): void {
const fromRow = Math.min(range.from.row, range.to.row) const fromRow = Math.min(range.from.row, range.to.row)
const toRow = Math.max(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) { showEditor(position: Position, initialString?: string) {
this.editor.show(position) this.editor.show(position, initialString)
} }
renderSheet() { renderSheet() {
@ -238,7 +246,8 @@ export default class Spreadsheet {
displayValue: cell.displayValue, displayValue: cell.displayValue,
position: cell.position, position: cell.position,
resultValue: cell.resultValue, resultValue: cell.resultValue,
value: cell.value value: cell.value,
style: cell.style
})) }))
} }
formattedData.push(innerRow) formattedData.push(innerRow)

View File

@ -6,6 +6,7 @@ export type CellConstructorProps = {
displayValue: string displayValue: string
resultValue: string resultValue: string
position: Position position: Position
style: CellStyles | null
} }
interface CellStylesConstructorProps { interface CellStylesConstructorProps {
@ -48,7 +49,7 @@ export class SerializableCell {
displayValue: string displayValue: string
resultValue: string resultValue: string
position: Position position: Position
style: CellStyles style: CellStyles | null
constructor(props: SerializableCell | SerializableCell) { constructor(props: SerializableCell | SerializableCell) {
this.value = props.value this.value = props.value
this.displayValue = props.displayValue this.displayValue = props.displayValue
@ -59,18 +60,21 @@ export class SerializableCell {
} }
export class Cell { export class Cell {
/** True value (data) */
value: string value: string
/** Value to render */
displayValue: string displayValue: string
/** This refers to the values that were obtained by calculations, for example, after calculating the formula */ /** This refers to the values that were obtained by calculations, for example, after calculating the formula */
resultValue: string resultValue: string
position: Position position: Position
style: CellStyles = new CellStyles() style: CellStyles | null = null
constructor(props: CellConstructorProps) { constructor(props: CellConstructorProps) {
this.value = props.value this.value = props.value
this.displayValue = props.displayValue this.displayValue = props.displayValue
this.resultValue = props.resultValue this.resultValue = props.resultValue
this.position = props.position this.position = props.position
this.style = props.style
} }
public getSerializableCell(): SerializableCell { public getSerializableCell(): SerializableCell {
@ -84,6 +88,10 @@ export class Cell {
return cell return cell
} }
changeStyles(styles: CellStyles) {
this.style = styles
}
changeValues(values: Partial<Omit<CellConstructorProps, 'position'>>) { changeValues(values: Partial<Omit<CellConstructorProps, 'position'>>) {
Object.assign(this, values) Object.assign(this, values)
} }
@ -109,15 +117,17 @@ export class Cell {
y -= root.viewport.top y -= root.viewport.top
x -= root.viewport.left x -= root.viewport.left
const styles = this.style ?? root.styles.cells
ctx.clearRect(x, y, width, height) 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.strokeStyle = 'black'
ctx.fillRect(x, y, width - 1, height - 1) ctx.fillRect(x, y, width - 1, height - 1)
ctx.strokeRect(x, y, width, height) 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.textAlign = 'left'
ctx.font = `${this.style.fontSize}px Arial` ctx.font = `${styles.fontSize}px Arial`
ctx.textBaseline = 'middle' ctx.textBaseline = 'middle'
ctx.fillText(this.displayValue, x + 2, y + height / 2) ctx.fillText(this.displayValue, x + 2, y + height / 2)
} }

View File

@ -1,4 +1,9 @@
import { CellStyles } from "./cell";
export class Styles { export class Styles {
cells: CellStyles
constructor() {
this.cells = new CellStyles()
}
} }

View File

@ -1,25 +1,23 @@
$css_prefix: "modern_sc_";
.#{$css_prefix}content {
.content {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
} }
.spreadsheet_container { .#{$css_prefix}spreadsheet_container {
position: relative; position: relative;
isolation: isolate; isolation: isolate;
border: 2px solid black; border: 2px solid black;
} }
.sheet{ .#{$css_prefix}sheet{
display: block; display: block;
contain: strict; contain: strict;
} }
.scroller { .#{$css_prefix}scroller {
overflow: scroll; overflow: scroll;
box-sizing: border-box; box-sizing: border-box;
transform: translateZ(0); transform: translateZ(0);
@ -28,13 +26,13 @@
} }
} }
.editor { .#{$css_prefix}editor {
position: absolute; position: absolute;
box-sizing: border-box; box-sizing: border-box;
font-size: 16px; font-size: 16px;
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
} }
.hide { .#{$css_prefix}hide {
visibility: hidden; visibility: hidden;
} }