diff --git a/src/common/dialog/DocumentPickerDialog.svelte b/src/common/dialog/DocumentPickerDialog.svelte index c18d95707..e4a57a725 100644 --- a/src/common/dialog/DocumentPickerDialog.svelte +++ b/src/common/dialog/DocumentPickerDialog.svelte @@ -1,5 +1,5 @@ {#if $modification.modifyHasSelection} @@ -27,28 +30,28 @@ {/if} - {#if $modification.modifyNumSelected < $modification.pageCount} - {/if} - - {#if $modification.modifyNumSelected < $modification.pageCount} - {/if} @@ -116,15 +119,15 @@

{#if $modification.hasInsert} - {:else} - {/if} -
@@ -134,7 +137,7 @@ - @@ -151,7 +154,7 @@ $modification.undo()}>{$_("modifyPane.undo")} $modification.redo()}>{$_("modifyPane.redo")} {/if} diff --git a/src/viewer/layout.js b/src/viewer/layout.js index c52eed7e5..b846fab9a 100644 --- a/src/viewer/layout.js +++ b/src/viewer/layout.js @@ -21,7 +21,7 @@ import { import { Note } from "@/structure/note.js"; import { DEFAULT_EXPAND } from "../api/common.js"; import { inIframe } from "@/util/iframe.js"; -import { modification } from "./modification/modification.js"; +import { modification } from "./modification/modification.ts"; import { MOBILE_BREAKPOINT, diff --git a/src/viewer/modification/modification.test.js b/src/viewer/modification/modification.test.ts similarity index 93% rename from src/viewer/modification/modification.test.js rename to src/viewer/modification/modification.test.ts index 7ac6230ad..f41983da0 100644 --- a/src/viewer/modification/modification.test.js +++ b/src/viewer/modification/modification.test.ts @@ -1,7 +1,10 @@ -import { modification } from "./modification.js"; -import { ModificationSpec } from "./modifySpec.js"; +import { Modification } from "./modification"; +import { ModificationSpec } from "./modifySpec"; + +let modification; beforeEach(() => { + modification = new Modification(); modification.clear(); modification.initSpec(ModificationSpec.getDocument(3)); }); diff --git a/src/viewer/modification/modification.js b/src/viewer/modification/modification.ts similarity index 54% rename from src/viewer/modification/modification.js rename to src/viewer/modification/modification.ts index f44a04a98..902685261 100644 --- a/src/viewer/modification/modification.js +++ b/src/viewer/modification/modification.ts @@ -1,102 +1,124 @@ -import { Svue } from "svue"; +import { Writable, writable } from "svelte/store"; import { runify, Empty, Individual, ModificationSpec, CLOCKWISE, -} from "./modifySpec.js"; +} from "./modifySpec"; + +// TODO replace with typed and de-Svue'd `structure/Document` +interface Document { + pageCount: number; + id: string; +} + +export class Modification { + copyBuffer: ModificationSpec | null; + modifySelectedMap: Record; + rewind: boolean; + insert: null; + history: ModificationSpec[]; + historyPosition: number; + insertDocument: Document | null; + documentCache: Record; -class Modification extends Svue { constructor() { - super({ - data() { - return { - copyBuffer: null, - modifySelectedMap: {}, - rewind: false, - insert: null, - history: [], - historyPosition: 0, - insertDocument: null, - documentCache: {}, - }; - }, - computed: { - historyLength(history) { - return history.length; - }, - modifySpec(history, historyLength, historyPosition) { - if (historyLength == 0) return null; - return history[historyPosition]; - }, - pageCount(modifySpec) { - if (modifySpec == null) return 0; - return modifySpec.length(); - }, - modifySelected(modifySelectedMap) { - const results = []; - for (let key in modifySelectedMap) { - if ( - modifySelectedMap.hasOwnProperty(key) && - modifySelectedMap[key] == true - ) { - results.push(key); - } - } - return results; - }, - modifyNumSelected(modifySelected) { - return modifySelected.length; - }, - modifyHasSelection(modifyNumSelected) { - const hasSelection = modifyNumSelected > 0; - if (hasSelection) { - this.clearInsertion(); - } - return hasSelection; - }, - modifySelectedPageSpec(modifySelected) { - return runify(modifySelected); - }, - modifySelectedSpec(modifySpec, modifySelectedPageSpec) { - if (modifySpec == null) return null; - const selectedSpec = new ModificationSpec( - modifySelectedPageSpec.specs.reduce((prev, spec) => { - if (spec instanceof Empty) return prev; - if (spec instanceof Individual) - return prev.concat(modifySpec.slice(spec.pg, 1).specs); - return prev.concat( - modifySpec.slice(spec.start, spec.length()).specs, - ); - }, []), - ).compress(); - return selectedSpec; - }, - hasInsert(insert) { - return insert != null; - }, - hasCopyBuffer(copyBuffer) { - return copyBuffer != null; - }, - copyBufferLength(copyBuffer) { - if (copyBuffer == null) return 0; - return copyBuffer.length(); - }, - canUndo(historyPosition) { - return historyPosition > 0; - }, - canRedo(historyLength, historyPosition) { - return historyPosition + 1 < historyLength; - }, - hasHistory(historyLength) { - return historyLength > 1; - }, - uncommittedChanges(historyPosition) { - return historyPosition > 0; - }, - }, - }); + this.copyBuffer = null; + this.modifySelectedMap = {}; + this.rewind = false; + this.insert = null; + this.history = []; + this.historyPosition = 0; + this.insertDocument = null; + this.documentCache = null; + } + + get historyLength() { + return this.history.length; + } + + get modifySpec() { + if (this.historyLength === 0) return null; + return this.history[this.historyPosition]; + } + + get pageCount() { + if (this.modifySpec === null) return 0; + return this.modifySpec.length(); + } + + get modifySelected() { + const results = []; + for (let key in this.modifySelectedMap) { + if ( + this.modifySelectedMap.hasOwnProperty(key) && + this.modifySelectedMap[key] == true + ) { + results.push(key); + } + } + return results; + } + + get modifyNumSelected() { + return this.modifySelected.length; + } + + get modifyHasSelection() { + const hasSelection = this.modifyNumSelected > 0; + if (hasSelection) { + this.clearInsertion(); + } + return hasSelection; + } + + get modifySelectedPageSpec() { + return runify(this.modifySelected); + } + + get modifySelectedSpec() { + if (this.modifySpec == null) return null; + const selectedSpec = new ModificationSpec( + this.modifySelectedPageSpec.specs.reduce((prev, spec) => { + if (spec instanceof Empty) return prev; + if (spec instanceof Individual) + return prev.concat(this.modifySpec.slice(spec.pg, 1).specs); + return prev.concat( + this.modifySpec.slice(spec.start, spec.length()).specs, + ); + }, []), + ).compress(); + return selectedSpec; + } + + get hasInsert() { + return this.insert != null; + } + + get hasCopyBuffer() { + return this.copyBuffer != null; + } + + get copyBufferLength() { + if (this.copyBuffer === null) return 0; + return this.copyBuffer.length(); + } + + get canUndo() { + return this.historyPosition > 0; + } + + get canRedo() { + return this.historyPosition + 1 < this.historyLength; + } + + get hasHistory() { + return this.historyLength > 1; + } + + get uncommittedChanges() { + return this.historyPosition > 0; } modifyUnselect() { @@ -250,4 +272,6 @@ class Modification extends Svue { } } -export const modification = new Modification(); +export const modification: Writable = writable( + new Modification(), +); diff --git a/src/viewer/modification/modifySpec.test.js b/src/viewer/modification/modifySpec.test.ts similarity index 100% rename from src/viewer/modification/modifySpec.test.js rename to src/viewer/modification/modifySpec.test.ts diff --git a/src/viewer/modification/modifySpec.js b/src/viewer/modification/modifySpec.ts similarity index 94% rename from src/viewer/modification/modifySpec.js rename to src/viewer/modification/modifySpec.ts index 863f949d1..73652a22a 100644 --- a/src/viewer/modification/modifySpec.js +++ b/src/viewer/modification/modifySpec.ts @@ -1,4 +1,4 @@ -import { arrayEq } from "@/util/array.js"; +import { arrayEq } from "../../util/array.js"; export class Empty { constructor() {} @@ -11,7 +11,7 @@ export class Empty { return ""; } - index(_) { + index(_: unknown) { return null; } @@ -19,13 +19,16 @@ export class Empty { return 0; } - slice(_a, _b) { + slice(_a: unknown, _b: unknown) { return new Empty(); } } export class Range { - constructor(startInclusive, endInclusive) { + start: number; + end: number; + + constructor(startInclusive: number, endInclusive: number) { this.start = startInclusive; this.end = endInclusive; } @@ -42,7 +45,7 @@ export class Range { return `${this.start}-${this.end}`; } - index(i) { + index(i: number) { return this.start + i; } @@ -50,7 +53,7 @@ export class Range { return this.end - this.start + 1; } - slice(i, l) { + slice(i: number, l: number) { const start = this.start + i; const end = this.start + i + l - 1; if (start == end) return new Individual(start); @@ -60,7 +63,9 @@ export class Range { } export class Individual { - constructor(pg) { + pg: number; + + constructor(pg: number) { this.pg = pg; } @@ -80,7 +85,7 @@ export class Individual { return 1; } - slice(i, l) { + slice(i: number, l: number) { if (i != 0 || l < 1) { return new Empty(); } @@ -89,6 +94,7 @@ export class Individual { } export class PageSpec { + specs: (Individual | Range)[]; constructor(specs) { this.specs = specs; } @@ -289,9 +295,13 @@ const ROTATION_MATRIX = { [HALFWAY]: null, }, }; + export const ROTATE = "rotate"; + export class Rotation { - constructor(angle) { + type: typeof ROTATE; + angle: typeof CLOCKWISE | typeof COUNTER_CLOCKWISE | typeof HALFWAY; + constructor(angle: "cc" | "ccw" | "hw") { this.type = ROTATE; this.angle = angle; } @@ -303,12 +313,17 @@ export class Rotation { }; } - eq(other) { + eq(other: Rotation) { return other.type == ROTATE && other.angle == this.angle; } } +type Modification = Rotation; + export class ModificationDescriptor { + pageSpec: PageSpec; + modifications: Modification[]; + id: string | null; constructor(pageSpec, modifications = [], id = null) { this.pageSpec = pageSpec; this.modifications = modifications; @@ -353,7 +368,11 @@ export class ModificationDescriptor { } json() { - const json = {}; + const json: { + id?: string; + page?: string; + modifications?: Modification[]; + } = {}; json.page = this.pageSpec.spec(); if (this.id != null) json.id = this.id; if (this.modifications.length > 0) json.modifications = this.modifications; @@ -438,6 +457,8 @@ export class ModificationDescriptor { } export class ModificationSpec { + specs: ModificationDescriptor[]; + pageSpec: PageSpec; constructor(specs) { this.specs = specs;