diff --git a/index.html b/index.html
index a9f0968..f3fc656 100644
--- a/index.html
+++ b/index.html
@@ -48,12 +48,25 @@
diff --git a/src/script.js b/src/script.js
index b14d90e..af139a8 100644
--- a/src/script.js
+++ b/src/script.js
@@ -41,6 +41,7 @@ define(function(require) {
require('ui/list').attachTo('#list');
require('ui/facet').attachTo('#facets');
require('ui/loading').attachTo('#loading');
+ require('ui/sidebar').attachTo('#sidebar');
require('ui/project').attachTo(document);
require('data/facet').attachTo(document);
require('data/analytics').attachTo(document);
diff --git a/src/ui/map.js b/src/ui/map.js
index e1619f4..248c27a 100644
--- a/src/ui/map.js
+++ b/src/ui/map.js
@@ -2,6 +2,7 @@ define(function(require, exports, module) {
'use strict';
var flight = require('flight');
var L = require('leaflet');
+ var $ = require('jquery');
var _ = require('lodash');
var timedWithObject = require('timed_with_object');
@@ -37,22 +38,13 @@ define(function(require, exports, module) {
this.configureMap = function(ev, config) {
this.trigger('mapStarted', {});
// if list or facets are emabled, give the map less space
- var addition = 0;
if (config.facets) {
- addition += 300;
+ $('body').addClass('has-facets');
}
if (config.list) {
- addition += 300;
+ $('body').addClass('has-list');
}
- if (addition > 0) {
- window.setTimeout(function() {
- if (this.map) {
- this.$node.css('left', '+=' + addition);
- this.map.invalidateSize();
- }
- }.bind(this), 50);
- }
var mapConfig = config.map;
diff --git a/src/ui/sidebar.js b/src/ui/sidebar.js
new file mode 100644
index 0000000..b6d47bb
--- /dev/null
+++ b/src/ui/sidebar.js
@@ -0,0 +1,80 @@
+define(function(require, exports, module) {
+ 'use strict';
+ var flight = require('flight');
+ var $ = require('jquery');
+ var _ = require('lodash');
+
+
+ module.exports = flight.component(function sidebar() {
+
+ var potentialSidebarItems = ['facets', 'list'];
+ var presentItems = [];
+
+ this.defaultAttrs({
+ toggleButtons: '.sidebar-action',
+ sidebarItems: '.sidebar-item',
+ sidebarToggle: '.sidebar-toggle'
+ });
+
+ this.configureSidebar = function(e, config){
+ var configAttr = _.keys(config);
+ presentItems = _.intersection(potentialSidebarItems, configAttr);
+
+ var $sidebar = this.$node;
+
+ // add class .sidebar-items-X based on active sidebar items
+ $sidebar.addClass('sidebar-items-'+presentItems.length);
+ // activate configured sidebar items
+ _.each(presentItems, function(item){
+ $sidebar.find(item).show();
+ $sidebar.find('[data-select="'+item+'"]').parent().show();
+ });
+
+ if( presentItems.length === 1) {
+ $sidebar.find('.sidebar-nav').hide();
+ $sidebar.addClass('no-tabs');
+ }
+ };
+
+ this.showItem = function(e, elemObj) {
+ // generic toggle fn for sidebar tabs
+ e.preventDefault();
+ var $el = $(elemObj.el);
+ var itemTitle = $el.data('select');
+ this.select('sidebarItems').hide();
+ this.$node.find('#'+itemTitle).show();
+ this.select('toggleButtons').removeClass('active');
+ $el.addClass('active');
+ };
+
+ this.showFullSidebar = function() {
+ var $sidebar = this.$node;
+ _.each(presentItems, function(item){
+ $sidebar.find(item).show();
+ });
+ };
+
+ this.toggleHiddenSidebar = function(e) {
+ e.preventDefault();
+ this.$node.toggleClass('open');
+ $('.sidebar-toggle').toggleClass('active');
+ };
+
+ this.after('initialize', function() {
+ this.on(document, 'config', this.configureSidebar);
+
+ this.on('click', {
+ toggleButtons: this.showItem,
+ sidebarToggle: this.toggleHiddenSidebar
+ });
+
+
+ $(window).on('resize', function(){
+ if( document.body.clientWidth > 955 ) {
+ this.showFullSidebar();
+ }
+ }.bind(this));
+
+ });
+ });
+});
diff --git a/styles/style.css b/styles/style.css
index 296aa98..953f1be 100644
--- a/styles/style.css
+++ b/styles/style.css
@@ -48,6 +48,10 @@ body > div.container {
color: #69579c;
}
+.navbar-form {
+ width: 80%;
+}
+
.navbar-form .btn {
border:none;
outline:none;
@@ -100,7 +104,7 @@ body > div.container {
top: 50px;
bottom: 0;
right:0;
- z-index: 1;
+ z-index: -1;
}
.control {
@@ -168,23 +172,223 @@ body > div.container {
}
-.control.control-sidebar {
- float: left;
- box-sizing: border-box;
- padding: 10px 14px 50px;
- width: 300px;
+
+
+
+
+/* ====================
+ SIDEBAR
+ ==================== */
+
+.sidebar-toggle {
+ position: fixed;
+ right: 0px;
+ bottom: 16px;
+ /* weird bottom offset to make space for leaflet credits*/
+ width: 50px;
+ height: 50px;
+ background: rgba(0,0,0,0.6);
+ cursor: pointer;
+ display: none;
+}
+
+/* Only show sidebar toggle on small screens, since sidebar is default visible on larger */
+@media (max-width: 768px) {
+ .sidebar-toggle {
+ display: block;
+ }
+}
+
+/* CSS hamburger */
+.sidebar-toggle a {
+ text-align: center;
+ display: block;
+ position: relative;
+ top: 50%;
+ width: 25px;
+ height: 3px;
+ background: #fff;
+ margin: -2px auto;
+ border-radius: 6px;
+}
+.sidebar-toggle a:before, .sidebar-toggle a:after {
+ content: "";
+ width: 25px;
+ height: 3px;
+ display: block;
+ position: relative;
+ background: inherit;
+ border-radius: 8px;
+}
+.sidebar-toggle a:before {
+ top: -8px;
+}
+.sidebar-toggle a:after {
+ bottom: -5px;
+}
+
+/* transform toggle into a X when active */
+.sidebar-toggle.active a:before {
+ transform: rotate(-45deg);
+ -webkit-transform: rotate(-45deg);
+ top: 1px;
+}
+.sidebar-toggle.active a {
+ height: 0px;
+}
+.sidebar-toggle.active a:after {
+ transform: rotate(45deg);
+ -webkit-transform: rotate(45deg);
+ top: -2px;
+}
+
+
+
+/*
+ The sidebar container:
+ two column large screens,
+ single column, tabbed on mid,
+ single column, tabbed, and hidden on small
+ */
+.sidebar {
height: 100%;
- border-right: 1px solid #AFAFAF;
- margin-bottom:30px;
- overflow-y: auto;
+ position: relative;
+ background: #f3f3f3;
+ transition: all 0.2s;
+}
+.sidebar.open {
+ margin-left: 0;
+}
+
+
+@media (max-width: 1200px) {
+ .sidebar {
+ width: 33%;
+ margin-left: 0%;
+ padding-top: 50px;
+ }
+ .sidebar.no-tabs {
+ padding-top: 0;
+ }
+}
+
+@media (max-width: 768px) {
+ .sidebar {
+ width: 80%;
+ margin-left: -80%;
+ }
+ .sidebar.open{
+ margin-left: 0%;
+ }
+}
+
+/* The sidebar columns (filter, list, etc) */
+.sidebar .control {
+ height: 100%;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ float: left;
+ padding: 0 10px;
+ display: none;
}
+.sidebar-items-2 .control {
+ width: 50%;
+}
+.sidebar-items-1 .control {
+ width: 100%;
+}
+
+@media (max-width: 1200px) {
+.sidebar .control {
+ width: 100%;
+ float: none;
+ }
+}
+
+
+/* sidebar button toggles to show list vs filter, etc */
+.sidebar-nav {
+ height: 50px;
+ width: 100%;
+ position: absolute;
+ top: 0;
+ display: none;
+}
+
+/* Only display this when we have a tabbed view (mid-screen size & lower) */
+@media (max-width: 1200px) {
+ .sidebar-nav {
+ display: initial;
+ }
+}
+
+.sidebar-nav .items{
+ overflow: auto;
+ zoom: 1;
+ list-style: none;
+ padding: 0;
+}
+
+.sidebar-nav .items li{
+ float: left;
+ display: none;
+}
+.sidebar-items-2 .sidebar-nav .items li{
+ width: 50%;
+}
+.sidebar-items-1 .sidebar-nav .items li{
+ width: 100%;
+}
+
+ /*buttons*/
+.sidebar-nav a{
+ text-decoration: none;
+ padding: 10px;
+ display: block;
+ text-align: center;
+ background-color: #e5e5e5;
+}
+.sidebar-nav a.active{
+ background-color: #f3f3f3;
+}
+
+
+/* map offset should be zero no matter the configuration of the sidebar since sidebar is an overlay*/
+body #map {
+ left: 0;
+}
+
+/* At larger sizes, adjust map to fit sidebar space depending on configuration */
+@media (min-width: 768px) {
+ .sidebar-items-1 + #map {
+ left: 15%;
+ }
+ .sidebar-items-2 + #map {
+ left: 33%;
+ }
+}
+@media (min-width: 1200px) {
+ .sidebar-items-1 {
+ width: 330px;
+ }
+ .sidebar-items-2 {
+ width: 500px;
+ }
+ .sidebar-items-1 + #map {
+ left: 330px;
+ }
+ .sidebar-items-2 + #map {
+ left: 500px;
+ }
+}
+
+
#facets .checkbox.selected label {
font-weight: bold;
}
#list ul {
- margin: -14px -14px 20px;
padding-bottom: 20px;
padding-left: 0;
}
diff --git a/test/spec/ui/sidebar_spec.js b/test/spec/ui/sidebar_spec.js
new file mode 100644
index 0000000..3dc8b1a
--- /dev/null
+++ b/test/spec/ui/sidebar_spec.js
@@ -0,0 +1,28 @@
+define(['test/mock', 'jquery', 'lodash'], function(mock, $, _) {
+ 'use strict';
+ describeComponent('ui/sidebar', function() {
+ beforeEach(function() {
+ setupComponent();
+ });
+
+ describe('on config', function() {
+ it('updates sets correct # of sidebar items', function() {
+ $(document).trigger('config', mock.config);
+ expect(this.$node.hasClass('sidebar-items-2')).toEqual(true);
+
+ var noListAttrMock = _.clone(mock.config);
+ delete noListAttrMock.list
+ $(document).trigger('config', noListAttrMock);
+ expect(this.$node.hasClass('sidebar-items-1')).toEqual(true);
+ });
+
+ it('only shows tabs if there is more than 1 item', function(){
+ var noListAttrMock = _.clone(mock.config);
+ delete noListAttrMock.list
+ $(document).trigger('config', noListAttrMock);
+ expect(this.$node.hasClass('no-tabs')).toEqual(true);
+ });
+ });
+
+ });
+});