import { css, PropertyValues } from '@lit/reactive-element'; import { FolkBaseSet } from './folk-base-set'; import { FolkShape } from './folk-shape'; import { CellTemplate, FolkSpreadsheet, FolkSpreadSheetCell, templateCells } from './folk-spreadsheet'; import { DOMRectTransform } from './common/DOMRectTransform'; FolkShape.define(); FolkSpreadsheet.define(); export class FolkSpaceProjector extends FolkBaseSet { static override tagName = 'folk-space-projector'; static override styles = [ FolkBaseSet.styles, css` folk-spreadsheet { position: absolute; bottom: 15px; right: 15px; pointer-events: auto; box-shadow: 0px 3px 5px 0px rgba(173, 168, 168, 0.6); } `, ]; #spreadsheet = document.createElement('folk-spreadsheet'); override createRenderRoot() { const root = super.createRenderRoot(); this.#spreadsheet.style.setProperty('--cell-width', '50px'); root.append(this.#spreadsheet); return root; } connectedCallback(): void { super.connectedCallback(); this.#spreadsheet.addEventListener('propagate', this.#onPropagate); this.#spreadsheet.addEventListener('focusin', this.#onFocusin); this.#spreadsheet.addEventListener('focusout', this.#onFocusout); } disconnectedCallback(): void { super.disconnectedCallback(); this.#spreadsheet.removeEventListener('propagate', this.#onPropagate); this.#spreadsheet.removeEventListener('focusin', this.#onFocusin); this.#spreadsheet.removeEventListener('focusout', this.#onFocusout); } #lock = false; #onPropagate = (event: Event) => { const cell = event.target as FolkSpreadSheetCell; if (this.#lock && cell.dependencies.length === 0) return; const shape = this.sourceElements .values() .drop(cell.row - 1) .find(() => true); if (!(shape instanceof FolkShape)) return; // beware of infinite event loop switch (cell.column) { case 'A': { shape.x = cell.value; return; } case 'B': { shape.y = cell.value; return; } case 'C': { shape.width = cell.value; return; } case 'D': { shape.height = cell.value; return; } case 'E': { shape.rotation = (cell.value * Math.PI) / 180; return; } } }; #onFocusin = (event: Event) => { const cell = event.target; if (!(cell instanceof FolkSpreadSheetCell)) return; const shape = this.sourceElements .values() .drop(cell.row - 1) .find(() => true); if (!(shape instanceof FolkShape)) return; shape.highlighted = true; }; #onFocusout = (event: Event) => { const cell = event.target; if (!(cell instanceof FolkSpreadSheetCell)) return; const shape = this.sourceElements .values() .drop(cell.row - 1) .find(() => true); if (!(shape instanceof FolkShape)) return; shape.highlighted = false; }; override update(changedProperties: PropertyValues): void { super.update(changedProperties); if (changedProperties.has('sourceElements')) { 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; 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); updateCell(this.#spreadsheet.getCell('A', row)!, x); updateCell(this.#spreadsheet.getCell('B', row)!, y); updateCell(this.#spreadsheet.getCell('C', row)!, rect.width); updateCell(this.#spreadsheet.getCell('D', row)!, rect.height); updateCell(this.#spreadsheet.getCell('E', row)!, (rect.rotation * 180) / Math.PI); } else { updateCell(this.#spreadsheet.getCell('A', row)!, rect.x); updateCell(this.#spreadsheet.getCell('B', row)!, rect.y); updateCell(this.#spreadsheet.getCell('C', row)!, rect.width); updateCell(this.#spreadsheet.getCell('D', row)!, rect.height); updateCell(this.#spreadsheet.getCell('E', row)!, 0); } row += 1; } Promise.resolve().then(() => { this.#lock = false; }); } } function updateCell(cell: FolkSpreadSheetCell, expression: number) { if (cell.dependencies.length) return; cell.expression = Math.round(expression); }