diff options
Diffstat (limited to 'web/src/components')
| -rw-r--r-- | web/src/components/app/index.ts | 47 | ||||
| -rw-r--r-- | web/src/components/grid/index.ts | 11 | ||||
| -rw-r--r-- | web/src/components/grid/selection.ts | 5 | ||||
| -rw-r--r-- | web/src/components/toolbar/index.css | 2 | ||||
| -rw-r--r-- | web/src/components/toolbar/index.ts | 62 |
5 files changed, 114 insertions, 13 deletions
diff --git a/web/src/components/app/index.ts b/web/src/components/app/index.ts index aa7c738..a2c0c9d 100644 --- a/web/src/components/app/index.ts +++ b/web/src/components/app/index.ts @@ -1,5 +1,10 @@ +import { produce } from "immer"; import defaultDoc from "../../defaultDoc"; import NotiveElement, { customElement } from "../../element"; +import { + changeSelectedSubdivisions, + getSelectedSubdivisionsCount, +} from "../../grid"; import { Doc } from "../../types"; import ntvGrid, { NotiveGridElement } from "../grid"; import renderGrid from "../grid/renderGrid"; @@ -15,9 +20,21 @@ export class NotiveAppElement extends NotiveElement { #selection?: GridSelection; setSelection(gridId: string, selection: GridSelection) { + const grid = this.doc.grids.find((grid) => grid.id === gridId); + if (!grid) throw new Error("Invalid grid ID"); + this.#selectedGridId = gridId; this.#selection = selection; this.#updateGridSelections(); + + this.#toolbar.subdivisions = getSelectedSubdivisionsCount(grid, selection); + } + + clearSelection() { + this.#selectedGridId = undefined; + this.#selection = undefined; + this.#updateGridSelections(); + this.#toolbar.subdivisions = undefined; } #updateGridSelections() { @@ -27,10 +44,36 @@ export class NotiveAppElement extends NotiveElement { }); } + #toolbar = ntvToolbar({ + onsubdivisionschange: ({ subdivisions }) => { + if (!subdivisions) return; + + const gridId = this.#selectedGridId; + const selection = this.#selection; + + if (!gridId || !selection) return; + + const gridIndex = this.doc.grids.findIndex((grid) => grid.id === gridId); + + this.doc = produce(this.doc, (doc) => { + doc.grids[gridIndex] = changeSelectedSubdivisions( + this.doc.grids[gridIndex], + selection, + subdivisions, + ); + }); + + this.querySelector<NotiveGridElement>( + `ntv-grid[data-grid-id="${gridId}"]`, + )!.grid = renderGrid(this.doc.grids[gridIndex]); + + this.clearSelection(); + }, + }); + connectedCallback() { this.append( - ntvToolbar(), - + this.#toolbar, ...this.doc.grids.map((grid) => ntvGrid({ grid: renderGrid(grid), diff --git a/web/src/components/grid/index.ts b/web/src/components/grid/index.ts index 78bb14e..3189409 100644 --- a/web/src/components/grid/index.ts +++ b/web/src/components/grid/index.ts @@ -12,7 +12,16 @@ import { extendSelection, GridSelection } from "./selection"; export class NotiveGridElement extends NotiveElement { #internals: ElementInternals = this.attachInternals(); - grid?: RenderedGrid; + #grid?: RenderedGrid; + + get grid(): RenderedGrid | undefined { + return this.#grid; + } + + set grid(grid: RenderedGrid | undefined) { + this.#grid = grid; + this.draw(); + } #selection?: GridSelection; diff --git a/web/src/components/grid/selection.ts b/web/src/components/grid/selection.ts index a24bbf5..517f8ae 100644 --- a/web/src/components/grid/selection.ts +++ b/web/src/components/grid/selection.ts @@ -1,4 +1,5 @@ import { CellRef, cellRefEquals } from "../../types"; +import { RenderedGrid } from "./renderGrid"; export type CellRange = [start: CellRef, end: CellRef]; @@ -21,3 +22,7 @@ export function extendSelection( return { ...selection, range: [selection.activeCellRef, cellRef] }; } + +export function getSelectionRange(selection: GridSelection): CellRange { + return selection.range ?? [selection.activeCellRef, selection.activeCellRef]; +} diff --git a/web/src/components/toolbar/index.css b/web/src/components/toolbar/index.css index e082f7d..653c326 100644 --- a/web/src/components/toolbar/index.css +++ b/web/src/components/toolbar/index.css @@ -18,11 +18,13 @@ padding: 0 0.5rem; height: 1.25rem; color: white; + font-weight: 600; font-size: 0.75rem; } ntv-toolbar button:hover { background: var(--color-green-400); + color: var(--color-neutral-900); } ntv-toolbar button[data-icon] { diff --git a/web/src/components/toolbar/index.ts b/web/src/components/toolbar/index.ts index da4b69d..b8a383d 100644 --- a/web/src/components/toolbar/index.ts +++ b/web/src/components/toolbar/index.ts @@ -1,24 +1,66 @@ -import NotiveElement, { customElement } from "../../element"; -import h, { fragment } from "../../html"; +import NotiveElement, { customElement, eventHandler } from "../../element"; +import h from "../../html"; +import { minus16Icon, plus16Icon } from "../icons"; import "./index.css"; -@customElement("ntv-toolbar") -class NotiveToolbarElement extends NotiveElement { - connectedCallback() { - this.append(this.#view()); +export class SubdivisionsChangeEvent extends Event { + static readonly TYPE = "ntv:toolbar:subdivisionschange"; + + constructor(public subdivisions: number | undefined) { + super(SubdivisionsChangeEvent.TYPE); } +} +@customElement("ntv-toolbar") +class NotiveToolbarElement extends NotiveElement { #subdivisionsInputEl: HTMLInputElement = h.input({ title: "Subdivisions", disabled: true, }); - #view() { - return fragment( + get subdivisions(): number | undefined { + if (this.#subdivisionsInputEl.value === "") return; + return parseInt(this.#subdivisionsInputEl.value); + } + + set subdivisions(n: number | undefined) { + const m = n && Math.max(n, 1); + this.#subdivisionsInputEl.value = m === undefined ? "" : m.toString(); + } + + @eventHandler(SubdivisionsChangeEvent.TYPE) + onsubdivisionschange?: (event: SubdivisionsChangeEvent) => any; + + connectedCallback() { + this.append( h.section( - h.button({ dataset: { icon: "" } }, "-"), + h.button( + { + dataset: { icon: "" }, + onclick: () => { + if (!this.subdivisions) return; + this.subdivisions = this.subdivisions - 1; + this.dispatchEvent( + new SubdivisionsChangeEvent(this.subdivisions), + ); + }, + }, + h.span(minus16Icon()), + ), this.#subdivisionsInputEl, - h.button({ dataset: { icon: "" } }, "+"), + h.button( + { + dataset: { icon: "" }, + onclick: () => { + if (!this.subdivisions) return; + this.subdivisions = this.subdivisions + 1; + this.dispatchEvent( + new SubdivisionsChangeEvent(this.subdivisions), + ); + }, + }, + h.span(plus16Icon()), + ), ), h.section(h.button("Play")), ); |
