Add logic for (de)serializing pages
This commit is contained in:
parent
ac4a5e8055
commit
bcd7628c0a
@ -63,7 +63,7 @@ Dark.set(true)
|
||||
<span style="font-family: 'Cinzel Decorative', cursive;">
|
||||
Crystal Math Worktable
|
||||
</span>
|
||||
<q-icon icon="fa-solid fa-arrow-up-left-from-circle" v-if="status" @click="logout()" label="Logout" />
|
||||
<q-btn icon="fa-solid fa-arrow-up-left-from-circle" v-if="status" @click="logout()" label="Logout" />
|
||||
</q-toolbar-title>
|
||||
</q-toolbar>
|
||||
|
||||
|
@ -18,7 +18,12 @@ import { stepX, stepY } from '../support/const'
|
||||
import { checkLoggedIn, loggedOut } from '../support/auth'
|
||||
import router from '../router'
|
||||
|
||||
const math = new MathPage(uuidv4());
|
||||
const props = defineProps<{
|
||||
pageId?: number,
|
||||
}>()
|
||||
|
||||
const math = ref(new MathPage(uuidv4()));
|
||||
const pageTitle = ref('New Page');
|
||||
const statements = ref<MathStatement[]>([]);
|
||||
const evaluation = ref<EvaluationResult | undefined>();
|
||||
const statementsKey = ref<string>(uuidv4());
|
||||
@ -99,9 +104,9 @@ const openEditFunctionModal = () => {
|
||||
}
|
||||
|
||||
const updateStatements = () => {
|
||||
statements.value = math.getStatements();
|
||||
statements.value = math.value.getStatements();
|
||||
try {
|
||||
evaluation.value = math.evaluate()
|
||||
evaluation.value = math.value.evaluate()
|
||||
const variableValues: ({ name: string, value: string })[] = []
|
||||
|
||||
for (const name in evaluation.value!.variables) {
|
||||
@ -124,7 +129,7 @@ const updateStatements = () => {
|
||||
variableListingRows.value = variableValues
|
||||
|
||||
const functionValues: ({ name: string, value: string })[] = []
|
||||
for (const stmt of math.functions()) {
|
||||
for (const stmt of math.value.functions()) {
|
||||
const node = stmt.parse() as math.FunctionAssignmentNode
|
||||
functionValues.push({
|
||||
name: node.name,
|
||||
@ -145,19 +150,19 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
const saveNewVariable = (stmt: MathStatement) => {
|
||||
math.addStatement(stmt)
|
||||
math.value.addStatement(stmt)
|
||||
newVariableModalOpen.value = false
|
||||
updateStatements()
|
||||
};
|
||||
|
||||
const saveNewExpression = (stmt: MathStatement) => {
|
||||
math.addStatement(stmt)
|
||||
math.value.addStatement(stmt)
|
||||
newExpressionModalOpen.value = false
|
||||
updateStatements()
|
||||
};
|
||||
|
||||
const saveNewFunction = (stmt: MathStatement) => {
|
||||
math.addStatement(stmt)
|
||||
math.value.addStatement(stmt)
|
||||
newFunctionModalOpen.value = false
|
||||
updateStatements()
|
||||
}
|
||||
@ -175,7 +180,7 @@ const editStatement = (stmt: MathStatement) => {
|
||||
}
|
||||
|
||||
const removeStatement = (stmt: MathStatement) => {
|
||||
math.removeStatement(stmt.id)
|
||||
math.value.removeStatement(stmt.id)
|
||||
updateStatements()
|
||||
}
|
||||
|
||||
@ -323,6 +328,79 @@ onMounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const serialize = () => {
|
||||
return {
|
||||
title: pageTitle.value,
|
||||
page: math.value.serialize(),
|
||||
textBoxes: richTextStatements.value.map(x => x.serialize()),
|
||||
chartBoxes: chartBoxes.value.map(x => x.serialize()),
|
||||
imageBoxes: images.value.map(x => x.serialize()),
|
||||
}
|
||||
}
|
||||
|
||||
const editorPageId = ref<number|undefined>()
|
||||
const saveEditorPage = async () => {
|
||||
const params = {
|
||||
serialData: JSON.stringify(serialize()),
|
||||
} as any
|
||||
|
||||
if ( editorPageId.value ) {
|
||||
params.pageId = editorPageId.value
|
||||
}
|
||||
|
||||
const response = await fetch('/api/editor/page', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
|
||||
const result = (await response.json()) as unknown as any
|
||||
if (!result.success) {
|
||||
return alert('Failed to save page: ' + result.message)
|
||||
}
|
||||
|
||||
editorPageId.value = result.data.pageId
|
||||
}
|
||||
|
||||
const loadEditorPage = async () => {
|
||||
if ( !editorPageId.value ) {
|
||||
return
|
||||
}
|
||||
|
||||
const pageId = editorPageId.value
|
||||
const response = await fetch(`/api/editor/page?pageId=${pageId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
})
|
||||
|
||||
const result = (await response.json()) as unknown as any
|
||||
if (!result.success) {
|
||||
return alert('Failed to load page: ' + result.message)
|
||||
}
|
||||
|
||||
const serialData = result.data.serialData
|
||||
const serialized = JSON.parse(serialData) as any
|
||||
math.value = MathPage.deserialize(serialized.page)
|
||||
richTextStatements.value = serialized.textBoxes.map((box: any) => RichTextBox.deserialize(box))
|
||||
chartBoxes.value = serialized.chartBoxes.map((box: any) => ChartBox.deserialize(box))
|
||||
images.value = serialized.imageBoxes.map((box: any) => ImageContainer.deserialize(box))
|
||||
pageTitle.value = serialized.title ?? 'New Page'
|
||||
updateStatements()
|
||||
chartBoxKey.value = uuidv4()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if ( props.pageId ) {
|
||||
editorPageId.value = props.pageId
|
||||
loadEditorPage()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -335,7 +413,9 @@ onMounted(() => {
|
||||
<q-avatar size="50px">
|
||||
<img src="../assets/l2.svg" />
|
||||
</q-avatar>
|
||||
<span style="font-family: 'Cinzel Decorative', cursive;">Crystal Math Worktable</span>
|
||||
<span style="font-family: 'Cinzel Decorative', cursive;">
|
||||
Crystal Math Worktable
|
||||
</span>
|
||||
</q-toolbar-title>
|
||||
|
||||
<span v-if="status" @click="logout()" label="Logout">Logout</span>
|
||||
@ -344,6 +424,13 @@ onMounted(() => {
|
||||
|
||||
<q-drawer show-if-above v-model="leftDrawerOpen" side="left" bordered>
|
||||
<div class="column" style="height: 100%">
|
||||
<div>
|
||||
<q-input
|
||||
v-model="pageTitle"
|
||||
label="Title"
|
||||
style="padding: 10px"
|
||||
></q-input>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-table
|
||||
flat
|
||||
@ -394,9 +481,7 @@ onMounted(() => {
|
||||
<!-- drawer content -->
|
||||
</q-drawer>
|
||||
|
||||
<q-page-container id="editor" style="padding=0">
|
||||
<!-- <WrapperBox />-->
|
||||
|
||||
<q-page-container id="editor" style='padding: 0'>
|
||||
<span v-for="(chartBox, index) in chartBoxes" style="display: flex">
|
||||
<RangeChart
|
||||
:fn="math.getFunctionByNameOrFail(chartBox.fnName)"
|
||||
@ -463,6 +548,10 @@ onMounted(() => {
|
||||
/>
|
||||
</q-dialog>
|
||||
|
||||
<q-page-sticky position="top-right" :offset="[18, 18]">
|
||||
<q-btn fab icon="save" color="primary" @click="() => saveEditorPage()"/>
|
||||
</q-page-sticky>
|
||||
|
||||
<q-page-sticky position="bottom-right" :offset="[32, 32]">
|
||||
<q-fab color="primary" icon="add" direction="left">
|
||||
<q-fab-action
|
||||
|
@ -11,11 +11,28 @@ export class MathPage {
|
||||
/** The statements on the page. */
|
||||
protected statements: Record<StatementID, MathStatement> = {}
|
||||
|
||||
static deserialize(vals: [string, [StatementID, string, number, number][]]) {
|
||||
const inst = new this(vals[0])
|
||||
|
||||
for ( const row of vals[1] ) {
|
||||
inst.statements[row[0]] = MathStatement.deserialize(row)
|
||||
}
|
||||
|
||||
return inst
|
||||
}
|
||||
|
||||
constructor(
|
||||
/** Unique page ID. */
|
||||
public readonly id: string,
|
||||
) {}
|
||||
|
||||
serialize(): [string, [StatementID, string, number, number][]] {
|
||||
return [
|
||||
this.id,
|
||||
Object.values(this.statements).map(x => x.serialize()),
|
||||
]
|
||||
}
|
||||
|
||||
/** Get all defined statements. */
|
||||
getStatements(): MathStatement[] {
|
||||
return Object.values(this.statements)
|
||||
|
@ -264,6 +264,10 @@ export class MathStatement {
|
||||
return new MathStatement(uuidv4() as StatementID, raw)
|
||||
}
|
||||
|
||||
static deserialize(vals: [StatementID, string, number, number]) {
|
||||
return new this(...vals)
|
||||
}
|
||||
|
||||
constructor(
|
||||
/** Unique ID of this statement. */
|
||||
public readonly id: StatementID,
|
||||
@ -278,6 +282,15 @@ export class MathStatement {
|
||||
public y: number = 0,
|
||||
) {}
|
||||
|
||||
serialize(): [StatementID, string, number, number] {
|
||||
return [
|
||||
this.id,
|
||||
this.raw,
|
||||
this.x,
|
||||
this.y,
|
||||
]
|
||||
}
|
||||
|
||||
/** Parse the raw statement to an AST. */
|
||||
parse(): math.MathNode {
|
||||
return math.parse(this.raw)
|
||||
|
@ -94,24 +94,52 @@ export interface EvaluationResult {
|
||||
|
||||
|
||||
export class RichTextBox {
|
||||
static deserialize(vals: [string, number, number]) {
|
||||
return new this(...vals)
|
||||
}
|
||||
|
||||
constructor(
|
||||
public text: string = '',
|
||||
public x: number = 0,
|
||||
public y: number = 0,
|
||||
) {}
|
||||
|
||||
serialize(): [string, number, number] {
|
||||
return [
|
||||
this.text,
|
||||
this.x,
|
||||
this.y,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ImageContainer {
|
||||
static deserialize(vals: [string, number, number]) {
|
||||
return new this(...vals)
|
||||
}
|
||||
|
||||
constructor(
|
||||
public url: string = '',
|
||||
public x: number = 0,
|
||||
public y: number = 0,
|
||||
) {}
|
||||
|
||||
serialize(): [string, number, number] {
|
||||
return [
|
||||
this.url,
|
||||
this.x,
|
||||
this.y,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class ChartBox {
|
||||
static deserialize(vals: [string, number, number, number, number, number]) {
|
||||
return new this(...vals)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-params
|
||||
constructor(
|
||||
public fnName: string,
|
||||
@ -121,6 +149,17 @@ export class ChartBox {
|
||||
public x: number = 0,
|
||||
public y: number = 0,
|
||||
) {}
|
||||
|
||||
serialize(): [string, number, number, number, number, number] {
|
||||
return [
|
||||
this.fnName,
|
||||
this.minX,
|
||||
this.maxX,
|
||||
this.stepX,
|
||||
this.x,
|
||||
this.y,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user