@eccenca/gui-elements
Version:
GUI elements based on other libraries, usable in React application, written in Typescript.
300 lines • 19.1 kB
JavaScript
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