From 962c035eab0b6812669d97b15bdda02eb36f5f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 15 Oct 2020 01:18:23 +0200 Subject: [PATCH 1/6] Add MenuWrapper component --- src/client/components/boardComponent.tsx | 33 ++++----- src/client/components/tableComponent.tsx | 30 ++++---- src/client/components/viewMenu.tsx | 5 +- src/client/widgets/menu.tsx | 27 +------ src/client/widgets/menuWrapper.tsx | 94 ++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 61 deletions(-) create mode 100644 src/client/widgets/menuWrapper.tsx diff --git a/src/client/components/boardComponent.tsx b/src/client/components/boardComponent.tsx index 510c37d54..3ba79613f 100644 --- a/src/client/components/boardComponent.tsx +++ b/src/client/components/boardComponent.tsx @@ -6,6 +6,7 @@ import { IPropertyOption } from "../board" import { BoardTree } from "../boardTree" import { CardFilter } from "../cardFilter" import ViewMenu from "../components/viewMenu" +import MenuWrapper from "../widgets/menuWrapper" import { Constants } from "../constants" import { Menu as OldMenu } from "../menu" import { Mutator } from "../mutator" @@ -29,7 +30,6 @@ type Props = { type State = { isHoverOnCover: boolean isSearching: boolean - viewMenu: boolean } class BoardComponent extends React.Component { @@ -39,7 +39,7 @@ class BoardComponent extends React.Component { constructor(props: Props) { super(props) - this.state = { isHoverOnCover: false, isSearching: !!this.props.boardTree?.getSearchText(), viewMenu: false } + this.state = { isHoverOnCover: false, isSearching: !!this.props.boardTree?.getSearchText()} } componentDidUpdate(prevPros: Props, prevState: State) { @@ -93,21 +93,20 @@ class BoardComponent extends React.Component {
{ mutator.changeTitle(activeView, text) }} /> -
this.setState({ viewMenu: true })} - > - {this.state.viewMenu && - this.setState({ viewMenu: false })} - mutator={mutator} - boardTree={boardTree} - showView={showView} - />} -
-
+ +
+
+
+ +
{ this.propertiesClicked(e) }}>Properties
{ this.groupByClicked(e) }}> diff --git a/src/client/components/tableComponent.tsx b/src/client/components/tableComponent.tsx index a55508ae5..a2d00991a 100644 --- a/src/client/components/tableComponent.tsx +++ b/src/client/components/tableComponent.tsx @@ -6,6 +6,7 @@ import { IPropertyTemplate } from "../board" import { BoardTree } from "../boardTree" import { CsvExporter } from "../csvExporter" import ViewMenu from "../components/viewMenu" +import MenuWrapper from "../widgets/menuWrapper" import { Menu as OldMenu } from "../menu" import { Mutator } from "../mutator" import { IBlock } from "../octoTypes" @@ -90,21 +91,20 @@ class TableComponent extends React.Component {
{ mutator.changeTitle(activeView, text) }} /> -
this.setState({ viewMenu: true })} - > - {this.state.viewMenu && - this.setState({ viewMenu: false })} - mutator={mutator} - boardTree={boardTree} - showView={showView} - />} -
-
+ +
+
+
+ +
{ this.propertiesClicked(e) }}>Properties
{ this.filterClicked(e) }}>Filter
diff --git a/src/client/components/viewMenu.tsx b/src/client/components/viewMenu.tsx index e5b48c34f..08c1af2ee 100644 --- a/src/client/components/viewMenu.tsx +++ b/src/client/components/viewMenu.tsx @@ -11,7 +11,6 @@ type Props = { boardTree?: BoardTree board: Board, showView: (id: string) => void - onClose: () => void, } export default class ViewMenu extends React.Component { @@ -68,9 +67,9 @@ export default class ViewMenu extends React.Component { } render() { - const { onClose, boardTree } = this.props + const { boardTree } = this.props return ( - + {boardTree.views.map((view) => ())} {boardTree.views.length > 1 && } diff --git a/src/client/widgets/menu.tsx b/src/client/widgets/menu.tsx index 78de42a8c..8c12c12f0 100644 --- a/src/client/widgets/menu.tsx +++ b/src/client/widgets/menu.tsx @@ -40,7 +40,7 @@ class SubMenuOption extends React.Component {
{this.props.name}
{this.state.isOpen && - + {this.props.children} } @@ -113,7 +113,6 @@ class TextOption extends React.Component { type MenuProps = { children: React.ReactNode - onClose: () => void } export default class Menu extends React.Component { @@ -123,30 +122,6 @@ export default class Menu extends React.Component { 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 (
diff --git a/src/client/widgets/menuWrapper.tsx b/src/client/widgets/menuWrapper.tsx new file mode 100644 index 000000000..cbe9167c9 --- /dev/null +++ b/src/client/widgets/menuWrapper.tsx @@ -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 { + private node: React.RefObject; + + 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) => { + /** + * 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 ( +
+ {children && !this.state.open ? Object.values(children)[0] : null} + {children && this.state.open ? Object.values(children)[1] : null} +
+ ); + } +} From fb9c237ba056da89e45ac4210e6aec1fc09c66a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 15 Oct 2020 01:49:31 +0200 Subject: [PATCH 2/6] Migrate a couple of menus using MenuWrapper --- src/client/components/boardComponent.tsx | 95 ++++++++---------------- src/client/components/tableComponent.tsx | 59 ++++++--------- src/client/widgets/menu.tsx | 10 ++- src/client/widgets/menuWrapper.tsx | 2 +- 4 files changed, 62 insertions(+), 104 deletions(-) diff --git a/src/client/components/boardComponent.tsx b/src/client/components/boardComponent.tsx index 3ba79613f..1240527a0 100644 --- a/src/client/components/boardComponent.tsx +++ b/src/client/components/boardComponent.tsx @@ -7,6 +7,7 @@ import { BoardTree } from "../boardTree" import { CardFilter } from "../cardFilter" import ViewMenu from "../components/viewMenu" import MenuWrapper from "../widgets/menuWrapper" +import Menu from "../widgets/menu" import { Constants } from "../constants" import { Menu as OldMenu } from "../menu" import { Mutator } from "../mutator" @@ -85,7 +86,13 @@ class BoardComponent extends React.Component {
{board.icon ? -
{ this.iconClicked(e) }}>{board.icon}
+ +
{board.icon}
+ + mutator.changeIcon(board, undefined, "remove icon")}/> + mutator.changeIcon(board, BlockIcons.shared.randomIcon())}/> + +
: undefined} { mutator.changeTitle(board, text) }} />
@@ -93,20 +100,20 @@ class BoardComponent extends React.Component {
{ mutator.changeTitle(activeView, text) }} /> - -
-
-
- -
+ +
+
+
+ +
{ this.propertiesClicked(e) }}>Properties
{ this.groupByClicked(e) }}> @@ -162,7 +169,16 @@ class BoardComponent extends React.Component { onChanged={(text) => { this.propertyNameChanged(group.option, text) }} /> + + + + mutator.deletePropertyOption(boardTree, boardTree.groupByProperty, group.option)}/> + + {Constants.menuColors.map((color) => + mutator.changePropertyOptionColor(boardTree.board, group.option, color.id)} /> + )} + +
)} @@ -216,28 +232,6 @@ class BoardComponent extends React.Component { ) } - 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 showCard(card?: IBlock) { console.log(`showCard: ${card?.title}`) @@ -262,31 +256,6 @@ class BoardComponent extends React.Component { await mutator.changePropertyOptionValue(boardTree, boardTree.groupByProperty, option, text) } - async valueOptionClicked(e: React.MouseEvent, 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) } diff --git a/src/client/components/tableComponent.tsx b/src/client/components/tableComponent.tsx index a2d00991a..62de747c4 100644 --- a/src/client/components/tableComponent.tsx +++ b/src/client/components/tableComponent.tsx @@ -7,6 +7,7 @@ import { BoardTree } from "../boardTree" import { CsvExporter } from "../csvExporter" import ViewMenu from "../components/viewMenu" import MenuWrapper from "../widgets/menuWrapper" +import Menu from "../widgets/menu" import { Menu as OldMenu } from "../menu" import { Mutator } from "../mutator" import { IBlock } from "../octoTypes" @@ -83,7 +84,13 @@ class TableComponent extends React.Component {
{board.icon ? -
{ this.iconClicked(e) }}>{board.icon}
+ +
{board.icon}
+ + mutator.changeIcon(board, undefined, "remove icon")}/> + mutator.changeIcon(board, BlockIcons.shared.randomIcon())}/> + +
: undefined} { mutator.changeTitle(board, text) }} />
@@ -91,20 +98,20 @@ class TableComponent extends React.Component {
{ mutator.changeTitle(activeView, text) }} /> - -
-
-
- -
+ +
+
+
+ +
{ this.propertiesClicked(e) }}>Properties
{ this.filterClicked(e) }}>Filter
@@ -213,28 +220,6 @@ class TableComponent extends React.Component { ) } - 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 { activeView } = boardTree diff --git a/src/client/widgets/menu.tsx b/src/client/widgets/menu.tsx index 8c12c12f0..7588ee4f0 100644 --- a/src/client/widgets/menu.tsx +++ b/src/client/widgets/menu.tsx @@ -54,13 +54,17 @@ type ColorOptionProps = MenuOptionProps & { } class ColorOption extends React.Component { + handleOnClick = () => { + this.props.onClick(this.props.id) + } + render() { - const {name, icon} = this.props; + const {id, name, icon} = this.props; return ( -
+
{name}
{icon &&
} -
+
) } diff --git a/src/client/widgets/menuWrapper.tsx b/src/client/widgets/menuWrapper.tsx index cbe9167c9..42bee48b0 100644 --- a/src/client/widgets/menuWrapper.tsx +++ b/src/client/widgets/menuWrapper.tsx @@ -86,7 +86,7 @@ export default class MenuWrapper extends React.PureComponent { onClick={this.toggle} ref={this.node} > - {children && !this.state.open ? Object.values(children)[0] : null} + {children ? Object.values(children)[0] : null} {children && this.state.open ? Object.values(children)[1] : null}
); From f4d3440376931359016204c1723b2c8b707917f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 15 Oct 2020 16:57:43 +0200 Subject: [PATCH 3/6] Making client and mutator singleton instances --- src/client/archiver.ts | 6 +- src/client/boardTree.ts | 8 +- src/client/cardTree.ts | 8 +- src/client/components/boardCard.tsx | 5 +- src/client/components/boardComponent.tsx | 27 ++- src/client/components/cardDialog.tsx | 13 +- src/client/components/filterComponent.tsx | 13 +- src/client/components/sidebar.tsx | 13 +- src/client/components/tableComponent.tsx | 19 +-- src/client/components/tableRow.tsx | 7 +- src/client/components/viewMenu.tsx | 9 +- src/client/components/workspaceComponent.tsx | 12 +- src/client/mutator.ts | 165 ++++++++----------- src/client/octoClient.ts | 4 +- src/client/octoUtils.tsx | 20 +-- src/client/pages/boardPage.tsx | 25 ++- src/client/pages/homePage.tsx | 19 +-- src/client/undomanager.ts | 5 +- src/client/workspaceTree.ts | 8 +- 19 files changed, 164 insertions(+), 222 deletions(-) diff --git a/src/client/archiver.ts b/src/client/archiver.ts index 6185de606..c2db067de 100644 --- a/src/client/archiver.ts +++ b/src/client/archiver.ts @@ -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" diff --git a/src/client/boardTree.ts b/src/client/boardTree.ts index 285bb2440..47fedc1bd 100644 --- a/src/client/boardTree.ts +++ b/src/client/boardTree.ts @@ -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) } diff --git a/src/client/cardTree.ts b/src/client/cardTree.ts index 819d522b3..92d8b0f12 100644 --- a/src/client/cardTree.ts +++ b/src/client/cardTree.ts @@ -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) } diff --git a/src/client/components/boardCard.tsx b/src/client/components/boardCard.tsx index 39ca5fb96..4a0177d31 100644 --- a/src/client/components/boardCard.tsx +++ b/src/client/components/boardCard.tsx @@ -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) => void @@ -58,7 +57,7 @@ class BoardCard extends React.Component { } private showOptionsMenu(e: React.MouseEvent) { - const { mutator, card } = this.props + const { card } = this.props e.stopPropagation() diff --git a/src/client/components/boardComponent.tsx b/src/client/components/boardComponent.tsx index 1266f893e..67fd39c26 100644 --- a/src/client/components/boardComponent.tsx +++ b/src/client/components/boardComponent.tsx @@ -9,7 +9,7 @@ import ViewMenu from "../components/viewMenu" 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" @@ -18,7 +18,6 @@ import Button from "./button" import { Editable } from "./editable" type Props = { - mutator: Mutator, boardTree?: BoardTree showView: (id: string) => void showCard: (card: Card) => void @@ -27,7 +26,6 @@ type Props = { } type State = { - isHoverOnCover: boolean isSearching: boolean viewMenu: boolean } @@ -49,7 +47,7 @@ class BoardComponent extends React.Component { } render() { - const { mutator, boardTree, showView } = this.props + const { boardTree, showView } = this.props if (!boardTree || !boardTree.board) { return ( @@ -102,7 +100,6 @@ class BoardComponent extends React.Component { this.setState({ viewMenu: false })} - mutator={mutator} boardTree={boardTree} showView={showView} />} @@ -182,7 +179,6 @@ class BoardComponent extends React.Component { { this.onDropToColumn(undefined) }}> {boardTree.emptyGroupCards.map(card => { { this.onDropToColumn(group.option) }} key={group.option.value}> {group.cards.map(card => { } private iconClicked(e: React.MouseEvent) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { board } = boardTree OldMenu.shared.options = [ @@ -246,7 +241,7 @@ class BoardComponent extends React.Component { } async addCard(groupByValue?: string) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { activeView, board } = boardTree const card = new Card() @@ -259,13 +254,13 @@ class BoardComponent extends React.Component { } 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, option: IPropertyOption) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props OldMenu.shared.options = [ { id: "delete", name: "Delete" }, @@ -322,7 +317,7 @@ class BoardComponent extends React.Component { } private async testAddCards(count: number) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { board, activeView } = boardTree const startCount = boardTree?.cards?.length @@ -345,7 +340,7 @@ class BoardComponent extends React.Component { } private async propertiesClicked(e: React.MouseEvent) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { activeView } = boardTree const selectProperties = boardTree.board.cardProperties @@ -371,7 +366,7 @@ class BoardComponent extends React.Component { } 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 } }) @@ -386,7 +381,7 @@ class BoardComponent extends React.Component { async addGroupClicked() { console.log(`onAddGroupClicked`) - const { mutator, boardTree } = this.props + const { boardTree } = this.props const option: IPropertyOption = { value: "New group", @@ -398,7 +393,7 @@ class BoardComponent extends React.Component { } async onDropToColumn(option: IPropertyOption) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { draggedCard, draggedHeaderOption } = this const propertyValue = option ? option.value : undefined diff --git a/src/client/components/cardDialog.tsx b/src/client/components/cardDialog.tsx index e664c55c9..81d42415e 100644 --- a/src/client/components/cardDialog.tsx +++ b/src/client/components/cardDialog.tsx @@ -4,7 +4,7 @@ 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" @@ -16,7 +16,6 @@ import { MarkdownEditor } from "./markdownEditor" type Props = { boardTree: BoardTree cardTree: CardTree - mutator: Mutator onClose: () => void } @@ -51,7 +50,7 @@ class CardDialog extends React.Component { } render() { - const { boardTree, cardTree, mutator } = this.props + const { boardTree, cardTree } = this.props const { board } = boardTree const { card, comments } = cardTree @@ -203,7 +202,7 @@ class CardDialog extends React.Component { } menu.showAtElement(e.target as HTMLElement) }}>{propertyTemplate.name}
- {OctoUtils.propertyValueEditableElement(mutator, card, propertyTemplate)} + {OctoUtils.propertyValueEditableElement(card, propertyTemplate)}
) })} @@ -332,7 +331,7 @@ class CardDialog extends React.Component { } async sendComment(text: string) { - const { mutator, cardTree } = this.props + const { cardTree } = this.props const { card } = cardTree Utils.assertValue(card) @@ -342,7 +341,7 @@ class CardDialog extends React.Component { } private showContentBlockMenu(e: React.MouseEvent, block: IBlock) { - const { mutator, cardTree } = this.props + const { cardTree } = this.props const { card } = cardTree const index = cardTree.contents.indexOf(block) @@ -409,7 +408,7 @@ class CardDialog extends React.Component { } private iconClicked(e: React.MouseEvent) { - const { mutator, cardTree } = this.props + const { cardTree } = this.props const { card } = cardTree Menu.shared.options = [ diff --git a/src/client/components/filterComponent.tsx b/src/client/components/filterComponent.tsx index 9047b736e..0642b5392 100644 --- a/src/client/components/filterComponent.tsx +++ b/src/client/components/filterComponent.tsx @@ -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 { } 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 { } 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 { } 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 { } 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 { } 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[] || [] diff --git a/src/client/components/sidebar.tsx b/src/client/components/sidebar.tsx index adace5a11..d7b989ea5 100644 --- a/src/client/components/sidebar.tsx +++ b/src/client/components/sidebar.tsx @@ -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 { } 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 { } 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 { 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 { } async addBoardClicked() { - const { mutator, boardTree, showBoard } = this.props + const { boardTree, showBoard } = this.props const oldBoardId = boardTree?.board?.id const board = new Board() diff --git a/src/client/components/tableComponent.tsx b/src/client/components/tableComponent.tsx index e3e6a3581..d4da8767e 100644 --- a/src/client/components/tableComponent.tsx +++ b/src/client/components/tableComponent.tsx @@ -8,7 +8,7 @@ import { Card } from "../card" import ViewMenu from "../components/viewMenu" 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" @@ -16,7 +16,6 @@ import { Editable } from "./editable" import { TableRow } from "./tableRow" type Props = { - mutator: Mutator, boardTree?: BoardTree showView: (id: string) => void showCard: (card: Card) => void @@ -48,7 +47,7 @@ class TableComponent extends React.Component { } render() { - const { mutator, boardTree, showView } = this.props + const { boardTree, showView } = this.props if (!boardTree || !boardTree.board) { return ( @@ -99,7 +98,6 @@ class TableComponent extends React.Component { this.setState({ viewMenu: false })} - mutator={mutator} boardTree={boardTree} showView={showView} />} @@ -108,7 +106,7 @@ class TableComponent extends React.Component {
{ this.propertiesClicked(e) }}>Properties
{ this.filterClicked(e) }}>Filter
-
{ OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort
+
{ OctoUtils.showSortMenu(e, boardTree) }}>Sort
{this.state.isSearching ? { const tableRow = { } private iconClicked(e: React.MouseEvent) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { board } = boardTree OldMenu.shared.options = [ @@ -236,7 +233,7 @@ class TableComponent extends React.Component { } private async propertiesClicked(e: React.MouseEvent) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { activeView } = boardTree const selectProperties = boardTree.board.cardProperties @@ -289,7 +286,7 @@ class TableComponent extends React.Component { } private async headerClicked(e: React.MouseEvent, templateId: string) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { board } = boardTree const { activeView } = boardTree @@ -376,7 +373,7 @@ class TableComponent extends React.Component { } async addCard(show: boolean = false) { - const { mutator, boardTree } = this.props + const { boardTree } = this.props const card = new Card() card.parentId = boardTree.board.id @@ -398,7 +395,7 @@ class TableComponent extends React.Component { const { draggedHeaderTemplate } = this if (!draggedHeaderTemplate) { return } - const { mutator, boardTree } = this.props + const { boardTree } = this.props const { board } = boardTree Utils.assertValue(mutator) diff --git a/src/client/components/tableRow.tsx b/src/client/components/tableRow.tsx index 3625fef47..67d055a73 100644 --- a/src/client/components/tableRow.tsx +++ b/src/client/components/tableRow.tsx @@ -1,12 +1,11 @@ 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" type Props = { - mutator: Mutator boardTree: BoardTree card: Card focusOnMount: boolean @@ -27,7 +26,7 @@ class TableRow extends React.Component { } render() { - const { mutator, boardTree, card, showCard, onKeyDown } = this.props + const { boardTree, card, showCard, onKeyDown } = this.props const { board, activeView } = boardTree const openButonRef = React.createRef() @@ -57,7 +56,7 @@ class TableRow extends React.Component { .filter(template => activeView.visiblePropertyIds.includes(template.id)) .map(template => { return
- {OctoUtils.propertyValueEditableElement(mutator, card, template)} + {OctoUtils.propertyValueEditableElement(card, template)}
})}
diff --git a/src/client/components/viewMenu.tsx b/src/client/components/viewMenu.tsx index e5b48c34f..f78ac6449 100644 --- a/src/client/components/viewMenu.tsx +++ b/src/client/components/viewMenu.tsx @@ -2,12 +2,11 @@ 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 @@ -16,7 +15,7 @@ type Props = { export default class ViewMenu extends React.Component { 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 +31,7 @@ export default class ViewMenu extends React.Component { } 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 +48,7 @@ export default class ViewMenu extends React.Component { } 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() diff --git a/src/client/components/workspaceComponent.tsx b/src/client/components/workspaceComponent.tsx index e7c37a30f..dd7042939 100644 --- a/src/client/components/workspaceComponent.tsx +++ b/src/client/components/workspaceComponent.tsx @@ -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 @@ -21,12 +19,12 @@ type Props = { class WorkspaceComponent extends React.Component { render() { - const { mutator, boardTree, workspaceTree, showBoard } = this.props + const { boardTree, workspaceTree, showBoard } = this.props Utils.assert(workspaceTree) const element =
- + {this.mainComponent()}
@@ -34,7 +32,7 @@ class WorkspaceComponent extends React.Component { } private mainComponent() { - const { mutator, boardTree, showCard, showFilter, setSearchText, showView } = this.props + const { boardTree, showCard, showFilter, setSearchText, showView } = this.props const { activeView } = boardTree || {} if (!activeView) { @@ -43,11 +41,11 @@ class WorkspaceComponent extends React.Component { switch (activeView?.viewType) { case "board": { - return + return } case "table": { - return + return } default: { diff --git a/src/client/mutator.ts b/src/client/mutator.ts index 918134935..a52ce423c 100644 --- a/src/client/mutator.ts +++ b/src/client/mutator.ts @@ -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, beforeUndo?: () => Promise) { - 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, beforeUndo?: () => Promise) { - 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, afterUndo?: () => Promise) { - 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 { - 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 } diff --git a/src/client/octoClient.ts b/src/client/octoClient.ts index 1c804bce9..0801446b2 100644 --- a/src/client/octoClient.ts +++ b/src/client/octoClient.ts @@ -152,4 +152,6 @@ class OctoClient { } } -export { OctoClient } +const client = new OctoClient() + +export default client diff --git a/src/client/octoUtils.tsx b/src/client/octoUtils.tsx index 5072c4665..b97f47bab 100644 --- a/src/client/octoUtils.tsx +++ b/src/client/octoUtils.tsx @@ -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}
} else if (propertyTemplate.type === "text" || propertyTemplate.type === "number") { - if (mutator) { + if (!readOnly) { element = 0 ? sortOptions[0] : undefined diff --git a/src/client/pages/boardPage.tsx b/src/client/pages/boardPage.tsx index cd35e991b..ff02e350e 100644 --- a/src/client/pages/boardPage.tsx +++ b/src/client/pages/boardPage.tsx @@ -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 { 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 { 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 { 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 { } } 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,12 +106,11 @@ export default class BoardPage extends React.Component { render() { const { workspaceTree, shownCardTree } = this.state const { board, activeView } = this.state.boardTree || {} - const mutator = new Mutator(this.octo) // TODO Move all this into the root portal component when that is merged if (this.state.boardTree && this.state.boardTree.board && shownCardTree) { ReactDOM.render( - { this.showCard(undefined) }}>, + { this.showCard(undefined) }}>, Utils.getElementById("overlay") ) } else { @@ -137,7 +134,6 @@ export default class BoardPage extends React.Component { ReactDOM.render( { return (
{ this.showView(id) }} @@ -187,7 +182,7 @@ export default class BoardPage extends React.Component { await workspaceTree.sync() if (boardId) { - const boardTree = new BoardTree(this.octo, boardId) + const boardTree = new BoardTree(boardId) await boardTree.sync() // Default to first view @@ -214,7 +209,7 @@ export default class BoardPage extends React.Component { this.cardListener.close() if (card) { - const cardTree = new CardTree(this.octo, card.id) + const cardTree = new CardTree(card.id) await cardTree.sync() this.setState({...this.state, shownCardTree: cardTree}) diff --git a/src/client/pages/homePage.tsx b/src/client/pages/homePage.tsx index 05fed1d02..b1177d822 100644 --- a/src/client/pages/homePage.tsx +++ b/src/client/pages/homePage.tsx @@ -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 { } 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 { diff --git a/src/client/undomanager.ts b/src/client/undomanager.ts index db750b38d..070939022 100644 --- a/src/client/undomanager.ts +++ b/src/client/undomanager.ts @@ -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 diff --git a/src/client/workspaceTree.ts b/src/client/workspaceTree.ts index c86fdb6d0..cc7d221c0 100644 --- a/src/client/workspaceTree.ts +++ b/src/client/workspaceTree.ts @@ -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) } From 18cb0e8a39c9f91bd89f1ddac638accaef1c57c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 15 Oct 2020 17:03:41 +0200 Subject: [PATCH 4/6] Fixing small compilation problems --- src/client/components/boardComponent.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/components/boardComponent.tsx b/src/client/components/boardComponent.tsx index 67fd39c26..8eafa52de 100644 --- a/src/client/components/boardComponent.tsx +++ b/src/client/components/boardComponent.tsx @@ -28,6 +28,7 @@ type Props = { type State = { isSearching: boolean viewMenu: boolean + isHoverOnCover: boolean } class BoardComponent extends React.Component { @@ -111,7 +112,7 @@ class BoardComponent extends React.Component { Group by {boardTree.groupByProperty?.name}
{ this.filterClicked(e) }}>Filter
-
{ OctoUtils.showSortMenu(e, mutator, boardTree) }}>Sort
+
{ OctoUtils.showSortMenu(e, boardTree) }}>Sort
{this.state.isSearching ? Date: Thu, 15 Oct 2020 08:13:36 -0700 Subject: [PATCH 5/6] Update CSS to support MenuWrapper --- src/static/main.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/static/main.css b/src/static/main.css index 893d8b8ec..a00df0499 100644 --- a/src/static/main.css +++ b/src/static/main.css @@ -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; } From a43ea026162dfa4b542d8f8cc7f1805c707f6c86 Mon Sep 17 00:00:00 2001 From: Chen-I Lim Date: Thu, 15 Oct 2020 08:53:38 -0700 Subject: [PATCH 6/6] Fix menu css --- src/static/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/static/main.css b/src/static/main.css index a00df0499..09054a7f8 100644 --- a/src/static/main.css +++ b/src/static/main.css @@ -349,7 +349,7 @@ hr { touch-action: none; } -.menu-option .menu-name { +.menu-option .name { flex-grow: 1; }