From d2feab9e042c5d1407548e1130e9d93225b335dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cchrisshank=E2=80=9D?= Date: Thu, 12 Dec 2024 19:09:08 -0800 Subject: [PATCH] readonly spreadsheet cells --- demo/spreadsheet-shape-projection.html | 6 ++++ src/folk-space-projector.ts | 31 ++++++++++++++----- src/folk-spreadsheet.ts | 41 +++++++++++++++++++------- 3 files changed, 60 insertions(+), 18 deletions(-) diff --git a/demo/spreadsheet-shape-projection.html b/demo/spreadsheet-shape-projection.html index 8a4d5a1..daefeae 100644 --- a/demo/spreadsheet-shape-projection.html +++ b/demo/spreadsheet-shape-projection.html @@ -21,10 +21,16 @@ folk-shape { background: rgb(187, 178, 178); } + + h1 { + margin: 0; + width: fit-content; + } +

Spatial projection into a spreadsheet

diff --git a/src/folk-space-projector.ts b/src/folk-space-projector.ts index f40fe62..a2837a7 100644 --- a/src/folk-space-projector.ts +++ b/src/folk-space-projector.ts @@ -1,7 +1,7 @@ import { css, PropertyValues } from '@lit/reactive-element'; import { FolkBaseSet } from './folk-base-set'; import { FolkShape } from './folk-shape'; -import { FolkSpreadsheet, FolkSpreadSheetCell, templateCells } from './folk-spreadsheet'; +import { CellTemplate, FolkSpreadsheet, FolkSpreadSheetCell, templateCells } from './folk-spreadsheet'; import { DOMRectTransform } from './common/DOMRectTransform'; FolkShape.define(); @@ -120,17 +120,31 @@ export class FolkSpaceProjector extends FolkBaseSet { super.update(changedProperties); if (changedProperties.has('sourceElements')) { - const cells = templateCells(this.sourceElements.size, 5, {}); - this.#spreadsheet.setHTMLUnsafe(cells); + const cells: Record = {}; + + let row = 1; + for (const el of this.sourceElements) { + if (!(el instanceof FolkShape)) { + cells[`A${row}`] = { readonly: true }; + cells[`B${row}`] = { readonly: true }; + cells[`C${row}`] = { readonly: true }; + cells[`D${row}`] = { readonly: true }; + cells[`E${row}`] = { readonly: true }; + } + row++; + } + + const html = templateCells(this.sourceElements.size, 5, cells); + this.#spreadsheet.setHTMLUnsafe(html); } if (this.sourcesMap.size !== this.sourceElements.size) return; this.#lock = true; - const rects = this.sourceRects; - for (let i = 0; i < rects.length; i += 1) { - const row = i + 1; - const rect = rects[i]; + let row = 1; + for (const el of this.sourceElements) { + const rect = this.sourcesMap.get(el)!; + if (rect instanceof DOMRectTransform) { const { x, y } = rect.toParentSpace(rect.topLeft); this.#spreadsheet.getCell('A', row)!.expression = Math.round(x); @@ -143,8 +157,11 @@ export class FolkSpaceProjector extends FolkBaseSet { this.#spreadsheet.getCell('B', row)!.expression = Math.round(rect.y); this.#spreadsheet.getCell('C', row)!.expression = Math.round(rect.width); this.#spreadsheet.getCell('D', row)!.expression = Math.round(rect.height); + this.#spreadsheet.getCell('E', row)!.expression = 0; } + row += 1; } + Promise.resolve().then(() => { this.#lock = false; }); diff --git a/src/folk-spreadsheet.ts b/src/folk-spreadsheet.ts index 4492218..15d1fbe 100644 --- a/src/folk-spreadsheet.ts +++ b/src/folk-spreadsheet.ts @@ -106,6 +106,10 @@ const styles = css` justify-content: end; } + ::slotted(folk-cell[readonly]) { + color: grey; + } + ::slotted(folk-cell:hover) { outline: 1px solid #1b73e8; z-index: 5; @@ -132,21 +136,30 @@ export function relativeColumnName(name: string, num: number) { return alphabet[index + num]; } -export function templateCells(numberOfRows: number, numberOfColumns: number, expressions: Record = {}) { - const cells: string[] = []; +export interface CellTemplate { + expression?: string; + readonly?: boolean; +} + +export function templateCells(numberOfRows: number, numberOfColumns: number, cells: Record = {}) { + const html: string[] = []; for (let i = 0; i < numberOfRows; i += 1) { for (let j = 0; j < numberOfColumns; j += 1) { const column = getColumnName(j); const row = i + 1; - const expression = expressions[`${column}${row}`]; - cells.push( - `` + const { expression, readonly } = cells[`${column}${row}`] || {}; + html.push( + `` ); } } - return cells.join('\n'); + return html.join('\n'); } declare global { @@ -168,7 +181,7 @@ export class FolkSpreadsheet extends HTMLElement { #shadow = this.attachShadow({ mode: 'open' }); - #textarea: HTMLTextAreaElement | null = null; + #textarea!: HTMLTextAreaElement; #editedCell: FolkSpreadSheetCell | null = null; @@ -326,7 +339,7 @@ export class FolkSpreadsheet extends HTMLElement { } #focusTextarea(cell: FolkSpreadSheetCell) { - if (this.#textarea === null) return; + if (cell.readonly) return; this.#editedCell = cell; const gridColumn = getColumnIndex(cell.column) + 2; const gridRow = cell.row + 1; @@ -339,7 +352,6 @@ export class FolkSpreadsheet extends HTMLElement { #resetTextarea() { if (this.#editedCell === null) return; - if (this.#textarea === null) return; this.#textarea.style.setProperty('--text-column', '0'); this.#textarea.style.setProperty('--text-row', '0'); this.#editedCell.expression = this.#textarea.value; @@ -471,6 +483,13 @@ export class FolkSpreadSheetCell extends HTMLElement { this.#evaluate(); } + get readonly() { + return this.hasAttribute('readonly'); + } + set readonly(readonly) { + readonly ? this.setAttribute('readonly', '') : this.removeAttribute('readonly'); + } + #value: any; get value() { return this.#value;