@awsui/components-react
Version:
AWS UI is a collection of [React](https://reactjs.org/) components that help create intuitive, responsive, and accessible user experiences for web applications. It is developed by Amazon Web Services (AWS). This work is available under the terms of the [A
177 lines (176 loc) • 11 kB
JavaScript
import { __assign, __rest } from "tslib";
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import clsx from 'clsx';
import { ResizableBox } from 'react-resizable';
import { InternalButton } from '../button/internal';
import { getBaseProps } from '../internal/base-component';
import { KeyCode } from '../internal/keycode';
import { useUniqueId } from '../internal/hooks/use-unique-id';
import { Pane } from './pane';
import { TabButton } from './tab-button';
import { onChangeEffect } from './listeners';
import { getDefaultConfig, getAceTheme, getDefaultTheme, getLanguageLabel } from './util';
import { fireNonCancelableEvent } from '../internal/events';
import { setupEditor } from './setup-editor';
import handler from './resize-handler';
import PreferencesModal from './preferences-modal';
import LoadingScreen from './loading-screen';
import ErrorScreen from './error-screen';
import styles from './styles.css.js';
import { useTelemetry } from '../internal/hooks/use-telemetry';
var CodeEditor = function (props) {
useTelemetry('CodeEditor');
var ace = props.ace, value = props.value, language = props.language, i18nStrings = props.i18nStrings, rest = __rest(props, ["ace", "value", "language", "i18nStrings"]);
var baseProps = getBaseProps(rest);
var _a = useState(), editor = _a[0], setEditor = _a[1];
var editorRef = useCallback(function (elem) {
if (!ace || !elem) {
return;
}
setEditor(ace.edit(elem, getDefaultConfig()));
}, [ace]);
var _b = useState('hidden'), paneStatus = _b[0], setPaneStatus = _b[1];
var _c = useState([]), annotations = _c[0], setAnnotations = _c[1];
var _d = useState(), highlightedAnnotation = _d[0], setHighlightedAnnotation = _d[1];
var _e = useState(''), languageLabel = _e[0], setLanguageLabel = _e[1];
var _f = useState({ row: 0, column: 0 }), cursorPosition = _f[0], setCursorPosition = _f[1];
var _g = useState(false), isTabFocused = _g[0], setTabFocused = _g[1];
var errorsTabRef = useRef(null);
var warningsTabRef = useRef(null);
var paneRef = useRef(null);
var paneId = useUniqueId('code-editor-pane');
useEffect(function () {
if (!ace || !editor) {
return;
}
setupEditor(ace, editor, setAnnotations, setCursorPosition, setHighlightedAnnotation, setPaneStatus);
return function () {
editor === null || editor === void 0 ? void 0 : editor.destroy();
};
}, [ace, editor]);
useEffect(function () {
if (!editor) {
return;
}
if (value === editor.getValue()) {
return;
}
var pos = editor.session.selection.toJSON();
editor.setValue(value, -1);
editor.session.selection.fromJSON(pos);
}, [editor, value]);
useEffect(function () {
if (!editor) {
return;
}
editor.session.setMode("ace/mode/" + language);
setLanguageLabel(getLanguageLabel(language));
}, [editor, language]);
useEffect(function () {
var _a, _b, _c, _d;
if (!editor) {
return;
}
var theme = (_b = (_a = props.preferences) === null || _a === void 0 ? void 0 : _a.theme) !== null && _b !== void 0 ? _b : getDefaultTheme();
editor.setTheme(getAceTheme(theme));
editor.session.setUseWrapMode((_d = (_c = props.preferences) === null || _c === void 0 ? void 0 : _c.wrapLines) !== null && _d !== void 0 ? _d : true);
}, [editor, props.preferences]);
useEffect(function () { return onChangeEffect(editor, props.onChange); }, [editor, props.onChange]);
useEffect(function () {
if (annotations.length === 0) {
setPaneStatus('hidden');
}
if (props.onValidate) {
fireNonCancelableEvent(props.onValidate, { annotations: annotations });
}
}, [annotations, props.onValidate]);
var errorCount = useMemo(function () { return annotations.filter(function (a) { return a.type === 'error'; }).length; }, [annotations]);
var warningCount = useMemo(function () { return annotations.filter(function (a) { return a.type === 'warning'; }).length; }, [annotations]);
var onEditorKeydown = useCallback(function (e) {
if (editor && e.target === editor.container && e.keyCode === KeyCode.enter) {
e.stopPropagation();
e.preventDefault();
editor.focus();
}
}, [editor]);
var onTabFocus = useCallback(function () { return setTabFocused(true); }, []);
var onTabBlur = useCallback(function () { return setTabFocused(false); }, []);
var onEditorResize = useCallback(function () {
editor === null || editor === void 0 ? void 0 : editor.resize();
}, [editor]);
var onErrorPaneToggle = useCallback(function () {
setPaneStatus(paneStatus !== 'error' ? 'error' : 'hidden');
}, [paneStatus]);
var onWarningPaneToggle = useCallback(function () {
setPaneStatus(paneStatus !== 'warning' ? 'warning' : 'hidden');
}, [paneStatus]);
var onPaneClose = useCallback(function () {
if (paneStatus === 'error' && errorsTabRef.current) {
errorsTabRef.current.focus();
}
if (paneStatus === 'warning' && warningsTabRef.current) {
warningsTabRef.current.focus();
}
setPaneStatus('hidden');
}, [paneStatus]);
var onAnnotationClick = function (_a) {
var _b = _a.row, row = _b === void 0 ? 0 : _b, _c = _a.column, column = _c === void 0 ? 0 : _c;
if (!editor) {
return;
}
editor.focus();
editor.gotoLine(row + 1, column, false);
setHighlightedAnnotation(undefined);
};
var onAnnotationClear = useCallback(function () {
setHighlightedAnnotation(undefined);
}, []);
var shouldHandleFocus = useCallback(function (activeElement) {
return (activeElement !== errorsTabRef.current &&
activeElement !== warningsTabRef.current &&
!(editor === null || editor === void 0 ? void 0 : editor.container.contains(activeElement)));
}, [editor]);
var _h = useState(false), isPreferencesModalVisible = _h[0], setPreferencesModalVisible = _h[1];
var onPreferencesOpen = function () { return setPreferencesModalVisible(true); };
var onPreferencesConfirm = function (p) {
fireNonCancelableEvent(props.onPreferencesChange, p);
setPreferencesModalVisible(false);
};
var onPreferencesDismiss = function () { return setPreferencesModalVisible(false); };
if (props.loading) {
return (React.createElement("div", __assign({}, baseProps, { className: clsx(styles['code-editor'], baseProps.className) }),
React.createElement(LoadingScreen, null, i18nStrings.loadingState)));
}
if (!ace && !props.loading) {
return (React.createElement("div", __assign({}, baseProps, { className: clsx(styles['code-editor'], baseProps.className) }),
React.createElement(ErrorScreen, { recoveryText: i18nStrings.errorStateRecovery, onRecoveryClick: props.onRecoveryClick }, i18nStrings.errorState)));
}
return (React.createElement("div", __assign({}, baseProps, { className: clsx(styles['code-editor'], baseProps.className) }),
React.createElement(ResizableBox, { className: styles['resizable-box'], width: Infinity, height: 480, minConstraints: [Infinity, 20], axis: "y", handle: handler, onResize: onEditorResize },
React.createElement("div", { ref: editorRef, className: clsx(styles.editor, styles.ace), onKeyDown: onEditorKeydown, tabIndex: 0, "aria-label": i18nStrings.editorGroupAriaLabel })),
React.createElement("div", { role: "group", "aria-label": i18nStrings.statusBarGroupAriaLabel },
React.createElement("div", { className: styles['status-bar'] },
React.createElement("div", { className: styles['status-bar__left'] },
React.createElement("span", { className: styles['status-bar__language-mode'] }, languageLabel),
React.createElement("span", { className: styles['status-bar__cursor-position'] }, i18nStrings.cursorPosition(cursorPosition.row + 1, cursorPosition.column + 1)),
React.createElement("div", { role: "tablist" },
React.createElement(TabButton, { text: i18nStrings.errorsTab + ": " + errorCount, className: styles['tab-button--errors'], iconName: "status-negative", disabled: errorCount === 0, active: paneStatus === 'error', onClick: onErrorPaneToggle, onFocus: onTabFocus, onBlur: onTabBlur, ref: errorsTabRef, paneId: paneId }),
React.createElement(TabButton, { text: i18nStrings.warningsTab + ": " + warningCount, className: styles['tab-button--warnings'], iconName: "status-warning", disabled: warningCount === 0, active: paneStatus === 'warning', onClick: onWarningPaneToggle, onFocus: onTabFocus, onBlur: onTabBlur, ref: warningsTabRef, tabIndex: paneStatus === 'error' && isTabFocused ? -1 : undefined, ariaHidden: paneStatus === 'error' && isTabFocused ? true : undefined, paneId: paneId }))),
React.createElement("div", { className: styles['status-bar__right'] },
React.createElement("div", { className: styles['status-bar__cog-button'] },
React.createElement(InternalButton, { formAction: "none", variant: "icon", iconName: "settings", iconAlt: "Settings", ariaLabel: i18nStrings.preferencesButtonAriaLabel, onClick: onPreferencesOpen, nativeAttributes: {
tabIndex: paneStatus !== 'hidden' && isTabFocused ? -1 : undefined,
'aria-hidden': paneStatus !== 'hidden' && isTabFocused ? true : undefined
} })))),
React.createElement(Pane, { id: paneId, visible: paneStatus !== 'hidden', annotations: annotations.filter(function (a) { return a.type === paneStatus; }), highlighted: highlightedAnnotation, onAnnotationClick: onAnnotationClick, onAnnotationClear: onAnnotationClear, onClose: onPaneClose, ref: paneRef, onWhitelist: shouldHandleFocus, cursorPositionLabel: i18nStrings.cursorPosition, closeButtonAriaLabel: i18nStrings.paneCloseButtonAriaLabel })),
React.createElement(PreferencesModal, { visible: isPreferencesModalVisible, onConfirm: onPreferencesConfirm, onDismiss: onPreferencesDismiss, preferences: props.preferences, i18nStrings: {
header: i18nStrings.preferencesModalHeader,
cancel: i18nStrings.preferencesModalCancel,
confirm: i18nStrings.preferencesModalConfirm,
wrapLines: i18nStrings.preferencesModalWrapLines,
theme: i18nStrings.preferencesModalTheme,
lightThemes: i18nStrings.preferencesModalLightThemes,
darkThemes: i18nStrings.preferencesModalDarkThemes
} })));
};
export default CodeEditor;