UNPKG

@eccenca/gui-elements

Version:

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

275 lines 18.5 kB
"use strict"; 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