added page export and import

This commit is contained in:
Gani Georgiev
2022-08-05 23:25:16 +03:00
parent f459dd8812
commit 93ab5fbea2
12 changed files with 665 additions and 108 deletions
+134
View File
@@ -0,0 +1,134 @@
<script>
import { onMount, createEventDispatcher } from "svelte";
// code mirror imports
// ---
import {
keymap,
highlightSpecialChars,
drawSelection,
dropCursor,
rectangularSelection,
EditorView,
placeholder as placeholderExt,
} from "@codemirror/view";
import { EditorState, Compartment } from "@codemirror/state";
import { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from "@codemirror/language";
import { defaultKeymap, history, historyKeymap, indentWithTab } from "@codemirror/commands";
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
import { javascript } from "@codemirror/lang-javascript";
// ---
const dispatch = createEventDispatcher();
export let value = "";
export let disabled = false;
export let placeholder = "";
export let singleLine = false;
let editor;
let container;
let editableCompartment = new Compartment();
let readOnlyCompartment = new Compartment();
let placeholderCompartment = new Compartment();
$: if (editor && typeof disabled !== "undefined") {
editor.dispatch({
effects: [
editableCompartment.reconfigure(EditorView.editable.of(!disabled)),
readOnlyCompartment.reconfigure(EditorState.readOnly.of(disabled)),
],
});
triggerNativeChange();
}
$: if (editor && value != editor.state.doc.toString()) {
editor.dispatch({
changes: {
from: 0,
to: editor.state.doc.length,
insert: value,
},
});
}
$: if (editor && typeof placeholder !== "undefined") {
editor.dispatch({
effects: [placeholderCompartment.reconfigure(placeholderExt(placeholder))],
});
}
// Focus the editor (if inited).
export function focus() {
editor?.focus();
}
// Emulate native change event for the editor container element.
function triggerNativeChange() {
container?.dispatchEvent(
new CustomEvent("change", {
detail: { value },
bubbles: true,
})
);
}
onMount(() => {
const submitShortcut = {
key: "Enter",
run: (_) => {
// trigger submit on enter for singleline input
if (singleLine) {
dispatch("submit", value);
}
},
};
editor = new EditorView({
parent: container,
state: EditorState.create({
doc: value,
extensions: [
highlightSpecialChars(),
history(),
drawSelection(),
dropCursor(),
EditorState.allowMultipleSelections.of(true),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
bracketMatching(),
closeBrackets(),
rectangularSelection(),
highlightSelectionMatches(),
keymap.of([
submitShortcut,
indentWithTab,
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
...historyKeymap,
]),
EditorView.lineWrapping,
javascript(),
placeholderCompartment.of(placeholderExt(placeholder)),
editableCompartment.of(EditorView.editable.of(true)),
readOnlyCompartment.of(EditorState.readOnly.of(false)),
EditorState.transactionFilter.of((tr) => {
return singleLine && tr.newDoc.lines > 1 ? [] : tr;
}),
EditorView.updateListener.of((v) => {
if (!v.docChanged || disabled) {
return;
}
value = v.state.doc.toString();
triggerNativeChange();
}),
],
}),
});
return () => editor?.destroy();
});
</script>
<div bind:this={container} class="code-editor" />
+96
View File
@@ -0,0 +1,96 @@
<script>
import OverlayPanel from "@/components/base/OverlayPanel.svelte";
export let title = "Side-by-side diff";
export let contentATitle = "Old state";
export let contentBTitle = "New state";
let panel;
let contentA = "";
let contentB = "";
export function show(a, b) {
contentA = a;
contentB = b;
panel?.show();
}
export function hide() {
return panel?.hide();
}
function diffsToHtml(diffs, ops = [DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL]) {
const html = [];
const pattern_amp = /&/g;
const pattern_lt = /</g;
const pattern_gt = />/g;
const pattern_para = /\n/g;
for (let i = 0; i < diffs.length; i++) {
const op = diffs[i][0]; // operation (insert, delete, equal)
if (!ops.includes(op)) {
continue;
}
const text = diffs[i][1]
.replace(pattern_amp, "&amp;")
.replace(pattern_lt, "&lt;")
.replace(pattern_gt, "&gt;")
.replace(pattern_para, "<br>");
switch (op) {
case DIFF_INSERT:
html[i] = '<ins class="block">' + text + "</ins>";
break;
case DIFF_DELETE:
html[i] = '<del class="block">' + text + "</del>";
break;
case DIFF_EQUAL:
html[i] = text;
break;
}
}
return html.join("");
}
function diff(ops = [DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL]) {
const dmp = new diff_match_patch();
const lines = dmp.diff_linesToChars_(contentA, contentB);
const diffs = dmp.diff_main(lines.chars1, lines.chars2, false);
dmp.diff_charsToLines_(diffs, lines.lineArray);
return diffsToHtml(diffs, ops);
}
</script>
<OverlayPanel bind:this={panel} class="full-width-popup diff-popup" popup on:show on:hide>
<svelte:fragment slot="header">
<h4 class="center txt-break">{title}</h4>
</svelte:fragment>
<div class="grid m-b-base">
<div class="col-6">
<div class="section-title">{contentATitle}</div>
<code class="code-block">{@html diff([DIFF_DELETE, DIFF_EQUAL])}</code>
</div>
<div class="col-6">
<div class="section-title">{contentBTitle}</div>
<code class="code-block">{@html diff([DIFF_INSERT, DIFF_EQUAL])}</code>
</div>
</div>
<svelte:fragment slot="footer">
<button type="button" class="btn btn-secondary" on:click={hide}>Close</button>
</svelte:fragment>
</OverlayPanel>
<style>
code {
color: var(--txtHintColor);
min-height: 100%;
}
</style>