readonly spreadsheet cells

This commit is contained in:
“chrisshank” 2024-12-12 19:09:08 -08:00
parent a69840c8ab
commit d2feab9e04
3 changed files with 60 additions and 18 deletions

View File

@ -21,10 +21,16 @@
folk-shape {
background: rgb(187, 178, 178);
}
h1 {
margin: 0;
width: fit-content;
}
</style>
</head>
<body>
<folk-space-projector>
<h1>Spatial projection into a spreadsheet</h1>
<folk-shape x="100" y="100" width="50" height="50"></folk-shape>
<folk-shape x="100" y="200" width="50" height="50"></folk-shape>
<folk-shape x="100" y="300" width="50" height="50" rotation="45"></folk-shape>

View File

@ -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<string, CellTemplate> = {};
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;
});

View File

@ -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<string, string> = {}) {
const cells: string[] = [];
export interface CellTemplate {
expression?: string;
readonly?: boolean;
}
export function templateCells(numberOfRows: number, numberOfColumns: number, cells: Record<string, CellTemplate> = {}) {
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(
`<folk-cell column="${column}" row="${row}" tabindex="0" ${
expression ? `expression="${expression}"` : ''
}></folk-cell>`
const { expression, readonly } = cells[`${column}${row}`] || {};
html.push(
`<folk-cell
column="${column}"
row="${row}"
tabindex="0"
${expression ? `expression="${expression}"` : ''}
${readonly ? 'readonly' : ''}
></folk-cell>`
);
}
}
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;