mirror of
https://github.com/mattermost/focalboard.git
synced 2026-06-18 19:23:23 +02:00
Move content registration to elements
This commit is contained in:
@@ -5,8 +5,15 @@ import React from 'react'
|
||||
import {injectIntl, IntlShape} from 'react-intl'
|
||||
|
||||
import {IContentBlock} from '../../blocks/contentBlock'
|
||||
import {Utils} from '../../utils'
|
||||
|
||||
import contentRegistry from './contentRegistry'
|
||||
import {contentRegistry} from './contentRegistry'
|
||||
|
||||
// Need to require here to prevent webpack from tree-shaking these away
|
||||
// TODO: Update webpack to avoid this
|
||||
require('./textElement')
|
||||
require('./imageElement')
|
||||
require('./dividerElement')
|
||||
|
||||
type Props = {
|
||||
block: IContentBlock
|
||||
@@ -16,9 +23,15 @@ type Props = {
|
||||
|
||||
class ContentElement extends React.PureComponent<Props> {
|
||||
public render(): JSX.Element | null {
|
||||
const {block, readonly} = this.props
|
||||
const {block, intl, readonly} = this.props
|
||||
|
||||
return contentRegistry.createComponent(block, readonly) || null
|
||||
const handler = contentRegistry.getHandler(block.type)
|
||||
if (!handler) {
|
||||
Utils.logError(`ContentElement, unknown content type: ${block.type}`)
|
||||
return null
|
||||
}
|
||||
|
||||
return handler.createComponent(block, intl, readonly)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,83 +1,27 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
/* eslint-disable react/require-optimization */
|
||||
import React from 'react'
|
||||
|
||||
import {IntlShape} from 'react-intl'
|
||||
|
||||
import {BlockTypes} from '../../blocks/block'
|
||||
import {IContentBlock, MutableContentBlock} from '../../blocks/contentBlock'
|
||||
import {MutableDividerBlock} from '../../blocks/dividerBlock'
|
||||
import {MutableImageBlock} from '../../blocks/imageBlock'
|
||||
import {MutableTextBlock} from '../../blocks/textBlock'
|
||||
import {Utils} from '../../utils'
|
||||
import DividerIcon from '../../widgets/icons/divider'
|
||||
import ImageIcon from '../../widgets/icons/image'
|
||||
import TextIcon from '../../widgets/icons/text'
|
||||
|
||||
import DividerElement from './dividerElement'
|
||||
import ImageElement from './imageElement'
|
||||
import TextElement from './textElement'
|
||||
|
||||
type RegistryEntry = {
|
||||
type ContentHandler = {
|
||||
type: BlockTypes,
|
||||
createBlock: () => MutableContentBlock,
|
||||
getDisplayText: (intl: IntlShape) => string,
|
||||
getIcon: () => JSX.Element,
|
||||
createComponent: (block: IContentBlock, readonly: boolean) => JSX.Element,
|
||||
createBlock: () => MutableContentBlock,
|
||||
createComponent: (block: IContentBlock, intl: IntlShape, readonly: boolean) => JSX.Element,
|
||||
}
|
||||
|
||||
class ContentRegistry {
|
||||
private registry: Map<BlockTypes, RegistryEntry> = new Map()
|
||||
private registry: Map<BlockTypes, ContentHandler> = new Map()
|
||||
|
||||
get contentTypes(): BlockTypes[] {
|
||||
return [...this.registry.keys()]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.registerContentType(
|
||||
{
|
||||
type: 'text',
|
||||
createBlock: () => {
|
||||
return new MutableTextBlock()
|
||||
},
|
||||
getDisplayText: (intl) => intl.formatMessage({id: 'ContentBlock.text', defaultMessage: 'text'}),
|
||||
getIcon: () => <TextIcon/>,
|
||||
createComponent: (block, readonly) => (
|
||||
<TextElement
|
||||
block={block}
|
||||
readonly={readonly}
|
||||
/>
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
this.registerContentType(
|
||||
{
|
||||
type: 'image',
|
||||
createBlock: () => {
|
||||
return new MutableImageBlock()
|
||||
},
|
||||
getDisplayText: (intl) => intl.formatMessage({id: 'ContentBlock.image', defaultMessage: 'image'}),
|
||||
getIcon: () => <ImageIcon/>,
|
||||
createComponent: (block) => <ImageElement block={block}/>,
|
||||
},
|
||||
)
|
||||
|
||||
this.registerContentType(
|
||||
{
|
||||
type: 'divider',
|
||||
createBlock: () => {
|
||||
return new MutableDividerBlock()
|
||||
},
|
||||
getDisplayText: (intl) => intl.formatMessage({id: 'ContentBlock.divider', defaultMessage: 'divider'}),
|
||||
getIcon: () => <DividerIcon/>,
|
||||
createComponent: () => <DividerElement/>,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private registerContentType(entry: RegistryEntry) {
|
||||
registerContentType(entry: ContentHandler) {
|
||||
this.registry.set(entry.type, entry)
|
||||
}
|
||||
|
||||
@@ -85,32 +29,37 @@ class ContentRegistry {
|
||||
return this.registry.has(type)
|
||||
}
|
||||
|
||||
createBlock(type: BlockTypes): MutableContentBlock | undefined {
|
||||
const entry = this.registry.get(type)
|
||||
return entry?.createBlock()
|
||||
getHandler(type: BlockTypes): ContentHandler | undefined {
|
||||
return this.registry.get(type)
|
||||
}
|
||||
|
||||
createComponent(block: IContentBlock, readonly: boolean): JSX.Element | undefined {
|
||||
const entry = this.registry.get(block.type)
|
||||
return entry?.createComponent(block, readonly)
|
||||
}
|
||||
// createBlock(type: BlockTypes): MutableContentBlock | undefined {
|
||||
// const entry = this.registry.get(type)
|
||||
// return entry?.createBlock()
|
||||
// }
|
||||
|
||||
typeDisplayText(intl: IntlShape, type: BlockTypes): string {
|
||||
const entry = this.registry.get(type)
|
||||
if (!entry) {
|
||||
Utils.logError(`Unknown type: ${type}`)
|
||||
return type
|
||||
}
|
||||
// createComponent(block: IContentBlock, intl: IntlShape, readonly: boolean): JSX.Element | undefined {
|
||||
// const entry = this.registry.get(block.type)
|
||||
// return entry?.createComponent(block, intl, readonly)
|
||||
// }
|
||||
|
||||
return entry.getDisplayText(intl)
|
||||
}
|
||||
// typeDisplayText(intl: IntlShape, type: BlockTypes): string {
|
||||
// const entry = this.registry.get(type)
|
||||
// if (!entry) {
|
||||
// Utils.logError(`Unknown type: ${type}`)
|
||||
// return type
|
||||
// }
|
||||
|
||||
getIcon(type: BlockTypes): JSX.Element | undefined {
|
||||
const entry = this.registry.get(type)
|
||||
return entry?.getIcon()
|
||||
}
|
||||
// return entry.getDisplayText(intl)
|
||||
// }
|
||||
|
||||
// getIcon(type: BlockTypes): JSX.Element | undefined {
|
||||
// const entry = this.registry.get(type)
|
||||
// return entry?.getIcon()
|
||||
// }
|
||||
}
|
||||
|
||||
const contentRegistry = new ContentRegistry()
|
||||
|
||||
export default contentRegistry
|
||||
// export type {ContentHandler}
|
||||
export {contentRegistry}
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {FC} from 'react'
|
||||
import React from 'react'
|
||||
|
||||
import {MutableDividerBlock} from '../../blocks/dividerBlock'
|
||||
import DividerIcon from '../../widgets/icons/divider'
|
||||
|
||||
import {contentRegistry} from './contentRegistry'
|
||||
import './dividerElement.scss'
|
||||
|
||||
const DividerElement: FC = (): JSX.Element => (
|
||||
<div className='DividerElement'/>
|
||||
)
|
||||
class DividerElement extends React.PureComponent {
|
||||
render(): JSX.Element {
|
||||
return <div className='DividerElement'/>
|
||||
}
|
||||
}
|
||||
|
||||
contentRegistry.registerContentType({
|
||||
type: 'divider',
|
||||
getDisplayText: (intl) => intl.formatMessage({id: 'ContentBlock.divider', defaultMessage: 'divider'}),
|
||||
getIcon: () => <DividerIcon/>,
|
||||
createBlock: () => {
|
||||
return new MutableDividerBlock()
|
||||
},
|
||||
createComponent: () => <DividerElement/>,
|
||||
})
|
||||
|
||||
export default DividerElement
|
||||
|
||||
@@ -3,8 +3,12 @@
|
||||
import React from 'react'
|
||||
import {injectIntl, IntlShape} from 'react-intl'
|
||||
|
||||
import {IContentBlock} from '../../blocks/contentBlock'
|
||||
import {IContentBlock, MutableContentBlock} from '../../blocks/contentBlock'
|
||||
import {MutableImageBlock} from '../../blocks/imageBlock'
|
||||
import octoClient from '../../octoClient'
|
||||
import ImageIcon from '../../widgets/icons/image'
|
||||
|
||||
import {contentRegistry} from './contentRegistry'
|
||||
|
||||
type Props = {
|
||||
block: IContentBlock
|
||||
@@ -46,4 +50,21 @@ class ImageElement extends React.PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
contentRegistry.registerContentType({
|
||||
type: 'image',
|
||||
getDisplayText: (intl) => intl.formatMessage({id: 'ContentBlock.image', defaultMessage: 'image'}),
|
||||
getIcon: () => <ImageIcon/>,
|
||||
createBlock: () => {
|
||||
return new MutableImageBlock()
|
||||
},
|
||||
createComponent: (block, intl) => {
|
||||
return (
|
||||
<ImageElement
|
||||
block={block}
|
||||
intl={intl}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default injectIntl(ImageElement)
|
||||
|
||||
@@ -3,10 +3,14 @@
|
||||
import React from 'react'
|
||||
import {injectIntl, IntlShape} from 'react-intl'
|
||||
|
||||
import {IContentBlock} from '../../blocks/contentBlock'
|
||||
import {IContentBlock, MutableContentBlock} from '../../blocks/contentBlock'
|
||||
import {MutableTextBlock} from '../../blocks/textBlock'
|
||||
import mutator from '../../mutator'
|
||||
import TextIcon from '../../widgets/icons/text'
|
||||
import {MarkdownEditor} from '../markdownEditor'
|
||||
|
||||
import {contentRegistry} from './contentRegistry'
|
||||
|
||||
type Props = {
|
||||
block: IContentBlock
|
||||
readonly: boolean
|
||||
@@ -14,7 +18,21 @@ type Props = {
|
||||
}
|
||||
|
||||
class TextElement extends React.PureComponent<Props> {
|
||||
public render(): JSX.Element {
|
||||
readonly type = 'text'
|
||||
|
||||
createBlock(): MutableContentBlock {
|
||||
return new MutableTextBlock()
|
||||
}
|
||||
|
||||
getDisplayText(intl: IntlShape): string {
|
||||
return intl.formatMessage({id: 'ContentBlock.text', defaultMessage: 'text'})
|
||||
}
|
||||
|
||||
getIcon(): JSX.Element {
|
||||
return <TextIcon/>
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const {intl, block, readonly} = this.props
|
||||
|
||||
return (
|
||||
@@ -30,4 +48,22 @@ class TextElement extends React.PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
contentRegistry.registerContentType({
|
||||
type: 'text',
|
||||
getDisplayText: (intl) => intl.formatMessage({id: 'ContentBlock.text', defaultMessage: 'text'}),
|
||||
getIcon: () => <TextIcon/>,
|
||||
createBlock: () => {
|
||||
return new MutableTextBlock()
|
||||
},
|
||||
createComponent: (block, intl, readonly) => {
|
||||
return (
|
||||
<TextElement
|
||||
block={block}
|
||||
intl={intl}
|
||||
readonly={readonly}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default injectIntl(TextElement)
|
||||
|
||||
@@ -19,7 +19,7 @@ import Menu from '../widgets/menu'
|
||||
import MenuWrapper from '../widgets/menuWrapper'
|
||||
|
||||
import ContentElement from './content/contentElement'
|
||||
import contentRegistry from './content/contentRegistry'
|
||||
import {contentRegistry} from './content/contentRegistry'
|
||||
import './contentBlock.scss'
|
||||
|
||||
type Props = {
|
||||
@@ -100,17 +100,23 @@ class ContentBlock extends React.PureComponent<Props> {
|
||||
const {intl, card, contents, block} = this.props
|
||||
const index = contents.indexOf(block)
|
||||
|
||||
const handler = contentRegistry.getHandler(type)
|
||||
if (!handler) {
|
||||
Utils.logError(`addContentMenu, unknown content type: ${type}`)
|
||||
return <></>
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'image': return (
|
||||
<Menu.Text
|
||||
ref={type}
|
||||
id={type}
|
||||
name={contentRegistry.typeDisplayText(intl, type)}
|
||||
icon={contentRegistry.getIcon(type)}
|
||||
name={handler.getDisplayText(intl)}
|
||||
icon={handler.getIcon()}
|
||||
onClick={() => {
|
||||
Utils.selectLocalFile((file) => {
|
||||
mutator.performAsUndoGroup(async () => {
|
||||
const description = intl.formatMessage({id: 'ContentBlock.addElement', defaultMessage: 'add {type}'}, {type: contentRegistry.typeDisplayText(intl, type)})
|
||||
const description = intl.formatMessage({id: 'ContentBlock.addElement', defaultMessage: 'add {type}'}, {type: handler.getDisplayText(intl)})
|
||||
const newBlock = await mutator.createImageBlock(card, file, description)
|
||||
if (newBlock) {
|
||||
const contentOrder = contents.map((o) => o.id)
|
||||
@@ -127,17 +133,17 @@ class ContentBlock extends React.PureComponent<Props> {
|
||||
<Menu.Text
|
||||
ref={type}
|
||||
id={type}
|
||||
name={contentRegistry.typeDisplayText(intl, type)}
|
||||
icon={contentRegistry.getIcon(type)}
|
||||
name={handler.getDisplayText(intl)}
|
||||
icon={handler.getIcon()}
|
||||
onClick={() => {
|
||||
const newBlock = contentRegistry.createBlock(type)!
|
||||
const newBlock = handler.createBlock()!
|
||||
newBlock.parentId = card.id
|
||||
newBlock.rootId = card.rootId
|
||||
|
||||
const contentOrder = contents.map((o) => o.id)
|
||||
contentOrder.splice(index, 0, newBlock.id)
|
||||
mutator.performAsUndoGroup(async () => {
|
||||
const description = intl.formatMessage({id: 'ContentBlock.addElement', defaultMessage: 'add {type}'}, {type: contentRegistry.typeDisplayText(intl, type)})
|
||||
const description = intl.formatMessage({id: 'ContentBlock.addElement', defaultMessage: 'add {type}'}, {type: handler.getDisplayText(intl)})
|
||||
await mutator.insertBlock(newBlock, description)
|
||||
await mutator.changeCardContentOrder(card, contentOrder, description)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user