From 47c1c99d893517efc679ab29d675cc0bf44be8be Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Thu, 11 Apr 2019 16:54:01 +0200 Subject: feat(App): Added Workspaces for all your daily routines 🥳 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * merge default and fetched feature configs * ignore intellij project files * basic setup for workspaces feature * define workspaces as premium feature * add workspaces menu item in settings dialog * basic setup of workspaces settings screen * fix eslint error * assign react key prop to workspace items * add styles for workspace table * setup logic to display workspace edit page * consolidate workspace feature for further development * prepare basic workspace edit form * add on enter key handler for form input component * add form for creating workspaces * small fixes * adds flow for deleting workspaces * stop tracking google analytics in components * pin gulp-sass-variables version to 1.1.1 * fix merge conflict * fix bug in form input library * improve workspace form setup * finish basic workspace settings * finish workspaces mvp * fix eslint issues * remove dev logs * detach service when underlying webview unmounts * disable no-param-reassign eslint rule * add workspace drawer * change workspace switch shortcuts to start with zero * add workspace drawer toggle menu item and shortcut * improve workspace switching ux * style add workspace icon in drawer like the sidebar icons * improve workspace drawer layout * add i18n messages for service loading and workspace switching * small fixes * add tooltip to add workspace button in drawer * add workspaces count badge in settings navigation * fix merge conflicts with latest develop * refactor state management for workspace feature * reset api requests when workspace feature is stopped * hide workspace feature if it is disabled * handle get workspaces request errors in the ui * show infobox when updating workspaces * indicate any server interaction with spinners and infoboxes * add analytic events for workspace actions * improve styling of workspace switch indicator * add workspace premium notice to dashboard * add workspace feature info in drawer for free users * add workspace premium badge in settings nav * fix premium workspace badge in settings menu for light theme * fix active workspaces settings premium badge in light theme * give upgrade account button a bit more padding * add open last used workspace logic * use mobx-localstorage directly in the store * fix wrong workspace tooltip shortcut in sidebar * fix bug in workspace feature initialization * show workspaces intro in drawer when user has none yet * fix issues for users that have workspace but downgraded to free * border radius for premium intro in workspace settings * close workspace drawer after clicking on a workspace * add hover effect for drawer workspace items * ensure drawer is open on workspace settings routes * add small text label for adding new workspace to drawer * make workspace settings list items taller * refactor workspace table css away from legacy styles * render workspace service list like services + toggle * change plus icon in workspace drawer to settings icon * autofocus create workspace input field * add css transition to drawer workspace item hover * fix drawer add workspace label styles * refactors workspace theme vars into object structure * improve contrast of workspace switching indicator * added generic pro badge component for settings nav * add premium badge to workspace drawer headline * add context menu for workspace drawer items * handle deleted services that are attached to workspaces --- packages/forms/src/input/index.tsx | 12 ++++- packages/forms/src/input/styles.ts | 5 ++ packages/forms/src/label/styles.ts | 4 +- packages/forms/src/select/index.tsx | 6 +++ packages/forms/src/toggle/index.tsx | 4 +- packages/theme/src/themes/dark/index.ts | 67 +++++++++++++++++++++++++ packages/theme/src/themes/default/index.ts | 78 ++++++++++++++++++++++++++++++ packages/ui/src/badge/ProBadge.tsx | 64 ++++++++++++++++++++++++ packages/ui/src/index.ts | 1 + packages/ui/src/infobox/index.tsx | 7 +++ packages/ui/src/loader/index.tsx | 4 +- 11 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 packages/ui/src/badge/ProBadge.tsx (limited to 'packages') diff --git a/packages/forms/src/input/index.tsx b/packages/forms/src/input/index.tsx index 5178904d3..a2d7c62d5 100644 --- a/packages/forms/src/input/index.tsx +++ b/packages/forms/src/input/index.tsx @@ -25,6 +25,7 @@ interface IProps extends React.InputHTMLAttributes, IFormField showPasswordToggle?: boolean; data: IData; inputClassName?: string; + onEnterKey?: Function; } interface IState { @@ -33,7 +34,7 @@ interface IState { } class InputComponent extends Component { - public static defaultProps = { + static defaultProps = { focus: false, onChange: () => {}, onBlur: () => {}, @@ -81,6 +82,13 @@ class InputComponent extends Component { } } + onInputKeyPress(e: React.KeyboardEvent) { + if (e.key === "Enter") { + const { onEnterKey } = this.props; + onEnterKey && onEnterKey(); + } + } + render() { const { classes, @@ -124,6 +132,7 @@ class InputComponent extends Component { title={label} showLabel={showLabel} htmlFor={id} + className={classes.label} isRequired={required} >
{ onFocus={onFocus} onBlur={onBlur} disabled={disabled} + onKeyPress={this.onInputKeyPress.bind(this)} min={min} max={max} step={step} diff --git a/packages/forms/src/input/styles.ts b/packages/forms/src/input/styles.ts index c038295cd..e2ab30a4f 100644 --- a/packages/forms/src/input/styles.ts +++ b/packages/forms/src/input/styles.ts @@ -10,6 +10,11 @@ const prefixStyles = (theme: Theme) => ({ }); export default (theme: Theme) => ({ + label: { + '& > div': { + marginTop: 5, + } + }, disabled: { opacity: theme.inputDisabledOpacity, }, diff --git a/packages/forms/src/label/styles.ts b/packages/forms/src/label/styles.ts index f3998de04..c64c9b285 100644 --- a/packages/forms/src/label/styles.ts +++ b/packages/forms/src/label/styles.ts @@ -1,9 +1,7 @@ import { Theme } from '../../../theme/lib'; export default (theme: Theme) => ({ - content: { - marginTop: 5, - }, + content: {}, label: { color: theme.labelColor, fontSize: theme.uiFontSize, diff --git a/packages/forms/src/select/index.tsx b/packages/forms/src/select/index.tsx index f419d0351..0e5ded176 100644 --- a/packages/forms/src/select/index.tsx +++ b/packages/forms/src/select/index.tsx @@ -56,6 +56,11 @@ const styles = (theme: Theme) => ({ textAlign: 'left', color: theme.selectColor, }, + label: { + '& > div': { + marginTop: 5, + } + }, popup: { opacity: 0, height: 0, @@ -335,6 +340,7 @@ class SelectComponent extends Component { title={label} showLabel={showLabel} htmlFor={id} + className={classes.label} isRequired={required} >
({ }, toggleLabel: { display: 'flex', + alignItems: 'center', '& > span': { order: 1, marginLeft: 15, - marginTop: 2, }, }, }); diff --git a/packages/theme/src/themes/dark/index.ts b/packages/theme/src/themes/dark/index.ts index 3a56719b2..fd04b106c 100644 --- a/packages/theme/src/themes/dark/index.ts +++ b/packages/theme/src/themes/dark/index.ts @@ -1,4 +1,5 @@ import color from 'color'; +import { merge, cloneDeep } from 'lodash'; import * as defaultStyles from '../default'; import * as legacyStyles from '../legacy'; @@ -63,3 +64,69 @@ export const selectSearchColor = inputBackground; // Modal export const colorModalOverlayBackground = color(legacyStyles.darkThemeBlack).alpha(0.8).rgb().string(); + +// Services +export const services = merge({}, defaultStyles.services, { + listItems: { + borderColor: legacyStyles.darkThemeGrayDarker, + hoverBgColor: legacyStyles.darkThemeGrayDarker, + disabled: { + color: legacyStyles.darkThemeGray, + }, + }, +}); + +// Service Icon +export const serviceIcon = merge({}, defaultStyles.serviceIcon, { + isCustom: { + border: `1px solid ${legacyStyles.darkThemeGrayDark}`, + }, +}); + +// Workspaces +const drawerBg = color(colorBackground).lighten(0.3).hex(); + +export const workspaces = merge({}, defaultStyles.workspaces, { + settings: { + listItems: cloneDeep(services.listItems), + }, + drawer: { + background: drawerBg, + addButton: { + color: legacyStyles.darkThemeGrayLighter, + hoverColor: legacyStyles.darkThemeGraySmoke, + }, + listItem: { + border: color(drawerBg).lighten(0.2).hex(), + hoverBackground: color(drawerBg).lighten(0.2).hex(), + activeBackground: defaultStyles.brandPrimary, + name: { + color: colorText, + activeColor: 'white', + }, + services: { + color: color(colorText).darken(0.5).hex(), + active: color(defaultStyles.brandPrimary).lighten(0.5).hex(), + }, + }, + }, +}); + +// // Workspace settings +// export const workspaceSettings = merge({}, defaultStyles.workspaceSettings, { +// listItemBorderColor: legacyStyles.darkThemeGrayDarker, +// listItemHoverBgColor: legacyStyles.darkThemeGrayDarker, +// }); +// +// // Workspace Drawer +// export const workspaceDrawerBackground = color(colorBackground).lighten(0.3).hex(); +// export const workspaceDrawerAddButtonColor = legacyStyles.darkThemeGrayLighter; +// export const workspaceDrawerAddButtonHoverColor = legacyStyles.darkThemeGraySmoke; +// export const workspaceDrawerItemBorder = color(workspaceDrawerBackground).lighten(0.2).hex(); +// export const workspaceDrawerItemHoverBackground = color(workspaceDrawerBackground).lighten(0.2).hex(); +// export const workspaceDrawerItemActiveBackground = defaultStyles.brandPrimary; +// export const workspaceDrawerItemNameColor = colorText; +// export const workspaceDrawerItemNameActiveColor = 'white'; +// export const workspaceDrawerServicesColor = color(colorText).darken(0.5).hex(); +// export const workspaceDrawerServicesActiveColor = color(defaultStyles.brandPrimary).lighten(0.5).hex(); +// diff --git a/packages/theme/src/themes/default/index.ts b/packages/theme/src/themes/default/index.ts index 273792d46..d0493b82f 100644 --- a/packages/theme/src/themes/default/index.ts +++ b/packages/theme/src/themes/default/index.ts @@ -1,4 +1,5 @@ import color from 'color'; +import { cloneDeep } from 'lodash'; import * as legacyStyles from '../legacy'; @@ -142,3 +143,80 @@ export const badgeBorderRadius = 50; // Modal export const colorModalOverlayBackground = color('#000').alpha(0.5).rgb().string(); + +// Services +export const services = { + listItems: { + padding: 10, + height: 57, + borderColor: legacyStyles.themeGrayLightest, + hoverBgColor: legacyStyles.themeGrayLightest, + disabled: { + color: legacyStyles.themeGrayLight, + }, + }, +}; + +// Service Icon +export const serviceIcon = { + width: 35, + isCustom: { + border: `1px solid ${legacyStyles.themeGrayLighter}`, + borderRadius: legacyStyles.themeBorderRadius, + width: 37, + }, +}; + +// Workspaces +const drawerBg = color(colorBackground).lighten(0.1).hex(); + +export const workspaces = { + settings: { + listItems: cloneDeep(services.listItems), + }, + drawer: { + width: 300, + padding: 20, + background: drawerBg, + buttons: { + color: color(legacyStyles.themeGrayLight).lighten(0.1).hex(), + hoverColor: legacyStyles.themeGrayLight, + }, + listItem: { + hoverBackground: color(drawerBg).darken(0.01).hex(), + activeBackground: legacyStyles.themeGrayLightest, + border: color(drawerBg).darken(0.05).hex(), + name: { + color: colorText, + activeColor: colorText, + }, + services: { + color: color(colorText).lighten(1.5).hex(), + active: color(colorText).lighten(1.5).hex(), + }, + }, + }, + switchingIndicator: { + spinnerColor: 'white', + }, +}; + +// export const workspaceSettings = { +// listItemHeight: 57, +// listItemBorderColor: legacyStyles.themeGrayLightest, +// listItemHoverBgColor: legacyStyles.themeGrayLightest, +// }; +// +// // Workspace Drawer +// export const workspaceDrawerWidth = 300; +// export const workspaceDrawerPadding = 20; +// export const workspaceDrawerBackground = color(colorBackground).lighten(0.1).hex(); +// export const workspaceDrawerAddButtonColor = legacyStyles.themeGrayLight; +// export const workspaceDrawerAddButtonHoverColor = color(legacyStyles.themeGrayLight).lighten(0.1).hex(); +// export const workspaceDrawerItemHoverBackground = color(workspaceDrawerBackground).darken(0.01).hex(); +// export const workspaceDrawerItemActiveBackground = legacyStyles.themeGrayLightest; +// export const workspaceDrawerItemBorder = color(workspaceDrawerBackground).darken(0.05).hex(); +// export const workspaceDrawerItemNameColor = colorText; +// export const workspaceDrawerItemNameActiveColor = colorText; +// export const workspaceDrawerServicesColor = color(colorText).lighten(1.5).hex(); +// export const workspaceDrawerServicesActiveColor = workspaceDrawerServicesColor; diff --git a/packages/ui/src/badge/ProBadge.tsx b/packages/ui/src/badge/ProBadge.tsx new file mode 100644 index 000000000..612e23210 --- /dev/null +++ b/packages/ui/src/badge/ProBadge.tsx @@ -0,0 +1,64 @@ +import { Theme } from '@meetfranz/theme'; +import classnames from 'classnames'; +import React, { Component } from 'react'; +import injectStyle from 'react-jss'; + +import { Icon, Badge } from '../'; +import { IWithStyle } from '../typings/generic'; + +interface IProps extends IWithStyle { + badgeClasses?: string; + iconClasses?: string; + inverted?: boolean; +} + +const styles = (theme: Theme) => ({ + badge: { + height: 'auto', + padding: [4, 6, 2, 7], + borderRadius: theme.borderRadiusSmall, + }, + invertedBadge: { + background: theme.styleTypes.primary.contrast, + color: theme.styleTypes.primary.accent, + }, + icon: { + fill: theme.styleTypes.primary.contrast, + }, + invertedIcon: { + fill: theme.styleTypes.primary.accent, + }, +}); + +class ProBadgeComponent extends Component { + render() { + const { + classes, + badgeClasses, + iconClasses, + inverted, + } = this.props; + + return ( + + + + ); + } +} + +export const ProBadge = injectStyle(styles)(ProBadgeComponent); diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 1eeec5144..666495ce9 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -3,3 +3,4 @@ export { Infobox } from './infobox'; export * from './headline'; export { Loader } from './loader'; export { Badge } from './badge'; +export { ProBadge } from './badge/ProBadge'; diff --git a/packages/ui/src/infobox/index.tsx b/packages/ui/src/infobox/index.tsx index 1a442a733..9066a623e 100644 --- a/packages/ui/src/infobox/index.tsx +++ b/packages/ui/src/infobox/index.tsx @@ -11,6 +11,7 @@ interface IProps extends IWithStyle { type?: string; dismissable?: boolean; onDismiss?: () => void; + onUnmount?: () => void; ctaOnClick?: () => void; ctaLabel?: string; ctaLoading?: boolean; @@ -46,6 +47,7 @@ const styles = (theme: Theme) => ({ wrapper: { position: 'relative', overflow: 'hidden', + height: 'auto', }, infobox: { alignItems: 'center', @@ -129,6 +131,11 @@ class InfoboxComponent extends Component { }, 3000); } + componentWillUnmount(): void { + const { onUnmount } = this.props; + if (onUnmount) onUnmount(); + } + render() { const { classes, diff --git a/packages/ui/src/loader/index.tsx b/packages/ui/src/loader/index.tsx index 46545c786..4a3c8274f 100644 --- a/packages/ui/src/loader/index.tsx +++ b/packages/ui/src/loader/index.tsx @@ -8,6 +8,7 @@ import { IWithStyle } from '../typings/generic'; interface IProps extends IWithStyle { className?: string; + color?: string; } const styles = (theme: Theme) => ({ @@ -22,6 +23,7 @@ class LoaderComponent extends Component { const { classes, className, + color, theme, } = this.props; @@ -37,7 +39,7 @@ class LoaderComponent extends Component { loaded={false} width={4} scale={0.75} - color={theme.colorText} + color={color || theme.colorText} parentClassName={classes.loader} />
-- cgit v1.2.3-70-g09d2