From 53b8eb3a104c991a246db32c66a19e702594c901 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Wed, 10 Jul 2019 14:43:24 +0200 Subject: basic integration of todos as static sidebar --- src/styles/layout.scss | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/styles') diff --git a/src/styles/layout.scss b/src/styles/layout.scss index e858b7904..c9cc94e15 100644 --- a/src/styles/layout.scss +++ b/src/styles/layout.scss @@ -33,10 +33,11 @@ html { overflow: hidden; } } .app { - display: flex; - flex-direction: column; + //display: flex; - .app__content { display: flex; } + .app__content { + display: flex; + } .app__service { position: relative; -- cgit v1.2.3-70-g09d2 From d7ed456a7b6f73e046ba3a2ef38eb21f82f06ca4 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 30 Jul 2019 11:41:54 +0200 Subject: Make todo layer resizable --- packages/theme/src/themes/default/index.ts | 7 ++ src/actions/index.js | 2 + src/components/layout/AppLayout.js | 16 +--- src/containers/layout/AppLayoutContainer.js | 4 - src/features/todos/actions.js | 10 ++ src/features/todos/components/TodosWebview.js | 129 +++++++++++++++++++++++--- src/features/todos/containers/TodosScreen.js | 45 +++++++++ src/features/todos/index.js | 33 +++++++ src/features/todos/store.js | 86 +++++++++++++++++ src/stores/FeaturesStore.js | 2 + src/styles/layout.scss | 7 +- 11 files changed, 311 insertions(+), 30 deletions(-) create mode 100644 src/features/todos/actions.js create mode 100644 src/features/todos/containers/TodosScreen.js create mode 100644 src/features/todos/index.js create mode 100644 src/features/todos/store.js (limited to 'src/styles') diff --git a/packages/theme/src/themes/default/index.ts b/packages/theme/src/themes/default/index.ts index 0f02fa3c8..4a49a4de0 100644 --- a/packages/theme/src/themes/default/index.ts +++ b/packages/theme/src/themes/default/index.ts @@ -207,3 +207,10 @@ export const announcements = { background: legacyStyles.themeGrayLightest, }, }; + +// Todos +export const todos = { + dragIndicator: { + background: legacyStyles.themeGrayLight, + }, +}; diff --git a/src/actions/index.js b/src/actions/index.js index fc525afeb..336344d76 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -13,6 +13,7 @@ import settings from './settings'; import requests from './requests'; import announcements from '../features/announcements/actions'; import workspaces from '../features/workspaces/actions'; +import todos from '../features/todos/actions'; const actions = Object.assign({}, { service, @@ -31,4 +32,5 @@ export default Object.assign( defineActions(actions, PropTypes.checkPropTypes), { announcements }, { workspaces }, + { todos }, ); diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 7f2f707fb..dbf7d3c21 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js @@ -17,7 +17,7 @@ import { isWindows } from '../../environment'; import WorkspaceSwitchingIndicator from '../../features/workspaces/components/WorkspaceSwitchingIndicator'; import { workspaceStore } from '../../features/workspaces'; import AppUpdateInfoBar from '../AppUpdateInfoBar'; -import TodosWebview from '../../features/todos/components/TodosWebview'; +import Todos from '../../features/todos/containers/TodosScreen'; function createMarkup(HTMLString) { return { __html: HTMLString }; @@ -52,7 +52,6 @@ const styles = theme => ({ @injectSheet(styles) @observer class AppLayout extends Component { static propTypes = { - authToken: PropTypes.string.isRequired, classes: PropTypes.object.isRequired, isFullScreen: PropTypes.bool.isRequired, sidebar: PropTypes.element.isRequired, @@ -60,7 +59,6 @@ class AppLayout extends Component { services: PropTypes.element.isRequired, children: PropTypes.element, news: MobxPropTypes.arrayOrObservableArray.isRequired, - // isOnline: PropTypes.bool.isRequired, showServicesUpdatedInfoBar: PropTypes.bool.isRequired, appUpdateIsDownloaded: PropTypes.bool.isRequired, nextAppReleaseVersion: PropTypes.string, @@ -85,7 +83,6 @@ class AppLayout extends Component { render() { const { - authToken, classes, isFullScreen, workspacesDrawer, @@ -129,15 +126,6 @@ class AppLayout extends Component { ))} - {/* {!isOnline && ( - - - {intl.formatMessage(globalMessages.notConnectedToTheInternet)} - - )} */} {!areRequiredRequestsSuccessful && showRequiredRequestsError && ( - + diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 8a48f4924..cf3da71e8 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js @@ -12,7 +12,6 @@ import NewsStore from '../../stores/NewsStore'; import SettingsStore from '../../stores/SettingsStore'; import RequestStore from '../../stores/RequestStore'; import GlobalErrorStore from '../../stores/GlobalErrorStore'; -import UserStore from '../../stores/UserStore'; import { oneOrManyChildElements } from '../../prop-types'; import AppLayout from '../../components/layout/AppLayout'; @@ -40,7 +39,6 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e settings, globalError, requests, - user, } = this.props.stores; const { @@ -133,7 +131,6 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e return ( ({ root: { background: theme.colorBackground, - height: '100%', - width: 300, - position: 'absolute', - top: 0, - right: -300, + position: 'relative', }, webview: { height: '100%', }, + resizeHandler: { + position: 'absolute', + left: 0, + marginLeft: -5, + width: 10, + zIndex: 400, + cursor: 'col-resize', + }, + dragIndicator: { + position: 'absolute', + left: 0, + width: 5, + zIndex: 400, + background: theme.todos.dragIndicator.background, + }, }); @injectSheet(styles) @observer @@ -24,17 +35,113 @@ class TodosWebview extends Component { static propTypes = { classes: PropTypes.object.isRequired, authToken: PropTypes.string.isRequired, + resize: PropTypes.func.isRequired, + width: PropTypes.number.isRequired, + minWidth: PropTypes.number.isRequired, }; + state = { + isDragging: false, + width: 300, + } + + componentWillMount() { + const { width } = this.props; + + this.setState({ + width, + }); + } + + componentDidMount() { + this.node.addEventListener('mousemove', this.resizePanel.bind(this)); + this.node.addEventListener('mouseup', this.stopResize.bind(this)); + this.node.addEventListener('mouseleave', this.stopResize.bind(this)); + } + + startResize = (event) => { + this.setState({ + isDragging: true, + initialPos: event.clientX, + delta: 0, + }); + } + + resizePanel(e) { + const { minWidth } = this.props; + + const { + isDragging, + initialPos, + } = this.state; + + if (isDragging && Math.abs(e.clientX - window.innerWidth) > minWidth) { + const delta = e.clientX - initialPos; + + this.setState({ + delta, + }); + } + } + + stopResize() { + const { + resize, + minWidth, + } = this.props; + + const { + isDragging, + delta, + width, + } = this.state; + + if (isDragging) { + let newWidth = width + (delta < 0 ? Math.abs(delta) : -Math.abs(delta)); + + if (newWidth < minWidth) { + newWidth = minWidth; + } + + this.setState({ + isDragging: false, + delta: 0, + width: newWidth, + }); + + resize(newWidth); + } + } + render() { const { authToken, classes } = this.props; + const { width, delta, isDragging } = this.state; + return ( -
- -
+ <> +
this.stopResize()} + ref={(node) => { this.node = node; }} + > +
this.startResize(e)} + /> + {isDragging && ( +
+ )} + +
+ ); } } diff --git a/src/features/todos/containers/TodosScreen.js b/src/features/todos/containers/TodosScreen.js new file mode 100644 index 000000000..0759c22db --- /dev/null +++ b/src/features/todos/containers/TodosScreen.js @@ -0,0 +1,45 @@ +import React, { Component } from 'react'; +import { inject, observer } from 'mobx-react'; +import PropTypes from 'prop-types'; + +import TodosWebview from '../components/TodosWebview'; +import ErrorBoundary from '../../../components/util/ErrorBoundary'; +import UserStore from '../../../stores/UserStore'; +import TodoStore from '../store'; +import { TODOS_MIN_WIDTH } from '..'; + +@inject('stores', 'actions') @observer +class TodosScreen extends Component { + static propTypes = { + stores: PropTypes.shape({ + user: PropTypes.instanceOf(UserStore).isRequired, + todos: PropTypes.instanceOf(TodoStore).isRequired, + }).isRequired, + actions: PropTypes.shape({ + todos: PropTypes.shape({ + resize: PropTypes.func.isRequired, + }), + }).isRequired, + }; + + render() { + const { stores, actions } = this.props; + + if (!stores.todos || !stores.todos.isFeatureActive) { + return null; + } + + return ( + + actions.todos.resize({ width })} + /> + + ); + } +} + +export default TodosScreen; diff --git a/src/features/todos/index.js b/src/features/todos/index.js new file mode 100644 index 000000000..0dfd35c78 --- /dev/null +++ b/src/features/todos/index.js @@ -0,0 +1,33 @@ +import { reaction } from 'mobx'; +import TodoStore from './store'; + +const debug = require('debug')('Franz:feature:todos'); + +export const GA_CATEGORY_TODOS = 'Todos'; + +export const DEFAULT_TODOS_WIDTH = 300; +export const TODOS_MIN_WIDTH = 200; + +export const todoStore = new TodoStore(); + +export default function initTodos(stores, actions) { + stores.todos = todoStore; + const { features } = stores; + + // Toggle todos feature + reaction( + () => features.features.isTodosEnabled, + (isEnabled) => { + if (isEnabled) { + debug('Initializing `todos` feature'); + todoStore.start(stores, actions); + } else if (todoStore.isFeatureActive) { + debug('Disabling `todos` feature'); + todoStore.stop(); + } + }, + { + fireImmediately: true, + }, + ); +} diff --git a/src/features/todos/store.js b/src/features/todos/store.js new file mode 100644 index 000000000..e7e13b37f --- /dev/null +++ b/src/features/todos/store.js @@ -0,0 +1,86 @@ +import { + computed, + action, + observable, +} from 'mobx'; +import localStorage from 'mobx-localstorage'; + +import { todoActions } from './actions'; +import { FeatureStore } from '../utils/FeatureStore'; +import { createReactions } from '../../stores/lib/Reaction'; +import { createActionBindings } from '../utils/ActionBinding'; +import { DEFAULT_TODOS_WIDTH, TODOS_MIN_WIDTH } from '.'; + +const debug = require('debug')('Franz:feature:todos:store'); + +export default class TodoStore extends FeatureStore { + @observable isFeatureEnabled = false; + + @observable isFeatureActive = false; + + @computed get width() { + const width = this.settings.width || DEFAULT_TODOS_WIDTH; + + return width < TODOS_MIN_WIDTH ? TODOS_MIN_WIDTH : width; + } + + @computed get settings() { + return localStorage.getItem('todos') || {}; + } + + // ========== PUBLIC API ========= // + + @action start(stores, actions) { + debug('TodoStore::start'); + this.stores = stores; + this.actions = actions; + + // ACTIONS + + this._registerActions(createActionBindings([ + [todoActions.resize, this._resize], + ])); + + // REACTIONS + + this._allReactions = createReactions([ + this._setFeatureEnabledReaction, + ]); + + this._registerReactions(this._allReactions); + + this.isFeatureActive = true; + } + + @action stop() { + super.stop(); + debug('TodoStore::stop'); + this.reset(); + this.isFeatureActive = false; + } + + // ========== PRIVATE METHODS ========= // + + _updateSettings = (changes) => { + localStorage.setItem('todos', { + ...this.settings, + ...changes, + }); + }; + + // Actions + + @action _resize = ({ width }) => { + this._updateSettings({ + width, + }); + }; + + // Reactions + + _setFeatureEnabledReaction = () => { + const { isTodosEnabled } = this.stores.features.features; + + this.isFeatureEnabled = isTodosEnabled; + }; +} diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js index e7832088b..35a050c67 100644 --- a/src/stores/FeaturesStore.js +++ b/src/stores/FeaturesStore.js @@ -16,6 +16,7 @@ import workspaces from '../features/workspaces'; import shareFranz from '../features/shareFranz'; import announcements from '../features/announcements'; import settingsWS from '../features/settingsWS'; +import todos from '../features/todos'; import { DEFAULT_FEATURES_CONFIG } from '../config'; @@ -75,5 +76,6 @@ export default class FeaturesStore extends Store { shareFranz(this.stores, this.actions); announcements(this.stores, this.actions); settingsWS(this.stores, this.actions); + todos(this.stores, this.actions); } } diff --git a/src/styles/layout.scss b/src/styles/layout.scss index 739082445..10027da60 100644 --- a/src/styles/layout.scss +++ b/src/styles/layout.scss @@ -37,10 +37,15 @@ html { overflow: hidden; } .app__content { display: flex; + width: calc(100% + 300px); + } + + .app__main-content { + display: flex; + width: 100%; } .app__service { - // position: relative; display: flex; flex: 1; flex-direction: column; -- cgit v1.2.3-70-g09d2