UNPKG

@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
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;