diff --git a/webapp/src/components/viewHeader/viewHeader.scss b/webapp/src/components/viewHeader/viewHeader.scss index c4a85e891..65e5081e2 100644 --- a/webapp/src/components/viewHeader/viewHeader.scss +++ b/webapp/src/components/viewHeader/viewHeader.scss @@ -24,6 +24,12 @@ .IconButton { background: none; + padding: 0; + .Icon { + width: 24px; + height: 24px; + margin: 0; + } &:hover { background: rgba(var(--body-color), 0.1); } diff --git a/webapp/src/components/viewHeader/viewHeader.tsx b/webapp/src/components/viewHeader/viewHeader.tsx index 60308350b..1598e6c57 100644 --- a/webapp/src/components/viewHeader/viewHeader.tsx +++ b/webapp/src/components/viewHeader/viewHeader.tsx @@ -10,25 +10,22 @@ import ViewMenu from '../../components/viewMenu' import {Constants} from '../../constants' import {CsvExporter} from '../../csvExporter' import mutator from '../../mutator' -import {UserContext} from '../../user' import {BoardTree} from '../../viewModel/boardTree' import Button from '../../widgets/buttons/button' import IconButton from '../../widgets/buttons/iconButton' import DropdownIcon from '../../widgets/icons/dropdown' -import OptionsIcon from '../../widgets/icons/options' -import Menu from '../../widgets/menu' import MenuWrapper from '../../widgets/menuWrapper' import Editable from '../editable' import FilterComponent from '../filterComponent' import ModalWrapper from '../modalWrapper' -import ShareBoardComponent from '../shareBoardComponent' import {sendFlashMessage} from '../flashMessages' import NewCardButton from './newCardButton' import ViewHeaderPropertiesMenu from './viewHeaderPropertiesMenu' import ViewHeaderGroupByMenu from './viewHeaderGroupByMenu' import ViewHeaderSortMenu from './viewHeaderSortMenu' +import ViewHeaderActionsMenu from './viewHeaderActionsMenu' import './viewHeader.scss' @@ -48,7 +45,6 @@ type Props = { type State = { isSearching: boolean showFilter: boolean - showShareDialog: boolean } class ViewHeader extends React.Component { @@ -60,7 +56,7 @@ class ViewHeader extends React.Component { constructor(props: Props) { super(props) - this.state = {isSearching: Boolean(this.props.boardTree.getSearchText()), showFilter: false, showShareDialog: false} + this.state = {isSearching: Boolean(this.props.boardTree.getSearchText()), showFilter: false} } componentDidUpdate(prevPros: Props, prevState: State): void { @@ -191,65 +187,9 @@ class ViewHeader extends React.Component { {!this.props.readonly && <> - - - }/> - - this.onExportCsvTrigger(boardTree)} - /> - {/* Archiver.exportBoardTree(boardTree)} - /> */} - - {(user) => (user && user.id !== 'single-user' && - - )} - - - {/* - - - - this.testAddCards(100)} - /> - this.testAddCards(1000)} - /> - this.testDistributeCards()} - /> - this.testRandomizeIcons()} - /> - - */} - - - {this.state.showShareDialog && - - } - + {/* New card button */} @@ -274,14 +214,6 @@ class ViewHeader extends React.Component { this.setState({showFilter: false}) } - private showShareDialog = () => { - this.setState({showShareDialog: true}) - } - - private hideShareDialog = () => { - this.setState({showShareDialog: false}) - } - private onSearchKeyDown = (e: React.KeyboardEvent) => { if (e.keyCode === 27) { // ESC: Clear search if (this.searchFieldRef.current) { @@ -296,71 +228,6 @@ class ViewHeader extends React.Component { private searchChanged(text?: string) { this.props.setSearchText(text) } - - private async testAddCards(count: number) { - const {boardTree} = this.props - const {board, activeView} = boardTree - - const startCount = boardTree.cards.length - let optionIndex = 0 - - mutator.performAsUndoGroup(async () => { - for (let i = 0; i < count; i++) { - const card = new MutableCard() - card.parentId = boardTree.board.id - card.rootId = boardTree.board.rootId - card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) - card.title = `Test Card ${startCount + i + 1}` - card.icon = BlockIcons.shared.randomIcon() - - if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { - // Cycle through options - const option = boardTree.groupByProperty.options[optionIndex] - optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length - card.properties[boardTree.groupByProperty.id] = option.id - } - mutator.insertBlock(card, 'test add card') - } - }) - } - - private async testDistributeCards() { - const {boardTree} = this.props - mutator.performAsUndoGroup(async () => { - let optionIndex = 0 - for (const card of boardTree.cards) { - if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { - // Cycle through options - const option = boardTree.groupByProperty.options[optionIndex] - optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length - const newCard = new MutableCard(card) - if (newCard.properties[boardTree.groupByProperty.id] !== option.id) { - newCard.properties[boardTree.groupByProperty.id] = option.id - mutator.updateBlock(newCard, card, 'test distribute cards') - } - } - } - }) - } - - private async testRandomizeIcons() { - const {boardTree} = this.props - - mutator.performAsUndoGroup(async () => { - for (const card of boardTree.cards) { - mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') - } - }) - } - - private sortDisplayOptions() { - const {boardTree} = this.props - - const options = boardTree.board.cardProperties.map((o) => ({id: o.id, name: o.name})) - options.unshift({id: Constants.titleColumnId, name: 'Name'}) - - return options - } } export default injectIntl(ViewHeader) diff --git a/webapp/src/components/viewHeader/viewHeaderActionsMenu.tsx b/webapp/src/components/viewHeader/viewHeaderActionsMenu.tsx new file mode 100644 index 000000000..4a4da49db --- /dev/null +++ b/webapp/src/components/viewHeader/viewHeaderActionsMenu.tsx @@ -0,0 +1,164 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React, {useState} from 'react' +import {injectIntl, IntlShape} from 'react-intl' + +import {BlockIcons} from '../../blockIcons' +import {MutableCard} from '../../blocks/card' +import {CardFilter} from '../../cardFilter' +import {CsvExporter} from '../../csvExporter' +import mutator from '../../mutator' +import {UserContext} from '../../user' +import {BoardTree} from '../../viewModel/boardTree' +import IconButton from '../../widgets/buttons/iconButton' +import OptionsIcon from '../../widgets/icons/options' +import Menu from '../../widgets/menu' +import MenuWrapper from '../../widgets/menuWrapper' + +import ModalWrapper from '../modalWrapper' +import ShareBoardComponent from '../shareBoardComponent' +import {sendFlashMessage} from '../flashMessages' + +type Props = { + boardTree: BoardTree + intl: IntlShape +} + +// async function testAddCards(boardTree: BoardTree, count: number) { +// const {board, activeView} = boardTree + +// const startCount = boardTree.cards.length +// let optionIndex = 0 + +// mutator.performAsUndoGroup(async () => { +// for (let i = 0; i < count; i++) { +// const card = new MutableCard() +// card.parentId = boardTree.board.id +// card.rootId = boardTree.board.rootId +// card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) +// card.title = `Test Card ${startCount + i + 1}` +// card.icon = BlockIcons.shared.randomIcon() + +// if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { +// // Cycle through options +// const option = boardTree.groupByProperty.options[optionIndex] +// optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length +// card.properties[boardTree.groupByProperty.id] = option.id +// } +// mutator.insertBlock(card, 'test add card') +// } +// }) +// } + +// async function testDistributeCards(boardTree: BoardTree) { +// mutator.performAsUndoGroup(async () => { +// let optionIndex = 0 +// for (const card of boardTree.cards) { +// if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { +// // Cycle through options +// const option = boardTree.groupByProperty.options[optionIndex] +// optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length +// const newCard = new MutableCard(card) +// if (newCard.properties[boardTree.groupByProperty.id] !== option.id) { +// newCard.properties[boardTree.groupByProperty.id] = option.id +// mutator.updateBlock(newCard, card, 'test distribute cards') +// } +// } +// } +// }) +// } + +// async function testRandomizeIcons(boardTree: BoardTree) { +// mutator.performAsUndoGroup(async () => { +// for (const card of boardTree.cards) { +// mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') +// } +// }) +// } + +function onExportCsvTrigger(boardTree: BoardTree, intl: IntlShape) { + try { + CsvExporter.exportTableCsv(boardTree) + const exportCompleteMessage = intl.formatMessage({ + id: 'ViewHeader.export-complete', + defaultMessage: 'Export complete!', + }) + sendFlashMessage({content: exportCompleteMessage, severity: 'normal'}) + } catch (e) { + const exportFailedMessage = intl.formatMessage({ + id: 'ViewHeader.export-failed', + defaultMessage: 'Export failed!', + }) + sendFlashMessage({content: exportFailedMessage, severity: 'high'}) + } +} + +const ViewHeaderActionsMenu = React.memo((props: Props) => { + const [showShareDialog, setShowShareDialog] = useState(false) + + const {boardTree, intl} = props + + return ( + + + }/> + + onExportCsvTrigger(boardTree, intl)} + /> + {/* Archiver.exportBoardTree(boardTree)} + /> */} + + {(user) => (user && user.id !== 'single-user' && + setShowShareDialog(true)} + /> + )} + + + {/* + + + + testAddCards(100)} + /> + testAddCards(1000)} + /> + testDistributeCards()} + /> + testRandomizeIcons()} + /> + + */} + + + {showShareDialog && + setShowShareDialog(false)} + /> + } + + ) +}) + +export default injectIntl(ViewHeaderActionsMenu)