2020-12-31 00:46:37 +13:00
|
|
|
<script>
|
2021-01-15 09:51:03 +13:00
|
|
|
import CodeMirror from "./codemirror"
|
2020-12-31 00:46:37 +13:00
|
|
|
import { onMount, createEventDispatcher } from "svelte"
|
2021-01-15 09:51:03 +13:00
|
|
|
import { themeStore } from "builderStore"
|
2020-12-31 00:46:37 +13:00
|
|
|
|
|
|
|
const dispatch = createEventDispatcher()
|
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
const THEMES = {
|
|
|
|
DARK: "tomorrow-night-eighties",
|
|
|
|
LIGHT: "default",
|
|
|
|
}
|
2020-12-31 00:46:37 +13:00
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
export let value = ""
|
|
|
|
export let readOnly = false
|
|
|
|
export let lineNumbers = true
|
|
|
|
export let tab = true
|
|
|
|
export let mode
|
|
|
|
|
|
|
|
let width
|
|
|
|
let height
|
2020-12-31 00:46:37 +13:00
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
// We have to expose set and update methods, rather
|
|
|
|
// than making this state-driven through props,
|
|
|
|
// because it's difficult to update an editor
|
|
|
|
// without resetting scroll otherwise
|
|
|
|
export async function set(new_value, new_mode) {
|
|
|
|
if (new_mode !== mode) {
|
|
|
|
await createEditor((mode = new_mode))
|
2021-01-12 06:18:22 +13:00
|
|
|
}
|
2021-01-15 09:51:03 +13:00
|
|
|
|
|
|
|
value = new_value
|
|
|
|
updating_externally = true
|
|
|
|
if (editor) editor.setValue(value)
|
|
|
|
updating_externally = false
|
|
|
|
}
|
|
|
|
|
|
|
|
export function update(new_value) {
|
|
|
|
value = new_value
|
|
|
|
|
|
|
|
if (editor) {
|
|
|
|
const { left, top } = editor.getScrollInfo()
|
|
|
|
editor.setValue((value = new_value))
|
|
|
|
editor.scrollTo(left, top)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function resize() {
|
|
|
|
editor.refresh()
|
|
|
|
}
|
|
|
|
|
|
|
|
export function focus() {
|
|
|
|
editor.focus()
|
|
|
|
}
|
|
|
|
|
|
|
|
const modes = {
|
|
|
|
js: {
|
|
|
|
name: "javascript",
|
|
|
|
json: false,
|
|
|
|
},
|
|
|
|
json: {
|
|
|
|
name: "javascript",
|
|
|
|
json: true,
|
|
|
|
},
|
|
|
|
sql: {
|
|
|
|
name: "sql",
|
|
|
|
},
|
|
|
|
svelte: {
|
|
|
|
name: "handlebars",
|
|
|
|
base: "text/html",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
const refs = {}
|
|
|
|
let editor
|
|
|
|
let updating_externally = false
|
|
|
|
let marker
|
|
|
|
let error_line
|
|
|
|
let destroyed = false
|
|
|
|
|
|
|
|
$: if (editor && width && height) {
|
|
|
|
editor.refresh()
|
2021-01-12 06:18:22 +13:00
|
|
|
}
|
2021-01-09 01:06:37 +13:00
|
|
|
|
2021-01-12 06:18:22 +13:00
|
|
|
onMount(() => {
|
2021-01-15 09:51:03 +13:00
|
|
|
createEditor(mode).then(() => {
|
|
|
|
if (editor) editor.setValue(value || "")
|
|
|
|
})
|
2021-01-09 01:06:37 +13:00
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
return () => {
|
|
|
|
destroyed = true
|
|
|
|
if (editor) editor.toTextArea()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
let first = true
|
|
|
|
|
|
|
|
async function createEditor(mode) {
|
|
|
|
if (destroyed || !CodeMirror) return
|
|
|
|
|
|
|
|
if (editor) editor.toTextArea()
|
|
|
|
|
|
|
|
const opts = {
|
|
|
|
lineNumbers,
|
2020-12-31 00:46:37 +13:00
|
|
|
lineWrapping: true,
|
2021-01-15 09:51:03 +13:00
|
|
|
indentWithTabs: true,
|
2020-12-31 00:46:37 +13:00
|
|
|
indentUnit: 2,
|
|
|
|
tabSize: 2,
|
2021-01-15 09:51:03 +13:00
|
|
|
value: "",
|
|
|
|
mode: modes[mode] || {
|
|
|
|
name: mode,
|
|
|
|
},
|
|
|
|
readOnly,
|
2020-12-31 00:46:37 +13:00
|
|
|
autoCloseBrackets: true,
|
|
|
|
autoCloseTags: true,
|
2021-01-15 09:51:03 +13:00
|
|
|
theme: $themeStore.darkMode ? THEMES.DARK : THEMES.LIGHT,
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tab)
|
|
|
|
opts.extraKeys = {
|
|
|
|
Tab: tab,
|
|
|
|
"Shift-Tab": tab,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creating a text editor is a lot of work, so we yield
|
|
|
|
// the main thread for a moment. This helps reduce jank
|
|
|
|
if (first) await sleep(50)
|
|
|
|
|
|
|
|
if (destroyed) return
|
|
|
|
|
|
|
|
editor = CodeMirror.fromTextArea(refs.editor, opts)
|
|
|
|
|
|
|
|
editor.on("change", instance => {
|
|
|
|
if (!updating_externally) {
|
|
|
|
const value = instance.getValue()
|
|
|
|
dispatch("change", { value })
|
|
|
|
}
|
2020-12-31 00:46:37 +13:00
|
|
|
})
|
2021-01-12 06:18:22 +13:00
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
if (first) await sleep(50)
|
|
|
|
editor.refresh()
|
2021-01-12 06:18:22 +13:00
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
first = false
|
|
|
|
}
|
|
|
|
|
|
|
|
function sleep(ms) {
|
|
|
|
return new Promise(fulfil => setTimeout(fulfil, ms))
|
|
|
|
}
|
2020-12-31 00:46:37 +13:00
|
|
|
</script>
|
|
|
|
|
2021-01-15 09:51:03 +13:00
|
|
|
<textarea tabindex="0" bind:this={refs.editor} readonly {value} />
|
2021-01-08 02:13:46 +13:00
|
|
|
|
|
|
|
<style>
|
|
|
|
textarea {
|
2021-01-15 09:51:03 +13:00
|
|
|
visibility: hidden;
|
2021-01-08 02:13:46 +13:00
|
|
|
}
|
2021-01-15 09:51:03 +13:00
|
|
|
|
2021-01-09 07:22:03 +13:00
|
|
|
:global(.CodeMirror) {
|
2021-01-15 09:51:03 +13:00
|
|
|
height: auto !important;
|
2021-01-09 07:22:03 +13:00
|
|
|
border-radius: var(--border-radius-m);
|
2021-01-15 09:51:03 +13:00
|
|
|
font-family: var(--font-sans) !important;
|
2021-01-09 07:22:03 +13:00
|
|
|
}
|
2021-01-08 02:13:46 +13:00
|
|
|
</style>
|