UNPKG

@instructure/canvas-rce

Version:

A component wrapping Canvas's usage of Tinymce

221 lines (218 loc) 6.41 kB
/* * Copyright (C) 2021 - 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 { View } from '@instructure/ui-view'; import { Flex } from '@instructure/ui-flex'; import { TextInput } from '@instructure/ui-text-input'; import { Popover } from '@instructure/ui-popover'; import { IconArrowOpenDownLine, IconArrowOpenUpLine } from '@instructure/ui-icons'; import { BaseButton, CloseButton, IconButton } from '@instructure/ui-buttons'; import { ScreenReaderContent } from '@instructure/ui-a11y-content'; import PreviewIcon from './PreviewIcon'; import formatMessage from '../../../format-message'; const NAMED_COLORS = [{ color: '#BD3C14', name: formatMessage('Brick') }, { color: '#FF2717', name: formatMessage('Red') }, { color: '#E71F63', name: formatMessage('Magenta') }, { color: '#8F3E97', name: formatMessage('Purple') }, { color: '#65499D', name: formatMessage('Deep Purple') }, { color: '#4554A4', name: formatMessage('Indigo') }, { color: '#1770AB', name: formatMessage('Blue') }, { color: '#0B9BE3', name: formatMessage('Light Blue') }, { color: '#06A3B7', name: formatMessage('Cyan') }, { color: '#009688', name: formatMessage('Teal') }, { color: '#009606', name: formatMessage('Green') }, { color: '#8D9900', name: formatMessage('Olive') }, { color: '#D97900', name: formatMessage('Pumpkin') }, { color: '#FD5D10', name: formatMessage('Orange') }, { color: '#F06291', name: formatMessage('Pink') }, { color: '#000000', name: formatMessage('Black') }, { color: '#556572', name: formatMessage('Steel Blue') }, { color: '#6B7780', name: formatMessage('Grey') }, { color: '#FFFFFF', name: formatMessage('White') }, null]; export const ColorInput = ({ color, label, name, onChange, popoverMountNode, width = '11rem', readonly = false, requireColor = false }) => { const [isOpen, setIsOpen] = useState(false); const [inputValue, setInputValue] = useState(color); const colorName = NAMED_COLORS.find(c => c?.color === color)?.name; useEffect(() => { setInputValue(color); }, [color]); // fire onChange in case value is valid const handleColorChange = hex => { if (isValidHex(hex)) { onChange(hex); } if (!hex || !hex.length) { onChange(null); } setInputValue(hex); }; // reset the input value on blur if invalid const handleInputBlur = () => { if (!inputValue || inputValue.length > 0 && !isValidHex(inputValue)) { setInputValue(color); } }; const colorPreviews = NAMED_COLORS.map(c => /*#__PURE__*/React.createElement(ColorPreview, { key: `${name}-${c?.color}`, color: c?.color, name: c?.name, disabled: !isOpen, onSelect: () => { handleColorChange(c?.color); setIsOpen(false); } })); function renderPopover() { const pickerLabel = colorName ? formatMessage('Color Picker ({colorName} selected)', { colorName }) : formatMessage('Color Picker'); return /*#__PURE__*/React.createElement(Popover, { on: "click", isShowingContent: isOpen, onShowContent: () => setIsOpen(true), onHideContent: () => setIsOpen(false), shouldContainFocus: true, shouldReturnFocus: true, mountNode: popoverMountNode, renderTrigger: /*#__PURE__*/React.createElement(IconButton, { screenReaderLabel: pickerLabel, size: "small", withBackground: false, withBorder: false, interaction: "enabled", "data-testid": `${name}-popover-trigger` }, isOpen ? /*#__PURE__*/React.createElement(IconArrowOpenUpLine, null) : /*#__PURE__*/React.createElement(IconArrowOpenDownLine, null)) }, /*#__PURE__*/React.createElement(CloseButton, { placement: "end", onClick: () => setIsOpen(false), screenReaderLabel: formatMessage('Close') }), /*#__PURE__*/React.createElement(Flex, { alignItems: "center", as: "div", justifyItems: requireColor ? 'start' : 'center', padding: "x-large x-small small", width: "175px", wrap: "wrap", "data-testid": `${name}-popover` }, requireColor ? colorPreviews.slice(0, -1) : colorPreviews)); } return /*#__PURE__*/React.createElement(View, { as: "div" }, /*#__PURE__*/React.createElement(TextInput, { "data-testid": `icon-maker-color-input-${name}`, display: "inline-block", name: name, onBlur: handleInputBlur, onChange: (e, value) => handleColorChange(value), placeholder: formatMessage('None'), renderBeforeInput: /*#__PURE__*/React.createElement(ColorPreview, { color: color, disabled: true, margin: "0" }), renderAfterInput: renderPopover, renderLabel: label, shouldNotWrap: true, value: inputValue || '', width: width, interaction: readonly ? 'readonly' : undefined })); }; function ColorPreview({ color, name, disabled, margin = 'xxx-small', onSelect }) { return /*#__PURE__*/React.createElement(BaseButton, { interaction: disabled ? 'readonly' : undefined, isCondensed: true, margin: margin, onClick: onSelect, size: "small", withBackground: false, withBorder: false, "aria-hidden": disabled }, /*#__PURE__*/React.createElement(ScreenReaderContent, null, color ? formatMessage('{name} ({color})', { name, color }) : formatMessage('None')), /*#__PURE__*/React.createElement(PreviewIcon, { color: color, testId: `colorPreview-${color || 'none'}` })); } function isValidHex(color) { if (!color) return false; switch (color.length) { case 4: return /^#[0-9A-F]{3}$/i.test(color); case 7: return /^#[0-9A-F]{6}$/i.test(color); default: return false; } }