mirror of
https://github.com/mattermost/focalboard.git
synced 2025-02-13 19:42:12 +02:00
Splitting the sidebar board item into its own functional component
This commit is contained in:
parent
bd7bd50e79
commit
852269e20e
@ -1,28 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React from 'react'
|
||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
|
||||
import {injectIntl, IntlShape} from 'react-intl'
|
||||
|
||||
import {Board, MutableBoard} from '../../blocks/board'
|
||||
import {BoardView, IViewType, MutableBoardView} from '../../blocks/boardView'
|
||||
import {Constants} from '../../constants'
|
||||
import mutator from '../../mutator'
|
||||
import octoClient from '../../octoClient'
|
||||
import {loadTheme, setTheme, Theme} from '../../theme'
|
||||
import {loadTheme} from '../../theme'
|
||||
import {IUser, UserContext} from '../../user'
|
||||
import {WorkspaceTree} from '../../viewModel/workspaceTree'
|
||||
import IconButton from '../../widgets/buttons/iconButton'
|
||||
import BoardIcon from '../../widgets/icons/board'
|
||||
import DeleteIcon from '../../widgets/icons/delete'
|
||||
import DisclosureTriangle from '../../widgets/icons/disclosureTriangle'
|
||||
import DuplicateIcon from '../../widgets/icons/duplicate'
|
||||
import HamburgerIcon from '../../widgets/icons/hamburger'
|
||||
import HideSidebarIcon from '../../widgets/icons/hideSidebar'
|
||||
import LogoWithNameIcon from '../../widgets/icons/logoWithName'
|
||||
import LogoWithNameWhiteIcon from '../../widgets/icons/logoWithNameWhite'
|
||||
import OptionsIcon from '../../widgets/icons/options'
|
||||
import ShowSidebarIcon from '../../widgets/icons/showSidebar'
|
||||
import TableIcon from '../../widgets/icons/table'
|
||||
import Menu from '../../widgets/menu'
|
||||
import MenuWrapper from '../../widgets/menuWrapper'
|
||||
|
||||
@ -31,6 +22,7 @@ import RegistrationLink from '../registrationLink'
|
||||
|
||||
import SidebarSettingsMenu from './sidebarSettingsMenu'
|
||||
import SidebarAddBoardMenu from './sidebarAddBoardMenu'
|
||||
import SidebarBoardItem from './sidebarBoardItem'
|
||||
import './sidebar.scss'
|
||||
|
||||
type Props = {
|
||||
@ -44,7 +36,6 @@ type Props = {
|
||||
|
||||
type State = {
|
||||
isHidden: boolean
|
||||
collapsedBoards: {[key: string]: boolean}
|
||||
showRegistrationLinkDialog?: boolean
|
||||
whiteLogo: boolean
|
||||
}
|
||||
@ -52,7 +43,7 @@ type State = {
|
||||
class Sidebar extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = {isHidden: false, collapsedBoards: {}, whiteLogo: false}
|
||||
this.state = {isHidden: false, whiteLogo: false}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(): boolean {
|
||||
@ -78,7 +69,6 @@ class Sidebar extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
const {boards, views} = workspaceTree
|
||||
const {collapsedBoards} = this.state
|
||||
|
||||
if (this.state.isHidden) {
|
||||
return (
|
||||
@ -121,97 +111,17 @@ class Sidebar extends React.Component<Props, State> {
|
||||
<div className='octo-sidebar-list'>
|
||||
{
|
||||
boards.map((board) => {
|
||||
const displayTitle: string = board.title || intl.formatMessage({id: 'Sidebar.untitled-board', defaultMessage: '(Untitled Board)'})
|
||||
const boardViews = views.filter((view) => view.parentId === board.id)
|
||||
const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id)?.id : undefined
|
||||
return (
|
||||
<div key={board.id}>
|
||||
<div className={'octo-sidebar-item ' + (collapsedBoards[board.id] ? 'collapsed' : 'expanded')}>
|
||||
<IconButton
|
||||
icon={<DisclosureTriangle/>}
|
||||
onClick={() => {
|
||||
const newCollapsedBoards = {...this.state.collapsedBoards}
|
||||
newCollapsedBoards[board.id] = !newCollapsedBoards[board.id]
|
||||
this.setState({collapsedBoards: newCollapsedBoards})
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className='octo-sidebar-title'
|
||||
onClick={() => {
|
||||
this.boardClicked(board)
|
||||
}}
|
||||
title={displayTitle}
|
||||
>
|
||||
{board.icon ? `${board.icon} ${displayTitle}` : displayTitle}
|
||||
</div>
|
||||
<MenuWrapper>
|
||||
<IconButton icon={<OptionsIcon/>}/>
|
||||
<Menu position='left'>
|
||||
<Menu.Text
|
||||
id='deleteBoard'
|
||||
name={intl.formatMessage({id: 'Sidebar.delete-board', defaultMessage: 'Delete board'})}
|
||||
icon={<DeleteIcon/>}
|
||||
onClick={async () => {
|
||||
const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id)?.id : undefined
|
||||
mutator.deleteBlock(
|
||||
board,
|
||||
intl.formatMessage({id: 'Sidebar.delete-board', defaultMessage: 'Delete board'}),
|
||||
async () => {
|
||||
// This delay is needed because OctoListener has a default 100 ms notification delay before updates
|
||||
setTimeout(() => {
|
||||
this.props.showBoard(nextBoardId)
|
||||
}, 120)
|
||||
},
|
||||
async () => {
|
||||
this.props.showBoard(board.id)
|
||||
},
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<Menu.Text
|
||||
id='duplicateBoard'
|
||||
name={intl.formatMessage({id: 'Sidebar.duplicate-board', defaultMessage: 'Duplicate board'})}
|
||||
icon={<DuplicateIcon/>}
|
||||
onClick={() => {
|
||||
this.duplicateBoard(board.id)
|
||||
}}
|
||||
/>
|
||||
|
||||
<Menu.Text
|
||||
id='templateFromBoard'
|
||||
name={intl.formatMessage({id: 'Sidebar.template-from-board', defaultMessage: 'New template from board'})}
|
||||
onClick={() => {
|
||||
this.addTemplateFromBoard(board.id)
|
||||
}}
|
||||
/>
|
||||
</Menu>
|
||||
</MenuWrapper>
|
||||
</div>
|
||||
{!collapsedBoards[board.id] && boardViews.length === 0 &&
|
||||
<div className='octo-sidebar-item subitem no-views'>
|
||||
<FormattedMessage
|
||||
id='Sidebar.no-views-in-board'
|
||||
defaultMessage='No pages inside'
|
||||
/>
|
||||
</div>}
|
||||
{!collapsedBoards[board.id] && boardViews.map((view) => (
|
||||
<div
|
||||
key={view.id}
|
||||
className='octo-sidebar-item subitem'
|
||||
>
|
||||
{this.iconForViewType(view.viewType)}
|
||||
<div
|
||||
className='octo-sidebar-title'
|
||||
onClick={() => {
|
||||
this.viewClicked(board, view)
|
||||
}}
|
||||
title={view.title || intl.formatMessage({id: 'Sidebar.untitled-view', defaultMessage: '(Untitled View)'})}
|
||||
>
|
||||
{view.title || intl.formatMessage({id: 'Sidebar.untitled-view', defaultMessage: '(Untitled View)'})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<SidebarBoardItem
|
||||
key={board.id}
|
||||
views={views}
|
||||
board={board}
|
||||
showBoard={this.props.showBoard}
|
||||
showView={this.props.showView}
|
||||
activeBoardId={this.props.activeBoardId}
|
||||
nextBoardId={nextBoardId}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -297,58 +207,6 @@ class Sidebar extends React.Component<Props, State> {
|
||||
)
|
||||
}
|
||||
|
||||
private boardClicked(board: Board): void {
|
||||
this.props.showBoard(board.id)
|
||||
}
|
||||
|
||||
private viewClicked(board: Board, view: BoardView): void {
|
||||
this.props.showView(view.id, board.id)
|
||||
}
|
||||
|
||||
private iconForViewType(viewType: IViewType): JSX.Element {
|
||||
switch (viewType) {
|
||||
case 'board': return <BoardIcon/>
|
||||
case 'table': return <TableIcon/>
|
||||
default: return <div/>
|
||||
}
|
||||
}
|
||||
|
||||
private async duplicateBoard(boardId: string) {
|
||||
const oldBoardId = this.props.activeBoardId
|
||||
|
||||
await mutator.duplicateBoard(
|
||||
boardId,
|
||||
this.props.intl.formatMessage({id: 'Mutator.duplicate-board', defaultMessage: 'duplicate board'}),
|
||||
false,
|
||||
async (newBoardId) => {
|
||||
this.props.showBoard(newBoardId)
|
||||
},
|
||||
async () => {
|
||||
if (oldBoardId) {
|
||||
this.props.showBoard(oldBoardId)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private async addTemplateFromBoard(boardId: string) {
|
||||
const oldBoardId = this.props.activeBoardId
|
||||
|
||||
await mutator.duplicateBoard(
|
||||
boardId,
|
||||
this.props.intl.formatMessage({id: 'Mutator.new-template-from-board', defaultMessage: 'new template from board'}),
|
||||
true,
|
||||
async (newBoardId) => {
|
||||
this.props.showBoard(newBoardId)
|
||||
},
|
||||
async () => {
|
||||
if (oldBoardId) {
|
||||
this.props.showBoard(oldBoardId)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private hideClicked = () => {
|
||||
this.setState({isHidden: true})
|
||||
}
|
||||
|
167
webapp/src/components/sidebar/sidebarBoardItem.tsx
Normal file
167
webapp/src/components/sidebar/sidebarBoardItem.tsx
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
import React, {useState} from 'react'
|
||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
|
||||
|
||||
import {Board} from '../../blocks/board'
|
||||
import {BoardView, IViewType} from '../../blocks/boardView'
|
||||
import mutator from '../../mutator'
|
||||
import IconButton from '../../widgets/buttons/iconButton'
|
||||
import BoardIcon from '../../widgets/icons/board'
|
||||
import DeleteIcon from '../../widgets/icons/delete'
|
||||
import DisclosureTriangle from '../../widgets/icons/disclosureTriangle'
|
||||
import DuplicateIcon from '../../widgets/icons/duplicate'
|
||||
import OptionsIcon from '../../widgets/icons/options'
|
||||
import TableIcon from '../../widgets/icons/table'
|
||||
import Menu from '../../widgets/menu'
|
||||
import MenuWrapper from '../../widgets/menuWrapper'
|
||||
|
||||
type Props = {
|
||||
views: readonly BoardView[]
|
||||
board: Board
|
||||
showBoard: (id?: string) => void
|
||||
showView: (id: string, boardId?: string) => void
|
||||
activeBoardId?: string
|
||||
intl: IntlShape
|
||||
nextBoardId?: string
|
||||
}
|
||||
|
||||
const SidebarBoardItem = React.memo((props: Props) => {
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
|
||||
const iconForViewType = (viewType: IViewType): JSX.Element => {
|
||||
switch (viewType) {
|
||||
case 'board': return <BoardIcon/>
|
||||
case 'table': return <TableIcon/>
|
||||
default: return <div/>
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateBoard = async (boardId: string) => {
|
||||
const oldBoardId = props.activeBoardId
|
||||
|
||||
await mutator.duplicateBoard(
|
||||
boardId,
|
||||
props.intl.formatMessage({id: 'Mutator.duplicate-board', defaultMessage: 'duplicate board'}),
|
||||
false,
|
||||
async (newBoardId) => {
|
||||
props.showBoard(newBoardId)
|
||||
},
|
||||
async () => {
|
||||
if (oldBoardId) {
|
||||
props.showBoard(oldBoardId)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const addTemplateFromBoard = async (boardId: string) => {
|
||||
const oldBoardId = props.activeBoardId
|
||||
|
||||
await mutator.duplicateBoard(
|
||||
boardId,
|
||||
props.intl.formatMessage({id: 'Mutator.new-template-from-board', defaultMessage: 'new template from board'}),
|
||||
true,
|
||||
async (newBoardId) => {
|
||||
props.showBoard(newBoardId)
|
||||
},
|
||||
async () => {
|
||||
if (oldBoardId) {
|
||||
props.showBoard(oldBoardId)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const {board, intl, views} = props
|
||||
const displayTitle: string = board.title || intl.formatMessage({id: 'Sidebar.untitled-board', defaultMessage: '(Untitled Board)'})
|
||||
const boardViews = views.filter((view) => view.parentId === board.id)
|
||||
|
||||
return (
|
||||
<div key={board.id}>
|
||||
<div className={'octo-sidebar-item ' + (collapsed ? 'collapsed' : 'expanded')}>
|
||||
<IconButton
|
||||
icon={<DisclosureTriangle/>}
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
/>
|
||||
<div
|
||||
className='octo-sidebar-title'
|
||||
onClick={() => {
|
||||
props.showBoard(board.id)
|
||||
}}
|
||||
title={displayTitle}
|
||||
>
|
||||
{board.icon ? `${board.icon} ${displayTitle}` : displayTitle}
|
||||
</div>
|
||||
<MenuWrapper>
|
||||
<IconButton icon={<OptionsIcon/>}/>
|
||||
<Menu position='left'>
|
||||
<Menu.Text
|
||||
id='deleteBoard'
|
||||
name={intl.formatMessage({id: 'Sidebar.delete-board', defaultMessage: 'Delete board'})}
|
||||
icon={<DeleteIcon/>}
|
||||
onClick={async () => {
|
||||
mutator.deleteBlock(
|
||||
board,
|
||||
intl.formatMessage({id: 'Sidebar.delete-board', defaultMessage: 'Delete board'}),
|
||||
async () => {
|
||||
// This delay is needed because OctoListener has a default 100 ms notification delay before updates
|
||||
setTimeout(() => {
|
||||
props.showBoard(props.nextBoardId)
|
||||
}, 120)
|
||||
},
|
||||
async () => {
|
||||
props.showBoard(board.id)
|
||||
},
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<Menu.Text
|
||||
id='duplicateBoard'
|
||||
name={intl.formatMessage({id: 'Sidebar.duplicate-board', defaultMessage: 'Duplicate board'})}
|
||||
icon={<DuplicateIcon/>}
|
||||
onClick={() => {
|
||||
duplicateBoard(board.id)
|
||||
}}
|
||||
/>
|
||||
|
||||
<Menu.Text
|
||||
id='templateFromBoard'
|
||||
name={intl.formatMessage({id: 'Sidebar.template-from-board', defaultMessage: 'New template from board'})}
|
||||
onClick={() => {
|
||||
addTemplateFromBoard(board.id)
|
||||
}}
|
||||
/>
|
||||
</Menu>
|
||||
</MenuWrapper>
|
||||
</div>
|
||||
{!collapsed && boardViews.length === 0 &&
|
||||
<div className='octo-sidebar-item subitem no-views'>
|
||||
<FormattedMessage
|
||||
id='Sidebar.no-views-in-board'
|
||||
defaultMessage='No pages inside'
|
||||
/>
|
||||
</div>}
|
||||
{!collapsed && boardViews.map((view) => (
|
||||
<div
|
||||
key={view.id}
|
||||
className='octo-sidebar-item subitem'
|
||||
>
|
||||
{iconForViewType(view.viewType)}
|
||||
<div
|
||||
className='octo-sidebar-title'
|
||||
onClick={() => {
|
||||
props.showView(view.id, board.id)
|
||||
}}
|
||||
title={view.title || intl.formatMessage({id: 'Sidebar.untitled-view', defaultMessage: '(Untitled View)'})}
|
||||
>
|
||||
{view.title || intl.formatMessage({id: 'Sidebar.untitled-view', defaultMessage: '(Untitled View)'})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export default injectIntl(SidebarBoardItem)
|
Loading…
x
Reference in New Issue
Block a user