Merge branch 'main' into remove-show-card

This commit is contained in:
Jesús Espino 2020-10-15 19:52:36 +02:00
commit e740ce43d5
22 changed files with 329 additions and 360 deletions

View file

@ -1,5 +1,5 @@
import { BoardTree } from "./boardTree"
import { Mutator } from "./mutator"
import mutator from "./mutator"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@ -21,7 +21,7 @@ class Archiver {
this.exportArchive(archive)
}
static async exportFullArchive(mutator: Mutator) {
static async exportFullArchive() {
const blocks = await mutator.exportFullArchive()
const archive: Archive = {
version: 1,
@ -50,7 +50,7 @@ class Archiver {
// TODO: Remove or reuse link
}
static importFullArchive(mutator: Mutator, onImported?: () => void): void {
static importFullArchive(onImported?: () => void): void {
const input = document.createElement("input")
input.type = "file"
input.accept = ".octo"

View file

@ -2,7 +2,7 @@ import { Board, IPropertyOption, IPropertyTemplate } from "./board"
import { BoardView } from "./boardView"
import { Card } from "./card"
import { CardFilter } from "./cardFilter"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@ -24,13 +24,11 @@ class BoardTree {
return [this.board, ...this.views, ...this.allCards]
}
constructor(
private octo: OctoClient,
private boardId: string) {
constructor(private boardId: string) {
}
async sync() {
const blocks = await this.octo.getSubtree(this.boardId)
const blocks = await octoClient.getSubtree(this.boardId)
this.rebuild(blocks)
}

View file

@ -1,5 +1,5 @@
import { Card } from "./card"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
class CardTree {
@ -8,13 +8,11 @@ class CardTree {
contents: IBlock[]
isSynched: boolean
constructor(
private octo: OctoClient,
private cardId: string) {
constructor(private cardId: string) {
}
async sync() {
const blocks = await this.octo.getSubtree(this.cardId)
const blocks = await octoClient.getSubtree(this.cardId)
this.rebuild(blocks)
}

View file

@ -3,12 +3,11 @@ import { Block } from "../block"
import { IPropertyTemplate } from "../board"
import { Card } from "../card"
import { Menu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
type BoardCardProps = {
mutator: Mutator
card: Card
visiblePropertyTemplates: IPropertyTemplate[]
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
@ -58,7 +57,7 @@ class BoardCard extends React.Component<BoardCardProps, BoardCardState> {
}
private showOptionsMenu(e: React.MouseEvent) {
const { mutator, card } = this.props
const { card } = this.props
e.stopPropagation()

View file

@ -6,10 +6,12 @@ import { BoardTree } from "../boardTree"
import { Card } from "../card"
import { CardFilter } from "../cardFilter"
import ViewMenu from "../components/viewMenu"
import MenuWrapper from "../widgets/menuWrapper"
import Menu from "../widgets/menu"
import { Constants } from "../constants"
import { randomEmojiList } from "../emojiList"
import { Menu as OldMenu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
import { BoardCard } from "./boardCard"
@ -20,7 +22,6 @@ import { CardDialog } from "./cardDialog"
import RootPortal from "./rootPortal"
type Props = {
mutator: Mutator,
boardTree?: BoardTree
showView: (id: string) => void
showFilter: (el: HTMLElement) => void
@ -28,10 +29,10 @@ type Props = {
}
type State = {
isHoverOnCover: boolean
isSearching: boolean
shownCard: IBlock | null
shownCard: Card | null
viewMenu: boolean
isHoverOnCover: boolean
}
class BoardComponent extends React.Component<Props, State> {
@ -51,7 +52,7 @@ class BoardComponent extends React.Component<Props, State> {
}
render() {
const { mutator, boardTree, showView } = this.props
const { boardTree, showView } = this.props
if (!boardTree || !boardTree.board) {
return (
@ -92,7 +93,13 @@ class BoardComponent extends React.Component<Props, State> {
<div className="octo-icontitle">
{board.icon ?
<div className="octo-button octo-icon" onClick={(e) => { this.iconClicked(e) }}>{board.icon}</div>
<MenuWrapper>
<div className="octo-button octo-icon">{board.icon}</div>
<Menu>
<Menu.Text id='random' name='Random' onClick={() => mutator.changeIcon(board, undefined, "remove icon")}/>
<Menu.Text id='remove' name='Remove Icon' onClick={() => mutator.changeIcon(board, BlockIcons.shared.randomIcon())}/>
</Menu>
</MenuWrapper>
: undefined}
<Editable className="title" text={board.title} placeholderText="Untitled Board" onChanged={(text) => { mutator.changeTitle(board, text) }} />
</div>
@ -100,28 +107,26 @@ class BoardComponent extends React.Component<Props, State> {
<div className="octo-board">
<div className="octo-controls">
<Editable style={{ color: "#000000", fontWeight: 600 }} text={activeView.title} placeholderText="Untitled View" onChanged={(text) => { mutator.changeTitle(activeView, text) }} />
<div
className="octo-button"
style={{ color: "#000000", fontWeight: 600 }}
onClick={() => this.setState({ viewMenu: true })}
>
{this.state.viewMenu &&
<ViewMenu
board={board}
onClose={() => this.setState({ viewMenu: false })}
mutator={mutator}
boardTree={boardTree}
showView={showView}
/>}
<div className="imageDropdown"></div>
</div>
<MenuWrapper>
<div
className="octo-button"
style={{ color: "#000000", fontWeight: 600 }}
>
<div className="imageDropdown"></div>
</div>
<ViewMenu
board={board}
boardTree={boardTree}
showView={showView}
/>
</MenuWrapper>
<div className="octo-spacer"></div>
<div className="octo-button" onClick={(e) => { this.propertiesClicked(e) }}>Properties</div>
<div className="octo-button" id="groupByButton" onClick={(e) => { this.groupByClicked(e) }}>
Group by <span style={groupByStyle} id="groupByLabel">{boardTree.groupByProperty?.name}</span>
</div>
<div className={hasFilter ? "octo-button active" : "octo-button"} onClick={(e) => { this.filterClicked(e) }}>Filter</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, boardTree) }}>Sort</div>
{this.state.isSearching
? <Editable
ref={this.searchFieldRef}
@ -170,7 +175,16 @@ class BoardComponent extends React.Component<Props, State> {
onChanged={(text) => { this.propertyNameChanged(group.option, text) }} />
<Button text={`${group.cards.length}`} />
<div className="octo-spacer" />
<Button onClick={(e) => { this.valueOptionClicked(e, group.option) }}><div className="imageOptions" /></Button>
<MenuWrapper>
<Button><div className="imageOptions" /></Button>
<Menu>
<Menu.Text id='delete' name='Delete' onClick={() => mutator.deletePropertyOption(boardTree, boardTree.groupByProperty, group.option)}/>
<Menu.Separator/>
{Constants.menuColors.map((color) =>
<Menu.Color key={color.id} id={color.id} name={color.name} onClick={() => mutator.changePropertyOptionColor(boardTree.board, group.option, color.id)} />
)}
</Menu>
</MenuWrapper>
<Button onClick={() => { this.addCard(group.option.value) }}><div className="imageAdd" /></Button>
</div>
)}
@ -189,7 +203,6 @@ class BoardComponent extends React.Component<Props, State> {
<BoardColumn onDrop={(e) => { this.onDropToColumn(undefined) }}>
{boardTree.emptyGroupCards.map(card =>
<BoardCard
mutator={mutator}
card={card}
visiblePropertyTemplates={visiblePropertyTemplates}
key={card.id}
@ -206,7 +219,6 @@ class BoardComponent extends React.Component<Props, State> {
<BoardColumn onDrop={(e) => { this.onDropToColumn(group.option) }} key={group.option.value}>
{group.cards.map(card =>
<BoardCard
mutator={mutator}
card={card}
visiblePropertyTemplates={visiblePropertyTemplates}
key={card.id}
@ -224,30 +236,8 @@ class BoardComponent extends React.Component<Props, State> {
)
}
private iconClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { board } = boardTree
OldMenu.shared.options = [
{ id: "random", name: "Random" },
{ id: "remove", name: "Remove Icon" },
]
OldMenu.shared.onMenuClicked = (optionId: string, type?: string) => {
switch (optionId) {
case "remove":
mutator.changeIcon(board, undefined, "remove icon")
break
case "random":
const newIcon = BlockIcons.shared.randomIcon()
mutator.changeIcon(board, newIcon)
break
}
}
OldMenu.shared.showAtElement(e.target as HTMLElement)
}
async addCard(groupByValue?: string) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView, board } = boardTree
const card = new Card()
@ -260,36 +250,11 @@ class BoardComponent extends React.Component<Props, State> {
}
async propertyNameChanged(option: IPropertyOption, text: string) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
await mutator.changePropertyOptionValue(boardTree, boardTree.groupByProperty, option, text)
}
async valueOptionClicked(e: React.MouseEvent<HTMLElement>, option: IPropertyOption) {
const { mutator, boardTree } = this.props
OldMenu.shared.options = [
{ id: "delete", name: "Delete" },
{ id: "", name: "", type: "separator" },
...Constants.menuColors
]
OldMenu.shared.onMenuClicked = async (optionId: string, type?: string) => {
switch (optionId) {
case "delete":
console.log(`Delete property value: ${option.value}`)
await mutator.deletePropertyOption(boardTree, boardTree.groupByProperty, option)
break
default:
if (type === "color") {
// id is the color
await mutator.changePropertyOptionColor(boardTree.board, option, optionId)
break
}
}
}
OldMenu.shared.showAtElement(e.target as HTMLElement)
}
private filterClicked(e: React.MouseEvent) {
this.props.showFilter(e.target as HTMLElement)
}
@ -323,7 +288,7 @@ class BoardComponent extends React.Component<Props, State> {
}
private async testAddCards(count: number) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView } = boardTree
const startCount = boardTree?.cards?.length
@ -346,7 +311,7 @@ class BoardComponent extends React.Component<Props, State> {
}
private async propertiesClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView } = boardTree
const selectProperties = boardTree.board.cardProperties
@ -372,7 +337,7 @@ class BoardComponent extends React.Component<Props, State> {
}
private async groupByClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const selectProperties = boardTree.board.cardProperties.filter(o => o.type === "select")
OldMenu.shared.options = selectProperties.map((o) => { return { id: o.id, name: o.name } })
@ -387,7 +352,7 @@ class BoardComponent extends React.Component<Props, State> {
async addGroupClicked() {
console.log(`onAddGroupClicked`)
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const option: IPropertyOption = {
value: "New group",
@ -399,7 +364,7 @@ class BoardComponent extends React.Component<Props, State> {
}
async onDropToColumn(option: IPropertyOption) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { draggedCard, draggedHeaderOption } = this
const propertyValue = option ? option.value : undefined

View file

@ -1,15 +1,15 @@
import React from "react"
import { Block } from "../block"
import { Card } from "../card"
import { BlockIcons } from "../blockIcons"
import { BoardTree } from "../boardTree"
import { CardTree } from "../cardTree"
import { Menu, MenuOption } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { IBlock } from "../octoTypes"
import { OctoUtils } from "../octoUtils"
import { PropertyMenu } from "../propertyMenu"
import { OctoListener } from "../octoListener"
import { OctoClient } from "../octoClient"
import { Utils } from "../utils"
import Button from "./button"
import { Editable } from "./editable"
@ -17,8 +17,7 @@ import { MarkdownEditor } from "./markdownEditor"
type Props = {
boardTree: BoardTree
card: IBlock
mutator: Mutator
card: Card
onClose: () => void
}
@ -51,7 +50,7 @@ class CardDialog extends React.Component<Props, State> {
await cardTree.sync()
this.setState({cardTree: cardTree})
})
const cardTree = new CardTree(new OctoClient(), this.props.card.id)
const cardTree = new CardTree(this.props.card.id)
cardTree.sync().then(() => {
this.setState({cardTree})
});
@ -70,7 +69,7 @@ class CardDialog extends React.Component<Props, State> {
}
render() {
const { boardTree, mutator, card } = this.props
const { boardTree, card } = this.props
const { cardTree } = this.state
const { board } = boardTree
if (cardTree === null) {
@ -227,7 +226,7 @@ class CardDialog extends React.Component<Props, State> {
}
menu.showAtElement(e.target as HTMLElement)
}}>{propertyTemplate.name}</div>
{OctoUtils.propertyValueEditableElement(mutator, card, propertyTemplate)}
{OctoUtils.propertyValueEditableElement(card, propertyTemplate)}
</div>
)
})}
@ -356,7 +355,7 @@ class CardDialog extends React.Component<Props, State> {
}
async sendComment(text: string) {
const { mutator, card } = this.props
const { card } = this.props
Utils.assertValue(card)
@ -365,7 +364,7 @@ class CardDialog extends React.Component<Props, State> {
}
private showContentBlockMenu(e: React.MouseEvent, block: IBlock) {
const { mutator, card } = this.props
const { card } = this.props
const { cardTree } = this.state
const index = cardTree.contents.indexOf(block)
@ -432,7 +431,7 @@ class CardDialog extends React.Component<Props, State> {
}
private iconClicked(e: React.MouseEvent) {
const { mutator, card } = this.props
const { card } = this.props
Menu.shared.options = [
{ id: "random", name: "Random" },

View file

@ -3,11 +3,10 @@ import { BoardTree } from "../boardTree"
import { FilterClause, FilterCondition } from "../filterClause"
import { FilterGroup } from "../filterGroup"
import { Menu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { Utils } from "../utils"
type Props = {
mutator: Mutator
boardTree: BoardTree
pageX: number
pageY: number
@ -67,7 +66,7 @@ class FilterComponent extends React.Component<Props> {
}
private propertyClicked(e: React.MouseEvent, filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView: view } = boardTree
const filterIndex = view.filter.filters.indexOf(filter)
@ -90,7 +89,7 @@ class FilterComponent extends React.Component<Props> {
}
private conditionClicked(e: React.MouseEvent, filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView: view } = boardTree
const filterIndex = view.filter.filters.indexOf(filter)
@ -115,7 +114,7 @@ class FilterComponent extends React.Component<Props> {
}
private valuesClicked(e: React.MouseEvent, filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView: view } = boardTree
const template = board.cardProperties.find(o => o.id === filter.propertyId)
@ -143,7 +142,7 @@ class FilterComponent extends React.Component<Props> {
}
private deleteClicked(filter: FilterClause) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView: view } = boardTree
const filterGroup = new FilterGroup(view.filter)
@ -153,7 +152,7 @@ class FilterComponent extends React.Component<Props> {
}
private addFilterClicked() {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board, activeView: view } = boardTree
const filters = view.filter?.filters.filter(o => !FilterGroup.isAnInstanceOf(o)) as FilterClause[] || []

View file

@ -3,12 +3,11 @@ import { Archiver } from "../archiver"
import { Board } from "../board"
import { BoardTree } from "../boardTree"
import { Menu, MenuOption } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { IPageController } from "../octoTypes"
import { WorkspaceTree } from "../workspaceTree"
type Props = {
mutator: Mutator
showBoard: (id: string) => void
workspaceTree: WorkspaceTree,
boardTree?: BoardTree
@ -51,7 +50,7 @@ class Sidebar extends React.Component<Props> {
}
private showOptions(e: React.MouseEvent, board: Board) {
const { mutator, showBoard, workspaceTree } = this.props
const { showBoard, workspaceTree } = this.props
const { boards } = workspaceTree
const options: MenuOption[] = []
@ -79,8 +78,6 @@ class Sidebar extends React.Component<Props> {
}
private settingsClicked(e: React.MouseEvent) {
const { mutator } = this.props
Menu.shared.options = [
{ id: "import", name: "Import Archive" },
{ id: "export", name: "Export Archive" },
@ -88,13 +85,13 @@ class Sidebar extends React.Component<Props> {
Menu.shared.onMenuClicked = (optionId: string, type?: string) => {
switch (optionId) {
case "import": {
Archiver.importFullArchive(mutator, () => {
Archiver.importFullArchive(() => {
this.forceUpdate()
})
break
}
case "export": {
Archiver.exportFullArchive(mutator)
Archiver.exportFullArchive()
break
}
}
@ -112,7 +109,7 @@ class Sidebar extends React.Component<Props> {
}
async addBoardClicked() {
const { mutator, boardTree, showBoard } = this.props
const { boardTree, showBoard } = this.props
const oldBoardId = boardTree?.board?.id
const board = new Board()

View file

@ -6,9 +6,11 @@ import { IPropertyTemplate } from "../board"
import { BoardTree } from "../boardTree"
import { Card } from "../card"
import ViewMenu from "../components/viewMenu"
import MenuWrapper from "../widgets/menuWrapper"
import Menu from "../widgets/menu"
import { CsvExporter } from "../csvExporter"
import { Menu as OldMenu } from "../menu"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Utils } from "../utils"
import Button from "./button"
@ -18,7 +20,6 @@ import { CardDialog } from "./cardDialog"
import RootPortal from "./rootPortal"
type Props = {
mutator: Mutator,
boardTree?: BoardTree
showView: (id: string) => void
showFilter: (el: HTMLElement) => void
@ -50,7 +51,7 @@ class TableComponent extends React.Component<Props, State> {
}
render() {
const { mutator, boardTree, showView } = this.props
const { boardTree, showView } = this.props
if (!boardTree || !boardTree.board) {
return (
@ -88,7 +89,13 @@ class TableComponent extends React.Component<Props, State> {
<div className="octo-icontitle">
{board.icon ?
<div className="octo-button octo-icon" onClick={(e) => { this.iconClicked(e) }}>{board.icon}</div>
<MenuWrapper>
<div className="octo-button octo-icon">{board.icon}</div>
<Menu>
<Menu.Text id='random' name='Random' onClick={() => mutator.changeIcon(board, undefined, "remove icon")}/>
<Menu.Text id='remove' name='Remove Icon' onClick={() => mutator.changeIcon(board, BlockIcons.shared.randomIcon())}/>
</Menu>
</MenuWrapper>
: undefined}
<Editable className="title" text={board.title} placeholderText="Untitled Board" onChanged={(text) => { mutator.changeTitle(board, text) }} />
</div>
@ -96,25 +103,23 @@ class TableComponent extends React.Component<Props, State> {
<div className="octo-table">
<div className="octo-controls">
<Editable style={{ color: "#000000", fontWeight: 600 }} text={activeView.title} placeholderText="Untitled View" onChanged={(text) => { mutator.changeTitle(activeView, text) }} />
<div
className="octo-button"
style={{ color: "#000000", fontWeight: 600 }}
onClick={() => this.setState({ viewMenu: true })}
>
{this.state.viewMenu &&
<ViewMenu
board={board}
onClose={() => this.setState({ viewMenu: false })}
mutator={mutator}
boardTree={boardTree}
showView={showView}
/>}
<div className="imageDropdown"></div>
</div>
<MenuWrapper>
<div
className="octo-button"
style={{ color: "#000000", fontWeight: 600 }}
>
<div className="imageDropdown"></div>
</div>
<ViewMenu
board={board}
boardTree={boardTree}
showView={showView}
/>
</MenuWrapper>
<div className="octo-spacer"></div>
<div className="octo-button" onClick={(e) => { this.propertiesClicked(e) }}>Properties</div>
<div className={hasFilter ? "octo-button active" : "octo-button"} onClick={(e) => { this.filterClicked(e) }}>Filter</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort</div>
<div className={hasSort ? "octo-button active" : "octo-button"} onClick={(e) => { OctoUtils.showSortMenu(e, boardTree) }}>Sort</div>
{this.state.isSearching
? <Editable
ref={this.searchFieldRef}
@ -185,7 +190,6 @@ class TableComponent extends React.Component<Props, State> {
const tableRow = <TableRow
key={card.id}
ref={tableRowRef}
mutator={mutator}
boardTree={boardTree}
card={card}
focusOnMount={focusOnMount}
@ -218,30 +222,8 @@ class TableComponent extends React.Component<Props, State> {
)
}
private iconClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { board } = boardTree
OldMenu.shared.options = [
{ id: "random", name: "Random" },
{ id: "remove", name: "Remove Icon" },
]
OldMenu.shared.onMenuClicked = (optionId: string, type?: string) => {
switch (optionId) {
case "remove":
mutator.changeIcon(board, undefined, "remove icon")
break
case "random":
const newIcon = BlockIcons.shared.randomIcon()
mutator.changeIcon(board, newIcon)
break
}
}
OldMenu.shared.showAtElement(e.target as HTMLElement)
}
private async propertiesClicked(e: React.MouseEvent) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { activeView } = boardTree
const selectProperties = boardTree.board.cardProperties
@ -294,7 +276,7 @@ class TableComponent extends React.Component<Props, State> {
}
private async headerClicked(e: React.MouseEvent<HTMLDivElement>, templateId: string) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board } = boardTree
const { activeView } = boardTree
@ -375,7 +357,7 @@ class TableComponent extends React.Component<Props, State> {
}
async addCard(show: boolean = false) {
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const card = new Card()
card.parentId = boardTree.board.id
@ -397,7 +379,7 @@ class TableComponent extends React.Component<Props, State> {
const { draggedHeaderTemplate } = this
if (!draggedHeaderTemplate) { return }
const { mutator, boardTree } = this.props
const { boardTree } = this.props
const { board } = boardTree
Utils.assertValue(mutator)

View file

@ -1,14 +1,13 @@
import React from "react"
import { BoardTree } from "../boardTree"
import { Card } from "../card"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { OctoUtils } from "../octoUtils"
import { Editable } from "./editable"
import { CardDialog } from "./cardDialog"
import RootPortal from "./rootPortal"
type Props = {
mutator: Mutator
boardTree: BoardTree
card: Card
focusOnMount: boolean
@ -32,7 +31,7 @@ class TableRow extends React.Component<Props, State> {
}
render() {
const { mutator, boardTree, card, onKeyDown } = this.props
const { boardTree, card, onKeyDown } = this.props
const { board, activeView } = boardTree
const openButonRef = React.createRef<HTMLDivElement>()
@ -56,7 +55,7 @@ class TableRow extends React.Component<Props, State> {
<div ref={openButonRef} className="octo-hoverbutton" style={{ display: "none" }} onClick={() => { this.setState({showCard: true}) }}>Open</div>
{this.state.showCard &&
<RootPortal>
<CardDialog boardTree={boardTree} card={card} mutator={mutator} onClose={() => this.setState({showCard: false})}/>
<CardDialog boardTree={boardTree} card={card} onClose={() => this.setState({showCard: false})}/>
</RootPortal>}
</div>
@ -66,7 +65,7 @@ class TableRow extends React.Component<Props, State> {
.filter(template => activeView.visiblePropertyIds.includes(template.id))
.map(template => {
return <div className="octo-table-cell" key={template.id}>
{OctoUtils.propertyValueEditableElement(mutator, card, template)}
{OctoUtils.propertyValueEditableElement(card, template)}
</div>
})}
</div>

View file

@ -2,21 +2,19 @@ import React from "react"
import { Board } from "../board"
import { BoardTree } from "../boardTree"
import { BoardView } from "../boardView"
import { Mutator } from "../mutator"
import mutator from "../mutator"
import { Utils } from "../utils"
import Menu from "../widgets/menu"
type Props = {
mutator: Mutator,
boardTree?: BoardTree
board: Board,
showView: (id: string) => void
onClose: () => void,
}
export default class ViewMenu extends React.Component<Props> {
handleDeleteView = async (id: string) => {
const { board, boardTree, mutator, showView } = this.props
const { board, boardTree, showView } = this.props
Utils.log(`deleteView`)
const view = boardTree.activeView
const nextView = boardTree.views.find(o => o !== view)
@ -32,7 +30,7 @@ export default class ViewMenu extends React.Component<Props> {
}
handleAddViewBoard = async (id: string) => {
const { board, boardTree, mutator, showView } = this.props
const { board, boardTree, showView } = this.props
Utils.log(`addview-board`)
const view = new BoardView()
view.title = "Board View"
@ -49,7 +47,7 @@ export default class ViewMenu extends React.Component<Props> {
}
handleAddViewTable = async (id: string) => {
const { board, boardTree, mutator, showView } = this.props
const { board, boardTree, showView } = this.props
Utils.log(`addview-table`)
const view = new BoardView()
@ -68,9 +66,9 @@ export default class ViewMenu extends React.Component<Props> {
}
render() {
const { onClose, boardTree } = this.props
const { boardTree } = this.props
return (
<Menu onClose={onClose}>
<Menu>
{boardTree.views.map((view) => (<Menu.Text key={view.id} id={view.id} name={view.title} onClick={this.handleViewClick} />))}
<Menu.Separator />
{boardTree.views.length > 1 && <Menu.Text id="__deleteView" name="Delete View" onClick={this.handleDeleteView} />}

View file

@ -1,7 +1,6 @@
import React from "react"
import { BoardTree } from "../boardTree"
import { Card } from "../card"
import { Mutator } from "../mutator"
import { Utils } from "../utils"
import { WorkspaceTree } from "../workspaceTree"
import { BoardComponent } from "./boardComponent"
@ -9,7 +8,6 @@ import { Sidebar } from "./sidebar"
import { TableComponent } from "./tableComponent"
type Props = {
mutator: Mutator,
workspaceTree: WorkspaceTree
boardTree?: BoardTree
showBoard: (id: string) => void
@ -20,12 +18,12 @@ type Props = {
class WorkspaceComponent extends React.Component<Props> {
render() {
const { mutator, boardTree, workspaceTree, showBoard } = this.props
const { boardTree, workspaceTree, showBoard } = this.props
Utils.assert(workspaceTree)
const element =
<div className="octo-workspace">
<Sidebar mutator={mutator} showBoard={showBoard} workspaceTree={workspaceTree} boardTree={boardTree}></Sidebar>
<Sidebar showBoard={showBoard} workspaceTree={workspaceTree} boardTree={boardTree}></Sidebar>
{this.mainComponent()}
</div>
@ -33,7 +31,7 @@ class WorkspaceComponent extends React.Component<Props> {
}
private mainComponent() {
const { mutator, boardTree, showFilter, setSearchText, showView } = this.props
const { boardTree, showFilter, setSearchText, showView } = this.props
const { activeView } = boardTree || {}
if (!activeView) {
@ -42,11 +40,11 @@ class WorkspaceComponent extends React.Component<Props> {
switch (activeView?.viewType) {
case "board": {
return <BoardComponent mutator={mutator} boardTree={boardTree} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
return <BoardComponent boardTree={boardTree} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
}
case "table": {
return <TableComponent mutator={mutator} boardTree={boardTree} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
return <TableComponent boardTree={boardTree} showFilter={showFilter} setSearchText={setSearchText} showView={showView} />
}
default: {

View file

@ -4,9 +4,9 @@ import { BoardTree } from "./boardTree"
import { BoardView, ISortOption } from "./boardView"
import { Card } from "./card"
import { FilterGroup } from "./filterGroup"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
import { UndoManager } from "./undomanager"
import undoManager from "./undomanager"
import { Utils } from "./utils"
//
@ -14,37 +14,33 @@ import { Utils } from "./utils"
// It also ensures that the Undo-manager is called for each action
//
class Mutator {
constructor(private octo: OctoClient, private undoManager = UndoManager.shared) {
constructor() {
}
async insertBlock(block: IBlock, description: string = "add", afterRedo?: () => Promise<void>, beforeUndo?: () => Promise<void>) {
const { octo, undoManager } = this
await undoManager.perform(
async () => {
await octo.insertBlock(block)
await octoClient.insertBlock(block)
await afterRedo?.()
},
async () => {
await beforeUndo?.()
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
},
description
)
}
async insertBlocks(blocks: IBlock[], description: string = "add", afterRedo?: () => Promise<void>, beforeUndo?: () => Promise<void>) {
const { octo, undoManager } = this
await undoManager.perform(
async () => {
await octo.insertBlocks(blocks)
await octoClient.insertBlocks(blocks)
await afterRedo?.()
},
async () => {
await beforeUndo?.()
for (const block of blocks) {
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
}
},
description
@ -52,8 +48,6 @@ class Mutator {
}
async deleteBlock(block: IBlock, description?: string, beforeRedo?: () => Promise<void>, afterUndo?: () => Promise<void>) {
const { octo, undoManager } = this
if (!description) {
description = `delete ${block.type}`
}
@ -61,10 +55,10 @@ class Mutator {
await undoManager.perform(
async () => {
await beforeRedo?.()
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
},
async () => {
await octo.insertBlock(block)
await octoClient.insertBlock(block)
await afterUndo?.()
},
description
@ -72,51 +66,45 @@ class Mutator {
}
async changeTitle(block: IBlock, title: string, description: string = "change title") {
const { octo, undoManager } = this
const oldValue = block.title
await undoManager.perform(
async () => {
block.title = title
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
async () => {
block.title = oldValue
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
description
)
}
async changeIcon(block: Card | Board, icon: string, description: string = "change icon") {
const { octo, undoManager } = this
const oldValue = block.icon
await undoManager.perform(
async () => {
block.icon = icon
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
async () => {
block.icon = oldValue
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
description
)
}
async changeOrder(block: IBlock, order: number, description: string = "change order") {
const { octo, undoManager } = this
const oldValue = block.order
await undoManager.perform(
async () => {
block.order = order
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
async () => {
block.order = oldValue
await octo.updateBlock(block)
await octoClient.updateBlock(block)
},
description
)
@ -125,7 +113,6 @@ class Mutator {
// Property Templates
async insertPropertyTemplate(boardTree: BoardTree, index: number = -1, template?: IPropertyTemplate) {
const { octo, undoManager } = this
const { board, activeView } = boardTree
if (index < 0) { index = board.cardProperties.length }
@ -156,17 +143,16 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
description
)
}
async duplicatePropertyTemplate(boardTree: BoardTree, propertyId: string) {
const { octo, undoManager } = this
const { board, activeView } = boardTree
const oldBlocks: IBlock[] = [new Board(board)]
@ -194,10 +180,10 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
description
)
@ -206,8 +192,6 @@ class Mutator {
}
async changePropertyTemplateOrder(board: Board, template: IPropertyTemplate, destIndex: number) {
const { octo, undoManager } = this
const templates = board.cardProperties
const oldValue = templates
const newValue = templates.slice()
@ -219,18 +203,17 @@ class Mutator {
await undoManager.perform(
async () => {
board.cardProperties = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
board.cardProperties = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"reorder properties"
)
}
async deleteProperty(boardTree: BoardTree, propertyId: string) {
const { octo, undoManager } = this
const { board, views, cards } = boardTree
const oldBlocks: IBlock[] = [new Board(board)]
@ -255,18 +238,16 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
"delete property"
)
}
async renameProperty(board: Board, propertyId: string, name: string) {
const { octo, undoManager } = this
const oldBlocks: IBlock[] = [new Board(board)]
const changedBlocks: IBlock[] = [board]
@ -277,10 +258,10 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
"rename property"
)
@ -289,7 +270,6 @@ class Mutator {
// Properties
async insertPropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption, description: string = "add option") {
const { octo, undoManager } = this
const { board } = boardTree
Utils.assert(board.cardProperties.includes(template))
@ -301,19 +281,18 @@ class Mutator {
await undoManager.perform(
async () => {
template.options = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
// TODO: Also remove property on cards
template.options = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
description
)
}
async deletePropertyOption(boardTree: BoardTree, template: IPropertyTemplate, option: IPropertyOption) {
const { octo, undoManager } = this
const { board } = boardTree
const oldValue = template.options.slice()
@ -324,19 +303,17 @@ class Mutator {
await undoManager.perform(
async () => {
template.options = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
template.options = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"delete option"
)
}
async changePropertyOptionOrder(board: Board, template: IPropertyTemplate, option: IPropertyOption, destIndex: number) {
const { octo, undoManager } = this
const oldValue = template.options
const newValue = template.options.slice()
@ -347,18 +324,17 @@ class Mutator {
await undoManager.perform(
async () => {
template.options = newValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
template.options = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"reorder options"
)
}
async changePropertyOptionValue(boardTree: BoardTree, propertyTemplate: IPropertyTemplate, option: IPropertyOption, value: string) {
const { octo, undoManager } = this
const { board, cards } = boardTree
const oldValue = option.value
@ -379,10 +355,10 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.updateBlocks(changedBlocks)
await octoClient.updateBlocks(changedBlocks)
},
async () => {
await octo.updateBlocks(oldBlocks)
await octoClient.updateBlocks(oldBlocks)
},
"rename option"
)
@ -391,52 +367,46 @@ class Mutator {
}
async changePropertyOptionColor(board: Board, option: IPropertyOption, color: string) {
const { octo, undoManager } = this
const oldValue = option.color
undoManager.perform(
async () => {
option.color = color
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
option.color = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"change option color"
)
}
async changePropertyValue(card: Card, propertyId: string, value?: string, description: string = "change property") {
const { octo, undoManager } = this
const oldValue = card.properties[propertyId]
await undoManager.perform(
async () => {
card.properties[propertyId] = value
await octo.updateBlock(card)
await octoClient.updateBlock(card)
},
async () => {
card.properties[propertyId] = oldValue
await octo.updateBlock(card)
await octoClient.updateBlock(card)
},
description
)
}
async changePropertyType(board: Board, propertyTemplate: IPropertyTemplate, type: PropertyType) {
const { octo, undoManager } = this
const oldValue = propertyTemplate.type
await undoManager.perform(
async () => {
propertyTemplate.type = type
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
async () => {
propertyTemplate.type = oldValue
await octo.updateBlock(board)
await octoClient.updateBlock(board)
},
"change property type"
)
@ -445,72 +415,64 @@ class Mutator {
// Views
async changeViewSortOptions(view: BoardView, sortOptions: ISortOption[]) {
const { octo, undoManager } = this
const oldValue = view.sortOptions
await undoManager.perform(
async () => {
view.sortOptions = sortOptions
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.sortOptions = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"sort"
)
}
async changeViewFilter(view: BoardView, filter?: FilterGroup) {
const { octo, undoManager } = this
const oldValue = view.filter
await undoManager.perform(
async () => {
view.filter = filter
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.filter = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"filter"
)
}
async changeViewVisibleProperties(view: BoardView, visiblePropertyIds: string[]) {
const { octo, undoManager } = this
const oldValue = view.visiblePropertyIds
await undoManager.perform(
async () => {
view.visiblePropertyIds = visiblePropertyIds
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.visiblePropertyIds = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"hide / show property"
)
}
async changeViewGroupById(view: BoardView, groupById: string) {
const { octo, undoManager } = this
const oldValue = view.groupById
await undoManager.perform(
async () => {
view.groupById = groupById
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
async () => {
view.groupById = oldValue
await octo.updateBlock(view)
await octoClient.updateBlock(view)
},
"group by"
)
@ -518,18 +480,16 @@ class Mutator {
// Not a mutator, but convenient to put here since Mutator wraps OctoClient
async exportFullArchive() {
return this.octo.exportFullArchive()
return octoClient.exportFullArchive()
}
// Not a mutator, but convenient to put here since Mutator wraps OctoClient
async importFullArchive(blocks: IBlock[]) {
return this.octo.importFullArchive(blocks)
return octoClient.importFullArchive(blocks)
}
async createImageBlock(parentId: string, file: File, order = 1000): Promise<IBlock | undefined> {
const { octo, undoManager } = this
const url = await octo.uploadFile(file)
const url = await octoClient.uploadFile(file)
if (!url) {
return undefined
}
@ -539,16 +499,35 @@ class Mutator {
await undoManager.perform(
async () => {
await octo.insertBlock(block)
await octoClient.insertBlock(block)
},
async () => {
await octo.deleteBlock(block.id)
await octoClient.deleteBlock(block.id)
},
"group by"
)
return block
}
async undo() {
await undoManager.undo()
}
undoDescription(): string | undefined {
return undoManager.undoDescription
}
async redo() {
await undoManager.redo()
}
redoDescription(): string | undefined {
return undoManager.redoDescription
}
}
export { Mutator }
const mutator = new Mutator()
export default mutator
export { mutator }

View file

@ -152,4 +152,6 @@ class OctoClient {
}
}
export { OctoClient }
const client = new OctoClient()
export default client

View file

@ -5,7 +5,7 @@ import { ISortOption } from "./boardView"
import { Card } from "./card"
import { Editable } from "./components/editable"
import { Menu } from "./menu"
import { Mutator } from "./mutator"
import mutator from "./mutator"
import { IBlock } from "./octoTypes"
import { Utils } from "./utils"
@ -27,14 +27,14 @@ class OctoUtils {
}
static propertyValueReadonlyElement(card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
return this.propertyValueElement(undefined, card, propertyTemplate, emptyDisplayValue)
return this.propertyValueElement(true, card, propertyTemplate, emptyDisplayValue)
}
static propertyValueEditableElement(mutator: Mutator, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue?: string): JSX.Element {
return this.propertyValueElement(mutator, card, propertyTemplate, emptyDisplayValue)
static propertyValueEditableElement(card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue?: string): JSX.Element {
return this.propertyValueElement(false, card, propertyTemplate, emptyDisplayValue)
}
private static propertyValueElement(mutator: Mutator | undefined, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
private static propertyValueElement(readOnly: boolean, card: Card, propertyTemplate: IPropertyTemplate, emptyDisplayValue: string = "Empty"): JSX.Element {
const propertyValue = card.properties[propertyTemplate.id]
const displayValue = OctoUtils.propertyDisplayValue(card, propertyValue, propertyTemplate)
const finalDisplayValue = displayValue || emptyDisplayValue
@ -69,18 +69,18 @@ class OctoUtils {
key={propertyTemplate.id}
className={`${className} ${propertyColorCssClassName}`}
tabIndex={0}
onClick={mutator ? (e) => { showMenu(e.target as HTMLElement) } : undefined}
onKeyDown={mutator ? (e) => {
onClick={!readOnly ? (e) => { showMenu(e.target as HTMLElement) } : undefined}
onKeyDown={!readOnly ? (e) => {
if (e.keyCode === 13) {
showMenu(e.target as HTMLElement)
}
} : undefined}
onFocus={mutator ? () => { Menu.shared.hide() } : undefined}
onFocus={!readOnly ? () => { Menu.shared.hide() } : undefined}
>
{finalDisplayValue}
</div>
} else if (propertyTemplate.type === "text" || propertyTemplate.type === "number") {
if (mutator) {
if (!readOnly) {
element = <Editable
key={propertyTemplate.id}
className="octo-propertyvalue"
@ -121,7 +121,7 @@ class OctoUtils {
return (block.order + nextBlock.order) / 2
}
static showSortMenu(e: React.MouseEvent, mutator: Mutator, boardTree: BoardTree) {
static showSortMenu(e: React.MouseEvent, boardTree: BoardTree) {
const { activeView } = boardTree
const { sortOptions } = activeView
const sortOption = sortOptions.length > 0 ? sortOptions[0] : undefined

View file

@ -8,10 +8,9 @@ import { CardDialog } from "../components/cardDialog"
import { FilterComponent } from "../components/filterComponent"
import { WorkspaceComponent } from "../components/workspaceComponent"
import { FlashMessage } from "../flashMessage"
import { Mutator } from "../mutator"
import { OctoClient } from "../octoClient"
import mutator from "../mutator"
import octoClient from "../octoClient"
import { OctoListener } from "../octoListener"
import { UndoManager } from "../undomanager"
import { Utils } from "../utils"
import { WorkspaceTree } from "../workspaceTree"
@ -33,7 +32,6 @@ export default class BoardPage extends React.Component<Props, State> {
updateTitleTimeout: number
updatePropertyLabelTimeout: number
private octo = new OctoClient()
private boardListener = new OctoListener()
private cardListener = new OctoListener()
@ -46,7 +44,7 @@ export default class BoardPage extends React.Component<Props, State> {
this.state = {
boardId,
viewId,
workspaceTree: new WorkspaceTree(this.octo),
workspaceTree: new WorkspaceTree(),
}
Utils.log(`BoardPage. boardId: ${boardId}`)
@ -73,8 +71,8 @@ export default class BoardPage extends React.Component<Props, State> {
if (e.keyCode === 90 && !e.shiftKey && (e.ctrlKey || e.metaKey) && !e.altKey) { // Cmd+Z
Utils.log(`Undo`)
const description = UndoManager.shared.undoDescription
await UndoManager.shared.undo()
const description = mutator.undoDescription()
await mutator.undo()
if (description) {
FlashMessage.show(`Undo ${description}`)
} else {
@ -82,8 +80,8 @@ export default class BoardPage extends React.Component<Props, State> {
}
} else if (e.keyCode === 90 && e.shiftKey && (e.ctrlKey || e.metaKey) && !e.altKey) { // Shift+Cmd+Z
Utils.log(`Redo`)
const description = UndoManager.shared.redoDescription
await UndoManager.shared.redo()
const description = mutator.redoDescription()
await mutator.redo()
if (description) {
FlashMessage.show(`Redo ${description}`)
} else {
@ -108,7 +106,6 @@ export default class BoardPage extends React.Component<Props, State> {
render() {
const { workspaceTree, shownCardTree } = this.state
const { board, activeView } = this.state.boardTree || {}
const mutator = new Mutator(this.octo)
if (this.state.filterAnchorElement) {
const element = this.state.filterAnchorElement
@ -121,7 +118,6 @@ export default class BoardPage extends React.Component<Props, State> {
ReactDOM.render(
<FilterComponent
mutator={mutator}
boardTree={this.state.boardTree}
pageX={pageX}
pageY={pageY}
@ -141,7 +137,6 @@ export default class BoardPage extends React.Component<Props, State> {
return (
<div className='BoardPage'>
<WorkspaceComponent
mutator={mutator}
workspaceTree={workspaceTree}
boardTree={this.state.boardTree}
showView={(id) => { this.showView(id) }}
@ -170,7 +165,7 @@ export default class BoardPage extends React.Component<Props, State> {
await workspaceTree.sync()
if (boardId) {
const boardTree = new BoardTree(this.octo, boardId)
const boardTree = new BoardTree(boardId)
await boardTree.sync()
// Default to first view

View file

@ -2,10 +2,9 @@ import React from 'react'
import { Archiver } from "../archiver"
import { Board } from "../board"
import Button from '../components/button'
import { Mutator } from "../mutator"
import { OctoClient } from "../octoClient"
import mutator from '../mutator'
import octoClient from "../octoClient"
import { IBlock } from "../octoTypes"
import { UndoManager } from "../undomanager"
import { Utils } from "../utils"
type Props = {}
@ -27,29 +26,23 @@ export default class HomePage extends React.Component<Props, State> {
}
loadBoards = async () => {
const octo = new OctoClient()
const boards = await octo.getBlocks(null, "board")
const boards = await octoClient.getBlocks(null, "board")
this.setState({ boards })
}
importClicked = async () => {
const octo = new OctoClient()
const mutator = new Mutator(octo, UndoManager.shared)
Archiver.importFullArchive(mutator, () => {
Archiver.importFullArchive(() => {
this.loadBoards()
})
}
exportClicked = async () => {
const octo = new OctoClient()
const mutator = new Mutator(octo, UndoManager.shared)
Archiver.exportFullArchive(mutator)
Archiver.exportFullArchive()
}
addClicked = async () => {
const octo = new OctoClient()
const board = new Board()
await octo.insertBlock(board)
await octoClient.insertBlock(board)
}
render(): React.ReactNode {

View file

@ -9,8 +9,6 @@ interface UndoCommand {
// General-purpose undo manager
//
class UndoManager {
static shared = new UndoManager()
onStateDidChange?: () => void
private commands: UndoCommand[] = []
@ -164,4 +162,5 @@ class UndoManager {
}
}
export { UndoManager }
const undoManager = new UndoManager()
export default undoManager

View file

@ -40,7 +40,7 @@ class SubMenuOption extends React.Component<SubMenuOptionProps, SubMenuState> {
<div className='name menu-name'>{this.props.name}</div>
<div className="imageSubmenuTriangle" style={{float: 'right'}}></div>
{this.state.isOpen &&
<Menu onClose={this.close}>
<Menu>
{this.props.children}
</Menu>
}
@ -54,13 +54,17 @@ type ColorOptionProps = MenuOptionProps & {
}
class ColorOption extends React.Component<ColorOptionProps> {
handleOnClick = () => {
this.props.onClick(this.props.id)
}
render() {
const {name, icon} = this.props;
const {id, name, icon} = this.props;
return (
<div className='MenuOption ColorOption menu-option'>
<div className='MenuOption ColorOption menu-option' onClick={this.handleOnClick}>
<div className='name'>{name}</div>
{icon && <div className={'icon ' + icon}></div>}
<div className='menu-colorbox'></div>
<div className={`menu-colorbox ${id}`}></div>
</div>
)
}
@ -113,7 +117,6 @@ class TextOption extends React.Component<TextOptionProps> {
type MenuProps = {
children: React.ReactNode
onClose: () => void
}
export default class Menu extends React.Component<MenuProps> {
@ -123,30 +126,6 @@ export default class Menu extends React.Component<MenuProps> {
static Separator = SeparatorOption
static Text = TextOption
onBodyClick = (e: MouseEvent) => {
this.props.onClose()
}
onBodyKeyDown = (e: KeyboardEvent) => {
// Ignore keydown events on other elements
if (e.target !== document.body) { return }
if (e.keyCode === 27) {
// ESC
this.props.onClose()
e.stopPropagation()
}
}
componentDidMount() {
document.addEventListener("click", this.onBodyClick)
document.addEventListener("keydown", this.onBodyKeyDown)
}
componentWillUnmount() {
document.removeEventListener("click", this.onBodyClick)
document.removeEventListener("keydown", this.onBodyKeyDown)
}
render() {
return (
<div className="Menu menu noselect">

View file

@ -0,0 +1,94 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
type Props = {
children?: React.ReactNode;
onToggle?: (open: boolean) => void;
isDisabled?: boolean;
stopPropagationOnToggle?: boolean;
}
type State = {
open: boolean;
}
export default class MenuWrapper extends React.PureComponent<Props, State> {
private node: React.RefObject<HTMLDivElement>;
public constructor(props: Props) {
super(props);
if (!Array.isArray(props.children) || props.children.length !== 2) {
throw new Error('MenuWrapper needs exactly 2 children');
}
this.state = {
open: false,
};
this.node = React.createRef();
}
public componentDidMount() {
document.addEventListener('click', this.closeOnBlur, true);
document.addEventListener('keyup', this.keyboardClose, true);
}
public componentWillUnmount() {
document.removeEventListener('click', this.closeOnBlur, true);
document.removeEventListener('keyup', this.keyboardClose, true);
}
private keyboardClose = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
this.close();
}
if (e.key === 'Tab') {
this.closeOnBlur(e);
}
}
private closeOnBlur = (e: Event) => {
if (this.node && this.node.current && e.target && this.node.current.contains(e.target as Node)) {
return;
}
this.close();
}
public close = () => {
if (this.state.open) {
this.setState({open: false});
}
}
toggle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
/**
* This is only here so that we can toggle the menus in the sidebar, because the default behavior of the mobile
* version (ie the one that uses a modal) needs propagation to close the modal after selecting something
* We need to refactor this so that the modal is explicitly closed on toggle, but for now I am aiming to preserve the existing logic
* so as to not break other things
**/
if (this.props.stopPropagationOnToggle) {
e.preventDefault();
e.stopPropagation();
}
const newState = !this.state.open;
this.setState({open: newState});
}
public render() {
const {children} = this.props;
return (
<div
className={'MenuWrapper'}
onClick={this.toggle}
ref={this.node}
>
{children ? Object.values(children)[0] : null}
{children && this.state.open ? Object.values(children)[1] : null}
</div>
);
}
}

View file

@ -1,16 +1,12 @@
import { Board } from "./board"
import { OctoClient } from "./octoClient"
import octoClient from "./octoClient"
import { IBlock } from "./octoTypes"
class WorkspaceTree {
boards: Board[] = []
constructor(
private octo: OctoClient) {
}
async sync() {
const blocks = await this.octo.getBlocks(undefined, "board")
const blocks = await octoClient.getBlocks(undefined, "board")
this.rebuild(blocks)
}

View file

@ -349,7 +349,7 @@ hr {
touch-action: none;
}
.menu-option .menu-name {
.menu-option .name {
flex-grow: 1;
}
@ -454,24 +454,24 @@ hr {
align-items: center;
}
.octo-frame > .octo-icontitle > .octo-icon {
.octo-frame .octo-icontitle .octo-icon {
font-size: 36px;
line-height: 36px;
margin-right: 15px;
}
.octo-board-card > .octo-icontitle {
.octo-board-card .octo-icontitle {
flex: 1 1 auto;
font-weight: 500;
}
.octo-board-card > .octo-icontitle > .octo-icon {
.octo-board-card .octo-icontitle .octo-icon {
font-size: 16px;
line-height: 16px;
margin-right: 5px;
}
.octo-table-cell > .octo-icontitle > .octo-icon {
.octo-table-cell .octo-icontitle .octo-icon {
min-width: 20px;
}