Skip to content
Snippets Groups Projects
Commit a6afc8ca authored by Allen Gilliland's avatar Allen Gilliland
Browse files

navbar is now rendered via React.

parent 943a9cbe
No related branches found
No related tags found
No related merge requests found
"use strict";
import React, { Component, PropTypes } from 'react';
import cx from "classnames";
import OnClickOut from 'react-onclickout';
import CreateDashboardModal from "metabase/components/CreateDashboardModal.react";
import Icon from "metabase/components/Icon.react";
import Modal from "metabase/components/Modal.react";
export default class DashboardsDropdown extends Component {
constructor(props) {
super(props);
this.state = {
dropdownOpen: false,
modalOpen: false
};
this.toggleDropdown = this.toggleDropdown.bind(this);
this.closeDropdown = this.closeDropdown.bind(this);
this.toggleModal = this.toggleModal.bind(this);
this.closeModal = this.closeModal.bind(this);
}
onCreateDashboard(newDashboard) {
let { createDashboardFn } = this.props;
createDashboardFn(newDashboard).then(function() {
// close modal and add new dash to our dashboards list
this.setState({
dropdownOpen: false,
modalOpen: false
});
}.bind(this));
}
toggleModal() {
if (!this.state.modalOpen) {
// when we open our modal we always close the dropdown
this.setState({
dropdownOpen: false,
modalOpen: !this.state.modalOpen
});
}
}
closeModal() {
this.setState({ modalOpen: false });
}
toggleDropdown() {
this.setState({ dropdownOpen: !this.state.dropdownOpen });
}
closeDropdown() {
this.setState({ dropdownOpen: false });
}
renderCreateDashboardModal() {
return (
<Modal>
<CreateDashboardModal
createDashboardFn={this.onCreateDashboard.bind(this)}
closeFn={() => this.closeModal()} />
</Modal>
);
}
render() {
let { dashboards } = this.props;
let { dropdownOpen, modalOpen } = this.state;
let dropDownClasses = cx({
'NavDropdown': true,
'inline-block': true,
'cursor-pointer': true,
'open': dropdownOpen,
})
return (
<div>
{ modalOpen ? this.renderCreateDashboardModal() : null }
<OnClickOut onClickOut={this.closeDropdown}>
<div className={dropDownClasses}>
<a className="NavDropdown-button NavItem text-white cursor-pointer p2 flex align-center" onClick={this.toggleDropdown}>
<span className="NavDropdown-button-layer">
Dashboards
<Icon className="ml1" name={'chevrondown'} width={8} height={8}></Icon>
</span>
</a>
{ dropdownOpen ?
<div className="NavDropdown-content DashboardList">
{ dashboards.length === 0 ?
<div className="NavDropdown-content-layer text-white text-centered">
<div className="p2"><span className="QuestionCircle">?</span></div>
<div className="px2 py1 text-bold">You dont have any dashboards yet.</div>
<div className="px2 pb2 text-light">Dashboards group visualizations for frequent questions in a single handy place.</div>
<div className="border-top border-light">
<a className="Dropdown-item block text-white no-decoration" href="#" onClick={this.toggleModal}>Create a new dashboard</a>
</div>
</div>
:
<ul className="NavDropdown-content-layer">
{ dashboards.map(dash =>
<li className="block">
<a className="Dropdown-item block text-white no-decoration" href={"/dash/"+dash.id} onClick={this.closeDropdown}>
<div className="flex text-bold">
{dash.name}
</div>
{ dash.description ?
<div className="mt1 text-light text-brand-light">
{dash.description}
</div>
: null }
</a>
</li>
)}
<li className="block border-top border-light">
<a className="Dropdown-item block text-white no-decoration" href="#" onClick={this.toggleModal}>Create a new dashboard</a>
</li>
</ul>
}
</div>
: null }
</div>
</OnClickOut>
</div>
);
}
}
DashboardsDropdown.propTypes = {
createDashboardFn: PropTypes.func.isRequired,
dashboards: PropTypes.array.isRequired
};
"use strict";
import React, { Component, PropTypes } from 'react';
import cx from "classnames";
import DashboardsDropdown from "metabase/components/DashboardsDropdown.react";
import Icon from "metabase/components/Icon.react";
import LogoIcon from "metabase/components/LogoIcon.react";
import ProfileLink from "metabase/components/ProfileLink.react";
// TODO - this relies on props.location, which is angular's $location service
export default class Navbar extends Component {
isActive(path) {
return this.props.location.path().indexOf(path) >= 0;
}
renderAdminNav() {
const classes = "NavItem py1 px2 no-decoration";
return (
<nav className="AdminNav">
<div className="wrapper flex align-center">
<div className="NavTitle flex align-center">
<Icon name={'gear'} className="AdminGear" width={22} height={22}></Icon>
<span className="NavItem-text ml1 hide sm-show">Site Administration</span>
</div>
<ul className="sm-ml4 flex flex-full">
<li>
<a className={cx(classes, {"is--selected": this.isActive("/admin/settings")})} href="/admin/settings/">
Settings
</a>
</li>
<li>
<a className={cx(classes, {"is--selected": this.isActive("/admin/people")})} href="/admin/people/">
People
</a>
</li>
<li>
<a className={cx(classes, {"is--selected": this.isActive("/admin/metadata")})} href="/admin/metadata/">
Metadata
</a>
</li>
<li>
<a className={cx(classes, {"is--selected": this.isActive("/admin/databases")})} href="/admin/databases/">
Databases
</a>
</li>
</ul>
<ProfileLink {...this.props}></ProfileLink>
</div>
</nav>
);
}
renderAuthNav() {
return (
<nav className="py2 sm-py1 xl-py3 relative"></nav>
);
}
renderEmptyNav() {
return (
<nav className="py2 sm-py1 xl-py3 relative">
<ul className="wrapper flex align-center">
<li>
<a className="NavItem cursor-pointer flex align-center" href="/">
<LogoIcon className="text-brand my2"></LogoIcon>
</a>
</li>
</ul>
</nav>
);
}
renderMainNav() {
return (
<nav className="CheckBg CheckBg-offset sm-py2 sm-py1 xl-py3 relative bg-brand">
<ul className="wrapper flex align-center">
<li>
<a className="NavItem cursor-pointer text-white flex align-center my1" href="/">
<LogoIcon className="text-white m1"></LogoIcon>
</a>
</li>
<li>
<DashboardsDropdown {...this.props}></DashboardsDropdown>
</li>
<li className="flex-align-right">
<a className="rounded inline-block bg-white text-brand cursor-pointer p2 no-decoration" href="/q">New <span className="hide sm-show">Question</span></a>
<div className="inline-block text-white"><ProfileLink {...this.props}></ProfileLink></div>
</li>
</ul>
</nav>
);
}
render() {
let { context, user } = this.props;
if (!user) return null;
switch (context) {
case "admin": return this.renderAdminNav();
case "auth": return this.renderAuthNav();
case "none": return this.renderEmptyNav();
case "setup": return null;
default: return this.renderMainNav();
}
}
}
Navbar.propTypes = {
context: PropTypes.string.isRequired,
location: PropTypes.string.isRequired,
user: PropTypes.object
};
'use strict';
import Navbar from 'metabase/components/Navbar.react';
// Global Controllers
var MetabaseControllers = angular.module('metabase.controllers', ['metabase.services', 'metabase.navbar.directives']);
......@@ -47,35 +50,71 @@ MetabaseControllers.controller('NotFound', ['AppState', function(AppState) {
AppState.setAppContext('none');
}]);
MetabaseControllers.controller('Nav', ['$scope', '$routeParams', '$location', 'AppState', function($scope, $routeParams, $location, AppState) {
$scope.isActive = function(location) {
return $location.path().indexOf(location) >= 0;
};
MetabaseControllers.controller('Nav', ['$scope', '$routeParams', '$location', '$rootScope', 'AppState', 'Dashboard',
function($scope, $routeParams, $location, $rootScope, AppState, Dashboard) {
function refreshDashboards() {
Dashboard.list({
'filterMode': 'all'
}, function (dashes) {
$scope.dashboards = dashes;
}, function (error) {
console.log('error getting dahsboards list', error);
});
}
var setNavContext = function(context) {
switch (context) {
case "admin":
$scope.nav = 'admin';
break;
case "setup":
$scope.nav = 'setup';
break;
case "auth":
$scope.nav = 'auth';
break;
case "none":
$scope.nav = 'none';
break;
default:
$scope.nav = 'main';
function setNavContext(context) {
switch (context) {
case "admin":
$scope.context = 'admin';
break;
case "setup":
$scope.context = 'setup';
break;
case "auth":
$scope.context = 'auth';
break;
case "none":
$scope.context = 'none';
break;
default:
$scope.context = 'main';
}
}
};
$scope.$on('appstate:context-changed', function(event, newAppContext) {
setNavContext(newAppContext);
});
$scope.Navbar = Navbar;
$scope.location = $location;
// initialize our state from the current AppState model, which we expect to have resolved already
setNavContext(AppState.model.appContext);
}]);
$scope.dashboards = [];
$scope.createDashboardFn = async function(newDashboard) {
var dashboard = await Dashboard.create(newDashboard).$promise;
$rootScope.$broadcast("dashboard:create", dashboard.id);
$location.path("/dash/" + dashboard.id);
// this is important because it allows our caller to perform any of their own actions after the promis resolves
return dashboard;
};
$scope.$on('appstate:context-changed', function(event, newAppContext) {
setNavContext(newAppContext);
});
$scope.$on("dashboard:create", function(event, dashboardId) {
refreshDashboards();
});
$scope.$on("dashboard:delete", function(event, dashboardId) {
refreshDashboards();
});
$scope.$on("dashboard:update", function(event, dashboardId) {
refreshDashboards();
});
// always initialize with a fresh listing
refreshDashboards();
// initialize our state from the current AppState model, which we expect to have resolved already
setNavContext(AppState.model.appContext);
}
]);
......@@ -16,110 +16,7 @@
</head>
<body ng-controller="Metabase">
<div class="Nav" ng-controller="Nav" ng-if="user" ng-cloak>
<!-- MAIN NAV -->
<nav class="CheckBg CheckBg-offset sm-py2 sm-py1 xl-py3 relative bg-brand" ng-show="nav === 'main'">
<ul class="wrapper flex align-center">
<li>
<a class="NavItem cursor-pointer text-white flex align-center my1" href="/">
<mb-logo-icon class="text-white m1"></mb-logo-icon>
</a>
</li>
<li>
<div class="NavDropdown ml1 md-ml3" dropdown on-toggle="toggled(open)">
<a class="NavDropdown-button NavItem text-white cursor-pointer p2 flex align-center" dropdown-toggle>
<span class="NavDropdown-button-layer">
Dashboards
<mb-icon class="ml1" name="chevrondown" width="8px" height="8px"></mb-icon>
</span>
</a>
<div class="NavDropdown-content DashboardList" ng-controller="DashList">
<div class="NavDropdown-content-layer text-white text-centered" ng-if="dashboards.length === 0">
<div class="p2"><span class="QuestionCircle">?</span></div>
<div class="px2 py1 text-bold">You don’t have any dashboards yet.</div>
<div class="px2 pb2 text-light">Dashboards group visualizations for frequent questions in a single handy place.</div>
<div class="border-top border-light">
<a class="Dropdown-item block text-white no-decoration" href="#" mb-dashboard-create>Create a new dashboard</a>
</div>
</div>
<ul class="NavDropdown-content-layer" ng-if="dashboards.length > 0">
<li class="block" ng-repeat="dash in dashboards">
<a class="Dropdown-item block text-white no-decoration" href="/dash/{{dash.id}}">
<div class="flex text-bold">
{{dash.name}}
<mb-icon class="ml1 flex flex-align-right align-center" name="lock" width="12px" height="12px" ng-if="dash.public_perms === 0"></mb-icon>
</div>
<div class="mt1 text-light text-brand-light" ng-if="dash.description">
{{dash.description}}
</div>
</a>
</li>
<li class="block border-top border-light">
<a class="Dropdown-item block text-white no-decoration" href="#" mb-dashboard-create>Create a new dashboard</a>
</li>
</ul>
</div>
</div>
</li>
<li class="flex-align-right">
<a class="rounded inline-block bg-white text-brand cursor-pointer p2 no-decoration" href="/q">New <span class="hide sm-show">Question</span></a>
<div mb-profile-link class="inline-block text-white" user="user" context="nav"></div>
</li>
</ul>
</nav>
<!-- ADMIN NAV -->
<nav class="AdminNav" ng-show="nav === 'admin'">
<div class="wrapper flex align-center">
<div class="NavTitle flex align-center">
<mb-icon name="gear" class="AdminGear" width="22px" height="22px"></mb-icon>
<span class="NavItem-text ml1 hide sm-show">Site Administration</span>
</div>
<!-- admin sections -->
<ul class="sm-ml4 flex flex-full">
<li>
<a class="NavItem py1 px2 no-decoration" ng-class="{ 'is--selected' : isActive('/admin/settings')}" href="/admin/settings/">
Settings
</a>
</li>
<li>
<a class="NavItem py1 px2 no-decoration" ng-class="{ 'is--selected' : isActive('/admin/people')}" href="/admin/people/">
People
</a>
</li>
<li>
<a class="NavItem py1 px2 no-decoration" ng-class="{ 'is--selected' : isActive('/admin/metadata')}" href="/admin/metadata/">
Metadata
</a>
</li>
<li>
<a class="NavItem py1 px2 no-decoration" ng-class="{ 'is--selected' : isActive('/admin/databases')}" href="/admin/databases/">
Databases
</a>
</li>
</ul>
<!-- user profile dropdown -->
<div mb-profile-link user="user" context="nav"></div>
</div>
</nav>
<!-- AUTH NAV -->
<nav class="py2 sm-py1 xl-py3 relative" ng-show="nav === 'auth'">
</nav>
<!-- NO NAV -->
<nav class="py2 sm-py1 xl-py3 relative" ng-show="nav === 'none'">
<ul class="wrapper flex align-center">
<li>
<a class="NavItem cursor-pointer flex align-center" href="/">
<mb-logo-icon class="text-brand my2"></mb-logo-icon>
</a>
</li>
</ul>
</nav>
</div>
<div class="Nav" ng-controller="Nav" mb-react-component="Navbar"></div>
<main class="Main flex flex-column flex-full" ng-view></main>
</body>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment