Diagrams working

This commit is contained in:
Sidharth Vinod
2021-05-10 17:49:52 +05:30
parent f1558d41b8
commit 1b94a2c6cf
12 changed files with 275 additions and 37 deletions
+2
View File
@@ -10,6 +10,7 @@
},
"devDependencies": {
"@sveltejs/kit": "next",
"@types/mermaid": "^8.2.5",
"@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0",
"autoprefixer": "^10.2.5",
@@ -31,6 +32,7 @@
"dependencies": {
"@fontsource/fira-mono": "^4.2.2",
"@lukeed/uuid": "^2.0.0",
"@tailwindcss/forms": "^0.3.2",
"cookie": "^0.4.1",
"js-base64": "^3.6.0",
"mermaid": "^8.9.3",
+12 -3
View File
@@ -6,15 +6,22 @@
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import { initEditor } from './util';
let divEl: HTMLDivElement = null;
let editor: monaco.editor.IStandaloneCodeEditor;
let Monaco;
export let text: string;
export let language: string;
export let editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = {
value: ['function x() {', '\tconsole.log("Hello world!");', '}'].join('\n'),
language: 'javascript'
// automaticLayout: true
value: text,
language: language
};
$: Monaco?.editor.setModelLanguage(editor.getModel(), language);
$: editor?.setValue(text);
const dispatch = createEventDispatcher<EditorEvents>();
onMount(async () => {
@@ -37,6 +44,8 @@
}
};
Monaco = await import('monaco-editor');
initEditor(Monaco);
// divEl = document.getElementById('editor') as HTMLDivElement;
editor = Monaco.editor.create(divEl, editorOptions);
editor.onDidChangeModelContent(async () => {
+75
View File
@@ -0,0 +1,75 @@
export const initEditor = (monaco) => {
monaco.languages.register({ id: 'mermaid' });
// Register a tokens provider for the language
monaco.languages.setMonarchTokensProvider('mermaid', {
typeKeywords: [
'graph',
'stateDiagram',
'sequenceDiagram',
'classDiagram',
'pie',
'flowchart',
'gantt'
],
keywords: ['patricipant', 'as'],
arrows: ['---', '===', '-->', '==>'],
tokenizer: {
root: [
[/[{}]/, 'delimiter.bracket'],
[/[a-z_$][\w$]*/, { cases: { '@typeKeywords': 'keyword', '@keywords': 'keyword' } }],
[/[-=>ox]+/, { cases: { '@arrows': 'transition' } }],
[/[\[\{\(}]+.+?[\)\]\}]+/, 'string'],
[/\".*\"/, 'string']
]
},
whitespace: [
[/[ \t\r\n]+/, 'white'],
[/\%\%.*$/, 'comment']
]
});
monaco.editor.defineTheme('myCoolTheme', {
base: 'vs',
inherit: false,
rules: [
{ token: 'keyword', foreground: '880000', fontStyle: 'bold' },
{ token: 'custom-error', foreground: 'ff0000', fontStyle: 'bold' },
{ token: 'string', foreground: 'AA8500' },
{ token: 'transition', foreground: '008800', fontStyle: 'bold' },
{ token: 'delimiter.bracket', foreground: '000000', fontStyle: 'bold' }
]
});
// Register a completion item provider for the new language
monaco.languages.registerCompletionItemProvider('mermaid', {
provideCompletionItems: () => {
var suggestions = [
{
label: 'simpleText',
kind: monaco.languages.CompletionItemKind.Text,
insertText: 'simpleText'
},
{
label: 'testing',
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: 'testing(${1:condition})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'ifelse',
kind: monaco.languages.CompletionItemKind.Snippet,
insertText: ['if (${1:condition}) {', '\t$0', '} else {', '\t', '}'].join('\n'),
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'If-Else Statement'
}
];
return { suggestions: suggestions };
}
});
};
export const getResizeHandler = (editor) => {
return (node) => editor && editor.layout();
};
+3
View File
@@ -0,0 +1,3 @@
import { writable } from 'svelte/store';
export const errorStore = writable(undefined);
+9
View File
@@ -0,0 +1,9 @@
import type { Mermaid } from 'mermaid';
let mer: Mermaid;
export const getMermaid = async (): Promise<Mermaid> => {
if (!mer) {
mer = await import('mermaid');
}
return mer;
};
+5 -2
View File
@@ -9,7 +9,9 @@ const defaultState: State = {
C -->|Two| E[iPhone]
C -->|Three| F[fa:fa-car Car]
`,
mermaid: 'default',
mermaid: JSON.stringify({
theme: 'default'
}),
updateEditor: false
};
@@ -17,6 +19,7 @@ export const codeStore = writable(defaultState);
export const loadState = (data: string): void => {
let state: State;
// debugger;
try {
const stateStr = decode(data);
console.log('state from url', stateStr);
@@ -38,7 +41,7 @@ export const updateCode = (code: string, updateEditor: boolean): void => {
});
};
export const updateConfig = (config: any, updateEditor: boolean): void => {
export const updateConfig = (config: string, updateEditor: boolean): void => {
codeStore.update((state) => {
return { ...state, mermaid: config, updateEditor };
});
+1 -2
View File
@@ -1,6 +1,5 @@
import { loadState } from './state';
export const loadStateFromURL = (): void => {
debugger;
loadState(window.location.hash);
loadState(window.location.hash.slice(1));
};
+54
View File
@@ -0,0 +1,54 @@
<script lang="ts">
import { errorStore } from '$lib/Util/error';
import { getMermaid } from '$lib/Util/mermaid';
import { codeStore } from '$lib/Util/state';
import { onMount } from 'svelte';
let container;
export let code = '';
export let errorClass = '';
onMount(async () => {
const mermaid = await getMermaid();
codeStore.subscribe((state) => {
try {
if (container && state) {
// Replacing special characters '<' and '>' with encoded '&lt;' and '&gt;'
code = state.code; //.replace(/</g, '&lt;').replace(/>/g, '&gt;');
container.innerHTML = code;
delete container.dataset.processed;
mermaid.initialize(Object.assign({}, state.mermaid));
mermaid.init(undefined, container);
mermaid.render('graph-div', code, insertSvg);
}
} catch (e) {
console.log('view fail', e);
errorClass = 'error';
}
});
errorStore.subscribe((error) => {
if (typeof error === 'undefined') {
errorClass = '';
} else {
errorClass = 'error';
console.log('Error: ', error);
}
});
});
let insertSvg = function (svgCode, bindFunctions) {};
</script>
<div id="view" class={`p-4 ${errorClass}`}>
<div bind:this={container} class="flex-grow overflow-auto" />
</div>
<style>
#view {
border: 1px solor darkred;
flex: 1;
}
.error {
opacity: 0.5;
}
</style>
+76 -17
View File
@@ -1,17 +1,38 @@
<script context="module">
export const ssr = false;
// import mermaid from 'mermaid';
</script>
<script lang="ts">
import Editor from '$lib/Editor/index.svelte';
import View from '$lib/View/index.svelte';
import Card from '$lib/Card/index.svelte';
import Tabs from '$lib/Tabs/index.svelte';
import { initURLSubscription, updateCode } from '$lib/Util/state';
import { initURLSubscription, updateCode, updateConfig, codeStore } from '$lib/Util/state';
import { loadStateFromURL } from '$lib/Util/util';
import { errorStore } from '$lib/Util/error';
let selectedTab = 'code';
let selectedMode = 'code';
let autoSync = true;
const languageMap = {
code: 'mermaid',
config: 'json'
};
let text: string = '';
let language: 'mermaid' | 'json' = 'mermaid';
$: language = languageMap[selectedMode];
$: {
if ($codeStore.updateEditor) {
if (selectedMode === 'code') {
text = $codeStore.code;
} else {
text = $codeStore.mermaid;
}
}
}
const tabSelectHandler = (message: CustomEvent<Tab>) => {
selectedTab = message.detail.id;
$codeStore.updateEditor = true;
selectedMode = message.detail.id;
};
const tabs: Tab[] = [
{
@@ -24,10 +45,33 @@
}
];
const updateHandler = async (message: CustomEvent<EditorUpdateEvent>) => {
updateCode(message.detail.text, false);
const handleCodeUpdate = (code: string): void => {
updateCode(code, false);
};
const handleConfigUpdate = (config: string): void => {
updateConfig(config, false);
};
let editorText: string = '';
const syncDiagram = () => {
try {
if (selectedMode === 'code') {
handleCodeUpdate(editorText);
} else {
handleConfigUpdate(editorText);
}
} catch (e) {
errorStore.set(e);
}
};
const updateHandler = (message: CustomEvent<EditorUpdateEvent>) => {
editorText = message.detail.text;
if (autoSync) {
syncDiagram();
}
};
loadStateFromURL();
initURLSubscription();
</script>
@@ -36,18 +80,33 @@
<title>Edit</title>
</svelte:head>
<div class="w-2/5 h-screen flex flex-col gap-6">
<Card class="h-1/2">
<div slot="title">
<Tabs on:select={tabSelectHandler} {tabs} />
</div>
<div class="flex">
<div class="w-2/5 h-screen flex flex-col gap-6">
<Card class="h-1/2">
<div slot="title">
<div class="flex">
<div class="flex"><Tabs on:select={tabSelectHandler} {tabs} /></div>
<div class="flex-grow" />
<div class="flex gap-x-4 text-white">
{#if !autoSync}
<button on:click={syncDiagram}> Sync</button>
{/if}
<label for="autoSync">
<input type="checkbox" name="autoSync" bind:checked={autoSync} />
Auto
</label>
</div>
</div>
</div>
{#if selectedTab == 'code'}
<Editor on:update={updateHandler} />
{:else}
<Editor on:update={updateHandler} />
{/if}
</Card>
<Editor on:update={updateHandler} {language} {text} />
</Card>
</div>
<Card class="h-1/3" />
<div class="w-3/5 h-screen">
<Card class="h-full">
<div slot="title" class="text-white">Diagram</div>
<View /></Card
>
</div>
</div>
+10 -2
View File
@@ -7,12 +7,20 @@ const config = {
preprocess: [
preprocess({
postcss: true
}),
})
],
kit: {
// hydrate the <div id="svelte"> element in src/app.html
target: '#svelte'
target: '#svelte',
vite: {
// ssr: {
// noExternal: []
// },
optimizeDeps: {
include: ['mermaid']
}
}
}
};
+11 -11
View File
@@ -1,26 +1,26 @@
const { tailwindExtractor } = require("tailwindcss/lib/lib/purgeUnusedStyles");
const { tailwindExtractor } = require('tailwindcss/lib/lib/purgeUnusedStyles');
module.exports = {
mode: "aot",
mode: 'aot',
purge: {
content: [
"./src/**/*.{html,js,svelte,ts}",
],
content: ['./src/**/*.{html,js,svelte,ts}'],
options: {
defaultExtractor: (content) => [
// If this stops working, please open an issue at https://github.com/svelte-add/tailwindcss/issues rather than bothering Tailwind Labs about it
...tailwindExtractor(content),
// Match Svelte class: directives (https://github.com/tailwindlabs/tailwindcss/discussions/1731)
...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(([_match, group, ..._rest]) => group),
],
...[...content.matchAll(/(?:class:)*([\w\d-/:%.]+)/gm)].map(
([_match, group, ..._rest]) => group
)
]
},
safelist: [/^svelte-[\d\w]+$/],
safelist: [/^svelte-[\d\w]+$/]
},
theme: {
extend: {},
extend: {}
},
variants: {
extend: {},
extend: {}
},
plugins: [],
plugins: [require('@tailwindcss/forms')]
};
+17
View File
@@ -127,6 +127,13 @@
source-map "^0.7.3"
svelte-hmr "^0.14.2"
"@tailwindcss/forms@^0.3.2":
version "0.3.2"
resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.3.2.tgz#e28c4514a53e69f725416a5a2a6d0f221683f069"
integrity sha512-aj2/rJsGb2whAZ/BQWHWWQRSbhH0r/l1ozOByiv+ZNjBD84GMvb5dhAyfpeasFky+EJrAwX5eaqft8NQMZFWvA==
dependencies:
mini-svg-data-uri "^1.2.3"
"@trysound/sax@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.1.1.tgz#3348564048e7a2d7398c935d466c0414ebb6a669"
@@ -137,6 +144,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
"@types/mermaid@^8.2.5":
version "8.2.5"
resolved "https://registry.yarnpkg.com/@types/mermaid/-/mermaid-8.2.5.tgz#85ef5be8a1532c0f596a920972a6242f009a6ce8"
integrity sha512-sVKakt9BN2T2qaEHsmQPtV77QAyu5kZh0CErCEaUW4ThBn4VJ47xYuLuvJttmeuSeKfdP/ACi5h184H4gWt4gA==
"@types/node@*":
version "15.0.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67"
@@ -1765,6 +1777,11 @@ min-indent@^1.0.0:
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==
mini-svg-data-uri@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.2.3.tgz#e16baa92ad55ddaa1c2c135759129f41910bc39f"
integrity sha512-zd6KCAyXgmq6FV1mR10oKXYtvmA9vRoB6xPSTUJTbFApCtkefDnYueVR1gkof3KcdLZo1Y8mjF2DFmQMIxsHNQ==
minify@^4.1.1:
version "4.1.3"
resolved "https://registry.yarnpkg.com/minify/-/minify-4.1.3.tgz#58467922d14303f55a3a28fa79641371955b8fbd"