@instructure/canvas-rce
Version:
A component wrapping Canvas's usage of Tinymce
89 lines (88 loc) • 3.26 kB
JavaScript
import _pt from "prop-types";
/*
* Copyright (C) 2020 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect, useState } from 'react';
import formatMessage from '../format-message';
import { SourceCodeEditor } from '@instructure/ui-source-code-editor';
import beautify from 'js-beautify';
const RceHtmlEditor = /*#__PURE__*/React.forwardRef((props, editorRef) => {
const [_code, setCode] = useState(props.code);
const label = formatMessage('html code editor');
const [dir, setDir] = useState(getComputedStyle(document.body, null).direction);
useEffect(() => {
// INSTUI sets the CodeEditor's surrounding label's
// display inline-block so it doesn't fill the width
// of its container unless there's wide content.
// Override that.
// It would be nice to use webpack's style-loader
// but babel doesn't copy css files to its output
// dir, and the instui babel plugin mangles class names
// the simplest approach is to manually inject the stylesheet
if (!document.getElementById('RceHtmlEditorStyle')) {
const stylesheet = document.createElement('style');
stylesheet.setAttribute('id', 'RceHtmlEditorStyle');
stylesheet.setAttribute('type', 'text/css');
stylesheet.textContent = `
.RceHtmlEditor label {
display: block;
margin-bottom: 0; /* get rid of the margin on CodeEditor's label */
}
`;
document.head.appendChild(stylesheet);
}
// odds are, this won't change the dir.
if (editorRef && 'current' in editorRef) {
setDir(getComputedStyle(editorRef.current || document.body, null).direction);
}
}, [dir, editorRef]);
const direction = ['ltr', 'rtl'].includes(dir) ? dir : 'ltr';
// setting height on the container keeps the RCE toolbar from jumping
return /*#__PURE__*/React.createElement("div", {
ref: editorRef,
className: "RceHtmlEditor",
style: {
height: props.height || 'auto',
overflow: 'hidden',
textAlign: 'start'
}
}, /*#__PURE__*/React.createElement(SourceCodeEditor, {
label: label,
language: "html",
lineNumbers: true,
lineWrapping: true,
autofocus: true,
spellcheck: true,
direction: direction,
rtlMoveVisually: true,
height: props.height || 'auto',
defaultValue: beautify.html(props.code),
onChange: value => {
setCode(value);
props.onChange?.(value);
}
}));
});
RceHtmlEditor.propTypes = {
code: _pt.string.isRequired,
height: _pt.string,
onChange: _pt.func
};
RceHtmlEditor.defaultProps = {
onChange: _value => {}
};
export default RceHtmlEditor;