@eccenca/gui-elements
Version:
GUI elements based on other libraries, usable in React application, written in Typescript.
275 lines • 18.5 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeEditor = void 0;
const react_1 = __importStar(require("react"));
const commands_1 = require("@codemirror/commands");
const language_1 = require("@codemirror/language");
const state_1 = require("@codemirror/state");
const view_1 = require("@codemirror/view");
const codemirror_1 = require("codemirror");
const markText_1 = require("../../components/AutoSuggestion/extensions/markText");
const markdown_toolbar_1 = require("./toolbars/markdown.toolbar");
const Markdown_1 = require("../../cmem/markdown/Markdown");
const constants_1 = require("../../configuration/constants");
//hooks
const useCodemirrorModeExtension_hooks_1 = require("./hooks/useCodemirrorModeExtension.hooks");
const jsLinter_1 = require("./linters/jsLinter");
const turtleLinter_1 = require("./linters/turtleLinter");
//adaptations
const codemirrorTestHelper_1 = require("./tests/codemirrorTestHelper");
const addExtensionsFor = (flag, ...extensions) => (flag ? [...extensions] : []);
const addToKeyMapConfigFor = (flag, ...keys) => (flag ? [...keys] : []);
const addHandlersFor = (flag, handlerName, handler) => flag ? { [handlerName]: handler } : {};
const ModeLinterMap = new Map([
["turtle", [turtleLinter_1.turtleLinter]],
["javascript", [jsLinter_1.jsLinter]],
]);
const ModeToolbarSupport = ["markdown"];
/**
* Includes a code editor, currently we use CodeMirror library as base.
*/
const CodeEditor = (_a) => {
var { className, onChange, onSelection, onMouseDown, onFocusChange, onKeyDown, onCursorChange, name, id, mode, preventLineNumbers = false, defaultValue = "", readOnly = false, shouldHaveMinimalSetup = true, wrapLines = false, onScroll, setEditorView, supportCodeFolding = false, shouldHighlightActiveLine = false, outerDivAttributes, tabIntentSize = 2, tabIntentStyle = "tab", placeholder, additionalExtensions = [], tabForceSpaceForModes = ["python", "yaml"], enableTab = false, height, useLinting = false, autoFocus = false, disabled = false, intent, useToolbar, translate } = _a, 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"]);
const parent = (0, react_1.useRef)(undefined);
const [view, setView] = react_1.default.useState();
const currentView = react_1.default.useRef();
currentView.current = view;
const currentReadOnly = react_1.default.useRef(readOnly);
currentReadOnly.current = readOnly;
const [showPreview, setShowPreview] = react_1.default.useState(false);
// CodeMirror Compartments in order to allow for re-configuration after initialization
const readOnlyCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const wrapLinesCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const preventLineNumbersCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const shouldHaveMinimalSetupCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const placeholderCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const modeCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const keyMapConfigsCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const tabIntentSizeCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const disabledCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const supportCodeFoldingCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const useLintingCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const shouldHighlightActiveLineCompartment = react_1.default.useRef((0, codemirrorTestHelper_1.compartment)());
const linters = (0, react_1.useMemo)(() => {
if (!mode) {
return [];
}
const values = [(0, codemirrorTestHelper_1.adaptedLintGutter)()];
const linters = ModeLinterMap.get(mode);
if (linters) {
values.push(...linters.map((linter) => linter()));
}
return values;
}, [mode]);
const onKeyDownHandler = (event, view) => {
if (onKeyDown && !onKeyDown(event)) {
if (event.key === "Enter" && !currentReadOnly.current) {
const cursor = view.state.selection.main.head;
view.dispatch({
changes: {
from: cursor,
insert: "\n",
},
selection: {
anchor: cursor + 1,
},
});
}
}
};
const getTranslation = (key) => {
if (translate && typeof translate === "function") {
return translate(key);
}
return false;
};
const createKeyMapConfigs = () => {
const tabIndent = !!(tabIntentStyle === "tab" && mode && !(tabForceSpaceForModes !== null && tabForceSpaceForModes !== void 0 ? tabForceSpaceForModes : []).includes(mode)) || enableTab;
return [
commands_1.defaultKeymap,
...addToKeyMapConfigFor(supportCodeFolding, ...language_1.foldKeymap),
...addToKeyMapConfigFor(tabIndent, commands_1.indentWithTab),
];
};
react_1.default.useEffect(() => {
const domEventHandlers = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, addHandlersFor(!!onScroll, "scroll", onScroll)), addHandlersFor(!!onMouseDown, "mousedown", (_, view) => onMouseDown && onMouseDown(view))), addHandlersFor(!!onFocusChange, "blur", () => onFocusChange && onFocusChange(false))), addHandlersFor(!!onFocusChange, "focus", () => onFocusChange && onFocusChange(true))), addHandlersFor(!!onKeyDown, "keydown", onKeyDownHandler));
const extensions = [
markText_1.markField,
placeholderCompartment.current.of((0, codemirrorTestHelper_1.adaptedPlaceholder)(placeholder)),
(0, codemirrorTestHelper_1.adaptedHighlightSpecialChars)(),
modeCompartment.current.of((0, useCodemirrorModeExtension_hooks_1.useCodeMirrorModeExtension)(mode)),
keyMapConfigsCompartment.current.of(view_1.keymap === null || view_1.keymap === void 0 ? void 0 : view_1.keymap.of(createKeyMapConfigs())),
tabIntentSizeCompartment.current.of(state_1.EditorState === null || state_1.EditorState === void 0 ? void 0 : state_1.EditorState.tabSize.of(tabIntentSize)),
readOnlyCompartment.current.of(state_1.EditorState === null || state_1.EditorState === void 0 ? void 0 : state_1.EditorState.readOnly.of(readOnly)),
disabledCompartment.current.of(view_1.EditorView === null || view_1.EditorView === void 0 ? void 0 : view_1.EditorView.editable.of(!disabled)),
(0, codemirrorTestHelper_1.AdaptedEditorViewDomEventHandlers)(domEventHandlers),
view_1.EditorView === null || view_1.EditorView === void 0 ? void 0 : view_1.EditorView.updateListener.of((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((r) => !r.empty).map(({ from, to }) => ({ from, to })));
if (onFocusChange && intent && !((_a = v.view.dom.classList) === null || _a === void 0 ? void 0 : _a.contains(`${constants_1.CLASSPREFIX}-intent--${intent}`))) {
v.view.dom.classList.add(`${constants_1.CLASSPREFIX}-intent--${intent}`);
}
if (onCursorChange) {
const cursorPosition = (_b = v.state.selection.main.head) !== null && _b !== void 0 ? _b : 0;
const editorRect = v.view.dom.getBoundingClientRect();
const coords = v.view.coordsAtPos(cursorPosition), scrollInfo = v.view.scrollDOM;
if (coords && scrollInfo && editorRect) {
// Calculate the coordinates relative to the editor's top-left corner
const relativeLeft = coords.left - editorRect.left;
const relativeBottom = coords.bottom - editorRect.bottom;
onCursorChange(cursorPosition, Object.assign(Object.assign({}, coords), { left: relativeLeft, bottom: relativeBottom }), scrollInfo, v.view);
}
}
}),
shouldHaveMinimalSetupCompartment.current.of(addExtensionsFor(shouldHaveMinimalSetup, codemirror_1.minimalSetup)),
preventLineNumbersCompartment.current.of(addExtensionsFor(!preventLineNumbers, (0, codemirrorTestHelper_1.adaptedLineNumbers)())),
shouldHighlightActiveLineCompartment.current.of(addExtensionsFor(shouldHighlightActiveLine, (0, codemirrorTestHelper_1.adaptedHighlightActiveLine)())),
wrapLinesCompartment.current.of(addExtensionsFor(wrapLines, view_1.EditorView === null || view_1.EditorView === void 0 ? void 0 : view_1.EditorView.lineWrapping)),
supportCodeFoldingCompartment.current.of(addExtensionsFor(supportCodeFolding, (0, codemirrorTestHelper_1.adaptedFoldGutter)(), (0, codemirrorTestHelper_1.adaptedCodeFolding)())),
useLintingCompartment.current.of(addExtensionsFor(useLinting, ...linters)),
(0, codemirrorTestHelper_1.adaptedSyntaxHighlighting)(language_1.defaultHighlightStyle),
additionalExtensions,
];
const view = new codemirrorTestHelper_1.AdaptedEditorView({
state: state_1.EditorState === null || state_1.EditorState === void 0 ? void 0 : state_1.EditorState.create({
doc: defaultValue,
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 : `${height}px`;
}
if (disabled) {
view.dom.className += ` ${constants_1.CLASSPREFIX}-disabled`;
}
if (intent) {
view.dom.className += ` ${constants_1.CLASSPREFIX}-intent--${intent}`;
}
if (autoFocus) {
view.focus();
}
if (setEditorView) {
setEditorView(view);
}
}
return () => {
view.destroy();
if (setEditorView) {
setEditorView(undefined);
setView(undefined);
}
};
}, [parent.current]);
// Updates an extension for a specific parameter that has changed after the initialization
const updateExtension = (extension, parameterCompartment) => {
var _a;
if (extension) {
(_a = currentView.current) === null || _a === void 0 ? void 0 : _a.dispatch({
effects: parameterCompartment.reconfigure(extension)
});
}
};
react_1.default.useEffect(() => {
updateExtension(state_1.EditorState === null || state_1.EditorState === void 0 ? void 0 : state_1.EditorState.readOnly.of(readOnly), readOnlyCompartment.current);
}, [readOnly]);
react_1.default.useEffect(() => {
updateExtension((0, codemirrorTestHelper_1.adaptedPlaceholder)(placeholder), placeholderCompartment.current);
}, [placeholder]);
react_1.default.useEffect(() => {
updateExtension((0, useCodemirrorModeExtension_hooks_1.useCodeMirrorModeExtension)(mode), modeCompartment.current);
}, [mode]);
react_1.default.useEffect(() => {
updateExtension(view_1.keymap === null || view_1.keymap === void 0 ? void 0 : view_1.keymap.of(createKeyMapConfigs()), keyMapConfigsCompartment.current);
}, [supportCodeFolding, mode, tabIntentStyle, (tabForceSpaceForModes !== null && tabForceSpaceForModes !== void 0 ? tabForceSpaceForModes : []).join(", "), enableTab]);
react_1.default.useEffect(() => {
updateExtension(state_1.EditorState === null || state_1.EditorState === void 0 ? void 0 : state_1.EditorState.tabSize.of(tabIntentSize !== null && tabIntentSize !== void 0 ? tabIntentSize : 2), tabIntentSizeCompartment.current);
}, [tabIntentSize]);
react_1.default.useEffect(() => {
updateExtension(view_1.EditorView === null || view_1.EditorView === void 0 ? void 0 : view_1.EditorView.editable.of(!disabled), disabledCompartment.current);
}, [disabled]);
react_1.default.useEffect(() => {
updateExtension(addExtensionsFor(shouldHaveMinimalSetup !== null && shouldHaveMinimalSetup !== void 0 ? shouldHaveMinimalSetup : true, codemirror_1.minimalSetup), shouldHaveMinimalSetupCompartment.current);
}, [shouldHaveMinimalSetup]);
react_1.default.useEffect(() => {
updateExtension(addExtensionsFor(!preventLineNumbers, (0, codemirrorTestHelper_1.adaptedLineNumbers)()), preventLineNumbersCompartment.current);
}, [preventLineNumbers]);
react_1.default.useEffect(() => {
updateExtension(addExtensionsFor(shouldHighlightActiveLine !== null && shouldHighlightActiveLine !== void 0 ? shouldHighlightActiveLine : false, (0, codemirrorTestHelper_1.adaptedHighlightActiveLine)()), shouldHighlightActiveLineCompartment.current);
}, [shouldHighlightActiveLine]);
react_1.default.useEffect(() => {
updateExtension(addExtensionsFor(wrapLines !== null && wrapLines !== void 0 ? wrapLines : false, view_1.EditorView === null || view_1.EditorView === void 0 ? void 0 : view_1.EditorView.lineWrapping), wrapLinesCompartment.current);
}, [wrapLines]);
react_1.default.useEffect(() => {
updateExtension(addExtensionsFor(supportCodeFolding !== null && supportCodeFolding !== void 0 ? supportCodeFolding : false, (0, codemirrorTestHelper_1.adaptedFoldGutter)(), (0, codemirrorTestHelper_1.adaptedCodeFolding)()), supportCodeFoldingCompartment.current);
}, [supportCodeFolding]);
react_1.default.useEffect(() => {
updateExtension(addExtensionsFor(useLinting !== null && useLinting !== void 0 ? useLinting : false, ...linters), useLintingCompartment.current);
}, [mode, useLinting]);
const hasToolbarSupport = mode && ModeToolbarSupport.indexOf(mode) > -1 && useToolbar;
const editorToolbar = (mode) => {
var _a;
switch (mode) {
case "markdown":
return (react_1.default.createElement("div", null,
react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-codeeditor__toolbar` },
react_1.default.createElement(markdown_toolbar_1.MarkdownToolbar, { view: view, togglePreviewStatus: () => setShowPreview((p) => !p), showPreview: showPreview, translate: getTranslation, disabled: disabled, readonly: readOnly })),
showPreview && (react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-codeeditor__preview` },
react_1.default.createElement(Markdown_1.Markdown, null, (_a = view === null || view === void 0 ? void 0 : view.state.doc.toString()) !== null && _a !== void 0 ? _a : "")))));
default:
return react_1.default.createElement(react_1.default.Fragment, null);
}
};
return (react_1.default.createElement("div", Object.assign({}, outerDivAttributes, {
// overwrite/extend some attributes
id: id ? id : name ? `codemirror-${name}` : undefined, ref: parent }, otherCodeEditorProps, { className: `${constants_1.CLASSPREFIX}-codeeditor ${constants_1.CLASSPREFIX}-codeeditor--mode-${mode}` +
(className ? ` ${className}` : "") +
((outerDivAttributes === null || outerDivAttributes === void 0 ? void 0 : outerDivAttributes.className) ? ` ${outerDivAttributes === null || outerDivAttributes === void 0 ? void 0 : outerDivAttributes.className}` : "") +
(hasToolbarSupport ? ` ${constants_1.CLASSPREFIX}-codeeditor--has-toolbar` : "") }), hasToolbarSupport && editorToolbar(mode)));
};
exports.CodeEditor = CodeEditor;
exports.CodeEditor.supportedModes = useCodemirrorModeExtension_hooks_1.supportedCodeEditorModes;
//# sourceMappingURL=CodeMirror.js.map