main

mattermost/focalboard

Last updated at: 29/12/2023 09:41

index.tsx

TLDR

This file contains a component called "Image" that represents an image block in a blocks editor. It includes two sub-components: "Display" and "Input" which handle the rendering and user input for the image block respectively.

Classes

Image

The Image component represents an image block in a blocks editor. It has the following properties:

  • name: The name of the component ("image").
  • displayName: The display name of the component ("Image").
  • slashCommand: The slash command associated with the component ("/image").
  • prefix: The prefix associated with the component (empty string).
  • runSlashCommand: A function that handles the execution of the slash command.
  • editable: A boolean indicating whether the component is editable.
  • Display: A sub-component that renders the image block.
  • Input: A sub-component that handles user input for the image block.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useRef, useEffect, useState} from 'react'

import {BlockInputProps, ContentType} from '../types'
import octoClient from '../../../../octoClient'

import './image.scss'

type FileInfo = {
    file: string|File
    width?: number
    align?: 'left'|'center'|'right'
}

const Image: ContentType<FileInfo> = {
    name: 'image',
    displayName: 'Image',
    slashCommand: '/image',
    prefix: '',
    runSlashCommand: (): void => {},
    editable: false,
    Display: (props: BlockInputProps<FileInfo>) => {
        const [imageDataUrl, setImageDataUrl] = useState<string|null>(null)

        useEffect(() => {
            if (!imageDataUrl) {
                const loadImage = async () => {
                    if (props.value && props.value.file && typeof props.value.file === 'string') {
                        const fileURL = await octoClient.getFileAsDataUrl(props.currentBoardId || '', props.value.file)
                        setImageDataUrl(fileURL.url || '')
                    }
                }
                loadImage()
            }
        }, [props.value, props.value.file, props.currentBoardId])

        if (imageDataUrl) {
            return (
                <img
                    data-testid='image'
                    className='ImageView'
                    src={imageDataUrl}
                />
            )
        }
        return null
    },
    Input: (props: BlockInputProps<FileInfo>) => {
        const ref = useRef<HTMLInputElement|null>(null)
        useEffect(() => {
            ref.current?.click()
        }, [])

        return (
            <div>
                {props.value.file && (typeof props.value.file === 'string') && (
                    <img
                        className='ImageView'
                        src={props.value.file}
                        onClick={() => ref.current?.click()}
                    />
                )}
                <input
                    ref={ref}
                    className='Image'
                    data-testid='image-input'
                    type='file'
                    accept='image/*'
                    onChange={(e) => {
                        const file = (e.currentTarget?.files || [])[0]
                        props.onSave({file})
                    }}
                />
            </div>
        )
    },
}

Image.runSlashCommand = (changeType: (contentType: ContentType<FileInfo>) => void, changeValue: (value: FileInfo) => void): void => {
    changeType(Image)
    changeValue({file: ''})
}

export default Image