1
0
Fork 0
mirror of synced 2024-08-24 14:31:44 +12:00
budibase/packages/builder/src/components/common/CodeMirrorEditor.svelte
2021-11-23 16:57:51 +00:00

163 lines
3.9 KiB
Svelte

<script context="module">
export const EditorModes = {
JS: {
name: "javascript",
json: false,
},
JSON: {
name: "javascript",
json: true,
},
SQL: {
name: "sql",
},
Handlebars: {
name: "handlebars",
base: "text/html",
},
}
</script>
<script>
import { Label } from "@budibase/bbui"
import CodeMirror from "components/integration/codemirror"
import { themeStore } from "builderStore"
import { createEventDispatcher, onMount } from "svelte"
export let mode = EditorModes.JS
export let value = ""
export let height = 300
export let resize = "none"
export let readonly = false
export let hints = []
export let label
const dispatch = createEventDispatcher()
let textarea
let editor
// Keep editor up to date with value
$: editor?.setValue(value || "")
// Creates an instance of a code mirror editor
async function createEditor(mode, value) {
if (!CodeMirror || !textarea || editor) {
return
}
// Configure CM options
const lightTheme = $themeStore.theme.includes("light")
const options = {
mode,
value: value || "",
readOnly: readonly,
theme: lightTheme ? "default" : "tomorrow-night-eighties",
// Style
lineNumbers: true,
lineWrapping: true,
indentWithTabs: true,
indentUnit: 2,
tabSize: 2,
// QOL addons
extraKeys: { "Ctrl-Space": "autocomplete" },
styleActiveLine: { nonEmpty: true },
autoCloseBrackets: true,
matchBrackets: true,
}
// Register hints plugin if desired
if (hints?.length) {
CodeMirror.registerHelper("hint", "dictionaryHint", function (editor) {
const cursor = editor.getCursor()
return {
list: hints,
from: CodeMirror.Pos(cursor.line, cursor.ch),
to: CodeMirror.Pos(cursor.line, cursor.ch),
}
})
CodeMirror.commands.autocomplete = function (cm) {
CodeMirror.showHint(cm, CodeMirror.hint.dictionaryHint)
}
}
// Construct CM instance
editor = CodeMirror.fromTextArea(textarea, options)
// Use a blur handler to update the value
editor.on("blur", instance => {
dispatch("change", instance.getValue())
})
}
// Export a function to expose caret position
export const getCaretPosition = () => {
const cursor = editor.getCursor()
return {
start: cursor.ch,
end: cursor.ch,
}
}
onMount(() => {
// Create the editor with initial value
createEditor(mode, value)
// Clean up editor on unmount
return () => {
if (editor) {
editor.toTextArea()
}
}
})
</script>
{#if label}
<div style="margin-bottom: var(--spacing-s)">
<Label small>{label}</Label>
</div>
{/if}
<div
style={`--code-mirror-height: ${height}px; --code-mirror-resize: ${resize}`}
>
<textarea tabindex="0" bind:this={textarea} readonly {value} />
</div>
<style>
div :global(.CodeMirror) {
height: var(--code-mirror-height);
min-height: var(--code-mirror-height);
font-family: monospace;
line-height: 1.3;
border: var(--spectrum-alias-border-size-thin) solid;
border-color: var(--spectrum-alias-border-color);
border-radius: var(--border-radius-s);
resize: var(--code-mirror-resize);
overflow: hidden;
}
/* Override default active line highlight colour in dark theme */
div
:global(.CodeMirror-focused.cm-s-tomorrow-night-eighties
.CodeMirror-activeline-background) {
background: rgba(255, 255, 255, 0.075);
}
/* Remove active line styling when not focused */
div
:global(.CodeMirror:not(.CodeMirror-focused)
.CodeMirror-activeline-background) {
background: unset;
}
/* Add a spectrum themed border when focused */
div :global(.CodeMirror-focused) {
border-color: var(--spectrum-alias-border-color-mouse-focus);
}
/* Ensure hints are always on top */
:global(.CodeMirror-hints) {
z-index: 999999;
}
</style>