rootInput.tsx
TLDR
This file is a TypeScript module that exports a React functional component called RootInput
. It provides a dropdown select input with custom styles and functionality for selecting and working with different block types.
Methods
There are no methods defined in this file.
Classes
There are no classes defined in this file.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useState} from 'react'
import Select from 'react-select'
import {CSSObject} from '@emotion/serialize'
import {getSelectBaseStyle} from '../../theme'
import * as registry from './blocks/'
import {ContentType} from './blocks/types'
type Props = {
onChange: (value: string) => void
onChangeType: (blockType: ContentType) => void
onSave: (value: string, blockType: string) => void
value: string
}
const baseStyles = getSelectBaseStyle()
const styles = {
...baseStyles,
control: (provided: CSSObject): CSSObject => ({
...provided,
width: '100%',
height: '100%',
display: 'flex',
background: 'rgb(var(--center-channel-bg-rgb))',
color: 'rgb(var(--center-channel-color-rgb))',
flexDirection: 'row',
}),
input: (provided: CSSObject): CSSObject => ({
...provided,
background: 'rgb(var(--center-channel-bg-rgb))',
color: 'rgb(var(--center-channel-color-rgb))',
}),
menu: (provided: CSSObject): CSSObject => ({
...provided,
minWidth: '100%',
width: 'max-content',
background: 'rgb(var(--center-channel-bg-rgb))',
left: '0',
marginBottom: '0',
}),
menuPortal: (provided: CSSObject): CSSObject => ({
...provided,
zIndex: 999,
}),
}
export default function RootInput(props: Props) {
const [showMenu, setShowMenu] = useState(false)
return (
<Select
styles={styles}
components={{DropdownIndicator: () => null, IndicatorSeparator: () => null}}
className='RootInput'
placeholder={'Introduce your text or your slash command'}
autoFocus={true}
menuIsOpen={showMenu}
menuPortalTarget={document.getElementById('focalboard-root-portal')}
menuPosition={'fixed'}
options={registry.list()}
getOptionValue={(ct: ContentType) => ct.slashCommand}
getOptionLabel={(ct: ContentType) => ct.slashCommand + ' Creates a new ' + ct.displayName + ' block.'}
filterOption={(option: any, inputValue: string): boolean => {
return inputValue.startsWith(option.value) || option.value.startsWith(inputValue)
}}
inputValue={props.value}
onInputChange={(inputValue: string) => {
props.onChange(inputValue)
if (inputValue.startsWith('/')) {
setShowMenu(true)
} else {
setShowMenu(false)
}
}}
onChange={(ct: ContentType|null) => {
if (ct) {
const args = props.value.split(' ').slice(1)
ct.runSlashCommand(props.onChangeType, props.onChange, ...args)
}
}}
onBlur={() => {
const command = props.value.trimStart().split(' ')[0]
const block = registry.getBySlashCommandPrefix(command)
if (command === '' || !block) {
props.onSave(props.value, 'text')
props.onChange('')
}
}}
onFocus={(e: React.FocusEvent) => {
const target = e.currentTarget
target.scrollIntoView({block: 'center'})
}}
onKeyDown={(e) => {
if (e.key === 'Escape') {
props.onSave('', 'text')
props.onChange('')
}
if (e.key === 'Enter') {
const command = props.value.trimStart().split(' ')[0]
const block = registry.getBySlashCommandPrefix(command)
if (command === '' || !block) {
e.preventDefault()
e.stopPropagation()
props.onSave(props.value, 'text')
props.onChange('')
}
}
}}
/>
)
}