/* * The MIT License (MIT) * * Copyright (c) 2018 - present Instructure, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { Component } from 'react' import { Flex } from '@instructure/ui-flex/latest' import { IconButton } from '@instructure/ui-buttons/latest' import type { IconButtonProps } from '@instructure/ui-buttons/latest' import { PencilInstUIIcon } from '@instructure/ui-icons' import { logWarn as warn } from '@instructure/console' import { createChainedFunction } from '@instructure/ui-utils' import { withStyleNew } from '@instructure/emotion' import { View } from '@instructure/ui-view/latest' import { Editable } from '../../Editable/v1/index.js' import generateStyle from './styles.js' import { allowedProps } from './props.js' import type { InPlaceEditProps } from './props' import type { EditableRenderProps } from '../../Editable/v1/props' /** --- category: components --- **/ @withStyleNew(generateStyle) class InPlaceEdit extends Component { static displayName = 'InPlaceEdit' static readonly componentId = 'InPlaceEdit' static allowedProps = allowedProps static defaultProps = { readOnly: false, showFocusRing: true, inline: true, editButtonPlacement: 'end' } ref: Element | null = null _editButtonRef: HTMLButtonElement | null = null handleRef = (el: Element | null) => { this.ref = el } constructor(props: InPlaceEditProps) { super(props) warn( props.readOnly ? props.mode === 'view' : true, '[InPlaceEdit] When readOnly is true, mode is forced to "view"' ) } componentDidMount() { this.props.makeStyles?.() } componentDidUpdate() { this.props.makeStyles?.() } handleEditButtonRef = (el: HTMLButtonElement) => { this._editButtonRef = el } renderEditor({ mode, onBlur, editorRef, readOnly }: ReturnType) { const { showFocusRing, renderEditor } = this.props const isEditMode = !readOnly && mode === 'edit' return isEditMode ? ( {renderEditor({ onBlur, editorRef })} ) : null } renderViewer({ readOnly, mode }: ReturnType) { return readOnly || mode === 'view' ? this.props.renderViewer() : null } renderEditButton({ buttonRef, ...rest }: ReturnType) { return this.props.renderEditButton({ elementRef: createChainedFunction(this.handleEditButtonRef, buttonRef), ...rest }) } // Render a default edit button, an icon button with the edit icon // the margin makes room for the focus ring static renderDefaultEditButton = ({ isVisible, readOnly, label, ...buttonProps }: { isVisible: boolean readOnly?: boolean label: string } & Partial) => { if (readOnly) { return null } return ( {isVisible ? PencilInstUIIcon : null} ) } renderAll = ({ getContainerProps, getViewerProps, getEditorProps, getEditButtonProps }: EditableRenderProps) => { const flexDir = this.props.editButtonPlacement === 'start' ? 'row-reverse' : 'row' const justifyItems = flexDir === 'row-reverse' ? 'end' : 'start' const buttonMargin = this.props.editButtonPlacement === 'start' ? '0 xx-small 0 0' : '0 0 0 xx-small' return ( {this.renderEditor(getEditorProps())} {this.renderViewer(getViewerProps())} {this.renderEditButton(getEditButtonProps())} ) } render() { const { mode, value, onChange, onChangeMode, readOnly } = this.props return ( ) } } export default InPlaceEdit export { InPlaceEdit }