rootInput.tsx
TLDR
This file, rootInput.tsx
, contains a React component called RootInput
that renders a dropdown input field. It is used for selecting different block types and executing slash commands.
Methods
There are no methods in this file.
Classes
There are no classes 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('')
}
}
}}
/>
)
}