11<template >
2- <Navbar />
3- <div id =" content" :class =" { onresize: onResize }" >
4- <div id =" editor" :style =" { width: sideRatio + 'vw' }" >
5- <div class =" editor-inner" >
6- <VueDraggable
7- v-model =" graphData"
8- :animation =" 150"
9- handle =" .datablock-drag"
10- >
11- <DataBlock
12- v-for =" (dataItem, i) in graphData"
13- v-model =" graphData[i]"
14- @delete =" graphData.splice(i, 1)"
15- :key =" dataItem.key"
16- @require-full-update =" fullUpdate"
17- />
18- </VueDraggable >
19- <div class =" plot-data add-data" >
20- <div
21- @click ="
22- graphData.push({
23- key: Math.random(),
24- fnType: 'linear',
25- graphType: 'polyline',
26- })
27- "
28- class =" add-data-opt add"
29- >
30- + {{ t("buttons.add") }}
31- </div >
32- <div @click =" handleImport()" class =" add-data-opt import" >
33- ↓ {{ t("buttons.import") }}
2+ <s-page theme =" auto" id =" soberpage" >
3+ <s-drawer >
4+ <Navbar @toggle-drawer =" toogleDrawer" />
5+ <s-drawer
6+ theme =" auto"
7+ id =" content"
8+ :class =" { onresize: onResize }"
9+ ref =" innerDrawer"
10+ >
11+ <div slot =" start" id =" editor" :style =" { width: sideRatio + 'vw' }" >
12+ <div class =" editor-inner" >
13+ <s-scroll-view >
14+ <VueDraggable
15+ v-model =" graphData"
16+ :animation =" 200"
17+ handle =" .datablock-drag"
18+ :key =" draggerKey"
19+ >
20+ <DataBlock
21+ v-for =" (dataItem, i) in graphData"
22+ v-model =" graphData[i]"
23+ @delete =" graphData.splice(i, 1)"
24+ :key =" dataItem.key"
25+ @require-full-update =" fullUpdate"
26+ />
27+ </VueDraggable >
28+
29+ <div
30+ class =" plot-data add-data"
31+ @click ="
32+ graphData.push({
33+ key: Math.random(),
34+ fnType: 'linear',
35+ graphType: 'polyline',
36+ })
37+ "
38+ >
39+ <s-icon name =" add" />
40+ {{ t("buttons.add") }}
41+ <s-ripple attached ></s-ripple >
42+ </div >
43+ </s-scroll-view >
44+ <s-dialog >
45+ <s-tooltip class =" data-import" slot =" trigger" >
46+ <s-fab slot =" trigger" >
47+ <s-icon >
48+ <svg viewBox =" 0 -960 960 960" >
49+ <path
50+ d =" M480-120v-80h280v-560H480v-80h280q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H480Zm-80-160-55-58 102-102H120v-80h327L345-622l55-58 200 200-200 200Z"
51+ ></path >
52+ </svg >
53+ </s-icon >
54+ </s-fab >
55+ {{ t("buttons.import") }}
56+ </s-tooltip >
57+ <div slot =" headline" >{{ t("title.source") }}</div >
58+ <s-text-field
59+ slot =" text"
60+ label =" JSON5 / JSON"
61+ multiLine
62+ style =" min-height : 180px ; min-width : 40vw "
63+ v-model.lazy =" importStr"
64+ class =" monospace"
65+ ></s-text-field >
66+ <s-button slot =" action" type =" text" >
67+ {{ t("buttons.cancel") }}
68+ </s-button >
69+ <s-button slot =" action" type =" text" @click =" handleImport" >
70+ {{ t("buttons.confirm") }}
71+ </s-button >
72+ </s-dialog >
3473 </div >
74+ <CodeDisplay :dataArr =" toOriginalDatum(graphData, true)" />
75+ <div id =" divider" @mousedown =" handleDrag" ></div >
76+ </div >
77+ <div id =" graph" ref =" shellRef" >
78+ <Graph
79+ :data =" toOriginalDatum(graphData)"
80+ :width =" graphWidth"
81+ :height =" graphHeight"
82+ :key =" graphKey"
83+ @requireFullUpdate =" fullUpdate"
84+ @requirePostUpdate =" graphKey++"
85+ v-model =" fullUpdateState"
86+ />
3587 </div >
36- </div >
37- <CodeDisplay :dataArr =" toOriginalDatum(graphData)" />
38- </div >
39- <div id =" divider" @mousedown =" handleDrag" ></div >
40- <div id =" graph" ref =" shellRef" >
41- <Graph
42- :data =" toOriginalDatum(graphData)"
43- :width =" graphWidth"
44- :height =" graphHeight"
45- :key =" key"
46- @requireFullUpdate =" fullUpdate"
47- @requirePostUpdate =" key++"
48- v-model =" fullUpdateState"
49- />
50- </div >
51- </div >
88+ </s-drawer >
89+ </s-drawer >
90+ </s-page >
5291</template >
5392
5493<script setup lang="ts">
55- import { useI18n } from ' vue-i18n'
56- const { t } = useI18n ()
94+ import { useI18n } from " vue-i18n" ;
95+ const { t, locale } = useI18n ();
5796
5897import Navbar from " ./components/nav.vue" ;
5998import Graph from " ./components/graph.vue" ;
6099import DataBlock from " ./components/dataBlock.vue" ;
61100import CodeDisplay from " ./components/codeDisplay.vue" ;
62101import { VueDraggable } from " vue-draggable-plus" ;
63102import type { FunctionPlotDatum } from " function-plot" ;
64- import { onMounted , ref } from " vue" ;
103+ import { onMounted , ref , watch } from " vue" ;
65104import JSON5 from " json5" ;
66105import base64 from " base-64" ;
67106import utf8 from " utf8" ;
68107import { InternalDatum , toInternalDatum , toOriginalDatum } from " ./consts" ;
108+ import { debounce } from " lodash-es" ;
69109
70110const graphData = ref <InternalDatum []>([
71111 { fnType: " linear" , graphType: " polyline" , fn: " x^2" , key: 1 },
72112]);
73113
114+ const innerDrawer = ref <HTMLElementTagNameMap [" s-drawer" ]>();
74115const graphWidth = ref (0 ),
75116 graphHeight = ref (0 );
76- const key = ref (0 );
117+ const graphKey = ref (0 );
77118const fullUpdateState = ref (false );
78119const sideRatio = ref (33 );
79120const onResize = ref (false );
121+
80122const shellRef = ref <HTMLDivElement >();
81- function handleResize() {
82- if (shellRef .value ) {
83- graphWidth .value = shellRef .value .clientWidth ;
84- graphHeight .value = shellRef .value .clientHeight ;
85- }
86- }
123+ onMounted (() => {
124+ const observer = new ResizeObserver (
125+ debounce (() => {
126+ graphWidth .value = shellRef .value ! .clientWidth ;
127+ graphHeight .value = shellRef .value ! .clientHeight ;
128+ }, 250 )
129+ );
130+ observer .observe (shellRef .value ! );
131+ });
87132
88133function fullUpdate() {
89134 fullUpdateState .value = true ;
90- key .value ++ ;
135+ graphKey .value ++ ;
91136}
92137
93138onMounted (() => {
@@ -102,106 +147,117 @@ onMounted(() => {
102147 console .log (code );
103148 console .log (data );
104149 } catch (e ) {}
105- window .addEventListener (" resize" , handleResize );
106- handleResize ();
107150});
108151
109152function handleDrag() {
110153 onResize .value = true ;
111154 const xfull = window .innerWidth ;
155+ const restrictRange = (x : number , min : number , max : number ) =>
156+ Math .max (min , Math .min (x , max ));
112157 const mousemove = (event : MouseEvent ) =>
113- (sideRatio .value = ( event .clientX / xfull ) * 100 );
158+ (sideRatio .value = restrictRange (( event .clientX / xfull ) * 100 , 25 , 75 ) );
114159 document .addEventListener (" mousemove" , mousemove );
115160 document .addEventListener (" mouseup" , () => {
116161 document .removeEventListener (" mousemove" , mousemove );
117162 onResize .value = false ;
118- handleResize ();
119163 });
120164}
165+ function toogleDrawer() {
166+ innerDrawer .value ?.toggle ();
167+ }
121168
169+ const importStr = ref (" " );
170+ import { Snackbar } from " sober" ;
122171function handleImport() {
123- const raw = prompt (" 源数据:" );
124- if (! raw ) return ;
125- graphData .value = toInternalDatum (
126- (JSON5 .parse (raw ).data as FunctionPlotDatum []) ?? []
127- );
172+ if (importStr .value !== " " ) {
173+ try {
174+ graphData .value = toInternalDatum (
175+ (JSON5 .parse (importStr .value ).data as FunctionPlotDatum []) ?? []
176+ );
177+ Snackbar .builder ({
178+ text: t (" title.importSuccess" ),
179+ type: " success" ,
180+ });
181+ } catch (e ) {
182+ Snackbar .builder ({
183+ text: t (" title.importFail" ),
184+ type: " error" ,
185+ });
186+ }
187+ importStr .value = " " ;
188+ }
128189}
190+
191+ const draggerKey = ref (0 );
192+ watch (locale , () => draggerKey .value ++ );
129193 </script >
130194
131195<style >
132- #app {
133- position : fixed ;
196+ html ,
197+ body {
134198 margin : 0 ;
135199 padding : 0 ;
200+ overflow : hidden ;
201+ }
202+ s-page {
203+ position : absolute ;
136204 top : 0 ;
137205 bottom : 0 ;
138206 left : 0 ;
139207 right : 0 ;
140- height : 100vh ;
141- display : flex ;
142- flex-wrap : nowrap ;
143- flex-direction : column ;
208+ }
209+ * {
210+ --s-color-outline : #6a757a ;
144211}
145212#content {
146- width : 100vw ;
147213 flex-grow : 1 ;
148214 display : flex ;
149- max-height : calc (100vh - 50px );
150- }
151- #navbar {
152- height : 50px ;
153- width : 100vw ;
154- box-sizing : border-box ;
155- background : var (--c-bk1 );
156- border-bottom : var (--c-border ) 1px solid ;
157- position : relative ;
158- flex-shrink : 0 ;
159215}
160216#editor {
161- border-right : var (--c-border ) 1px solid ;
217+ border-right : var (--s-color-outline-variant ) 1px solid ;
162218 position : relative ;
163219 display : flex ;
164220 flex-direction : column ;
221+ margin-right : 3px ; /* 为宽度调节条留空间 */
165222}
166223.editor-inner {
167- overflow : scroll ;
224+ height : 0 ;
168225 flex-grow : 1 ;
226+ position : relative ;
227+ }
228+
229+ .editor-inner s-scroll-view {
230+ height : 100% ;
169231}
170232
171233#graph {
172234 flex-grow : 1 ;
173235 position : relative ;
174236 overflow : hidden ;
175237}
176- .add-data {
177- padding : 0 ;
238+
239+ .plot-data.add-data {
240+ position : relative ;
241+ padding-top : 15px ;
242+ padding-bottom : 15px ;
243+ margin-bottom : 50px ;
178244 display : flex ;
179- flex-direction : row ;
180- }
181- .add-data-opt {
182- padding : 10px 30px ;
245+ gap : 5px ;
183246}
184- .add-data-opt.add {
185- flex-grow : 1 ;
186- }
187- .add-data-opt :not (:nth-child (1 )) {
188- border-left : 1px solid var (--c-border );
189- }
190- .editor-inner {
191- padding-bottom : 50px ;
192- }
193- .add-data-opt :hover {
194- background : var (--c-bk3 );
195- }
196- .add-data-opt :active {
197- background : var (--c-bk1 );
247+
248+ .data-import {
249+ position : absolute ;
250+ bottom : 20px ;
251+ right : 20px ;
198252}
199253
200254#divider {
201- width : 6px ;
202- background : var (--c-accent );
203- margin-left : -3px ;
204- margin-right : -3px ;
255+ position : absolute ;
256+ right : -3px ;
257+ top : 0 ;
258+ bottom : 0 ;
259+ width : 5px ;
260+ background : var (--s-color-secondary );
205261 z-index : 999 ;
206262 opacity : 0 ;
207263 transition : opacity 0.1s ;
0 commit comments