UNPKG

@formkl/editor

Version:

A universal Editor for Formkl using web component API

212 lines (205 loc) 7.02 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const commands = require('@codemirror/commands'); const state = require('@codemirror/state'); const view = require('@codemirror/view'); const codemirror = require('codemirror'); const autocomplete = require('@codemirror/autocomplete'); const lint = require('@codemirror/lint'); const FormklParser = require('formkl'); const completions = [ { label: "formkl", type: "class", detail: "Formkl block", info: "Start declaring a Form" }, { label: "base", type: "type", detail: "Model type", info: `Default structure form data, for ex: [ { key: "fullname", value: "Michael Jackson" } ]` }, { label: "flat", type: "type", detail: "Model type", info: `Flatten structure form data, for ex: { fullname: "Michael Jackson" }` }, { label: "multiple", type: "keyword", detail: "Multiple responses", info: "Declare whether a field of a section has multiple responses" }, { label: "includes", type: "keyword", detail: "Section block", info: "Declare a group of fields" }, { label: "or", type: "keyword", detail: "OR condition operator", info: "Use as: valid(>500 or ==300)" }, { label: "and", type: "keyword", detail: "AND condition operator", info: "Use as: valid(>500 and <1000)" }, { label: "has", type: "keyword", detail: "HAS condition operator", info: "Use as: valid(has 'some keyword')" }, { label: "as", type: "keyword", detail: "Field alias", info: "Declare custom key for field in form data" }, { label: "require", type: "keyword", detail: "Require field" }, { label: "valid", type: "function", detail: "Validation", info: "Declare simple validation" }, { label: "regex", type: "function", detail: "RegExp", info: "Declare a validation with Regular Expression" }, { label: "url", type: "function", detail: "Selection", info: "Declare data source url for select field's options" }, { label: "get", type: "function", detail: "Submit method" }, { label: "post", type: "function", detail: "Submit method" }, { label: "put", type: "function", detail: "Submit method" }, { label: "patch", type: "function", detail: "Submit method" }, { label: "delete", type: "function", detail: "Submit method" }, { label: "text", type: "constant", detail: "Field" }, { label: "paragraph", type: "constant", detail: "Field" }, { label: "number", type: "constant", detail: "Field" }, { label: "switch", type: "constant", detail: "Field" }, { label: "checkbox", type: "constant", detail: "Field" }, { label: "radio", type: "constant", detail: "Field" }, { label: "select", type: "constant", detail: "Field" }, { label: "datetimerange", type: "constant", detail: "Field" }, { label: "datetime", type: "constant", detail: "Field" }, { label: "daterange", type: "constant", detail: "Field" }, { label: "timerange", type: "constant", detail: "Field" }, { label: "time", type: "constant", detail: "Field" }, { label: "date", type: "constant", detail: "Field" }, { label: "future date", type: "constant", detail: "Field" }, { label: "past date", type: "constant", detail: "Field" }, { label: "birthday", type: "constant", detail: "Field" }, { label: "US phone", type: "constant", detail: "Field" }, { label: "VN phone", type: "constant", detail: "Field" } ]; const keywordSuggestion = (context) => { let before = context.matchBefore(/\w+/); if (!context.explicit && !before) return null; return { from: before ? before.from : context.pos, options: completions, validFor: /^\w*$/ }; }; const AutoCompleteExtension = autocomplete.autocompletion({ override: [keywordSuggestion] }); function lintSyntax(view) { const diagnostics = []; const content = view.state.doc.toString(); try { FormklParser.parse(content); } catch (err) { const errorIndex = /at \d+\:\d+/.exec(err.message)[0].replace(/at /g, "").split(":").map(Number); diagnostics.push({ from: errorIndex ? view.state.doc.line(errorIndex[0]).from : view.state.doc.length, to: errorIndex ? view.state.doc.line(errorIndex[0]).to : view.state.doc.length, severity: "error", message: err.message }); } return diagnostics; } const LintExtension = lint.linter(lintSyntax); const createEditor = (options) => { return class FormklEditor extends HTMLElement { constructor() { super(); this.modified = false; this.editor = null; this.attachShadow({ mode: "open" }); codemirror.EditorView.theme(options?.theme || {}, { dark: Boolean(options?.dark) }); const wrapper = document.createElement("div"); wrapper.id = "formkl__editor"; this.shadowRoot.appendChild(wrapper); this.editor = new codemirror.EditorView({ ...options, parent: this.shadowRoot.getElementById("formkl__editor"), state: state.EditorState.create({ doc: this.value, extensions: [ codemirror.basicSetup, view.keymap.of([commands.indentWithTab]), AutoCompleteExtension, LintExtension, codemirror.EditorView.updateListener.of((viewUpdate) => { const eventInput = new CustomEvent("input", { detail: this.value }); const eventFocus = new CustomEvent("focus"); const eventBlur = new CustomEvent("blur"); const eventChange = new CustomEvent("change"); if (viewUpdate.docChanged) { this.dispatchEvent(eventInput); this.modified = true; } if (viewUpdate.focusChanged) { viewUpdate.view.hasFocus ? this.dispatchEvent(eventFocus) : this.dispatchEvent(eventBlur); if (this.modified && !viewUpdate.view.hasFocus) { this.dispatchEvent(eventChange); this.modified = false; } } }) ].concat(options?.extensions || []) }) }); } static get observedAttributes() { return ["value"]; } get value() { return this.getContent(); } set value(newValue) { this.setContent(newValue); } connectedCallback() { } attributeChangedCallback(name, oldValue, newValue) { if (name === "value") { this.setContent(newValue); } } getContent() { return this.editor?.state.doc.toString() || ""; } setContent(content) { if (content !== this.getContent()) { this.editor.dispatch({ changes: { from: 0, to: this.editor.state.doc.length, insert: content } }); } } }; }; const index = { createEditor }; if (window && window.customElements) { window.customElements.define("formkl-editor", createEditor()); } exports.createEditor = createEditor; exports.default = index;