Merge conflict
This commit is contained in:
commit
29c2885475
@ -54,7 +54,7 @@ Dark.set(true)
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-layout view="hHr LpR fFf">
|
<q-layout view="hHr LpR fFf">
|
||||||
<q-header elevated class="bg-primary text-white" height-hint="98">
|
<q-header elevated class="bg-primary text-white" >
|
||||||
<q-toolbar>
|
<q-toolbar>
|
||||||
<q-toolbar-title>
|
<q-toolbar-title>
|
||||||
<q-avatar size="50px">
|
<q-avatar size="50px">
|
||||||
@ -63,8 +63,7 @@ Dark.set(true)
|
|||||||
<span style="font-family: 'Cinzel Decorative', cursive;">
|
<span style="font-family: 'Cinzel Decorative', cursive;">
|
||||||
Crystal Math Worktable
|
Crystal Math Worktable
|
||||||
</span>
|
</span>
|
||||||
<q-tab v-if="status" @click="logout()" label="Logout" />
|
<q-icon icon="fa-solid fa-arrow-up-left-from-circle" v-if="status" @click="logout()" label="Logout" />
|
||||||
|
|
||||||
</q-toolbar-title>
|
</q-toolbar-title>
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ Dark.set(true)
|
|||||||
</q-tabs> -->
|
</q-tabs> -->
|
||||||
</q-header>
|
</q-header>
|
||||||
|
|
||||||
<q-page-container style="display: flex;padding-top: 20px;">
|
<q-page-container style="display: flex;padding-top: 0px;">
|
||||||
<router-view />
|
<router-view />
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
</q-layout>
|
</q-layout>
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
<defs>
|
<defs>
|
||||||
<pattern id="smallGrid" width="25" height="25" patternUnits="userSpaceOnUse">
|
<pattern id="smallGrid" width="25" height="25" patternUnits="userSpaceOnUse">
|
||||||
<!-- <path d="M 25 0 L 0 0 0 25" fill="none" stroke="gray" stroke-width="0.5"/> -->
|
<!-- <path d="M 25 0 L 0 0 0 25" fill="none" stroke="gray" stroke-width="0.5"/> -->
|
||||||
<circle cx="0.5" cy=".5" r=".25" stroke="gray" fill="transparent" stroke-width="1"/>
|
<circle cx="0.5" cy=".5" r=".25" stroke="#bcbcbc" fill="transparent" stroke-width="1"/>
|
||||||
</pattern>
|
</pattern>
|
||||||
<pattern id="grid" width="250" height="250" patternUnits="userSpaceOnUse">
|
<pattern id="grid" width="250" height="250" patternUnits="userSpaceOnUse">
|
||||||
<rect width="250" height="250" fill="url(#smallGrid)"/>
|
<rect width="250" height="250" fill="url(#smallGrid)"/>
|
||||||
<path d="M 250 0 L 0 0 0 250" fill="none" stroke="gray" stroke-width="2"/>
|
<path d="M 250 0 L 0 0 0 250" fill="none" stroke="#f5f5f5" stroke-width="2"/>
|
||||||
</pattern>
|
</pattern>
|
||||||
</defs>
|
</defs>
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 679 B |
@ -19,20 +19,20 @@
|
|||||||
bordercolor="#000000"
|
bordercolor="#000000"
|
||||||
borderopacity="0.25"
|
borderopacity="0.25"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:pageopacity="0.0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pagecheckerboard="0"
|
inkscape:pagecheckerboard="true"
|
||||||
inkscape:deskcolor="#d1d1d1"
|
inkscape:deskcolor="#d1d1d1"
|
||||||
inkscape:document-units="mm"
|
inkscape:document-units="mm"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="0.51843089"
|
inkscape:zoom="0.733172"
|
||||||
inkscape:cx="-180.35191"
|
inkscape:cx="497.15483"
|
||||||
inkscape:cy="555.52245"
|
inkscape:cy="634.23044"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1001"
|
inkscape:window-height="1001"
|
||||||
inkscape:window-x="-7"
|
inkscape:window-x="-7"
|
||||||
inkscape:window-y="-7"
|
inkscape:window-y="-7"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="layer2">
|
inkscape:current-layer="g5278">
|
||||||
<inkscape:grid
|
<inkscape:grid
|
||||||
type="xygrid"
|
type="xygrid"
|
||||||
id="grid59924" />
|
id="grid59924" />
|
||||||
@ -49,30 +49,29 @@
|
|||||||
inkscape:label="Text"
|
inkscape:label="Text"
|
||||||
transform="matrix(0.26458333,0,0,0.26458333,10.166581,13.777983)"
|
transform="matrix(0.26458333,0,0,0.26458333,10.166581,13.777983)"
|
||||||
style="fill:#550000">
|
style="fill:#550000">
|
||||||
<text
|
<g
|
||||||
xml:space="preserve"
|
id="g5278">
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:969.291px;font-family:'Cinzel Decorative';-inkscape-font-specification:'Cinzel Decorative Bold';fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4.8;stroke-dasharray:none;stroke-opacity:1"
|
<g
|
||||||
x="-37.659836"
|
aria-label="C"
|
||||||
y="831.96069"
|
transform="scale(0.96764101,1.0334411)"
|
||||||
id="text6305"
|
id="text6305"
|
||||||
transform="scale(0.96764101,1.0334411)"><tspan
|
style="font-weight:bold;font-size:969.291px;font-family:'Cinzel Decorative';-inkscape-font-specification:'Cinzel Decorative Bold';fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4.8">
|
||||||
sodipodi:role="line"
|
<path
|
||||||
id="tspan6303"
|
d="m 734.8651,491.73955 q 0,166.71805 -90.14406,260.73928 -89.17477,93.05194 -252.98496,93.05194 -110.49917,0 -199.67395,-46.52597 Q 103.85665,752.47883 54.422811,672.02768 5.9582599,591.57652 5.9582599,492.70884 q 0,-99.83698 48.4645511,-180.28813 48.464549,-80.45116 133.762159,-126.00783 85.29761,-46.52597 189.98104,-46.52597 65.91179,0 133.76216,18.41653 68.81966,17.44724 114.37634,47.49526 l 19.38582,136.67003 h -8.72362 Q 616.6116,261.04829 542.94548,215.49161 469.27937,168.96564 379.1353,168.96564 q -128.9157,0 -215.1826,92.08265 -85.297614,91.11335 -85.297614,229.72197 0,138.60861 88.205484,232.62984 89.17477,93.05194 232.62984,93.05194 143.45507,0 233.59914,-93.05194 91.11335,-93.05194 91.11335,-254.92354 h 9.69291 q 0.96929,11.63149 0.96929,23.26299 z"
|
||||||
x="-37.659836"
|
style="font-weight:normal;-inkscape-font-specification:'Cinzel Decorative'"
|
||||||
y="831.96069"
|
id="path5280" />
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:969.291px;font-family:'Cinzel Decorative';-inkscape-font-specification:'Cinzel Decorative';fill:#ffffff;stroke-width:0;stroke:#ffffff;stroke-opacity:1;stroke-dasharray:none">C</tspan></text>
|
</g>
|
||||||
<text
|
<g
|
||||||
xml:space="preserve"
|
aria-label="M"
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:463.256px;font-family:'Cinzel Decorative';-inkscape-font-specification:'Cinzel Decorative';fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4.8;stroke-dasharray:none;stroke-opacity:1"
|
transform="scale(0.97653789,1.0240258)"
|
||||||
x="120.35316"
|
id="text6413"
|
||||||
y="634.82605"
|
style="font-size:463.256px;font-family:'Cinzel Decorative';-inkscape-font-specification:'Cinzel Decorative';fill:#ffffff;stroke:#ffffff;stroke-width:0;stroke-miterlimit:4.8">
|
||||||
id="text6413"
|
<path
|
||||||
transform="scale(0.97653789,1.0240258)"><tspan
|
d="m 75.88058,279.97194 q 26.40559,-11.11814 45.86235,-11.11814 19.45675,0 34.28094,3.70605 14.82419,3.70605 30.11164,12.50791 15.75071,8.33861 29.18513,20.84652 25.94234,24.55257 44.00932,55.59072 l 92.6512,173.72101 134.34425,-231.16475 h 4.1693 l 48.17863,272.39453 q 11.11814,63.00282 38.45024,100.0633 27.33211,37.52374 75.04748,37.52374 3.24279,0 6.02233,0 v 4.63256 q -25.47908,6.94884 -50.03165,6.94884 -50.03165,0 -91.26144,-39.37676 -39.84001,-37.52374 -54.20095,-109.79168 l -22.69954,-134.8075 -87.09213,151.02146 q -17.60373,29.18513 -18.06699,51.42142 h -4.1693 L 215.32064,425.43433 192.62109,610.73674 q -1.38976,10.65489 9.26513,17.14047 4.1693,2.77954 8.80186,2.77954 h 7.4121 v 4.1693 h -96.82051 v -4.63256 h 7.4121 q 12.04465,0 21.30977,-6.94884 9.72838,-7.4121 12.04466,-19.92001 l 31.50141,-220.97312 q -20.84652,-37.06048 -47.71537,-65.78235 -30.11164,-32.42792 -63.00282,-32.42792 -3.242792,0 -6.022328,0.46325 z"
|
||||||
sodipodi:role="line"
|
style="font-weight:bold;-inkscape-font-specification:'Cinzel Decorative Bold'"
|
||||||
id="tspan6411"
|
id="path5283" />
|
||||||
x="120.35316"
|
</g>
|
||||||
y="634.82605"
|
</g>
|
||||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Cinzel Decorative';-inkscape-font-specification:'Cinzel Decorative Bold';fill:#ffffff;stroke-width:0;stroke:#ffffff;stroke-opacity:1;stroke-dasharray:none">M</tspan></text>
|
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.1 KiB |
110
src/components/RangeChart.vue
Normal file
110
src/components/RangeChart.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as math from 'mathjs'
|
||||||
|
import {v4 as uuidv4} from 'uuid'
|
||||||
|
import {LineChart} from 'vue-chart-3'
|
||||||
|
import {computed, ref} from 'vue'
|
||||||
|
import {Chart, ChartData, registerables} from 'chart.js'
|
||||||
|
import {MathStatement} from '../support/parse'
|
||||||
|
import {ChartBox} from '../support/types'
|
||||||
|
import {stepX, stepY} from '../support/const'
|
||||||
|
|
||||||
|
Chart.register(...registerables)
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(eventName: 'move', x: number, y:number): void,
|
||||||
|
(eventName: 'edit'): void,
|
||||||
|
(eventName: 'remove'): void,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
fn: MathStatement,
|
||||||
|
value: ChartBox,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const getChartData = (): ChartData<'line'> => {
|
||||||
|
const range = []
|
||||||
|
const min = Math.min(props.value.minX, props.value.maxX)
|
||||||
|
const max = Math.max(props.value.minX, props.value.maxX)
|
||||||
|
for ( let i = min; i <= max; i += parseFloat(String(props.value.stepX)) || 1 ) {
|
||||||
|
range.push(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !props.fn.isFunctionDeclaration() ) {
|
||||||
|
throw new TypeError('Cannot chart node that is not a function.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = props.fn.parse() as math.FunctionAssignmentNode
|
||||||
|
const fn = node.compile().evaluate() // FIXME need dependencies in scope
|
||||||
|
|
||||||
|
console.log('getChartData', {
|
||||||
|
labels: range.map(x => `${x}`),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: node.name,
|
||||||
|
data: range.map(n => fn(n)),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
labels: range.map(x => `${x}`),
|
||||||
|
datasets: [{
|
||||||
|
label: node.name,
|
||||||
|
backgroundColor: '#553564',
|
||||||
|
data: range.map(x => fn(x)),
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartData = ref(getChartData())
|
||||||
|
const chartKey = ref(uuidv4())
|
||||||
|
computed(() => {
|
||||||
|
chartData.value = getChartData()
|
||||||
|
chartKey.value = uuidv4()
|
||||||
|
})
|
||||||
|
|
||||||
|
function onControlledDrag(e: {event: MouseEvent, data: {x: number, y: number}}) {
|
||||||
|
|
||||||
|
// const x = e.x;
|
||||||
|
// const y = e.y;
|
||||||
|
const { x, y } = e.data;
|
||||||
|
props.value.x = x;
|
||||||
|
props.value.y = y;
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
function onControlledDragStop(e: {event: MouseEvent, data: {x: number, y: number}}) {
|
||||||
|
// console.log(typeof(e))
|
||||||
|
const { x, y } = e.data;
|
||||||
|
// const x = e.x;
|
||||||
|
// const y = e.y;
|
||||||
|
console.log(self)
|
||||||
|
emit('move', x, y);
|
||||||
|
onControlledDrag(e);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Draggable
|
||||||
|
:grid="[stepX, stepY]"
|
||||||
|
:default-position="{ x: props.value.x, y: props.value.y }"
|
||||||
|
:position="{ x: props.value.x, y: props.value.y }"
|
||||||
|
@stop="onControlledDragStop"
|
||||||
|
>
|
||||||
|
<div style="background: white; display: flex; flex-direction: row; border: 1px solid #ccc; border-radius: 3px;">
|
||||||
|
<LineChart :chartData="chartData" :key="chartKey" class="inner-chart"/>
|
||||||
|
<div class="sidebar">
|
||||||
|
<q-btn color="grey-7" round flat icon="more_vert">
|
||||||
|
<q-menu cover auto-close>
|
||||||
|
<q-list>
|
||||||
|
<q-item clickable>
|
||||||
|
<q-item-section @click="() => $emit('edit')">Edit</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable>
|
||||||
|
<q-item-section @click="() => $emit('remove')">Remove</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Draggable>
|
||||||
|
</template>
|
@ -50,7 +50,7 @@ app.use(Quasar, {
|
|||||||
config: {
|
config: {
|
||||||
brand: {
|
brand: {
|
||||||
primary: '#553564',
|
primary: '#553564',
|
||||||
secondary: '#c1eeff',
|
secondary: '#7da9b2',
|
||||||
accent: '#9C27B0',
|
accent: '#9C27B0',
|
||||||
|
|
||||||
dark: '#1d1d1d',
|
dark: '#1d1d1d',
|
||||||
|
@ -3,20 +3,26 @@ import { onMounted, ref } from 'vue'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { MathPage } from '../support/page'
|
import { MathPage } from '../support/page'
|
||||||
import { MathStatement } from '../support/parse'
|
import { MathStatement } from '../support/parse'
|
||||||
import { EvaluationResult, hasOwnProperty } from '../support/types'
|
import { ChartBox, EvaluationResult, hasOwnProperty } from '../support/types'
|
||||||
import Statement from '../components/Statement.vue'
|
import Statement from '../components/Statement.vue'
|
||||||
import VarDeclEditor from './VarDeclEditor.vue'
|
import VarDeclEditor from './VarDeclEditor.vue'
|
||||||
import ExpressionEditor from './ExpressionEditor.vue'
|
import ExpressionEditor from './ExpressionEditor.vue'
|
||||||
import TextBox from '../components/TextBox.vue'
|
import TextBox from '../components/TextBox.vue'
|
||||||
|
<<<<<<< HEAD
|
||||||
import ImageBox from '../components/ImageBox.vue'
|
import ImageBox from '../components/ImageBox.vue'
|
||||||
|
|
||||||
import FunctionEditor from '../components/FunctionEditor.vue'
|
import FunctionEditor from '../components/FunctionEditor.vue'
|
||||||
import { RichTextBox, ImageContainer } from '../support/types'
|
import { RichTextBox, ImageContainer } from '../support/types'
|
||||||
|
=======
|
||||||
|
import FunctionEditor from '../components/FunctionEditor.vue'
|
||||||
|
import RangeChart from '../components/RangeChart.vue'
|
||||||
|
import RangeChartEditor from './RangeChartEditor.vue'
|
||||||
|
import { RichTextBox } from '../support/types'
|
||||||
|
>>>>>>> 17a6aea76db7a943a190d4db6524e6b16c01392e
|
||||||
import { stepX, stepY } from '../support/const'
|
import { stepX, stepY } from '../support/const'
|
||||||
import { checkLoggedIn, loggedOut } from '../support/auth'
|
import { checkLoggedIn, loggedOut } from '../support/auth'
|
||||||
import router from '../router'
|
import router from '../router'
|
||||||
|
|
||||||
|
|
||||||
const math = new MathPage(uuidv4());
|
const math = new MathPage(uuidv4());
|
||||||
const statements = ref<MathStatement[]>([]);
|
const statements = ref<MathStatement[]>([]);
|
||||||
const evaluation = ref<EvaluationResult | undefined>();
|
const evaluation = ref<EvaluationResult | undefined>();
|
||||||
@ -39,7 +45,7 @@ const variableListingColumns = [
|
|||||||
label: 'Value',
|
label: 'Value',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
const stmOnControlledDragStop = (stmt: MathStatement) => (e: { event: MouseEvent, data: { x: number, y: number } }) => {
|
const stmOnControlledDragStop = (stmt: MathStatement) => (e: { event: MouseEvent, data: { x: number, y: number } }) => {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
@ -196,8 +202,42 @@ const makeNewRichTextBox = () => {
|
|||||||
richEditModal.value = true;
|
richEditModal.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const richTextStatements = ref<RichTextBox[]>([]);
|
const chartBoxKey = ref(uuidv4())
|
||||||
|
const chartBoxes = ref<ChartBox[]>([])
|
||||||
|
|
||||||
|
const newChartModalOpen = ref(false)
|
||||||
|
const openNewChartModal = () => {
|
||||||
|
newChartModalOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveNewChartBox = (chartBox: ChartBox) => {
|
||||||
|
chartBoxes.value.push(chartBox)
|
||||||
|
newChartModalOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const editingChartBox = ref<ChartBox | undefined>()
|
||||||
|
const chartEditModalOpen = ref(false)
|
||||||
|
const openChartEditModal = (box: ChartBox) => {
|
||||||
|
editingChartBox.value = box
|
||||||
|
chartEditModalOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveEditingChartBox = () => {
|
||||||
|
chartEditModalOpen.value = false
|
||||||
|
chartBoxKey.value = uuidv4()
|
||||||
|
}
|
||||||
|
|
||||||
|
const moveChartBox = (id: number, x: number, y: number) => {
|
||||||
|
chartBoxes.value[id].x = x
|
||||||
|
chartBoxes.value[id].y = y
|
||||||
|
}
|
||||||
|
const removeChartBox = (id: number) => {
|
||||||
|
chartBoxes.value.splice(id, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const richTextStatements = ref<RichTextBox[]>([
|
||||||
|
new RichTextBox("Hello World"),
|
||||||
|
]);
|
||||||
const richEditModal = ref(false);
|
const richEditModal = ref(false);
|
||||||
const richEditExpression = ref("");
|
const richEditExpression = ref("");
|
||||||
const richEditID = ref(0);
|
const richEditID = ref(0);
|
||||||
@ -297,16 +337,14 @@ onMounted(() => {
|
|||||||
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer" />
|
<q-btn dense flat round icon="menu" @click="toggleLeftDrawer" />
|
||||||
|
|
||||||
<q-toolbar-title>
|
<q-toolbar-title>
|
||||||
<q-avatar>
|
<q-avatar size="50px">
|
||||||
<img src="https://cdn.quasar.dev/logo-v2/svg/logo-mono-white.svg" />
|
<img src="../assets/l2.svg" />
|
||||||
</q-avatar>Title
|
</q-avatar>
|
||||||
|
<span style="font-family: 'Cinzel Decorative', cursive;">Crystal Math Worktable</span>
|
||||||
</q-toolbar-title>
|
</q-toolbar-title>
|
||||||
|
|
||||||
|
<span v-if="status" @click="logout()" label="Logout">Logout</span>
|
||||||
</q-toolbar>
|
</q-toolbar>
|
||||||
<q-tabs>
|
|
||||||
<q-route-tab to="/Scratch" label="Scratch" />
|
|
||||||
<q-route-tab to="/Editor" label="Editor" />
|
|
||||||
<q-tab v-if="status" @click="logout()" label="Logout" />
|
|
||||||
</q-tabs>
|
|
||||||
</q-header>
|
</q-header>
|
||||||
|
|
||||||
<q-drawer show-if-above v-model="leftDrawerOpen" side="left" bordered>
|
<q-drawer show-if-above v-model="leftDrawerOpen" side="left" bordered>
|
||||||
@ -361,9 +399,20 @@ onMounted(() => {
|
|||||||
<!-- drawer content -->
|
<!-- drawer content -->
|
||||||
</q-drawer>
|
</q-drawer>
|
||||||
|
|
||||||
<q-page-container id="editor">
|
<q-page-container id="editor" style="padding=0">
|
||||||
<!-- <WrapperBox />-->
|
<!-- <WrapperBox />-->
|
||||||
|
|
||||||
|
<span v-for="(chartBox, index) in chartBoxes" style="display: flex">
|
||||||
|
<RangeChart
|
||||||
|
:fn="math.getFunctionByNameOrFail(chartBox.fnName)"
|
||||||
|
:key="chartBoxKey"
|
||||||
|
:value="chartBox"
|
||||||
|
v-on:move="(x, y) => moveChartBox(index, x, y)"
|
||||||
|
v-on:remove="() => removeChartBox(index)"
|
||||||
|
v-on:edit="() => openChartEditModal(chartBox)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
|
||||||
<span v-for="statement in statements" style="display: flex">
|
<span v-for="statement in statements" style="display: flex">
|
||||||
<Draggable
|
<Draggable
|
||||||
:grid="[stepX, stepY]"
|
:grid="[stepX, stepY]"
|
||||||
@ -407,6 +456,18 @@ onMounted(() => {
|
|||||||
<FunctionEditor :statement="editingStatement" v-on:save="() => finishEditStatement()" />
|
<FunctionEditor :statement="editingStatement" v-on:save="() => finishEditStatement()" />
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="newChartModalOpen">
|
||||||
|
<RangeChartEditor :page="math" v-on:save="c => saveNewChartBox(c)" />
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="chartEditModalOpen">
|
||||||
|
<RangeChartEditor
|
||||||
|
:page="math"
|
||||||
|
:chartBox="editingChartBox"
|
||||||
|
v-on:save="() => saveEditingChartBox()"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
<q-page-sticky position="bottom-right" :offset="[32, 32]">
|
<q-page-sticky position="bottom-right" :offset="[32, 32]">
|
||||||
<q-fab color="primary" icon="add" direction="left">
|
<q-fab color="primary" icon="add" direction="left">
|
||||||
<q-fab-action
|
<q-fab-action
|
||||||
@ -430,7 +491,7 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
<q-fab-action
|
<q-fab-action
|
||||||
color="secondary"
|
color="secondary"
|
||||||
icon="text"
|
icon="format_quote"
|
||||||
title="Add a text box"
|
title="Add a text box"
|
||||||
@click="() => makeNewRichTextBox()"
|
@click="() => makeNewRichTextBox()"
|
||||||
/>
|
/>
|
||||||
@ -440,6 +501,12 @@ onMounted(() => {
|
|||||||
title="Add an image box"
|
title="Add an image box"
|
||||||
@click="() => makeNewImageBox()"
|
@click="() => makeNewImageBox()"
|
||||||
/>
|
/>
|
||||||
|
<q-fab-action
|
||||||
|
color="secondary"
|
||||||
|
icon="show_chart"
|
||||||
|
title="Add a new chart"
|
||||||
|
@click="() => openNewChartModal()"
|
||||||
|
/>
|
||||||
</q-fab>
|
</q-fab>
|
||||||
</q-page-sticky>
|
</q-page-sticky>
|
||||||
|
|
||||||
@ -464,7 +531,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<q-dialog v-model="imageModal">
|
<q-dialog v-model="imageModal">
|
||||||
<q-card autogrow style="min-width: 25em;">
|
<q-card autogrow style="min-width: 25em;">
|
||||||
<q-input autogrow v-model="imageURL"/>
|
<q-input autogrow v-model="imageURL" />
|
||||||
<q-card-actions align="right" class="text-primary">
|
<q-card-actions align="right" class="text-primary">
|
||||||
<q-btn flat label="Cancel" v-close-popup></q-btn>
|
<q-btn flat label="Cancel" v-close-popup></q-btn>
|
||||||
<q-btn flat label="Save" @click="imageUpdateValue" v-close-popup></q-btn>
|
<q-btn flat label="Save" @click="imageUpdateValue" v-close-popup></q-btn>
|
||||||
@ -481,21 +548,16 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-page-container>
|
</q-page-container>
|
||||||
|
|
||||||
<q-footer reveal elevated class="bg-grey-8 text-white">
|
|
||||||
<q-toolbar>
|
|
||||||
<q-toolbar-title>
|
|
||||||
<div>Status</div>
|
|
||||||
</q-toolbar-title>
|
|
||||||
</q-toolbar>
|
|
||||||
</q-footer>
|
|
||||||
</q-layout>
|
</q-layout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@700&display=swap");
|
||||||
|
|
||||||
#editor {
|
#editor {
|
||||||
background-image: url(../assets/grid.svg);
|
background-image: url(../assets/grid.svg);
|
||||||
background-repeat: repeat;
|
background-repeat: repeat;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding-top: 0px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
118
src/pages/RangeChartEditor.vue
Normal file
118
src/pages/RangeChartEditor.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import * as math from 'mathjs'
|
||||||
|
import {onMounted, ref} from 'vue'
|
||||||
|
import {MathStatement} from '../support/parse'
|
||||||
|
import {v4 as uuidv4} from 'uuid'
|
||||||
|
import Katex from '../components/Katex.vue'
|
||||||
|
import {ChartBox, StatementID} from '../support/types'
|
||||||
|
import {MathPage} from '../support/page'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
page: MathPage,
|
||||||
|
chartBox?: ChartBox,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(eventName: 'save', statement: ChartBox): void,
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const chartBoxModalError = ref<string|undefined>(undefined)
|
||||||
|
const chartBoxFunctionName = ref('')
|
||||||
|
const chartBoxMinXValue = ref(0)
|
||||||
|
const chartBoxMaxXValue = ref(30)
|
||||||
|
const chartBoxStepXValue = ref(1)
|
||||||
|
|
||||||
|
const validateChartBox = () => {
|
||||||
|
if ( !chartBoxFunctionName.value ) {
|
||||||
|
return 'Missing function name'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isNaN(parseFloat(String(chartBoxMinXValue.value))) ) {
|
||||||
|
return 'Invalid minimum X-value'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isNaN(parseFloat(String(chartBoxMaxXValue.value))) ) {
|
||||||
|
return 'Invalid maximum X-value'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isNaN(parseFloat(String(chartBoxStepXValue.value))) ) {
|
||||||
|
return 'Invalid X-value step size'
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !props.page.getFunctionByName(chartBoxFunctionName.value) ) {
|
||||||
|
return 'Invalid function name'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveChartBox = () => {
|
||||||
|
chartBoxModalError.value = validateChartBox()
|
||||||
|
if ( chartBoxModalError.value ) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( props.chartBox ) {
|
||||||
|
props.chartBox.fnName = chartBoxFunctionName.value
|
||||||
|
props.chartBox.minX = chartBoxMinXValue.value
|
||||||
|
props.chartBox.maxX = chartBoxMaxXValue.value
|
||||||
|
props.chartBox.stepX = chartBoxStepXValue.value
|
||||||
|
emit('save', props.chartBox)
|
||||||
|
} else {
|
||||||
|
emit('save', new ChartBox(
|
||||||
|
chartBoxFunctionName.value,
|
||||||
|
chartBoxMinXValue.value,
|
||||||
|
chartBoxMaxXValue.value,
|
||||||
|
chartBoxStepXValue.value,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if ( props.chartBox ) {
|
||||||
|
chartBoxFunctionName.value = props.chartBox.fnName
|
||||||
|
chartBoxMinXValue.value = props.chartBox.minX
|
||||||
|
chartBoxMaxXValue.value = props.chartBox.maxX
|
||||||
|
chartBoxStepXValue.value = props.chartBox.stepX
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section v-if="chartBoxModalError">
|
||||||
|
<div style="color: darkred; font-weight: bold">{{ chartBoxModalError }}</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<q-input
|
||||||
|
v-model="chartBoxFunctionName"
|
||||||
|
outlined
|
||||||
|
autofocus
|
||||||
|
label="Function name"
|
||||||
|
/>
|
||||||
|
<br>
|
||||||
|
<q-input
|
||||||
|
v-model="chartBoxMinXValue"
|
||||||
|
outlined
|
||||||
|
autofocus
|
||||||
|
label="Minimum X-value"
|
||||||
|
/>
|
||||||
|
<br>
|
||||||
|
<q-input
|
||||||
|
v-model="chartBoxMaxXValue"
|
||||||
|
outlined
|
||||||
|
autofocus
|
||||||
|
label="Maximum X-value"
|
||||||
|
/>
|
||||||
|
<br>
|
||||||
|
<q-input
|
||||||
|
v-model="chartBoxStepXValue"
|
||||||
|
outlined
|
||||||
|
autofocus
|
||||||
|
label="X-value step size"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions align="right" class="text-primary">
|
||||||
|
<q-btn flat label="Cancel" v-close-popup></q-btn>
|
||||||
|
<q-btn flat label="Save" @click="() => saveChartBox()"></q-btn>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
@ -1,5 +1,5 @@
|
|||||||
$primary : #553564
|
$primary : #553564
|
||||||
$secondary : #26A69A
|
$secondary : #008e80
|
||||||
$accent : #9C27B0
|
$accent : #9C27B0
|
||||||
|
|
||||||
$dark : #1D1D1D
|
$dark : #1D1D1D
|
||||||
@ -8,5 +8,5 @@ $dark : #1D1D1D
|
|||||||
|
|
||||||
$positive : #21BA45
|
$positive : #21BA45
|
||||||
$negative : #C10015
|
$negative : #C10015
|
||||||
$info : #31CCEC
|
$info : #7da9b2
|
||||||
$warning : #F2C037
|
$warning : #F2C037
|
@ -128,6 +128,25 @@ export class MathPage {
|
|||||||
.filter(x => x.isFunctionDeclaration())
|
.filter(x => x.isFunctionDeclaration())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Look up a function statement by name, if it exists. */
|
||||||
|
getFunctionByName(name: string): MathStatement|undefined {
|
||||||
|
for ( const fn of this.functions() ) {
|
||||||
|
const node = fn.parse() as math.FunctionAssignmentNode
|
||||||
|
if ( node.name === name ) {
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Look up a function statement by name, if it exists. */
|
||||||
|
getFunctionByNameOrFail(name: string): MathStatement {
|
||||||
|
const fn = this.getFunctionByName(name)
|
||||||
|
if ( !fn ) {
|
||||||
|
throw new Error('Unable to find function with name: ' + name)
|
||||||
|
}
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
/** Evaluate the current state of the page and get the result. */
|
/** Evaluate the current state of the page and get the result. */
|
||||||
evaluate(): EvaluationResult {
|
evaluate(): EvaluationResult {
|
||||||
const evaluations: Record<StatementID, any> = {}
|
const evaluations: Record<StatementID, any> = {}
|
||||||
|
@ -111,3 +111,16 @@ export class ImageContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class ChartBox {
|
||||||
|
// eslint-disable-next-line max-params
|
||||||
|
constructor(
|
||||||
|
public fnName: string,
|
||||||
|
public minX: number,
|
||||||
|
public maxX: number,
|
||||||
|
public stepX: number = 1,
|
||||||
|
public x: number = 0,
|
||||||
|
public y: number = 0,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user