UNPKG

@instructure/canvas-rce

Version:

A component wrapping Canvas's usage of Tinymce

197 lines (196 loc) 6.82 kB
/* * Copyright (C) 2019 - 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, { useState } from 'react'; import { bool, func, oneOf, string } from 'prop-types'; import { linkShape } from './propTypes'; import formatMessage from '../../../../format-message'; import { renderLink as renderLinkHtml } from '../../../contentRendering'; import dragHtml from '../../../../sidebar/dragHtml'; import { applyTimezoneOffsetToDate } from '../../shared/dateUtils'; import { AccessibleContent, ScreenReaderContent } from '@instructure/ui-a11y-content'; import { Flex } from '@instructure/ui-flex'; import { View } from '@instructure/ui-view'; import { Text } from '@instructure/ui-text'; import { Focusable } from '@instructure/ui-focusable'; import { IconDragHandleLine, IconPublishSolid, IconUnpublishedSolid } from '@instructure/ui-icons'; import RCEGlobals from '../../../RCEGlobals'; import { getIcon } from '../../shared/linkUtils'; export default function Link(props) { const internalLink = { ...props.link }; const [isHovering, setIsHovering] = useState(false); const { title, published, date, date_type } = props.link; const type = props.type === 'quizzes' && props.link.quiz_type === 'quizzes.next' ? 'quizzes.next' : props.type; internalLink['data-course-type'] = type; // Only included published attr if it makes sense for the link type const publishable = !['navigation', 'announcements'].includes(type); internalLink['data-published'] = publishable ? published : null; const Icon = getIcon(type); const color = published ? 'success' : 'primary'; let dateString = null; if (date) { if (date === 'multiple') { dateString = formatMessage('Due: Multiple Dates'); } else { // Uses user locale and timezone const configuredTimezone = RCEGlobals.getConfig()?.timezone; const when = formatMessage.date(applyTimezoneOffsetToDate(date, configuredTimezone), 'long'); switch (date_type) { case 'todo': dateString = formatMessage('To Do: {when}', { when }); break; case 'published': dateString = formatMessage('Published: {when}', { when }); break; case 'posted': dateString = formatMessage('Posted: {when}', { when }); break; case 'delayed_post': dateString = formatMessage('To Be Posted: {when}', { when }); break; case 'due': default: dateString = formatMessage('Due: {when}', { when }); break; } } } const publishedMsg = props.link.published ? formatMessage('published') : formatMessage('unpublished'); function handleLinkClick(e) { e.preventDefault(); if (props.editing) { props.onEditClick({ ...internalLink, type }); } else { props.onClick(internalLink); } } function handleLinkKey(e) { // press the button on enter or space if (e.keyCode === 13 || e.keyCode === 32) { handleLinkClick(e); } } function handleDragStart(e) { dragHtml(e, renderLinkHtml(internalLink, internalLink.title)); } function handleDragEnd(_e) { document.body.click(); // closes the tray } function handleHover(e) { setIsHovering(e.type === 'mouseenter'); } return /*#__PURE__*/React.createElement("div", { "data-testid": "instructure_links-Link", draggable: !props.editing, onDragStart: handleDragStart, onDragEnd: handleDragEnd, onMouseEnter: handleHover, onMouseLeave: handleHover, style: { position: 'relative' } }, /*#__PURE__*/React.createElement(Focusable, null, ({ focused }) => /*#__PURE__*/React.createElement(View, { withFocusOutline: focused, focusPosition: "inset", position: "relative", as: "div", role: "button", tabIndex: 0, background: "primary", display: "block", width: "100%", borderWidth: "0 0 small 0", padding: "x-small", "aria-describedby": props.describedByID, onClick: handleLinkClick, onKeyDown: handleLinkKey, elementRef: props.elementRef }, /*#__PURE__*/React.createElement("div", { style: { pointerEvents: 'none' } }, /*#__PURE__*/React.createElement(Flex, null, /*#__PURE__*/React.createElement(Flex.Item, { margin: "0 xx-small 0 0", size: "1.125rem" }, isHovering && !props.editing ? /*#__PURE__*/React.createElement(IconDragHandleLine, { size: "x-small", inline: false }) : null), /*#__PURE__*/React.createElement(Flex.Item, { shouldGrow: true, shouldShrink: true }, /*#__PURE__*/React.createElement(Flex, null, /*#__PURE__*/React.createElement(Flex.Item, { padding: "0 x-small 0 0" }, /*#__PURE__*/React.createElement(Text, { color: color }, /*#__PURE__*/React.createElement(Icon, { size: "x-small", inline: false, "data-type": type }))), /*#__PURE__*/React.createElement(Flex.Item, { padding: "0 x-small 0 0", shouldGrow: true, shouldShrink: true, textAlign: "start" }, /*#__PURE__*/React.createElement(View, { as: "div", margin: "0" }, title, props.isSelected && /*#__PURE__*/React.createElement(ScreenReaderContent, { "data-testid": "selected-link-indicator" }, formatMessage('Selected'))), dateString ? /*#__PURE__*/React.createElement(View, { as: "div" }, dateString) : null), 'published' in props.link && /*#__PURE__*/React.createElement(Flex.Item, null, /*#__PURE__*/React.createElement(AccessibleContent, { alt: publishedMsg }, /*#__PURE__*/React.createElement(Text, { color: color }, published ? /*#__PURE__*/React.createElement(IconPublishSolid, { inline: false }) : /*#__PURE__*/React.createElement(IconUnpublishedSolid, { inline: false }))))))))))); } Link.propTypes = { link: linkShape.isRequired, type: oneOf(['assignments', 'discussions', 'modules', 'quizzes', 'announcements', 'wikiPages', 'navigation']).isRequired, onClick: func.isRequired, describedByID: string.isRequired, elementRef: func, editing: bool, onEditClick: func, isSelected: bool };