UNPKG

@eccenca/gui-elements

Version:

GUI elements based on other libraries, usable in React application, written in Typescript.

300 lines 19.1 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import React, { useMemo, useRef } from "react"; import { defaultKeymap, indentWithTab } from "@codemirror/commands"; import { defaultHighlightStyle, foldKeymap } from "@codemirror/language"; import { EditorState } from "@codemirror/state"; import { EditorView, keymap } from "@codemirror/view"; import { minimalSetup } from "codemirror"; import { markField } from "../../components/AutoSuggestion/extensions/markText.js"; import { MarkdownToolbar } from "./toolbars/markdown.toolbar.js"; import { Markdown } from "../../cmem/markdown/Markdown.js"; import { CLASSPREFIX as eccgui } from "../../configuration/constants.js"; //hooks import { supportedCodeEditorModes, useCodeMirrorModeExtension, } from "./hooks/useCodemirrorModeExtension.hooks.js"; import { jsLinter } from "./linters/jsLinter.js"; import { turtleLinter } from "./linters/turtleLinter.js"; //adaptations import { adaptedCodeFolding, AdaptedEditorView, AdaptedEditorViewDomEventHandlers, adaptedFoldGutter, adaptedHighlightActiveLine, adaptedHighlightSpecialChars, adaptedLineNumbers, adaptedLintGutter, adaptedPlaceholder, compartment, adaptedSyntaxHighlighting, } from "./tests/codemirrorTestHelper.js"; var addExtensionsFor = function (flag) { var extensions = []; for (var _i = 1; _i < arguments.length; _i++) { extensions[_i - 1] = arguments[_i]; } return (flag ? __spreadArray([], __read(extensions), false) : []); }; var addToKeyMapConfigFor = function (flag) { var keys = []; for (var _i = 1; _i < arguments.length; _i++) { keys[_i - 1] = arguments[_i]; } return (flag ? __spreadArray([], __read(keys), false) : []); }; var addHandlersFor = function (flag, handlerName, handler) { var _a; return flag ? (_a = {}, _a[handlerName] = handler, _a) : {}; }; var ModeLinterMap = new Map([ ["turtle", [turtleLinter]], ["javascript", [jsLinter]], ]); var ModeToolbarSupport = ["markdown"]; /** * Includes a code editor, currently we use CodeMirror library as base. */ export var CodeEditor = function (_a) { var className = _a.className, onChange = _a.onChange, onSelection = _a.onSelection, onMouseDown = _a.onMouseDown, onFocusChange = _a.onFocusChange, onKeyDown = _a.onKeyDown, onCursorChange = _a.onCursorChange, name = _a.name, id = _a.id, mode = _a.mode, _b = _a.preventLineNumbers, preventLineNumbers = _b === void 0 ? false : _b, _c = _a.defaultValue, defaultValue = _c === void 0 ? "" : _c, _d = _a.readOnly, readOnly = _d === void 0 ? false : _d, _e = _a.shouldHaveMinimalSetup, shouldHaveMinimalSetup = _e === void 0 ? true : _e, _f = _a.wrapLines, wrapLines = _f === void 0 ? false : _f, onScroll = _a.onScroll, setEditorView = _a.setEditorView, _g = _a.supportCodeFolding, supportCodeFolding = _g === void 0 ? false : _g, _h = _a.shouldHighlightActiveLine, shouldHighlightActiveLine = _h === void 0 ? false : _h, outerDivAttributes = _a.outerDivAttributes, _j = _a.tabIntentSize, tabIntentSize = _j === void 0 ? 2 : _j, _k = _a.tabIntentStyle, tabIntentStyle = _k === void 0 ? "tab" : _k, placeholder = _a.placeholder, _l = _a.additionalExtensions, additionalExtensions = _l === void 0 ? [] : _l, _m = _a.tabForceSpaceForModes, tabForceSpaceForModes = _m === void 0 ? ["python", "yaml"] : _m, _o = _a.enableTab, enableTab = _o === void 0 ? false : _o, height = _a.height, _p = _a.useLinting, useLinting = _p === void 0 ? false : _p, _q = _a.autoFocus, autoFocus = _q === void 0 ? false : _q, _r = _a.disabled, disabled = _r === void 0 ? false : _r, intent = _a.intent, useToolbar = _a.useToolbar, translate = _a.translate, otherCodeEditorProps = __rest(_a, ["className", "onChange", "onSelection", "onMouseDown", "onFocusChange", "onKeyDown", "onCursorChange", "name", "id", "mode", "preventLineNumbers", "defaultValue", "readOnly", "shouldHaveMinimalSetup", "wrapLines", "onScroll", "setEditorView", "supportCodeFolding", "shouldHighlightActiveLine", "outerDivAttributes", "tabIntentSize", "tabIntentStyle", "placeholder", "additionalExtensions", "tabForceSpaceForModes", "enableTab", "height", "useLinting", "autoFocus", "disabled", "intent", "useToolbar", "translate"]); var parent = useRef(undefined); var _s = __read(React.useState(), 2), view = _s[0], setView = _s[1]; var currentView = React.useRef(); currentView.current = view; var currentReadOnly = React.useRef(readOnly); currentReadOnly.current = readOnly; var _t = __read(React.useState(false), 2), showPreview = _t[0], setShowPreview = _t[1]; // CodeMirror Compartments in order to allow for re-configuration after initialization var readOnlyCompartment = React.useRef(compartment()); var wrapLinesCompartment = React.useRef(compartment()); var preventLineNumbersCompartment = React.useRef(compartment()); var shouldHaveMinimalSetupCompartment = React.useRef(compartment()); var placeholderCompartment = React.useRef(compartment()); var modeCompartment = React.useRef(compartment()); var keyMapConfigsCompartment = React.useRef(compartment()); var tabIntentSizeCompartment = React.useRef(compartment()); var disabledCompartment = React.useRef(compartment()); var supportCodeFoldingCompartment = React.useRef(compartment()); var useLintingCompartment = React.useRef(compartment()); var shouldHighlightActiveLineCompartment = React.useRef(compartment()); var linters = useMemo(function () { if (!mode) { return []; } var values = [adaptedLintGutter()]; var linters = ModeLinterMap.get(mode); if (linters) { values.push.apply(values, __spreadArray([], __read(linters.map(function (linter) { return linter(); })), false)); } return values; }, [mode]); var onKeyDownHandler = function (event, view) { if (onKeyDown && !onKeyDown(event)) { if (event.key === "Enter" && !currentReadOnly.current) { var cursor = view.state.selection.main.head; view.dispatch({ changes: { from: cursor, insert: "\n", }, selection: { anchor: cursor + 1, }, }); } } }; var getTranslation = function (key) { if (translate && typeof translate === "function") { return translate(key); } return false; }; var createKeyMapConfigs = function () { var tabIndent = !!(tabIntentStyle === "tab" && mode && !(tabForceSpaceForModes !== null && tabForceSpaceForModes !== void 0 ? tabForceSpaceForModes : []).includes(mode)) || enableTab; return __spreadArray(__spreadArray([ defaultKeymap ], __read(addToKeyMapConfigFor.apply(void 0, __spreadArray([supportCodeFolding], __read(foldKeymap), false))), false), __read(addToKeyMapConfigFor(tabIndent, indentWithTab)), false); }; React.useEffect(function () { var domEventHandlers = __assign(__assign(__assign(__assign(__assign({}, addHandlersFor(!!onScroll, "scroll", onScroll)), addHandlersFor(!!onMouseDown, "mousedown", function (_, view) { return onMouseDown && onMouseDown(view); })), addHandlersFor(!!onFocusChange, "blur", function () { return onFocusChange && onFocusChange(false); })), addHandlersFor(!!onFocusChange, "focus", function () { return onFocusChange && onFocusChange(true); })), addHandlersFor(!!onKeyDown, "keydown", onKeyDownHandler)); var extensions = [ markField, placeholderCompartment.current.of(adaptedPlaceholder(placeholder)), adaptedHighlightSpecialChars(), modeCompartment.current.of(useCodeMirrorModeExtension(mode)), keyMapConfigsCompartment.current.of(keymap === null || keymap === void 0 ? void 0 : keymap.of(createKeyMapConfigs())), tabIntentSizeCompartment.current.of(EditorState === null || EditorState === void 0 ? void 0 : EditorState.tabSize.of(tabIntentSize)), readOnlyCompartment.current.of(EditorState === null || EditorState === void 0 ? void 0 : EditorState.readOnly.of(readOnly)), disabledCompartment.current.of(EditorView === null || EditorView === void 0 ? void 0 : EditorView.editable.of(!disabled)), AdaptedEditorViewDomEventHandlers(domEventHandlers), EditorView === null || EditorView === void 0 ? void 0 : EditorView.updateListener.of(function (v) { var _a, _b; if (disabled) return; if (onChange && v.docChanged) { // Only fire if the text has actually been changed onChange(v.state.doc.toString()); } if (onSelection) onSelection(v.state.selection.ranges.filter(function (r) { return !r.empty; }).map(function (_a) { var from = _a.from, to = _a.to; return ({ from: from, to: to }); })); if (onFocusChange && intent && !((_a = v.view.dom.classList) === null || _a === void 0 ? void 0 : _a.contains("".concat(eccgui, "-intent--").concat(intent)))) { v.view.dom.classList.add("".concat(eccgui, "-intent--").concat(intent)); } if (onCursorChange) { var cursorPosition = (_b = v.state.selection.main.head) !== null && _b !== void 0 ? _b : 0; var editorRect = v.view.dom.getBoundingClientRect(); var coords = v.view.coordsAtPos(cursorPosition), scrollInfo = v.view.scrollDOM; if (coords && scrollInfo && editorRect) { // Calculate the coordinates relative to the editor's top-left corner var relativeLeft = coords.left - editorRect.left; var relativeBottom = coords.bottom - editorRect.bottom; onCursorChange(cursorPosition, __assign(__assign({}, coords), { left: relativeLeft, bottom: relativeBottom }), scrollInfo, v.view); } } }), shouldHaveMinimalSetupCompartment.current.of(addExtensionsFor(shouldHaveMinimalSetup, minimalSetup)), preventLineNumbersCompartment.current.of(addExtensionsFor(!preventLineNumbers, adaptedLineNumbers())), shouldHighlightActiveLineCompartment.current.of(addExtensionsFor(shouldHighlightActiveLine, adaptedHighlightActiveLine())), wrapLinesCompartment.current.of(addExtensionsFor(wrapLines, EditorView === null || EditorView === void 0 ? void 0 : EditorView.lineWrapping)), supportCodeFoldingCompartment.current.of(addExtensionsFor(supportCodeFolding, adaptedFoldGutter(), adaptedCodeFolding())), useLintingCompartment.current.of(addExtensionsFor.apply(void 0, __spreadArray([useLinting], __read(linters), false))), adaptedSyntaxHighlighting(defaultHighlightStyle), additionalExtensions, ]; var view = new AdaptedEditorView({ state: EditorState === null || EditorState === void 0 ? void 0 : EditorState.create({ doc: defaultValue, extensions: extensions, }), parent: parent.current, }); setView(view); if (view === null || view === void 0 ? void 0 : view.dom) { if (height) { view.dom.style.height = typeof height === "string" ? height : "".concat(height, "px"); } if (disabled) { view.dom.className += " ".concat(eccgui, "-disabled"); } if (intent) { view.dom.className += " ".concat(eccgui, "-intent--").concat(intent); } if (autoFocus) { view.focus(); } if (setEditorView) { setEditorView(view); } } return function () { view.destroy(); if (setEditorView) { setEditorView(undefined); setView(undefined); } }; }, [parent.current]); // Updates an extension for a specific parameter that has changed after the initialization var updateExtension = function (extension, parameterCompartment) { var _a; if (extension) { (_a = currentView.current) === null || _a === void 0 ? void 0 : _a.dispatch({ effects: parameterCompartment.reconfigure(extension) }); } }; React.useEffect(function () { updateExtension(EditorState === null || EditorState === void 0 ? void 0 : EditorState.readOnly.of(readOnly), readOnlyCompartment.current); }, [readOnly]); React.useEffect(function () { updateExtension(adaptedPlaceholder(placeholder), placeholderCompartment.current); }, [placeholder]); React.useEffect(function () { updateExtension(useCodeMirrorModeExtension(mode), modeCompartment.current); }, [mode]); React.useEffect(function () { updateExtension(keymap === null || keymap === void 0 ? void 0 : keymap.of(createKeyMapConfigs()), keyMapConfigsCompartment.current); }, [supportCodeFolding, mode, tabIntentStyle, (tabForceSpaceForModes !== null && tabForceSpaceForModes !== void 0 ? tabForceSpaceForModes : []).join(", "), enableTab]); React.useEffect(function () { updateExtension(EditorState === null || EditorState === void 0 ? void 0 : EditorState.tabSize.of(tabIntentSize !== null && tabIntentSize !== void 0 ? tabIntentSize : 2), tabIntentSizeCompartment.current); }, [tabIntentSize]); React.useEffect(function () { updateExtension(EditorView === null || EditorView === void 0 ? void 0 : EditorView.editable.of(!disabled), disabledCompartment.current); }, [disabled]); React.useEffect(function () { updateExtension(addExtensionsFor(shouldHaveMinimalSetup !== null && shouldHaveMinimalSetup !== void 0 ? shouldHaveMinimalSetup : true, minimalSetup), shouldHaveMinimalSetupCompartment.current); }, [shouldHaveMinimalSetup]); React.useEffect(function () { updateExtension(addExtensionsFor(!preventLineNumbers, adaptedLineNumbers()), preventLineNumbersCompartment.current); }, [preventLineNumbers]); React.useEffect(function () { updateExtension(addExtensionsFor(shouldHighlightActiveLine !== null && shouldHighlightActiveLine !== void 0 ? shouldHighlightActiveLine : false, adaptedHighlightActiveLine()), shouldHighlightActiveLineCompartment.current); }, [shouldHighlightActiveLine]); React.useEffect(function () { updateExtension(addExtensionsFor(wrapLines !== null && wrapLines !== void 0 ? wrapLines : false, EditorView === null || EditorView === void 0 ? void 0 : EditorView.lineWrapping), wrapLinesCompartment.current); }, [wrapLines]); React.useEffect(function () { updateExtension(addExtensionsFor(supportCodeFolding !== null && supportCodeFolding !== void 0 ? supportCodeFolding : false, adaptedFoldGutter(), adaptedCodeFolding()), supportCodeFoldingCompartment.current); }, [supportCodeFolding]); React.useEffect(function () { updateExtension(addExtensionsFor.apply(void 0, __spreadArray([useLinting !== null && useLinting !== void 0 ? useLinting : false], __read(linters), false)), useLintingCompartment.current); }, [mode, useLinting]); var hasToolbarSupport = mode && ModeToolbarSupport.indexOf(mode) > -1 && useToolbar; var editorToolbar = function (mode) { var _a; switch (mode) { case "markdown": return (React.createElement("div", null, React.createElement("div", { className: "".concat(eccgui, "-codeeditor__toolbar") }, React.createElement(MarkdownToolbar, { view: view, togglePreviewStatus: function () { return setShowPreview(function (p) { return !p; }); }, showPreview: showPreview, translate: getTranslation, disabled: disabled, readonly: readOnly })), showPreview && (React.createElement("div", { className: "".concat(eccgui, "-codeeditor__preview") }, React.createElement(Markdown, null, (_a = view === null || view === void 0 ? void 0 : view.state.doc.toString()) !== null && _a !== void 0 ? _a : ""))))); default: return React.createElement(React.Fragment, null); } }; return (React.createElement("div", __assign({}, outerDivAttributes, { // overwrite/extend some attributes id: id ? id : name ? "codemirror-".concat(name) : undefined, ref: parent }, otherCodeEditorProps, { className: "".concat(eccgui, "-codeeditor ").concat(eccgui, "-codeeditor--mode-").concat(mode) + (className ? " ".concat(className) : "") + ((outerDivAttributes === null || outerDivAttributes === void 0 ? void 0 : outerDivAttributes.className) ? " ".concat(outerDivAttributes === null || outerDivAttributes === void 0 ? void 0 : outerDivAttributes.className) : "") + (hasToolbarSupport ? " ".concat(eccgui, "-codeeditor--has-toolbar") : "") }), hasToolbarSupport && editorToolbar(mode))); }; CodeEditor.supportedModes = supportedCodeEditorModes; //# sourceMappingURL=CodeMirror.js.map