From 4441102c662da51dc668d623d06dfbe1e813dbfc Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 12 Aug 2022 20:41:27 +0530 Subject: [PATCH 01/20] Add PanZoom --- package.json | 3 ++- src/lib/components/view.svelte | 36 ++++++++++++++++++++++++++++++++-- src/routes/edit.svelte | 27 ++++++++++++------------- yarn.lock | 5 +++++ 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 9a44508d..837d04af 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,8 @@ "monaco-editor": "0.34.0", "monaco-mermaid": "1.0.6", "pako": "2.0.4", - "random-word-slugs": "0.1.6" + "random-word-slugs": "0.1.6", + "svg-pan-zoom": "^3.6.1" }, "lint-staged": { "*.{ts,svelte,js,css,md,json}": [ diff --git a/src/lib/components/view.svelte b/src/lib/components/view.svelte index a4036093..3579723d 100644 --- a/src/lib/components/view.svelte +++ b/src/lib/components/view.svelte @@ -2,6 +2,7 @@ import { inputStateStore, stateStore } from '$lib/util/state'; import { onMount } from 'svelte'; import mermaid from 'mermaid'; + import panzoom from 'svg-pan-zoom'; let code = ''; let config = ''; @@ -9,7 +10,11 @@ let view: HTMLDivElement; let error = false; let outOfSync = false; + let hide = false; let manualUpdate = true; + let pan: SvgPanZoom.Point; + let zoom: number; + let pzoom: SvgPanZoom.Instance; onMount(() => { stateStore.subscribe((state) => { if (state.error !== undefined) { @@ -35,8 +40,31 @@ mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid))); mermaid.render('graph-div', code, (svgCode) => { if (svgCode.length > 0) { - console.log(svgCode); + let oldPan = pan ? { ...pan } : undefined; + let oldZoom = zoom; + pzoom?.destroy(); + hide = true; container.innerHTML = svgCode; + setTimeout(() => { + pzoom = panzoom('#graph-div', { + onPan: (p) => { + pan = p; + zoom = pzoom.getZoom(); + }, + onZoom: (z) => { + zoom = z; + pan = pzoom.getPan(); + }, + zoomEnabled: true, + panEnabled: true, + controlIconsEnabled: true + }); + if (oldPan !== undefined && oldZoom !== undefined) { + pzoom.zoom(oldZoom); + pzoom.pan(oldPan); + } + hide = false; + }, 0); } }); view.parentElement.scrollTop = scroll; @@ -59,7 +87,7 @@ {/if}
-
+
diff --git a/src/routes/edit.svelte b/src/routes/edit.svelte index 9db0e8aa..e420bfea 100644 --- a/src/routes/edit.svelte +++ b/src/routes/edit.svelte @@ -64,13 +64,6 @@ let docURL = docURLBase; let language: Languages = 'mermaid'; $: language = languageMap[selectedMode]; - $: { - if (selectedMode === 'code') { - text = $stateStore.code; - } else { - text = $stateStore.mermaid; - } - } stateStore.subscribe((state: State) => { if (state.updateEditor) { @@ -100,15 +93,19 @@ } ]; + let debounce: { [key: string]: any } = {}; const updateHandler = (message: CustomEvent) => { - const code = message.detail.text; - if (selectedMode === 'code') { - updateCode(code, { - updateEditor: false - }); - } else { - updateConfig(code, false); - } + clearTimeout(debounce[selectedMode]); + debounce[selectedMode] = setTimeout(() => { + const code = message.detail.text; + if (selectedMode === 'code') { + updateCode(code, { + updateEditor: false + }); + } else { + updateConfig(code, false); + } + }, 300); }; onMount(async () => { diff --git a/yarn.lock b/yarn.lock index 6057b6d2..2df48f38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4807,6 +4807,11 @@ svelte@3.49.0: resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029" integrity sha512-+lmjic1pApJWDfPCpUUTc1m8azDqYCG1JN9YEngrx/hUyIcFJo6VZhj0A1Ai0wqoHcEIuQy+e9tk+4uDgdtsFA== +svg-pan-zoom@^3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/svg-pan-zoom/-/svg-pan-zoom-3.6.1.tgz#f880a1bb32d18e9c625d7715350bebc269b450cf" + integrity sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g== + svgo@^2.7.0: version "2.8.0" resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" From 1bc07189010589ff7141b7ffc0cd53481a2fedae Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Fri, 12 Aug 2022 22:15:36 +0530 Subject: [PATCH 02/20] Persist pan & zoom in state --- src/lib/components/preset.svelte | 3 +- src/lib/components/view.svelte | 57 ++++++++++++++++++++------------ src/lib/types.d.ts | 2 ++ src/lib/util/state.ts | 10 +++++- src/routes/edit.svelte | 4 +-- 5 files changed, 50 insertions(+), 26 deletions(-) diff --git a/src/lib/components/preset.svelte b/src/lib/components/preset.svelte index 2c31cbaf..310b73b4 100644 --- a/src/lib/components/preset.svelte +++ b/src/lib/components/preset.svelte @@ -97,7 +97,8 @@ const loadSampleDiagram = (diagramType: string): void => { updateCode(samples[diagramType], { updateDiagram: true, - updateEditor: true + updateEditor: true, + resetPanZoom: true }); }; diff --git a/src/lib/components/view.svelte b/src/lib/components/view.svelte index 3579723d..89360b21 100644 --- a/src/lib/components/view.svelte +++ b/src/lib/components/view.svelte @@ -1,5 +1,5 @@ @@ -86,8 +99,8 @@
{$stateStore.error}
{/if} -
-
+
+
From 856bc1ebe33b0f7b3b44f8a9b0ee21ee8db38486 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 13 Aug 2022 12:58:56 +0530 Subject: [PATCH 14/20] Add unit tests --- .github/workflows/unit-tests.yml | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/unit-tests.yml diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 00000000..ee582139 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,36 @@ +name: Unit Tests + +on: + pull_request: + branches: + - master + - develop + +jobs: + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - uses: actions/cache@v3 + id: yarn-and-build-cache + with: + path: | + build + node_modules + key: ${{ runner.os }}-node_modules-build-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-node_modules-build- + + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'yarn' + + - name: Lint & Test + run: | + yarn install + yarn lint + yarn test:unit From cf4cbc42995fe569c4abdcb2cf17c51df9a4dfa9 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 13 Aug 2022 13:19:23 +0530 Subject: [PATCH 15/20] Fix flashes --- src/lib/components/view.svelte | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/components/view.svelte b/src/lib/components/view.svelte index 68e5e145..01ca38ad 100644 --- a/src/lib/components/view.svelte +++ b/src/lib/components/view.svelte @@ -21,7 +21,7 @@ clearTimeout(debounce); debounce = window.setTimeout(() => { updateCodeStore({ pan, zoom }); - }, 500); + }, 200); }; onMount(() => { @@ -53,7 +53,7 @@ pzoom = undefined; hide = true; container.innerHTML = svgCode; - setTimeout(() => { + Promise.resolve().then(() => { const graphDiv = document.getElementById('graph-div'); graphDiv.setAttribute('height', '100%'); graphDiv.style.maxWidth = '100%'; @@ -65,12 +65,12 @@ center: true }); const { pan, zoom } = state; - if (pan !== undefined && zoom !== undefined) { + if (pan !== undefined && zoom !== undefined && Number.isFinite(zoom)) { pzoom.zoom(zoom); pzoom.pan(pan); } hide = false; - }, 1); + }); } }); view.parentElement.scrollTop = scroll; @@ -107,13 +107,17 @@ #view { flex: 1; } + + #container { + transition: visibility 0.3s; + } + .error, .outOfSync { opacity: 0.5; } .hide { - opacity: 0; - transition: opacity 0.3s; + visibility: hidden; } From 16ff107b6e38c242d9ce183cd1ff86b41edb6556 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 13 Aug 2022 13:41:20 +0530 Subject: [PATCH 16/20] Add test retries --- cypress.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cypress.config.js b/cypress.config.js index c394294b..43fa133c 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -8,6 +8,10 @@ export default defineConfig({ snapshotFileName: './cypress/snapshots.js', defaultCommandTimeout: 16000, requestTimeout: 16000, + retries: { + runMode: 2, + openMode: 0 + }, e2e: { setupNodeEvents(on, config) { on('task', { From 285da3f63ddd1341a5f03630736f5a685cfb0e93 Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Sat, 13 Aug 2022 13:46:01 +0530 Subject: [PATCH 17/20] Fix specPattern --- cypress.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress.config.js b/cypress.config.js index 43fa133c..40248bb5 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -30,6 +30,6 @@ export default defineConfig({ }); }, baseUrl: 'http://localhost:3000', - specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}' + specPattern: 'cypress/e2e/**/*.spec.ts' } }); From a713f34354979eccf6fb4eb53b038b3d7fda111a Mon Sep 17 00:00:00 2001 From: Sidharth Vinod Date: Mon, 15 Aug 2022 11:33:20 +0530 Subject: [PATCH 18/20] Add toggle for pan & zoom --- src/lib/components/view.svelte | 55 ++++++++++++++------------- src/lib/types.d.ts | 1 + src/routes/edit.svelte | 69 +++++++++++++++++++--------------- 3 files changed, 68 insertions(+), 57 deletions(-) diff --git a/src/lib/components/view.svelte b/src/lib/components/view.svelte index 01ca38ad..950f65e0 100644 --- a/src/lib/components/view.svelte +++ b/src/lib/components/view.svelte @@ -12,6 +12,7 @@ let outOfSync = false; let hide = false; let manualUpdate = true; + let panZoomEnabled = $stateStore.panZoom; let pzoom: SvgPanZoom.Instance; let debounce: number; @@ -38,39 +39,43 @@ } outOfSync = false; manualUpdate = true; - if (code === state.code && config === state.mermaid) { - // Do not render if there is no change in Code/Config + if (code === state.code && config === state.mermaid && panZoomEnabled === state.panZoom) { + // Do not render if there is no change in Code/Config/PanZoom return; } code = state.code; config = state.mermaid; + panZoomEnabled = state.panZoom; const scroll = view.parentElement.scrollTop; delete container.dataset.processed; mermaid.initialize(Object.assign({}, JSON.parse(state.mermaid))); mermaid.render('graph-div', code, (svgCode) => { if (svgCode.length > 0) { - pzoom?.destroy(); - pzoom = undefined; - hide = true; - container.innerHTML = svgCode; - Promise.resolve().then(() => { - const graphDiv = document.getElementById('graph-div'); - graphDiv.setAttribute('height', '100%'); - graphDiv.style.maxWidth = '100%'; - pzoom = panzoom(graphDiv, { - onPan: handlePanZoomChange, - onZoom: handlePanZoomChange, - controlIconsEnabled: true, - fit: true, - center: true + if (state.panZoom) { + hide = true; + pzoom?.destroy(); + pzoom = undefined; + Promise.resolve().then(() => { + const graphDiv = document.getElementById('graph-div'); + pzoom = panzoom(graphDiv, { + onPan: handlePanZoomChange, + onZoom: handlePanZoomChange, + controlIconsEnabled: true, + fit: true, + center: true + }); + const { pan, zoom } = state; + if (pan !== undefined && zoom !== undefined && Number.isFinite(zoom)) { + pzoom.zoom(zoom); + pzoom.pan(pan); + } + hide = false; }); - const { pan, zoom } = state; - if (pan !== undefined && zoom !== undefined && Number.isFinite(zoom)) { - pzoom.zoom(zoom); - pzoom.pan(pan); - } - hide = false; - }); + } + container.innerHTML = svgCode; + const graphDiv = document.getElementById('graph-div'); + graphDiv.setAttribute('height', '100%'); + graphDiv.style.maxWidth = '100%'; } }); view.parentElement.scrollTop = scroll; @@ -86,10 +91,8 @@ } }); window.addEventListener('resize', () => { - if (pzoom) { + if ($stateStore.panZoom && pzoom) { pzoom.resize(); - pzoom.fit(); - pzoom.center(); } }); }); diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts index 9ff6d86b..1aaf954a 100644 --- a/src/lib/types.d.ts +++ b/src/lib/types.d.ts @@ -39,6 +39,7 @@ export interface State { updateEditor: boolean; updateDiagram: boolean; autoSync: boolean; + panZoom?: boolean; pan?: { x: number; y: number }; zoom?: number; loader?: LoaderConfig; diff --git a/src/routes/edit.svelte b/src/routes/edit.svelte index 45bbd3e9..616ff0a0 100644 --- a/src/routes/edit.svelte +++ b/src/routes/edit.svelte @@ -151,31 +151,29 @@