mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-24 13:43:12 +02:00
Show views in sidebar
This commit is contained in:
parent
0b07f454bb
commit
277505e6f9
@ -8,10 +8,12 @@ import mutator from '../mutator'
|
|||||||
import Menu from '../widgets/menu'
|
import Menu from '../widgets/menu'
|
||||||
import MenuWrapper from '../widgets/menuWrapper'
|
import MenuWrapper from '../widgets/menuWrapper'
|
||||||
import { WorkspaceTree } from '../viewModel/workspaceTree'
|
import { WorkspaceTree } from '../viewModel/workspaceTree'
|
||||||
|
import { BoardView } from '../blocks/boardView'
|
||||||
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
showBoard: (id: string) => void
|
showBoard: (id: string) => void
|
||||||
|
showView: (id: string, boardId?: string) => void
|
||||||
workspaceTree: WorkspaceTree,
|
workspaceTree: WorkspaceTree,
|
||||||
boardTree?: BoardTree
|
boardTree?: BoardTree
|
||||||
}
|
}
|
||||||
@ -23,47 +25,51 @@ class Sidebar extends React.Component<Props> {
|
|||||||
return <div/>
|
return <div/>
|
||||||
}
|
}
|
||||||
|
|
||||||
const {boards} = workspaceTree
|
const {boards, views} = workspaceTree
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='octo-sidebar'>
|
<div className='octo-sidebar'>
|
||||||
{
|
{
|
||||||
boards.map((board) => {
|
boards.map((board) => {
|
||||||
const displayTitle = board.title || '(Untitled Board)'
|
const displayTitle = board.title || '(Untitled Board)'
|
||||||
|
const boardViews = views.filter(view => view.parentId === board.id)
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={board.id}>
|
||||||
key={board.id}
|
<div className='octo-sidebar-item octo-hover-container'>
|
||||||
className='octo-sidebar-item octo-hover-container'
|
<div className='octo-sidebar-title' onClick={() => { this.boardClicked(board) }}>
|
||||||
>
|
{board.icon ? `${board.icon} ${displayTitle}` : displayTitle}
|
||||||
<div
|
</div>
|
||||||
className='octo-sidebar-title'
|
<div className='octo-spacer'/>
|
||||||
onClick={() => {
|
<MenuWrapper>
|
||||||
this.boardClicked(board)
|
<div className='octo-button square octo-hover-item'><div className='imageOptions'/></div>
|
||||||
}}
|
<Menu>
|
||||||
>{board.icon ? `${board.icon} ${displayTitle}` : displayTitle}</div>
|
<Menu.Text
|
||||||
<div className='octo-spacer'/>
|
id='delete'
|
||||||
<MenuWrapper>
|
name='Delete board'
|
||||||
<div className='octo-button square octo-hover-item'><div className='imageOptions'/></div>
|
onClick={async () => {
|
||||||
<Menu>
|
const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id).id : undefined
|
||||||
<Menu.Text
|
mutator.deleteBlock(
|
||||||
id='delete'
|
board,
|
||||||
name='Delete board'
|
'delete block',
|
||||||
onClick={async () => {
|
async () => {
|
||||||
const nextBoardId = boards.length > 1 ? boards.find((o) => o.id !== board.id).id : undefined
|
nextBoardId && this.props.showBoard(nextBoardId!)
|
||||||
mutator.deleteBlock(
|
},
|
||||||
board,
|
async () => {
|
||||||
'delete block',
|
this.props.showBoard(board.id)
|
||||||
async () => {
|
},
|
||||||
nextBoardId && this.props.showBoard(nextBoardId!)
|
)
|
||||||
},
|
}}
|
||||||
async () => {
|
/>
|
||||||
this.props.showBoard(board.id)
|
</Menu>
|
||||||
},
|
</MenuWrapper>
|
||||||
)
|
</div>
|
||||||
}}
|
{boardViews.map(view => {
|
||||||
/>
|
return <div key={view.id} className='octo-sidebar-item subitem octo-hover-container'>
|
||||||
</Menu>
|
<div className='octo-sidebar-title' onClick={() => { this.viewClicked(board, view) }}>
|
||||||
</MenuWrapper>
|
{view.title || '(Untitled View)'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -105,6 +111,10 @@ class Sidebar extends React.Component<Props> {
|
|||||||
this.props.showBoard(board.id)
|
this.props.showBoard(board.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private viewClicked(board: Board, view: BoardView) {
|
||||||
|
this.props.showView(view.id, board.id)
|
||||||
|
}
|
||||||
|
|
||||||
async addBoardClicked() {
|
async addBoardClicked() {
|
||||||
const {boardTree, showBoard} = this.props
|
const {boardTree, showBoard} = this.props
|
||||||
|
|
||||||
@ -127,4 +137,3 @@ class Sidebar extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export { Sidebar }
|
export { Sidebar }
|
||||||
|
|
||||||
|
@ -14,20 +14,21 @@ type Props = {
|
|||||||
workspaceTree: WorkspaceTree
|
workspaceTree: WorkspaceTree
|
||||||
boardTree?: BoardTree
|
boardTree?: BoardTree
|
||||||
showBoard: (id: string) => void
|
showBoard: (id: string) => void
|
||||||
showView: (id: string) => void
|
showView: (id: string, boardId?: string) => void
|
||||||
showFilter: (el: HTMLElement) => void
|
showFilter: (el: HTMLElement) => void
|
||||||
setSearchText: (text: string) => void
|
setSearchText: (text: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
class WorkspaceComponent extends React.Component<Props> {
|
class WorkspaceComponent extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const {boardTree, workspaceTree, showBoard} = this.props
|
const {boardTree, workspaceTree, showBoard, showView} = this.props
|
||||||
|
|
||||||
Utils.assert(workspaceTree)
|
Utils.assert(workspaceTree)
|
||||||
const element =
|
const element =
|
||||||
(<div className='octo-workspace'>
|
(<div className='octo-workspace'>
|
||||||
<Sidebar
|
<Sidebar
|
||||||
showBoard={showBoard}
|
showBoard={showBoard}
|
||||||
|
showView={showView}
|
||||||
workspaceTree={workspaceTree}
|
workspaceTree={workspaceTree}
|
||||||
boardTree={boardTree}
|
boardTree={boardTree}
|
||||||
/>
|
/>
|
||||||
|
@ -47,18 +47,22 @@ class OctoClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBlocks(parentId?: string, type?: string): Promise<IBlock[]> {
|
async getBlocksWithParent(parentId: string, type?: string): Promise<IBlock[]> {
|
||||||
let path: string
|
let path: string
|
||||||
if (parentId && type) {
|
if (type) {
|
||||||
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}&type=${encodeURIComponent(type)}`
|
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}&type=${encodeURIComponent(type)}`
|
||||||
} else if (parentId) {
|
|
||||||
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}`
|
|
||||||
} else if (type) {
|
|
||||||
path = `/api/v1/blocks?type=${encodeURIComponent(type)}`
|
|
||||||
} else {
|
} else {
|
||||||
path = '/api/v1/blocks'
|
path = `/api/v1/blocks?parent_id=${encodeURIComponent(parentId)}`
|
||||||
}
|
}
|
||||||
|
return this.getBlocksWithPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBlocksWithType(type: string): Promise<IBlock[]> {
|
||||||
|
const path = `/api/v1/blocks?type=${encodeURIComponent(type)}`
|
||||||
|
return this.getBlocksWithPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBlocksWithPath(path: string): Promise<IBlock[]> {
|
||||||
const response = await fetch(this.serverUrl + path)
|
const response = await fetch(this.serverUrl + path)
|
||||||
const blocks = (await response.json() || []) as IMutableBlock[]
|
const blocks = (await response.json() || []) as IMutableBlock[]
|
||||||
this.fixBlocks(blocks)
|
this.fixBlocks(blocks)
|
||||||
|
@ -141,8 +141,8 @@ export default class BoardPage extends React.Component<Props, State> {
|
|||||||
<WorkspaceComponent
|
<WorkspaceComponent
|
||||||
workspaceTree={workspaceTree}
|
workspaceTree={workspaceTree}
|
||||||
boardTree={this.state.boardTree}
|
boardTree={this.state.boardTree}
|
||||||
showView={(id) => {
|
showView={(id, boardId) => {
|
||||||
this.showView(id)
|
this.showView(id, boardId)
|
||||||
}}
|
}}
|
||||||
showBoard={(id) => {
|
showBoard={(id) => {
|
||||||
this.showBoard(id)
|
this.showBoard(id)
|
||||||
@ -218,10 +218,15 @@ export default class BoardPage extends React.Component<Props, State> {
|
|||||||
this.attachToBoard(boardId)
|
this.attachToBoard(boardId)
|
||||||
}
|
}
|
||||||
|
|
||||||
showView(viewId: string) {
|
showView(viewId: string, boardId: string = this.state.boardId) {
|
||||||
this.state.boardTree.setActiveView(viewId)
|
if (this.state.boardId !== boardId) {
|
||||||
this.setState({...this.state, viewId})
|
this.attachToBoard(boardId, viewId)
|
||||||
const newUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + `?id=${encodeURIComponent(this.state.boardId)}&v=${encodeURIComponent(viewId)}`
|
} else {
|
||||||
|
this.state.boardTree.setActiveView(viewId)
|
||||||
|
this.setState({...this.state, viewId})
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUrl = window.location.protocol + '//' + window.location.host + window.location.pathname + `?id=${encodeURIComponent(boardId)}&v=${encodeURIComponent(viewId)}`
|
||||||
window.history.pushState({path: newUrl}, '', newUrl)
|
window.history.pushState({path: newUrl}, '', newUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export default class HomePage extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadBoards = async () => {
|
loadBoards = async () => {
|
||||||
const boards = await octoClient.getBlocks(null, 'board')
|
const boards = await octoClient.getBlocksWithType('board')
|
||||||
this.setState({boards})
|
this.setState({boards})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,21 +4,29 @@ import {Board} from '../blocks/board'
|
|||||||
import octoClient from '../octoClient'
|
import octoClient from '../octoClient'
|
||||||
import {IBlock} from '../blocks/block'
|
import {IBlock} from '../blocks/block'
|
||||||
import {OctoUtils} from '../octoUtils'
|
import {OctoUtils} from '../octoUtils'
|
||||||
|
import {BoardView} from '../blocks/boardView'
|
||||||
|
|
||||||
interface WorkspaceTree {
|
interface WorkspaceTree {
|
||||||
readonly boards: readonly Board[]
|
readonly boards: readonly Board[]
|
||||||
|
readonly views: readonly BoardView[]
|
||||||
}
|
}
|
||||||
|
|
||||||
class MutableWorkspaceTree {
|
class MutableWorkspaceTree {
|
||||||
boards: Board[] = []
|
boards: Board[] = []
|
||||||
|
views: BoardView[] = []
|
||||||
|
|
||||||
async sync() {
|
async sync() {
|
||||||
const blocks = await octoClient.getBlocks(undefined, "board")
|
const boards = await octoClient.getBlocksWithType("board")
|
||||||
this.rebuild(OctoUtils.hydrateBlocks(blocks))
|
const views = await octoClient.getBlocksWithType("view")
|
||||||
|
this.rebuild(
|
||||||
|
OctoUtils.hydrateBlocks(boards),
|
||||||
|
OctoUtils.hydrateBlocks(views)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private rebuild(blocks: IBlock[]) {
|
private rebuild(boards: IBlock[], views: IBlock[]) {
|
||||||
this.boards = blocks.filter(block => block.type === "board") as Board[]
|
this.boards = boards.filter(block => block.type === "board") as Board[]
|
||||||
|
this.views = views.filter(block => block.type === "view") as BoardView[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,12 +92,13 @@ hr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.octo-sidebar {
|
.octo-sidebar {
|
||||||
|
flex: 0 0 230px;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
background-color: rgb(247, 246, 243);
|
background-color: rgb(247, 246, 243);
|
||||||
min-width: 230px;
|
|
||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +110,12 @@ hr {
|
|||||||
padding: 3px 20px;
|
padding: 3px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.octo-sidebar-item.subitem {
|
||||||
|
color: rgba(25, 23, 18, 0.6);
|
||||||
|
font-weight: 400;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.octo-sidebar-title {
|
.octo-sidebar-title {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user