diff --git a/resources/frontend_client/app/components/ProfileLink.react.js b/resources/frontend_client/app/components/ProfileLink.react.js index 0721ed1040c8f6afa742fdb02fd7f514aec64f96..ba6ef5f82b986bb83719ce6bb704e464febd9c7b 100644 --- a/resources/frontend_client/app/components/ProfileLink.react.js +++ b/resources/frontend_client/app/components/ProfileLink.react.js @@ -4,6 +4,7 @@ import React, { Component, PropTypes } from 'react'; import OnClickOut from 'react-onclickout'; import cx from 'classnames'; +import UserAvatar from './UserAvatar.react'; import Icon from './Icon.react'; export default class ProfileLink extends Component { @@ -22,23 +23,8 @@ export default class ProfileLink extends Component { this.setState({ dropdownOpen: false }); } - displayInitials() { - let initials = '??'; - const { user } = this.props; - - if (user.first_name !== 'undefined') { - initials = user.first_name.substring(0, 1); - } - - if (user.last_name !== 'undefined') { - initials = initials + user.last_name.substring(0, 1); - } - return initials; - } - render() { const { user, context } = this.props; - let dropDownClasses = cx({ 'NavDropdown': true, 'inline-block': true, @@ -46,15 +32,14 @@ export default class ProfileLink extends Component { 'open': this.state.dropdownOpen, }) + return ( <OnClickOut onClickOut={this.closeDropdown}> <div className={dropDownClasses}> <a className="NavDropdown-button NavItem flex align-center p2" onClick={this.toggleDropdown}> <div className="NavDropdown-button-layer"> <div className="flex align-center"> - <span className="UserNick"> - <span className="UserInitials NavItem-text">{this.displayInitials()}</span> - </span> + <UserAvatar user={user} style={{backgroundColor: 'transparent'}}/> <Icon name="chevrondown" className="Dropdown-chevron ml1" width="8px" height="8px" /> </div> </div> diff --git a/resources/frontend_client/app/components/UserAvatar.react.js b/resources/frontend_client/app/components/UserAvatar.react.js new file mode 100644 index 0000000000000000000000000000000000000000..aa7053a5edf586e4f44400f8088060b3cf0033cc --- /dev/null +++ b/resources/frontend_client/app/components/UserAvatar.react.js @@ -0,0 +1,55 @@ +'use strict'; + +import React, { Component } from 'react'; +import cx from 'classnames'; + +export default class UserAvatar extends Component { + constructor(props) { + super(props) + this.styles = { + fontSize: '0.85rem', + borderWidth: '1px', + borderStyle: 'solid', + borderRadius: '99px', + width: '2rem', + height: '2rem', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + } + } + userInitials() { + const { first_name, last_name } = this.props.user; + + let initials = '??'; + + if (first_name !== 'undefined') { + initials = first_name.substring(0, 1); + } + + if (last_name !== 'undefined') { + initials = initials + last_name.substring(0, 1); + } + + return initials; + } + + render() { + const { background } = this.props; + const classes = { + 'flex': true, + 'align-center': true, + } + classes[background] = true; + + return ( + <div className={cx(classes)} style={Object.assign(this.styles, this.props.style)}> + {this.userInitials()} + </div> + ) + } +} + +UserAvatar.defaultProps = { + background: 'bg-brand', +} diff --git a/resources/frontend_client/app/css/admin.css b/resources/frontend_client/app/css/admin.css index 7e3be1df36c04d0c0a8b390683286e9c7d895515..93669d954caaa2a889148dbe6612ffb9c0cdf20a 100644 --- a/resources/frontend_client/app/css/admin.css +++ b/resources/frontend_client/app/css/admin.css @@ -50,17 +50,6 @@ transition: background .2s linear; } -.UserNick { - border-width: 1px; - border-style: solid; - border-radius: 99px; - width: 2rem; - height: 2rem; /* set an explicit height since we want it to be square */ - display: flex; - align-items: center; - justify-content: center; -} - .AdminNav .UserNick { color: #fff; border-color: #fff; @@ -72,13 +61,6 @@ color: #fff; } -.UserInitials, -.UserInitials:hover { - font-size: 0.85rem; - color: currentColor; - cursor: pointer; -} - .Actions { background-color: rgba(243,243,243,0.46); border: 1px solid #E0E0E0; diff --git a/resources/frontend_client/app/home/components/Activity.react.js b/resources/frontend_client/app/home/components/Activity.react.js index 511d8885f2b830c45ee54ed8e7877ea9b6f51cac..57b0bbb3cab1d0dc8ae64b009467951365eec539 100644 --- a/resources/frontend_client/app/home/components/Activity.react.js +++ b/resources/frontend_client/app/home/components/Activity.react.js @@ -141,13 +141,8 @@ export default class Activity extends Component { if (user) { const userColorIndex = userColors[user.id]; const colorCssClass = this.colorClasses[userColorIndex]; - const cssClasses = { - 'UserNick': true, - 'text-white': true - }; - cssClasses[colorCssClass] = true; - return cx(cssClasses); + return colorCssClass; } else { return cx({ 'UserNick': true, diff --git a/resources/frontend_client/app/home/components/ActivityItem.react.js b/resources/frontend_client/app/home/components/ActivityItem.react.js index e3d97752255e9bce84261fb45b3e07be048e63ba..aa6924ed1301dcc283c16ce6acfa9a55e85b788e 100644 --- a/resources/frontend_client/app/home/components/ActivityItem.react.js +++ b/resources/frontend_client/app/home/components/ActivityItem.react.js @@ -1,52 +1,32 @@ 'use strict'; import React, { Component } from 'react'; - import Icon from 'metabase/components/Icon.react'; - +import IconBorder from '../../query_builder/IconBorder.react'; +import UserAvatar from 'metabase/components/UserAvatar.react'; export default class ActivityItem extends Component { constructor(props) { super(props); - - this.styles = { - initials: { - borderRadius: '0px', - } - }; - } - - userInitials(user) { - let initials = '??'; - - if (user.first_name !== 'undefined') { - initials = user.first_name.substring(0, 1); - } - - if (user.last_name !== 'undefined') { - initials = initials + user.last_name.substring(0, 1); - } - - return initials; } render() { const { item, description, userColors } = this.props; return ( - <div className="flex align-center"> - { item.user ? - <span styles={this.styles.initials} className={userColors}> - <span className="UserInitials">{this.userInitials(item.user)}</span> - </span> - : - <span styles={this.styles.initials} className={userColors}> - <span className="UserInitials"><Icon name={'return'}></Icon></span> - </span> - } - - <div className="ml2 full flex align-center"> + <div className="ml1 flex align-center"> + <span> + { item.user ? + <UserAvatar user={item.user} background={userColors} style={{color: '#fff', borderWidth: '0'}}/> + : + <IconBorder style={{color: '#B8C0C8'}}> + <Icon name='sync' width={16} height={16} /> + </IconBorder> + } + </span> + + <div className="ml2 full flex align-center" style={{fontSize: '1rem'}}> <div className="text-grey-4"> <span className="text-dark">{description.userName}</span> diff --git a/resources/frontend_client/app/home/components/ActivityStory.react.js b/resources/frontend_client/app/home/components/ActivityStory.react.js index bf3e8a3d639ba2e0df40d2aac9316999d8fbe851..75587aa3f5a6bfd00b0360c4a53e19ce53d679ce 100644 --- a/resources/frontend_client/app/home/components/ActivityStory.react.js +++ b/resources/frontend_client/app/home/components/ActivityStory.react.js @@ -2,16 +2,15 @@ import React, { Component } from 'react'; - export default class ActivityStory extends Component { constructor(props) { super(props); this.styles = { - modelLink: { - borderWidth: "2px" - }, + borderWidth: '2px', + fontSize: '1.08rem', + borderColor: '#DFE8EA', } } @@ -23,8 +22,8 @@ export default class ActivityStory extends Component { } return ( - <div className="ml2 mt1 border-left flex" style={{borderWidth: '3px'}}> - <div style={this.styles.modelLink} className="flex full ml4 bordered rounded p2"> + <div className="mt1 border-left flex" style={{borderWidth: '3px', marginLeft: '22px', borderColor: '#F2F5F6'}}> + <div className="flex full ml4 bordered rounded p2" style={this.styles}> { story.bodyLink ? <a className="link" href={story.bodyLink}>{story.body}</a> : diff --git a/resources/frontend_client/app/home/components/CardFilters.react.js b/resources/frontend_client/app/home/components/CardFilters.react.js index 48a5178d1ce8016d872628a42ae4f116613d40c1..20a55e54957fc8eeffaeb9760ca876016042cae0 100644 --- a/resources/frontend_client/app/home/components/CardFilters.react.js +++ b/resources/frontend_client/app/home/components/CardFilters.react.js @@ -43,7 +43,7 @@ export default class CardFilters extends Component { <Icon className="float-left" name={'filter'} width={36} height={36}></Icon> <div className="h3">Filter saved questions</div> </div> - <div className="bordered rounded bg-white"> + <div className="rounded bg-white" style={{border: '1px solid #E5E5E5'}}> <ul className="cursor-pointer"> {databases.map(item => <li key={item.id} className="border-row-divider"> diff --git a/resources/frontend_client/app/home/components/HeaderTabs.react.js b/resources/frontend_client/app/home/components/HeaderTabs.react.js index 251e1c284b37b6e63b1dbf9192a5185b3817e2f7..705fe690c58c1aa73740dfcc5ab9d9a68bf4ff2a 100644 --- a/resources/frontend_client/app/home/components/HeaderTabs.react.js +++ b/resources/frontend_client/app/home/components/HeaderTabs.react.js @@ -43,7 +43,7 @@ export default class HeaderTabs extends Component { return ( <div className="bg-brand text-white"> - <a className={activityTab} href="/">Activity</a> + <a className={activityTab} style={{marginLeft: '10px'}} href="/">Activity</a> <a className={questionsTab} href="/?questions">Saved Questions</a> </div> ); diff --git a/resources/frontend_client/app/home/components/Homepage.react.js b/resources/frontend_client/app/home/components/Homepage.react.js index 2534d81b022167cb78ed07f1c16c133214e0bf36..b154c48aa2a58e20538513734b8f89c3ceb61b50 100644 --- a/resources/frontend_client/app/home/components/Homepage.react.js +++ b/resources/frontend_client/app/home/components/Homepage.react.js @@ -54,7 +54,7 @@ export default class Homepage extends Component { const { selectedTab, user } = this.props; return ( - <div> + <div className="flex flex-column flex-full"> { this.state.onboarding ? <Modal> <NewUserOnboardingModal user={user} closeFn={() => (this.completeOnboarding())}></NewUserOnboardingModal> @@ -79,7 +79,7 @@ export default class Homepage extends Component { </div> </div> </div> - <div className="relative pl4"> + <div className="relative felx flex-column flex-full pl4"> <div style={this.styles.main}> <div style={this.styles.mainWrapper}> { selectedTab === 'activity' ? diff --git a/resources/frontend_client/app/home/components/RecentViews.react.js b/resources/frontend_client/app/home/components/RecentViews.react.js index ca3d802f3385b6148e9b38834b18fd5b74cd5b80..b5ddfaf81d8aad47c990b8521e61bc150c4f9fb0 100644 --- a/resources/frontend_client/app/home/components/RecentViews.react.js +++ b/resources/frontend_client/app/home/components/RecentViews.react.js @@ -33,12 +33,12 @@ export default class RecentViews extends Component { <Icon className="float-left" name={'clock'} width={18} height={18}></Icon> <span className="pl1 h3">Recents</span> </div> - <div className="bordered rounded bg-white"> + <div className="rounded bg-white" style={{border: '1px solid #E5E5E5'}}> {recentViews.length > 0 ? - <ul className="px3 py1"> + <ul className="px3 py2"> {recentViews.map(item => - <li key={item.id} className="py1"> - <a className="link text-dark ml1" href={Urls.modelToUrl(item.model, item.model_id)}>{item.model_object.name}</a> + <li key={item.id} className="py1 ml1"> + <a className="link text-dark" href={Urls.modelToUrl(item.model, item.model_id)}>{item.model_object.name}</a> </li> )} </ul> diff --git a/resources/frontend_client/app/icon_paths.js b/resources/frontend_client/app/icon_paths.js index 9d9ecdb51542ea97aa748440aa8542e44300c944..c970ec20c7be276e44b49a441d83ad1c761478e6 100644 --- a/resources/frontend_client/app/icon_paths.js +++ b/resources/frontend_client/app/icon_paths.js @@ -59,6 +59,7 @@ export var ICON_PATHS = { pie: 'M16.0113299,15.368011 L16.0113299,7.6605591 L16.0113246,7.66055936 C16.1469053,7.65372627 16.283376,7.65026855 16.4206543,7.65026855 C18.4538187,7.65026855 20.309836,8.40872524 21.7212043,9.65813664 L16.0113299,15.368011 Z M16.5768268,16.0595929 L24.4103638,16.0595929 C24.4171966,15.9240175 24.4206543,15.7875468 24.4206543,15.6502686 C24.4206543,13.5849976 23.6380543,11.7025127 22.35323,10.2831897 L16.5768268,16.0595929 Z M24.2956851,17.0665012 L15.0044217,17.0665012 L15.0044217,7.77523777 C11.2616718,8.44383611 8.4206543,11.7152747 8.4206543,15.6502686 C8.4206543,20.0685466 12.0023763,23.6502686 16.4206543,23.6502686 C20.3556481,23.6502686 23.6270867,20.8092511 24.2956851,17.0665012 L24.2956851,17.0665012 Z', pinmap: 'M15,16.8999819 L15,21 L16,23 L17,21.0076904 L17,16.8999819 C16.6768901,16.9655697 16.3424658,17 16,17 C15.6575342,17 15.3231099,16.9655697 15,16.8999819 L15,16.8999819 Z M16,16 C18.209139,16 20,14.209139 20,12 C20,9.790861 18.209139,8 16,8 C13.790861,8 12,9.790861 12,12 C12,14.209139 13.790861,16 16,16 Z', popular: 'M22.7319639,13.7319639 L16.5643756,19.8995521 L15.4656976,18.8008741 L15.4640332,18.8024814 L15.3719504,18.707127 L15.1500476,18.4852242 L15.1539209,18.4813508 L15.1522861,18.4796579 L15.1522861,18.4796579 L15.1103863,18.52012 L13.4809348,16.8327737 L13.4809348,16.8327737 L7.98163972,22.3320688 L6.56742615,20.9178552 L13.4852814,14 L14.5837075,15.0984261 L14.5851122,15.0970697 L14.6628229,15.1775415 L14.8994949,15.4142136 L14.8953638,15.4183447 L14.8968592,15.4198933 L14.9388754,15.3793187 L16.5684644,17.0668074 L16.5684644,17.0668074 L21.3176359,12.3176359 L19,10 L26,9 L25,16 L22.7319639,13.7319639 Z', + sync: 'M16 2 A14 14 0 0 0 2 16 A14 14 0 0 0 16 30 A14 14 0 0 0 26 26 L 23.25 23 A10 10 0 0 1 16 26 A10 10 0 0 1 6 16 A10 10 0 0 1 16 6 A10 10 0 0 1 23.25 9 L19 13 L30 13 L30 2 L26 6 A14 14 0 0 0 16 2', return:'M15.3040432,11.8500793 C22.1434689,13.0450349 27.291257,18.2496116 27.291257,24.4890512 C27.291257,25.7084278 27.0946472,26.8882798 26.7272246,28.0064033 L26.7272246,28.0064033 C25.214579,22.4825472 20.8068367,18.2141694 15.3040432,17.0604596 L15.3040432,25.1841972 L4.70874296,14.5888969 L15.3040432,3.99359668 L15.3040432,3.99359668 L15.3040432,11.8500793 Z', reference: { path: 'M15.9670388,2.91102126 L14.5202438,1.46422626 L14.5202438,13.9807372 C14.5202438,15.0873683 13.6272253,15.9844701 12.5215507,15.9844701 L2.89359,15.9844701 C2.16147687,15.9844701 1.446795,15.6184135 1.446795,14.5376751 L11.0747557,14.5376751 C12.1786034,14.5376751 13.0734488,13.6501624 13.0734488,12.5467556 L13.0734488,0 L2.17890813,0 C0,0 0,0 0,2.17890813 L0,14.5202438 C0,16.6991519 1.81285157,17.4312651 3.62570313,17.4312651 L13.9704736,17.4312651 C15.0731461,17.4312651 15.9670388,16.5448165 15.9670388,15.4275322 L15.9670388,2.91102126 Z', diff --git a/resources/frontend_client/app/query_builder/IconBorder.react.js b/resources/frontend_client/app/query_builder/IconBorder.react.js index dcf7535fa1e5f46f06c1edffccf7e0614e113399..e90f7e2156ef48b3fdf860b0319a086a17ac5628 100644 --- a/resources/frontend_client/app/query_builder/IconBorder.react.js +++ b/resources/frontend_client/app/query_builder/IconBorder.react.js @@ -1,33 +1,36 @@ 'use strict'; +import React, { Component } from 'react'; import cx from "classnames"; -var IconBorder = React.createClass({ - displayName: 'IconBorder', - getDefaultProps: function () { - return { - borderWidth: '1px', - borderStyle: 'solid', - borderColor: 'currentcolor', - rounded: true - } - }, - computeSize: function () { - var width = parseInt(this.props.children.props.width, 10); +export default class IconBorder extends Component { + constructor() { + super(); + this.state = {} + } + componentDidMount() { + this.setState({ + childWidth: React.findDOMNode(this.refs.child).offsetWidth + }) + } + computeSize () { + let width = parseInt(this.state.childWidth, 10); return width * 2; - }, - render: function () { - var classes = cx({ + } + + render() { + const classes = cx({ 'flex': true, 'layout-centered': true }); - var styles = { + const styles = { width: this.computeSize(), height: this.computeSize(), borderWidth: this.props.borderWidth, borderStyle: this.props.borderStyle, - borderColor: this.props.borderColor + borderColor: this.props.borderColor, + lineHeight: '1px', /* HACK this is dumb but it centers the icon in the border */ } if (this.props.borderRadius) { @@ -37,11 +40,17 @@ var IconBorder = React.createClass({ } return ( - <span className={classes + ' ' + this.props.className} style={styles}> - {this.props.children} + <span className={classes + ' ' + this.props.className} style={Object.assign(styles, this.props.style)}> + <span ref="child">{this.props.children}</span> </span> ); } -}); +} -export default IconBorder; +IconBorder.defaultProps = { + borderWidth: '1px', + borderStyle: 'solid', + borderColor: 'currentcolor', + rounded: true, + style: {}, +} diff --git a/resources/frontend_client/index_template.html b/resources/frontend_client/index_template.html index 6f4b16fb9bb52275f9e37fcad99f5b05c73a67fe..20307cdf2669ab1abd2292243b23bc90ef0feeb9 100644 --- a/resources/frontend_client/index_template.html +++ b/resources/frontend_client/index_template.html @@ -59,8 +59,8 @@ </div> </li> <li class="flex-align-right"> - <a class="NavItem bg-white text-brand cursor-pointer p2 no-decoration" href="/q">New Question</a> - <div mb-profile-link class="text-white" user="user" context="nav"></div> + <a class="rounded inline-block bg-white text-brand cursor-pointer p2 no-decoration" href="/q">New Question</a> + <div mb-profile-link class="inline-block text-white" user="user" context="nav"></div> </li> </ul> </nav>