diff --git a/webapp/src/components/modal.scss b/webapp/src/components/modal.scss new file mode 100644 index 000000000..3084820a5 --- /dev/null +++ b/webapp/src/components/modal.scss @@ -0,0 +1,33 @@ +.Modal { + position: absolute; + top: 25px; + left: -200px; + z-index: 10; + + min-width: 430px; + box-shadow: rgba(var(--main-fg), 0.1) 0px 0px 0px 1px, rgba(var(--main-fg), 0.1) 0px 2px 4px; + background-color: rgb(var(--main-bg)); + padding: 10px; + + @media screen and (max-width: 430px) { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-width: 0; + } + + .hideOnWidescreen { + /* Hide controls (e.g. close button) on larger screens */ + @media not screen and (max-width: 430px) { + display: none !important; + } + } + > .toolbar { + display: flex; + flex-direction: row; + height: 30px; + margin-bottom: 10px; + } +} diff --git a/webapp/src/components/modal.tsx b/webapp/src/components/modal.tsx new file mode 100644 index 000000000..dc9f5ea71 --- /dev/null +++ b/webapp/src/components/modal.tsx @@ -0,0 +1,62 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React from 'react' +import {injectIntl, IntlShape} from 'react-intl' + +import IconButton from '../widgets/buttons/iconButton' +import CloseIcon from '../widgets/icons/close' +import './modal.scss' + +type Props = { + onClose: () => void + intl: IntlShape +} + +class Modal extends React.PureComponent { + private node: React.RefObject + + public constructor(props: Props) { + super(props) + this.node = React.createRef() + } + + public componentDidMount(): void { + document.addEventListener('click', this.closeOnBlur, true) + } + + public componentWillUnmount(): void { + document.removeEventListener('click', this.closeOnBlur, true) + } + + private closeOnBlur = (e: Event) => { + if (this.node && this.node.current && e.target && this.node.current.contains(e.target as Node)) { + return + } + + this.props.onClose() + } + + render(): JSX.Element { + return ( +
+
+ } + title={'Close'} + /> +
+ {this.props.children} +
+ ) + } + + private closeClicked = () => { + this.props.onClose() + } +} + +export default injectIntl(Modal) diff --git a/webapp/src/components/modalWrapper.scss b/webapp/src/components/modalWrapper.scss new file mode 100644 index 000000000..007506085 --- /dev/null +++ b/webapp/src/components/modalWrapper.scss @@ -0,0 +1,4 @@ +.ModalWrapper { + position: relative; + overflow: unset; +} diff --git a/webapp/src/components/modalWrapper.tsx b/webapp/src/components/modalWrapper.tsx new file mode 100644 index 000000000..8f0034fb0 --- /dev/null +++ b/webapp/src/components/modalWrapper.tsx @@ -0,0 +1,19 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React from 'react' +import './modalWrapper.scss' + +type Props = { +} + +class ModalWrapper extends React.PureComponent { + render(): JSX.Element { + return ( +
+ {this.props.children} +
+ ) + } +} + +export default ModalWrapper diff --git a/webapp/src/components/shareBoardComponent.tsx b/webapp/src/components/shareBoardComponent.tsx new file mode 100644 index 000000000..fb3355b67 --- /dev/null +++ b/webapp/src/components/shareBoardComponent.tsx @@ -0,0 +1,25 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +import React from 'react' +import {injectIntl, IntlShape} from 'react-intl' + +import Modal from './modal' + +type Props = { + onClose: () => void + intl: IntlShape +} + +class ShareBoardComponent extends React.PureComponent { + render(): JSX.Element { + return ( + + {'TODO'} + + ) + } +} + +export default injectIntl(ShareBoardComponent) diff --git a/webapp/src/components/viewHeader.tsx b/webapp/src/components/viewHeader.tsx index c7311abd0..9aa0d1fea 100644 --- a/webapp/src/components/viewHeader.tsx +++ b/webapp/src/components/viewHeader.tsx @@ -26,7 +26,9 @@ import MenuWrapper from '../widgets/menuWrapper' import {Editable} from './editable' import FilterComponent from './filterComponent' +import ModalWrapper from './modalWrapper' import NewCardButton from './newCardButton' +import ShareBoardComponent from './shareBoardComponent' import './viewHeader.scss' type Props = { @@ -45,6 +47,7 @@ type Props = { type State = { isSearching: boolean showFilter: boolean + showShareDialog: boolean } class ViewHeader extends React.Component { @@ -56,7 +59,7 @@ class ViewHeader extends React.Component { constructor(props: Props) { super(props) - this.state = {isSearching: Boolean(this.props.boardTree.getSearchText()), showFilter: false} + this.state = {isSearching: Boolean(this.props.boardTree.getSearchText()), showFilter: false, showShareDialog: false} } componentDidUpdate(prevPros: Props, prevState: State): void { @@ -287,21 +290,27 @@ class ViewHeader extends React.Component { {!this.props.readonly && <> - - }/> - - CsvExporter.exportTableCsv(boardTree)} - /> - Archiver.exportBoardTree(boardTree)} - /> + + + }/> + + CsvExporter.exportTableCsv(boardTree)} + /> + Archiver.exportBoardTree(boardTree)} + /> + - {/* + {/* @@ -327,8 +336,14 @@ class ViewHeader extends React.Component { /> */} - - + + + {this.state.showShareDialog && + + } + {/* New card button */} @@ -353,6 +368,14 @@ 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) {