Просмотр исходного кода

Merge branch 'master' of github.com:grafana/grafana

Conflicts:
	public/sass/.sass-lint.yml
	tasks/options/sasslint.js
Torkel Ödegaard 9 лет назад
Родитель
Сommit
145c14ed57
74 измененных файлов с 937 добавлено и 554 удалено
  1. 4 2
      package.json
  2. 5 0
      pkg/api/api.go
  3. 253 0
      pkg/api/avatar/avatar.go
  4. 2 1
      pkg/api/dtos/models.go
  5. 7 1
      pkg/api/index.go
  6. 14 0
      public/app/app.ts
  7. 11 14
      public/app/core/components/navbar/navbar.html
  8. 0 24
      public/app/core/components/navbar/navbar.ts
  9. 4 1
      public/app/core/components/sidemenu/sidemenu.html
  10. 1 1
      public/app/core/routes/routes.ts
  11. 4 0
      public/app/features/admin/partials/admin_home.html
  12. 7 8
      public/app/features/apps/partials/list.html
  13. 6 7
      public/app/features/apps/partials/page.html
  14. 12 16
      public/app/features/dashboard/dashnav/dashnav.html
  15. 4 6
      public/app/features/dashboard/timepicker/dropdown.html
  16. 7 5
      public/app/features/datasources/partials/edit.html
  17. 8 8
      public/app/features/datasources/partials/http_settings.html
  18. 2 2
      public/app/features/datasources/partials/list.html
  19. 3 3
      public/app/features/org/partials/invite.html
  20. 2 2
      public/app/features/org/partials/orgApiKeys.html
  21. 14 14
      public/app/features/org/partials/orgDetails.html
  22. 1 1
      public/app/features/org/partials/orgUsers.html
  23. 17 18
      public/app/features/panel/panel_menu.js
  24. 5 5
      public/app/features/playlist/partials/playlist.html
  25. 6 6
      public/app/features/profile/partials/password.html
  26. 7 7
      public/app/features/profile/partials/profile.html
  27. 26 27
      public/app/features/styleguide/styleguide.html
  28. 21 3
      public/app/features/styleguide/styleguide.ts
  29. 1 1
      public/app/partials/dashboard.html
  30. 7 7
      public/app/partials/login.html
  31. 9 9
      public/app/partials/panelgeneral.html
  32. 8 8
      public/app/partials/signup_invited.html
  33. 10 10
      public/app/partials/signup_step2.html
  34. 18 40
      public/app/plugins/datasource/cloudwatch/partials/config.html
  35. 8 8
      public/app/plugins/datasource/elasticsearch/partials/config.html
  36. 26 45
      public/app/plugins/datasource/influxdb/partials/config.html
  37. 24 15
      public/app/plugins/panel/text/editor.html
  38. 2 1
      public/app/plugins/panel/text/module.html
  39. 1 0
      public/app/system.conf.js
  40. BIN
      public/img/angle_gradient_light_rev.png
  41. BIN
      public/img/angle_gradient_rev.png
  42. BIN
      public/img/checkbox.png
  43. BIN
      public/img/transparent.png
  44. 80 26
      public/sass/.sass-lint.yml
  45. 3 0
      public/sass/_grafana.scss
  46. 5 5
      public/sass/_old_responsive.scss
  47. 26 25
      public/sass/_variables.dark.scss
  48. 16 17
      public/sass/_variables.light.scss
  49. 9 15
      public/sass/_variables.scss
  50. 8 0
      public/sass/base/_fonts.scss
  51. 2 2
      public/sass/base/_forms.scss
  52. 0 2
      public/sass/base/_reboot.scss
  53. 4 4
      public/sass/components/_alerts.scss
  54. 9 6
      public/sass/components/_buttons.scss
  55. 4 4
      public/sass/components/_dashboard.scss
  56. 32 16
      public/sass/components/_gf-form.scss
  57. 31 66
      public/sass/components/_navbar.scss
  58. 6 0
      public/sass/components/_panel_text.scss
  59. 2 1
      public/sass/components/_search.scss
  60. 50 24
      public/sass/components/_sidemenu.scss
  61. 1 3
      public/sass/components/_timepicker.scss
  62. 2 4
      public/sass/layout/_page.scss
  63. 25 0
      public/sass/mixins/_animations.scss
  64. 11 0
      public/sass/mixins/_mixins.scss
  65. 12 2
      public/sass/pages/_login.scss
  66. 1 0
      public/sass/pages/_styleguide.scss
  67. 1 0
      public/sass/utils/_spacings.scss
  68. 3 1
      public/sass/utils/_utils.scss
  69. 19 0
      public/sass/utils/_widths.scss
  70. 1 1
      public/vendor/angular/angular.js
  71. 1 1
      tasks/default_task.js
  72. 2 0
      tasks/options/copy.js
  73. 4 2
      tasks/options/sasslint.js
  74. 0 1
      tslint.json

+ 4 - 2
package.json

@@ -36,7 +36,7 @@
     "grunt-sass": "^1.1.0",
     "grunt-string-replace": "~1.2.1",
     "grunt-systemjs-builder": "^0.2.5",
-    "grunt-tslint": "^3.0.1",
+    "grunt-tslint": "^3.0.2",
     "grunt-typescript": "^0.8.0",
     "grunt-usemin": "3.0.0",
     "jshint-stylish": "~0.1.5",
@@ -74,7 +74,9 @@
     "lodash": "^2.4.1",
     "sinon": "1.16.1",
     "systemjs-builder": "^0.15.7",
-    "tslint": "^3.2.1",
+    "tether": "^1.2.0",
+    "tether-drop": "^1.4.2",
+    "tslint": "^3.4.0",
     "typescript": "^1.7.5"
   }
 }

+ 5 - 0
pkg/api/api.go

@@ -2,6 +2,7 @@ package api
 
 import (
 	"github.com/go-macaron/binding"
+	"github.com/grafana/grafana/pkg/api/avatar"
 	"github.com/grafana/grafana/pkg/api/dtos"
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
@@ -224,6 +225,10 @@ func Register(r *macaron.Macaron) {
 	// rendering
 	r.Get("/render/*", reqSignedIn, RenderToPng)
 
+	// Gravatar service.
+	avt := avatar.CacheServer()
+	r.Get("/avatar/:hash", avt.ServeHTTP)
+
 	InitAppPluginRoutes(r)
 
 	r.NotFound(NotFoundHandler)

+ 253 - 0
pkg/api/avatar/avatar.go

@@ -0,0 +1,253 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+// Code from https://github.com/gogits/gogs/blob/v0.7.0/modules/avatar/avatar.go
+
+package avatar
+
+import (
+	"bufio"
+	"bytes"
+	"crypto/md5"
+	"encoding/hex"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/grafana/grafana/pkg/log"
+	"github.com/grafana/grafana/pkg/setting"
+)
+
+var gravatarSource string
+
+func UpdateGravatarSource() {
+	srcCfg := "//secure.gravatar.com/avatar/"
+
+	gravatarSource = srcCfg
+	if strings.HasPrefix(gravatarSource, "//") {
+		gravatarSource = "http:" + gravatarSource
+	} else if !strings.HasPrefix(gravatarSource, "http://") &&
+		!strings.HasPrefix(gravatarSource, "https://") {
+		gravatarSource = "http://" + gravatarSource
+	}
+}
+
+// hash email to md5 string
+// keep this func in order to make this package independent
+func HashEmail(email string) string {
+	// https://en.gravatar.com/site/implement/hash/
+	email = strings.TrimSpace(email)
+	email = strings.ToLower(email)
+
+	h := md5.New()
+	h.Write([]byte(email))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+// Avatar represents the avatar object.
+type Avatar struct {
+	hash      string
+	reqParams string
+	data      *bytes.Buffer
+	notFound  bool
+	timestamp time.Time
+}
+
+func New(hash string) *Avatar {
+	return &Avatar{
+		hash: hash,
+		reqParams: url.Values{
+			"d":    {"404"},
+			"size": {"200"},
+			"r":    {"pg"}}.Encode(),
+	}
+}
+
+func (this *Avatar) Expired() bool {
+	return time.Since(this.timestamp) > (time.Minute * 10)
+}
+
+func (this *Avatar) Encode(wr io.Writer) error {
+	_, err := wr.Write(this.data.Bytes())
+	return err
+}
+
+func (this *Avatar) Update() (err error) {
+	select {
+	case <-time.After(time.Second * 3):
+		err = fmt.Errorf("get gravatar image %s timeout", this.hash)
+	case err = <-thunder.GoFetch(gravatarSource+this.hash+"?"+this.reqParams, this):
+	}
+	return err
+}
+
+type service struct {
+	notFound *Avatar
+	cache    map[string]*Avatar
+}
+
+func (this *service) mustInt(r *http.Request, defaultValue int, keys ...string) (v int) {
+	for _, k := range keys {
+		if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil {
+			defaultValue = v
+		}
+	}
+	return defaultValue
+}
+
+func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	urlPath := r.URL.Path
+	hash := urlPath[strings.LastIndex(urlPath, "/")+1:]
+
+	var avatar *Avatar
+
+	if avatar, _ = this.cache[hash]; avatar == nil {
+		avatar = New(hash)
+	}
+
+	if avatar.Expired() {
+		if err := avatar.Update(); err != nil {
+			log.Trace("avatar update error: %v", err)
+		}
+	}
+
+	if avatar.notFound {
+		avatar = this.notFound
+	} else {
+		this.cache[hash] = avatar
+	}
+
+	w.Header().Set("Content-Type", "image/jpeg")
+	w.Header().Set("Content-Length", strconv.Itoa(len(avatar.data.Bytes())))
+	w.Header().Set("Cache-Control", "private, max-age=3600")
+
+	if err := avatar.Encode(w); err != nil {
+		log.Warn("avatar encode error: %v", err)
+		w.WriteHeader(500)
+	}
+}
+
+func CacheServer() http.Handler {
+	UpdateGravatarSource()
+
+	return &service{
+		notFound: newNotFound(),
+		cache:    make(map[string]*Avatar),
+	}
+}
+
+func newNotFound() *Avatar {
+	avatar := &Avatar{}
+
+	// load transparent png into buffer
+	path := filepath.Join(setting.StaticRootPath, "img", "transparent.png")
+
+	if data, err := ioutil.ReadFile(path); err != nil {
+		log.Error(3, "Failed to read transparent.png, %v", path)
+	} else {
+		avatar.data = bytes.NewBuffer(data)
+	}
+
+	return avatar
+}
+
+// thunder downloader
+var thunder = &Thunder{QueueSize: 10}
+
+type Thunder struct {
+	QueueSize int // download queue size
+	q         chan *thunderTask
+	once      sync.Once
+}
+
+func (t *Thunder) init() {
+	if t.QueueSize < 1 {
+		t.QueueSize = 1
+	}
+	t.q = make(chan *thunderTask, t.QueueSize)
+	for i := 0; i < t.QueueSize; i++ {
+		go func() {
+			for {
+				task := <-t.q
+				task.Fetch()
+			}
+		}()
+	}
+}
+
+func (t *Thunder) Fetch(url string, avatar *Avatar) error {
+	t.once.Do(t.init)
+	task := &thunderTask{
+		Url:    url,
+		Avatar: avatar,
+	}
+	task.Add(1)
+	t.q <- task
+	task.Wait()
+	return task.err
+}
+
+func (t *Thunder) GoFetch(url string, avatar *Avatar) chan error {
+	c := make(chan error)
+	go func() {
+		c <- t.Fetch(url, avatar)
+	}()
+	return c
+}
+
+// thunder download
+type thunderTask struct {
+	Url    string
+	Avatar *Avatar
+	sync.WaitGroup
+	err error
+}
+
+func (this *thunderTask) Fetch() {
+	this.err = this.fetch()
+	this.Done()
+}
+
+var client = &http.Client{}
+
+func (this *thunderTask) fetch() error {
+	this.Avatar.timestamp = time.Now()
+
+	log.Debug("avatar.fetch(fetch new avatar): %s", this.Url)
+	req, _ := http.NewRequest("GET", this.Url, nil)
+	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/jpeg,image/png,*/*;q=0.8")
+	req.Header.Set("Accept-Encoding", "deflate,sdch")
+	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8")
+	req.Header.Set("Cache-Control", "no-cache")
+	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36")
+	resp, err := client.Do(req)
+
+	if err != nil {
+		this.Avatar.notFound = true
+		return fmt.Errorf("gravatar unreachable, %v", err)
+	}
+
+	defer resp.Body.Close()
+
+	if resp.StatusCode != 200 {
+		this.Avatar.notFound = true
+		return fmt.Errorf("status code: %d", resp.StatusCode)
+	}
+
+	this.Avatar.data = &bytes.Buffer{}
+	writer := bufio.NewWriter(this.Avatar.data)
+
+	if _, err = io.Copy(writer, resp.Body); err != nil {
+		return err
+	}
+
+	return nil
+}

+ 2 - 1
pkg/api/dtos/models.go

@@ -7,6 +7,7 @@ import (
 	"time"
 
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/setting"
 )
 
 type LoginCommand struct {
@@ -89,5 +90,5 @@ func GetGravatarUrl(text string) string {
 
 	hasher := md5.New()
 	hasher.Write([]byte(strings.ToLower(text)))
-	return fmt.Sprintf("https://secure.gravatar.com/avatar/%x?s=90&default=mm", hasher.Sum(nil))
+	return fmt.Sprintf(setting.AppSubUrl+"/avatar/%x", hasher.Sum(nil))
 }

+ 7 - 1
pkg/api/index.go

@@ -36,7 +36,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 	}
 
 	if setting.DisableGravatar {
-		data.User.GravatarUrl = setting.AppSubUrl + "/public/img/user_profile.png"
+		data.User.GravatarUrl = setting.AppSubUrl + "/public/img/transparent.png"
 	}
 
 	if len(data.User.Name) == 0 {
@@ -105,6 +105,12 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
 			Text: "Admin",
 			Icon: "fa fa-fw fa-cogs",
 			Url:  setting.AppSubUrl + "/admin",
+			Children: []*dtos.NavLink{
+				{Text: "Global Users", Icon: "fa fa-fw fa-cogs", Url: setting.AppSubUrl + "/admin/users"},
+				{Text: "Global Orgs", Icon: "fa fa-fw fa-cogs", Url: setting.AppSubUrl + "/admin/orgs"},
+				{Text: "Server Settings", Icon: "fa fa-fw fa-cogs", Url: setting.AppSubUrl + "/admin/settings"},
+				{Text: "Server Stats", Icon: "fa-fw fa-cogs", Url: setting.AppSubUrl + "/admin/stats"},
+			},
 		})
 	}
 

+ 14 - 0
public/app/app.ts

@@ -47,6 +47,20 @@ export class GrafanaApp {
       this.registerFunctions.factory    = $provide.factory;
       this.registerFunctions.service    = $provide.service;
       this.registerFunctions.filter     = $filterProvider.register;
+
+      $provide.decorator("$http", ["$delegate", "$templateCache", function($delegate, $templateCache) {
+        var get = $delegate.get;
+        $delegate.get = function(url, config) {
+          if (url.match(/\.html$/)) {
+            // some template's already exist in the cache
+            if (!$templateCache.get(url)) {
+              url += "?v=" + new Date().getTime();
+            }
+          }
+          return get(url, config);
+        };
+        return $delegate;
+      }]);
     });
 
     this.ngModuleDependencies = [

+ 11 - 14
public/app/core/components/navbar/navbar.html

@@ -1,19 +1,16 @@
 <div class="navbar-inner">
-	<div class="top-nav-btn top-nav-menu-btn">
-		<a class="pointer" ng-click="ctrl.contextSrv.toggleSideMenu()">
-			<span class="top-nav-logo-background">
-				<img class="logo-icon" src="public/img/grafana_icon.svg"></img>
-			</span>
-			<i class="icon-gf icon-gf-grafana_wordmark"></i>
-			<i class="fa fa-caret-down"></i>
-		</a>
-	</div>
+	<a class="navbar-brand-btn pointer" ng-click="ctrl.contextSrv.toggleSideMenu()">
+		<span class="navbar-brand-btn-background">
+			<img src="public/img/grafana_icon.svg"></img>
+		</span>
+		<i class="icon-gf icon-gf-grafana_wordmark"></i>
+		<i class="fa fa-caret-down"></i>
+	</a>
 
-
-	<div ng-if="ctrl.title">
-		<nav-button title="{{ctrl.title}}" title-url="{{ctrl.titleUrl}}" icon="{{ctrl.icon}}">
-		</nav-button>
-	</div>
+	<a href="{{::ctrl.titleUrl}}" class="navbar-page-btn" ng-show="ctrl.title">
+		<i class="{{::ctrl.icon}}"></i>
+		{{::ctrl.title}}
+	</a>
 
 	<div ng-transclude></div>
 </div>

+ 0 - 24
public/app/core/components/navbar/navbar.ts

@@ -30,28 +30,4 @@ export function navbarDirective() {
   };
 }
 
-var navButtonTemplate = `
-<div class="top-nav-btn dashnav-dashboards-btn">
-  <a href="{{::titleUrl}}">
-    <i class="{{::icon}}"></i>
-    <span class="dashboard-title">{{::title}}</span>
-  </a>
-</div>
-`;
-
-function navButton() {
-  return {
-    restrict: 'E',
-    template: navButtonTemplate,
-    scope: {
-      title: "@",
-      titleUrl: "@",
-    },
-    link: function(scope, elem, attrs, ctrl) {
-      scope.icon = attrs.icon;
-    }
-  };
-}
-
 coreModule.directive('navbar', navbarDirective);
-coreModule.directive('navButton', navButton);

+ 4 - 1
public/app/core/components/sidemenu/sidemenu.html

@@ -3,7 +3,10 @@
 	<li class="sidemenu-org-section" ng-if="ctrl.isSignedIn" class="dropdown">
 		<div class="sidemenu-org">
 			<div class="sidemenu-org-avatar">
-				<img ng-src="{{ctrl.user.gravatarUrl}}">
+				<img ng-if="ctrl.user.gravatarUrl" ng-src="{{ctrl.user.gravatarUrl}}">
+				<span class="sidemenu-org-avatar--missing">
+					<i class="fa fa-fw fa-user"></i>
+				</span>
 			</div>
 			<div class="sidemenu-org-details">
 				<span class="sidemenu-org-user sidemenu-item-text">{{ctrl.user.name}}</span>

+ 1 - 1
public/app/core/routes/routes.ts

@@ -186,7 +186,7 @@ function setupAngularRoutes($routeProvider, $locationProvider) {
   .when('/global-alerts', {
     templateUrl: 'public/app/features/dashboard/partials/globalAlerts.html',
   })
-  .when('/styleguide', {
+  .when('/styleguide/:page?', {
     controller: 'StyleGuideCtrl',
     controllerAs: 'ctrl',
     templateUrl: 'public/app/features/styleguide/styleguide.html',

+ 4 - 0
public/app/features/admin/partials/admin_home.html

@@ -24,5 +24,9 @@
 		View Server Stats
 	</a>
 
+	<a class="btn btn-inverse" href="styleguide">
+	  Style guide
+	</a>
+
 </div>
 

+ 7 - 8
public/app/features/apps/partials/list.html

@@ -2,14 +2,14 @@
 </navbar>
 
 <div class="page-container">
-  <div class="page-wide">
+  <div class="page-header">
     <h1>Plugins</h1>
+    </div>
+	<div ng-if="!ctrl.apps">
+		<em>No apps defined</em>
+	</div>
 
-		<div ng-if="!ctrl.apps">
-			<em>No apps defined</em>
-		</div>
-
-		<ul class="filter-list">
+	<ul class="filter-list">
       <li ng-repeat="app in ctrl.apps">
         <ul class="filter-list-card">
 					<li class="filter-list-card-image">
@@ -43,6 +43,5 @@
           </li>
         </ul>
       </li>
-		</ul>
-	</div>
+	</ul>
 </div>

+ 6 - 7
public/app/features/apps/partials/page.html

@@ -1,14 +1,13 @@
-<navbar icon="fa fa-fw fa-cubes" title="{{ctrl.appModel.name}}" title-url="apps/{{ctrl.appId}}/edit">
+<navbar icon="icon-gf icon-gf-apps" title="{{ctrl.appModel.name}}" title-url="apps/{{ctrl.appId}}/edit">
 </navbar>
 
 <div class="page-container">
-	<div class="page-wide">
+	<div class="page-header">
 		<h1>{{ctrl.page.name}}</h1>
+	</div>
 
-		<div ng-if="ctrl.page">
-			<plugin-component type="app-page">
-			</plugin-component>
-		</div>
-
+	<div ng-if="ctrl.page">
+		<plugin-component type="app-page">
+		</plugin-component>
 	</div>
 </div>

+ 12 - 16
public/app/features/dashboard/dashnav/dashnav.html

@@ -1,22 +1,18 @@
 <navbar>
 
-<div class="top-nav-btn dashnav-dashboards-btn" ng-if="!dashboardMeta.isSnapshot">
-	<a class="pointer" ng-click="openSearch()">
-		<i class="icon-gf icon-gf-dashboard"></i>
-		<span class="dashboard-title">{{dashboard.title}}</span>
-		<i class="fa fa-caret-down"></i>
-	</a>
-</div>
+<a class="pointer navbar-page-btn" ng-show="!dashboardMeta.isSnapshot" ng-click="openSearch()">
+	<i class="icon-gf icon-gf-dashboard"></i>
+	<span>{{dashboard.title}}</span>
+	<i class="fa fa-caret-down"></i>
+</a>
 
-<div class="top-nav-btn dashnav-dashboards-btn" ng-if="dashboardMeta.isSnapshot">
-	<a class="pointer" bs-tooltip="titleTooltip" data-placement="bottom" ng-click="openSearch()">
-		<i class="icon-gf icon-gf-snapshot"></i>
-		<span class="dashboard-title">
-			{{dashboard.title}}
-			<em class="small">&nbsp;&nbsp;(snapshot)</em>
-		</span>
-	</a>
-</div>
+<a class="pointer navbar-page-btn" ng-if="dashboardMeta.isSnapshot" bs-tooltip="titleTooltip" data-placement="bottom" ng-click="openSearch()">
+	<i class="icon-gf icon-gf-snapshot"></i>
+	<span>
+		{{dashboard.title}}
+		<em class="small">&nbsp;&nbsp;(snapshot)</em>
+	</span>
+</a>
 
 <ul class="nav pull-left dashnav-action-icons">
 	<li ng-show="dashboardMeta.canStar">

+ 4 - 6
public/app/features/dashboard/timepicker/dropdown.html

@@ -4,7 +4,7 @@
 
 		<label class="small">From:</label>
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
+			<div class="gf-form max-width-28">
 				<input type="text" class="gf-form-input input-large" ng-model="ctrl.timeRaw.from" input-datetime>
 			</div>
 			<div class="gf-form">
@@ -21,7 +21,7 @@
 
 		<label class="small">To:</label>
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
+			<div class="gf-form max-width-28">
 				<input type="text" class="gf-form-input input-large" ng-model="ctrl.timeRaw.to" input-datetime>
 			</div>
 			<div class="gf-form">
@@ -35,15 +35,13 @@
 			<datepicker ng-model="ctrl.absolute.toJs" class="gf-timepicker-component" show-weeks="false" ng-change="ctrl.absoluteToChanged()"></datepicker>
 		</div>
 
-
-
 		<label class="small">Refreshing every:</label>
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
+			<div class="gf-form max-width-28">
 				<select ng-model="ctrl.refresh.value" class="gf-form-input input-medium" ng-options="f.value as f.text for f in ctrl.refresh.options"></select>
 			</div>
 			<div class="gf-form">
-				<button type="submit" class="btn gf-form-btn btn-primary" ng-click="ctrl.applyCustom();" ng-disabled="!timeForm.$valid">Apply</button>
+				<button type="submit" class="btn gf-form-btn btn-secondary" ng-click="ctrl.applyCustom();" ng-disabled="!timeForm.$valid">Apply</button>
 			</div>
 		</div>
 

+ 7 - 5
public/app/features/datasources/partials/edit.html

@@ -13,14 +13,16 @@
 	<form name="editForm">
 		<div class="gf-form-group">
 			<div class="gf-form">
-				<span class="gf-form-label gf-size-sm">Name</span>
-				<input class="gf-form-input gf-size-max-xxl" type="text" ng-model="current.name" placeholder="My data source name" required>
+				<span class="gf-form-label width-7">Name</span>
+				<input class="gf-form-input max-width-21" type="text" ng-model="current.name" placeholder="My data source name" required>
 				<editor-checkbox text="Default" model="current.isDefault"></editor-checkbox>
 			</div>
 
 			<div class="gf-form">
-				<span class="gf-form-label gf-size-sm">Type</span>
-				<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
+				<span class="gf-form-label width-7">Type</span>
+				<div class="gf-form-select-wrapper">
+					<select class="gf-form-input gf-size-auto" ng-model="current.type" ng-options="k as v.name for (k, v) in types" ng-change="typeChanged()"></select>
+				</div>
 			</div>
 		</div>
 
@@ -41,7 +43,7 @@
 		<div class="gf-form-button-row">
 			<button type="submit" class="btn btn-success" ng-show="isNew" ng-click="saveChanges()">Add</button>
 			<button type="submit" class="btn btn-success" ng-show="!isNew" ng-click="saveChanges()">Save</button>
-			<button type="submit" class="btn btn-primary" ng-show="!isNew" ng-click="saveChanges(true)">
+			<button type="submit" class="btn btn-secondary" ng-show="!isNew" ng-click="saveChanges(true)">
 				Test Connection
 			</button>
 			<a class="btn btn-link" href="datasources">Cancel</a>

+ 8 - 8
public/app/features/datasources/partials/http_settings.html

@@ -4,19 +4,19 @@
 	<h3 class="page-heading">Http settings</h3>
 
 	<div class="gf-form">
-		<span class="gf-form-label gf-size-sm">Url</span>
-		<input class="gf-form-input gf-size-max-xxl" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
+		<span class="gf-form-label width-7">Url</span>
+		<input class="gf-form-input max-width-21" type="text" ng-model='current.url' placeholder="http://my.server.com:8080" ng-pattern="/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/" required></input>
 	</div>
 
 	<div class="gf-form">
-		<span class="gf-form-label gf-size-sm">
+		<span class="gf-form-label width-7">
 			Access <tip>Direct = url is used directly from browser, Proxy = Grafana backend will proxy the request</tip>
 		</span>
 		<select class="gf-form-input gf-size-auto" ng-model="current.access" ng-options="f for f in ['direct', 'proxy']"></select>
 	</div>
 
 	<div class="gf-form">
-		<span class="gf-form-label gf-size-sm">
+		<span class="gf-form-label width-7">
 			Http Auth
 		</span>
 		<editor-checkbox text="Basic Auth" model="current.basicAuth"></editor-checkbox>
@@ -24,17 +24,17 @@
 	</div>
 
 	<div class="gf-form" ng-if="current.basicAuth">
-		<span class="gf-form-label gf-size-sm">
+		<span class="gf-form-label width-7">
 			User
 		</span>
-		<input class="gf-form-input gf-size-max-xxl" type="text"  ng-model='current.basicAuthUser' placeholder="user" required></input>
+		<input class="gf-form-input max-width-21" type="text"  ng-model='current.basicAuthUser' placeholder="user" required></input>
 	</div>
 
 	<div class="gf-form" ng-if="current.basicAuth">
-		<span class="gf-form-label gf-size-sm">
+		<span class="gf-form-label width-7">
 			Passord
 		</span>
-		<input class="gf-form-input gf-size-max-xxl" type="password" ng-model='current.basicAuthPassword' placeholder="password" required></input>
+		<input class="gf-form-input max-width-21" type="password" ng-model='current.basicAuthPassword' placeholder="password" required></input>
 	</div>
 </div>
 

+ 2 - 2
public/app/features/datasources/partials/list.html

@@ -33,7 +33,7 @@
 				<tr ng-repeat="ds in ctrl.datasources">
 					<td>
 						<a href="datasources/edit/{{ds.id}}">
-							<i class="fa fa-database"></i> &nbsp; {{ds.name}}
+							<i class="icon-gf inline-icon-gf icon-gf-datasources"></i> &nbsp; {{ds.name}}
 						</a>
 					</td>
 					<td>
@@ -41,7 +41,7 @@
 					</td>
 					<td class="text-center">
 						<span ng-if="ds.isDefault">
-							<span class="label label-info">default</span>
+							<span class="btn btn-secondary btn-mini">default</span>
 						</span>
 					</td>
 					<td class="text-right">

+ 3 - 3
public/app/features/org/partials/invite.html

@@ -19,15 +19,15 @@
 
 		<form name="inviteForm">
 			<div class="gf-form-inline" ng-repeat="invite in invites">
-				<div class="gf-form gf-size-max-xxl">
+				<div class="gf-form max-width-21">
 					<span class="gf-form-label">Email or Username</span>
 					<input type="text" ng-model="invite.loginOrEmail" required class="gf-form-input" placeholder="email@test.com">
 				</div>
-				<div class="gf-form gf-size-max-xl">
+				<div class="gf-form max-width-14">
 					<span class="gf-form-label">Name</span>
 					<input type="text" ng-model="invite.name" class="gf-form-input" placeholder="name (optional)">
 				</div>
-				<div class="gf-form gf-size-max-lg">
+				<div class="gf-form max-width-10">
 					<span class="gf-form-label">Role</span>
 					<select ng-model="invite.role" class="gf-form-input" ng-options="f for f in ['Viewer', 'Editor', 'Read Only Editor', 'Admin']">
 					</select>

+ 2 - 2
public/app/features/org/partials/orgApiKeys.html

@@ -1,4 +1,4 @@
-<navbar icon="icon-gf icon-gf-users" title="Organization">
+<navbar icon="icon-gf icon-gf-users" title="Organization" title-url="org">
 </navbar>
 
 <div class="page-container">
@@ -9,7 +9,7 @@
 	<h3 class="page-heading">Add new</h3>
 	<form name="addTokenForm" class="gf-form-group">
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxl">
+			<div class="gf-form max-width-21">
 				<span class="gf-form-label">Add a key</span>
 				<input type="text" class="gf-form-input" ng-model='token.name' placeholder="Name"></input>
 			</div>

+ 14 - 14
public/app/features/org/partials/orgDetails.html

@@ -9,8 +9,8 @@
 	<h3 class="page-heading">General</h3>
 	<form name="orgForm" class="gf-form-group">
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-xs">Name</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-6">Name</span>
 				<input class="gf-form-input" type="text" required ng-model="org.name">
 			</div>
 			<div class="gf-form">
@@ -24,34 +24,34 @@
 
 	<form name="addressForm" class="gf-form-group">
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-sm">Address1</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-7">Address1</span>
 				<input class="gf-form-input" type="text" ng-model="address.address1">
 			</div>
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-sm">Address2</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-7">Address2</span>
 				<input class="gf-form-input" type="text" ng-model="address.address2">
 			</div>
 		</div>
 
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-sm">City</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-7">City</span>
 				<input class="gf-form-input" type="text" ng-model="address.city">
 			</div>
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-sm">Postal code</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-7">Postal code</span>
 				<input class="gf-form-input" type="text" ng-model="address.zipCode">
 			</div>
 		</div>
 
 		<div class="gf-form-inline">
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-sm">State</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-7">State</span>
 				<input class="gf-form-input" type="text" ng-model="address.state">
 			</div>
-			<div class="gf-form gf-size-max-xxxl">
-				<span class="gf-form-label gf-size-sm">Country</span>
+			<div class="gf-form max-width-28">
+				<span class="gf-form-label width-7">Country</span>
 				<input class="gf-form-input" type="text" ng-model="address.country">
 			</div>
 		</div>

+ 1 - 1
public/app/features/org/partials/orgUsers.html

@@ -1,4 +1,4 @@
-<navbar icon="icon-gf icon-gf-users" title="Organization Users">
+<navbar icon="icon-gf icon-gf-users" title="Organization Users" title-url="org">
 </navbar>
 
 <div class="page-container">

+ 17 - 18
public/app/features/panel/panel_menu.js

@@ -2,8 +2,9 @@ define([
   'angular',
   'jquery',
   'lodash',
+  'tether',
 ],
-function (angular, $, _) {
+function (angular, $, _, Tether) {
   'use strict';
 
   angular
@@ -79,6 +80,7 @@ function (angular, $, _) {
           var ctrl = $scope.ctrl;
           var timeout = null;
           var $menu = null;
+          var teather;
 
           elem.append($link);
 
@@ -105,6 +107,7 @@ function (angular, $, _) {
             }
 
             if (menuScope) {
+              teather.destroy();
               $menu.unbind();
               $menu.remove();
               menuScope.$destroy();
@@ -153,23 +156,19 @@ function (angular, $, _) {
             $scope.$apply(function() {
               $compile($menu.contents())(menuScope);
 
-              var menuWidth =  $menu[0].offsetWidth;
-              var menuHeight =  $menu[0].offsetHeight;
-
-              var windowWidth = $(window).width();
-              var panelLeftPos = $(elem).offset().left;
-              var panelWidth = $(elem).width();
-
-              var menuLeftPos = (panelWidth / 2) - (menuWidth/2);
-              var stickingOut = panelLeftPos + menuLeftPos + menuWidth - windowWidth;
-              if (stickingOut > 0) {
-                menuLeftPos -= stickingOut + 10;
-              }
-              if (panelLeftPos + menuLeftPos < 0) {
-                menuLeftPos = 0;
-              }
-
-              $menu.css({'left': menuLeftPos, top: -menuHeight});
+              teather = new Tether({
+                element: $menu,
+                target: $panelContainer,
+                attachment: 'bottom center',
+                targetAttachment: 'top center',
+                constraints: [
+                  {
+                    to: 'window',
+                    attachment: 'together',
+                    pin: true
+                  }
+                ]
+              });
             });
 
             dismiss(2200);

+ 5 - 5
public/app/features/playlist/partials/playlist.html

@@ -9,17 +9,17 @@
 
 	<div class="gf-form-group">
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-sm">Name</span>
-			<input type="text" required ng-model="ctrl.playlist.name" class="gf-form-input gf-size-max-xxl">
+			<span class="gf-form-label width-7">Name</span>
+			<input type="text" required ng-model="ctrl.playlist.name" class="gf-form-input max-width-21">
 		</div>
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-sm">Interval</span>
-			<input type="text" required ng-model="ctrl.playlist.interval" placeholder="5m" class="gf-form-input gf-size-max-xxl">
+			<span class="gf-form-label width-7">Interval</span>
+			<input type="text" required ng-model="ctrl.playlist.interval" placeholder="5m" class="gf-form-input max-width-21">
 		</div>
 	</div>
 
 	<div class="gf-form-group">
-		<div class="gf-size-max-xxxl">
+		<div class="max-width-28">
 			<h5 class="page-headering">Add dashboards</h5>
 			<div style="">
 				<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>

+ 6 - 6
public/app/features/profile/partials/password.html

@@ -8,18 +8,18 @@
 
 	<form name="userForm" class="gf-form-group">
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-lg">Old Password</span>
-			<input class="gf-form-input gf-size-max-xxl" type="text" required ng-model="command.oldPassword">
+			<span class="gf-form-label width-10">Old Password</span>
+			<input class="gf-form-input max-width-21" type="text" required ng-model="command.oldPassword">
 		</div>
 
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-lg">New Password</span>
-			<input class="gf-form-input gf-size-max-xxl" type="text" required ng-minlength="4"  ng-model="command.newPassword">
+			<span class="gf-form-label width-10">New Password</span>
+			<input class="gf-form-input max-width-21" type="text" required ng-minlength="4"  ng-model="command.newPassword">
 		</div>
 
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-lg">Confirm Password</span>
-			<input class="gf-form-input gf-size-max-xxl" type="text" required ng-minlength="4"  ng-model="command.confirmNew">
+			<span class="gf-form-label width-10">Confirm Password</span>
+			<input class="gf-form-input max-width-21" type="text" required ng-minlength="4"  ng-model="command.confirmNew">
 		</div>
 
 		<div class="gf-form-button-row">

+ 7 - 7
public/app/features/profile/partials/profile.html

@@ -10,19 +10,19 @@
 		<h3 class="page-heading">Preferences</h3>
 
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-sm">Name</span>
-			<input class="gf-form-input gf-size-max-xxl" type="text" required ng-model="user.name" >
+			<span class="gf-form-label width-7">Name</span>
+			<input class="gf-form-input max-width-21" type="text" required ng-model="user.name" >
 		</div>
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-sm">Email</span>
-			<input class="gf-form-input gf-size-max-xxl" type="email" required ng-model="user.email">
+			<span class="gf-form-label width-7">Email</span>
+			<input class="gf-form-input max-width-21" type="email" required ng-model="user.email">
 		</div>
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-sm">Username</span>
-			<input class="gf-form-input gf-size-max-xxl" type="text" required ng-model="user.login">
+			<span class="gf-form-label width-7">Username</span>
+			<input class="gf-form-input max-width-21" type="text" required ng-model="user.login">
 		</div>
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-sm">UI Theme</span>
+			<span class="gf-form-label width-7">UI Theme</span>
 			<select class="gf-form-input gf-size-auto" ng-model="user.theme" ng-options="f for f in ['dark', 'light']"></select>
 		</div>
 

+ 26 - 27
public/app/features/styleguide/styleguide.html

@@ -15,35 +15,34 @@
 		</a>
 	</div>
 
-	<tabset>
-		<tab heading="Colors">
-			<ul>
-				<li class="style-guide-color-card" ng-repeat="color in ctrl.colors" style="background-color: {{color.value}}">
-					<strong>${{color.name}}</strong>
-					<em>{{color.value}}</em>
-				</li>
-			</ul>
-		</tab>
-		<tab heading="Buttons">
-
-			<div class="style-guide-button-list p-a-2">
-				<button class="btn btn-primary">btn-primary</button>
-				<button class="btn btn-info">btn-info</button>
-				<button class="btn btn-inverse">btn-inverse</button>
-				<button class="btn btn-success">btn-success</button>
-				<button class="btn btn-warning">btn-warning</button>
-				<button class="btn btn-danger">btn-danger</button>
-			</div>
-
+	<ul class="nav nav-tabs">
+		<li ng-repeat="page in ctrl.pages" ng-class="{active: ctrl.page[page]}" class="tab">
+			<a href="styleguide/{{page}}">{{page}}</a>
+		</li>
+	</ul>
+
+	<div class="tab-pane" ng-if="ctrl.page.colors">
+		<ul>
+			<li class="style-guide-color-card" ng-repeat="color in ctrl.colors" style="background-color: {{color.value}}">
+				<strong>${{color.name}}</strong>
+				<em>{{color.value}}</em>
+			</li>
+		</ul>
+	</div>
 
-			<div class="style-guide-button-list p-a-2">
-				<button class="btn btn-primary btn-small">btn-small</button>
-				<button class="btn btn-success btn-large">btn-large</button>
+	<div class="tab-pane" ng-if="ctrl.page.buttons">
+		<div ng-repeat="variant in ctrl.buttonVariants" class="row">
+			<div ng-repeat="btnSize in ctrl.buttonSizes" class="style-guide-button-list p-a-2 col-md-4">
+				<button ng-repeat="buttonName in ctrl.buttonNames" class="btn btn{{variant}}{{buttonName}} {{btnSize}}">
+					btn{{variant}}{{buttonName}}
+				</button>
 			</div>
-		</tab>
-		<tab heading="Forms">
-		</tab>
-	</tabset>
+		</div>
+	</div>
+
+	<div class="tab-pane" ng-if="ctrl.page.forms">
+		forms
+	</div>
 
 </div>
 

+ 21 - 3
public/app/features/styleguide/styleguide.ts

@@ -6,12 +6,30 @@ import _ from 'lodash';
 class StyleGuideCtrl {
   colors: any = [];
   theme: string;
+  buttonNames = ['primary', 'secondary', 'inverse', 'success', 'warning', 'danger'];
+  buttonSizes = ['btn-small', '', 'btn-large'];
+  buttonVariants = ['-', '-outline-'];
+  page: any;
+  pages = ['colors', 'buttons', 'forms', 'dashboard', 'query-editors'];
 
   /** @ngInject **/
-  constructor($http) {
+  constructor(private $http, $routeParams) {
     this.theme = config.bootData.user.lightTheme ? 'light': 'dark';
+    this.page = {};
 
-    $http.get('public/sass/styleguide.json').then(res => {
+    if ($routeParams.page) {
+      this.page[$routeParams.page] = 1;
+    } else {
+      this.page.colors = true;
+    }
+
+    if (this.page.colors) {
+      this.loadColors();
+    }
+   }
+
+  loadColors() {
+   this.$http.get('public/sass/styleguide.json').then(res => {
       this.colors = _.map(res.data[this.theme], (value, key) => {
         return {name: key, value: value};
       });
@@ -20,7 +38,7 @@ class StyleGuideCtrl {
 
   switchTheme() {
     var other = this.theme === 'dark' ? 'light' : 'dark';
-    window.location.href = config.appSubUrl + '/styleguide?theme=' + other;
+    window.location.href = window.location.href + '?theme=' + other;
   }
 
 }

+ 1 - 1
public/app/partials/dashboard.html

@@ -100,7 +100,7 @@
 
 		<div ng-show='dashboardMeta.canEdit' class="row-fluid add-row-panel-hint" ng-hide="dashboard.meta.fullscreen">
 			<div class="span12" style="text-align:right;">
-				<span style="margin-right: 10px;" ng-click="addRowDefault()" class="pointer btn btn-info btn-small">
+				<span style="margin-right: 10px;" ng-click="addRowDefault()" class="pointer btn btn-secondary btn-small">
 					<span><i class="fa fa-plus"></i> ADD ROW</span>
 				</span>
 			</div>

+ 7 - 7
public/app/partials/login.html

@@ -1,4 +1,4 @@
-<div class="container">
+<div class="login-container container">
 
 	<div class="login-box">
 
@@ -19,17 +19,17 @@
 
       <form name="loginForm" class="login-form gf-form-group">
 				<div class="gf-form" ng-if="loginMode">
-					<span class="gf-form-label gf-size-sm">User</span>
-					<input type="text" name="username" class="gf-form-input gf-size-max-xl" required ng-model='formModel.user' placeholder={{loginHint}}>
+					<span class="gf-form-label width-7">User</span>
+					<input type="text" name="username" class="gf-form-input max-width-14" required ng-model='formModel.user' placeholder={{loginHint}}>
 				</div>
 				<div class="gf-form" ng-if="loginMode">
-					<span class="gf-form-label gf-size-sm">Password</span>
-					<input type="password" name="password" class="gf-form-input gf-size-max-xl" required ng-model="formModel.password" id="inputPassword" placeholder="password">
+					<span class="gf-form-label width-7">Password</span>
+					<input type="password" name="password" class="gf-form-input max-width-14" required ng-model="formModel.password" id="inputPassword" placeholder="password">
 				</div>
 
 				<div class="gf-form" ng-if="!loginMode">
-						<span class="gf-form-label gf-size-sm">Email</span>
-						<input type="email" class="gf-form-input gf-size-max-xl" required ng-model='formModel.email' placeholder="email">
+						<span class="gf-form-label width-7">Email</span>
+						<input type="email" class="gf-form-input max-width-14" required ng-model='formModel.email' placeholder="email">
 				</div>
 
 				<div class="gf-form-button-row">

+ 9 - 9
public/app/partials/panelgeneral.html

@@ -1,29 +1,29 @@
 <div class="gf-form-group">
 	<div class="gf-form-inline">
-		<div class="gf-form gf-size-max-xxl">
-			<span class="gf-form-label gf-size-sm">Title</span>
+		<div class="gf-form max-width-21">
+			<span class="gf-form-label width-8">Title</span>
 			<input type="text" class="gf-form-input" ng-model='ctrl.panel.title'></input>
 		</div>
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-xs">Span</span>
+			<span class="gf-form-label width-6">Span</span>
 			<select class="gf-form-input gf-size-auto" ng-model="ctrl.panel.span" ng-options="f for f in [0,1,2,3,4,5,6,7,8,9,10,11,12]"></select>
 		</div>
-		<div class="gf-form gf-size-max-xxl">
-			<span class="gf-form-label gf-size-sm">Height</span>
-			<input type="text" class="gf-form-input gf-size-max-xs" ng-model='ctrl.panel.height' placeholder="100px"></input>
+		<div class="gf-form max-width-21">
+			<span class="gf-form-label width-8">Height</span>
+			<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.panel.height' placeholder="100px"></input>
 			<editor-checkbox text="Transparent" model="ctrl.panel.transparent"></editor-checkbox>
 		</div>
 	</div>
 
 	<div class="gf-form-inline">
-		<div class="gf-form gf-size-max-xxl">
-			<span class="gf-form-label gf-size-sm">Repeat Panel</span>
+		<div class="gf-form max-width-21">
+			<span class="gf-form-label width-8">Repeat Panel</span>
 			<select class="gf-form-input" ng-model="ctrl.panel.repeat" ng-options="f.name as f.name for f in ctrl.dashboard.templating.list">
 				<option value=""></option>
 			</select>
 		</div>
 		<div class="gf-form">
-			<span class="gf-form-label gf-size-xs">Min span</span>
+			<span class="gf-form-label width-6">Min span</span>
 			<select class="gf-form-input" ng-model="ctrl.panel.minSpan" ng-options="f for f in [1,2,3,4,5,6,7,8,9,10,11,12]">
 				<option value=""></option>
 			</select>

+ 8 - 8
public/app/partials/signup_invited.html

@@ -21,20 +21,20 @@
 
 			<form name="inviteForm" class="login-form gf-form-group">
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-sm">Email</span>
-					<input type="email" name="email" class="gf-form-input gf-size-max-xxl" required ng-model='formModel.email' placeholder="Email">
+					<span class="gf-form-label width-7">Email</span>
+					<input type="email" name="email" class="gf-form-input max-width-21" required ng-model='formModel.email' placeholder="Email">
 				</div>
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-sm">Name</span>
-					<input type="text" name="name" class="gf-form-input gf-size-max-xxl" ng-model='formModel.name' placeholder="Name (optional)">
+					<span class="gf-form-label width-7">Name</span>
+					<input type="text" name="name" class="gf-form-input max-width-21" ng-model='formModel.name' placeholder="Name (optional)">
 				</div>
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-sm">Username</span>
-					<input type="text" name="username" class="gf-form-input gf-size-max-xxl" required ng-model='formModel.username' placeholder="Username">
+					<span class="gf-form-label width-7">Username</span>
+					<input type="text" name="username" class="gf-form-input max-width-21" required ng-model='formModel.username' placeholder="Username">
 				</div>
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-sm">Password</span>
-					<input type="password" name="password" class="gf-form-input gf-size-max-xxl" required ng-model="formModel.password" id="inputPassword" placeholder="password">
+					<span class="gf-form-label width-7">Password</span>
+					<input type="password" name="password" class="gf-form-input max-width-21" required ng-model="formModel.password" id="inputPassword" placeholder="password">
 				</div>
 
 				<div style="margin-left: 7.5rem; width: 254px;">

+ 10 - 10
public/app/partials/signup_step2.html

@@ -31,28 +31,28 @@
 			<form name="signUpForm" class="login-form gf-form-group">
 
 				<div class="gf-form" ng-if="verifyEmailEnabled">
-					<span class="gf-form-label gf-size-md">
+					<span class="gf-form-label width-9">
 						Email code<tip>Email verification code (sent to your email)</tip>
 					</span>
-					<input type="text" class="gf-form-input gf-size-max-xl" ng-model="formModel.code" required></input>
+					<input type="text" class="gf-form-input max-width-14" ng-model="formModel.code" required></input>
 				</div>
 
 				<div class="gf-form" ng-if="!autoAssignOrg">
-					<span class="gf-form-label gf-size-md">Org. name</span>
-					<input type="text" name="orgName" class="gf-form-input gf-size-max-xl" ng-model='formModel.orgName' placeholder="Name your organization">
+					<span class="gf-form-label width-9">Org. name</span>
+					<input type="text" name="orgName" class="gf-form-input max-width-14" ng-model='formModel.orgName' placeholder="Name your organization">
 				</div>
 
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-md">Your name</span>
-					<input type="text" name="name" class="gf-form-input gf-size-max-xl" ng-model='formModel.name' placeholder="(optional)">
+					<span class="gf-form-label width-9">Your name</span>
+					<input type="text" name="name" class="gf-form-input max-width-14" ng-model='formModel.name' placeholder="(optional)">
 				</div>
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-md">Username</span>
-					<input type="text" class="gf-form-input gf-size-max-xl" required ng-model='formModel.username' placeholder="Username" autocomplete="off">
+					<span class="gf-form-label width-9">Username</span>
+					<input type="text" class="gf-form-input max-width-14" required ng-model='formModel.username' placeholder="Username" autocomplete="off">
 				</div>
 				<div class="gf-form">
-					<span class="gf-form-label gf-size-md">Password</span>
-					<input type="password" class="gf-form-input gf-size-max-xl" required ng-model="formModel.password" id="inputPassword" placeholder="password" autocomplete="off">
+					<span class="gf-form-label width-9">Password</span>
+					<input type="password" class="gf-form-input max-width-14" required ng-model="formModel.password" id="inputPassword" placeholder="password" autocomplete="off">
 				</div>
 
 				<div style="margin-left: 7.5rem; width: 254px;">

+ 18 - 40
public/app/plugins/datasource/cloudwatch/partials/config.html

@@ -1,44 +1,22 @@
-<br>
-<h5>CloudWatch details</h5>
+<h3 class="page-heading">CloudWatch details</h3>
 
-<div class="editor-row">
-	<div class="section tight-form-container" style="margin-bottom: 20px">
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 200px">
-					Credentials profile name<tip>Credentials profile name, as specified in ~/.aws/credentials, leave blank for default</tip>
-				</li>
-				<li>
-					<input type="text" class="tight-form-input input-large last" ng-model='ctrl.current.database' placeholder="default"></input>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 200px">
-					Default Region<tip>Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region.</tip>
-				</li>
-        <!--
-          Whenever this list is updated, backend list should also be updated.
-          Please update the region list in pkg/api/cloudwatch/metric.go
-				-->
-        <li>
-          <select class="tight-form-input input-large last" ng-model="ctrl.current.jsonData.defaultRegion" ng-options="region for region in ['ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'cn-north-1', 'eu-central-1', 'eu-west-1', 'sa-east-1', 'us-east-1', 'us-west-1', 'us-west-2']"></select>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
-		</div>
-		<div class="tight-form">
-			<ul class="tight-form-list">
-				<li class="tight-form-item" style="width: 200px">
-					Custom Metrics namespace<tip>Namespaces of Custom Metrics</tip>
-				</li>
-				<li>
-					<input type="text" class="tight-form-input input-large last" ng-model='ctrl.current.jsonData.customMetricsNamespaces' placeholder="Namespace1,Namespace2"></input>
-				</li>
-			</ul>
-			<div class="clearfix"></div>
+<div class="gf-form-group">
+	<div class="gf-form">
+		<label class="gf-form-label width-14">
+			Credentials profile name<tip>Credentials profile name, as specified in ~/.aws/credentials, leave blank for default</tip>
+		</label>
+		<input type="text" class="gf-form-input max-width-15" ng-model='ctrl.current.database' placeholder="default"></input>
+	</div>
+	<div class="gf-form">
+		<label class="gf-form-label width-14">
+			Default Region<tip>Specify the region, such as for US West (Oregon) use ` us-west-2 ` as the region.</tip>
+		</label>
+		<div class="gf-form-select-wrapper">
+			<select class="gf-form-input max-width-15" ng-model="ctrl.current.jsonData.defaultRegion" ng-options="region for region in ['ap-northeast-1', 'ap-northeast-2', 'ap-southeast-1', 'ap-southeast-2', 'cn-north-1', 'eu-central-1', 'eu-west-1', 'sa-east-1', 'us-east-1', 'us-west-1', 'us-west-2']"></select>
 		</div>
 	</div>
+	<div class="gf-form">
+		<label class="gf-form-label width-14">Custom Metrics namespace<tip>Namespaces of Custom Metrics</tip></label>
+		<input type="text" class="gf-form-input max-width-15" ng-model='ctrl.current.jsonData.customMetricsNamespaces' placeholder="Namespace1,Namespace2"></input>
+	</div>
 </div>

+ 8 - 8
public/app/plugins/datasource/elasticsearch/partials/config.html

@@ -5,24 +5,24 @@
 
 <div class="gf-form-group">
 	<div class="gf-form-inline">
-		<div class="gf-form gf-size-max-xxxl">
-			<span class="gf-form-label gf-size-md">Index name</span>
+		<div class="gf-form max-width-19">
+			<span class="gf-form-label width-9">Index name</span>
 			<input class="gf-form-input" type="text" ng-model='ctrl.current.database' placeholder="" required></input>
 		</div>
 
-		<div class="gf-form gf-size-xl">
-			<span class="gf-form-label gf-size-md">Pattern</span>
+		<div class="gf-form width-14">
+			<span class="gf-form-label width-9">Pattern</span>
 			<select class="gf-form-input gf-size-auto" ng-model="ctrl.current.jsonData.interval" ng-options="f.value as f.name for f in ctrl.indexPatternTypes" ng-change="ctrl.indexPatternTypeChanged()" ></select>
 		</div>
 	</div>
 
-	<div class="gf-form gf-size-max-xxxl">
-		<span class="gf-form-label gf-size-md">Time field name</span>
+	<div class="gf-form max-width-28">
+		<span class="gf-form-label width-9">Time field name</span>
 		<input class="gf-form-input" type="text"  ng-model='ctrl.current.jsonData.timeField' placeholder="" required ng-init=""></input>
 	</div>
 
 	<div class="gf-form">
-		<span class="gf-form-label gf-size-md">Version</span>
+		<span class="gf-form-label width-9">Version</span>
 		<select class="gf-form-input gf-size-auto" ng-model="ctrl.current.jsonData.esVersion" ng-options="f.value as f.name for f in ctrl.esVersions"></select>
 	</div>
 
@@ -32,6 +32,6 @@
 <div class="gf-form-group">
 	<div class="gf-form">
 		<span class="gf-form-label">Group by time interval</span>
-		<input class="gf-form-input gf-size-max-md" type="text" ng-model="ctrl.current.jsonData.timeInterval" spellcheck='false' placeholder="example: >10s">
+		<input class="gf-form-input max-width-9" type="text" ng-model="ctrl.current.jsonData.timeInterval" spellcheck='false' placeholder="example: >10s">
 	</div>
 </div>

+ 26 - 45
public/app/plugins/datasource/influxdb/partials/config.html

@@ -1,52 +1,33 @@
 <datasource-http-settings current="ctrl.current">
 </datasource-http-settings>
 
-<h4>InfluxDB Details</h4>
+<h3 class="page-heading">InfluxDB Details</h3>
 
-<div class="tight-form">
-	<ul class="tight-form-list">
-		<li class="tight-form-item" style="width: 80px">
-			Database
-		</li>
-		<li>
-			<input type="text" class="tight-form-input input-large" ng-model='ctrl.current.database' placeholder="" required></input>
-		</li>
-	</ul>
-	<div class="clearfix"></div>
-</div>
-<div class="tight-form last">
-	<ul class="tight-form-list">
-		<li class="tight-form-item" style="width: 80px">
-			User
-		</li>
-		<li>
-			<input type="text" class="tight-form-input input-large" ng-model='ctrl.current.user' placeholder="" required></input>
-		</li>
-		<li class="tight-form-item">
-			Password
-		</li>
-		<li>
-			<input type="password" class="tight-form-input input-large" ng-model='ctrl.current.password' placeholder="" required></input>
-		</li>
-	</ul>
-	<div class="clearfix"></div>
-</div>
+<div class="gf-form-group">
+	<div class="gf-form-inline">
+		<div class="gf-form max-width-30">
+			<span class="gf-form-label width-7">Database</span>
+			<input type="text" class="gf-form-input" ng-model='ctrl.current.database' placeholder="" required></input>
+		</div>
+	</div>
 
+	<div class="gf-form-inline">
+		<div class="gf-form max-width-15">
+			<span class="gf-form-label width-7">User</span>
+			<input type="text" class="gf-form-input" ng-model='ctrl.current.user' placeholder="" required></input>
+		</div>
+		<div class="gf-form max-width-15">
+			<span class="gf-form-label width-7">Password</span>
+			<input type="password" class="gf-form-input" ng-model='ctrl.current.password' placeholder="" required></input>
+		</div>
+	</div>
+</div>
 
-<br>
-<h4>Default query settings</h4>
-<div class="tight-form last">
-	<ul class="tight-form-list">
-		<li class="tight-form-item" style="width: 200px">
-			Group by time interval
-		</li>
-		<li>
-			<input type="text" class="input-medium tight-form-input input-xlarge" ng-model="ctrl.current.jsonData.timeInterval"
-			spellcheck='false' placeholder="example: >10s">
-		</li>
-		<li class="tight-form-item">
-			<i class="fa fa-question-circle" bs-tooltip="'Set a low limit by having a greater sign: example: >10s'" data-placement="right"></i>
-		</li>
-	</ul>
-	<div class="clearfix"></div>
+<div class="gf-form-group">
+	<div class="gf-form max-width-21">
+		<span class="gf-form-label">Default group by time</span>
+		<input type="text" class="gf-form-input width-6" ng-model="ctrl.current.jsonData.timeInterval"
+		spellcheck='false' placeholder="example: >10s"></input>
+		<i class="fa fa-question-circle" bs-tooltip="'Set a low limit by having a greater sign: example: >10s'" data-placement="right"></i>
+	</div>
 </div>

+ 24 - 15
public/app/plugins/panel/text/editor.html

@@ -1,17 +1,26 @@
-<div>
-  <div class="row-fluid">
-    <div class="span4">
-      <label class="small">Mode</label> <select class="input-medium" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
-    </div>
-    <div class="span2" ng-show="ctrl.panel.mode == 'text'">
-      <label class="small">Font Size</label> <select class="input-mini" ng-model="ctrl.panel.style['font-size']" ng-options="f for f in ['6pt','7pt','8pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select>
-    </div>
-  </div>
+<div class="gf-form-group">
+	<div class="gf-form-inline">
+		<div class="gf-form">
+			<span class="gf-form-label">Mode</span>
+			<span class="gf-form-select-wrapper">
+				<select class="gf-form-input" ng-model="ctrl.panel.mode" ng-options="f for f in ['html','markdown','text']"></select>
+			</span>
+		</div>
 
-  <label class=small>Content
-    <span ng-show="ctrl.panel.mode == 'markdown'">(This area uses <a target="_blank" href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>. HTML is not supported)</span>
-  </label>
-
-  <textarea ng-model="ctrl.panel.content" rows="20" style="width:95%" ng-change="ctrl.render()" ng-model-onblur>
-  </textarea>
+		<div class="gf-form" ng-show="ctrl.panel.mode == 'text'">
+			<span class="gf-form-label">Font Size</span>
+			<span class="gf-form-select-wrapper">
+				<select class="gf-form-input" ng-model="ctrl.panel.style['font-size']" ng-options="f for f in ['6pt','7pt','8pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select>
+			</span>
+		</div>
+	</div>
 </div>
+
+<h3 class="page-heading">Content</h3>
+
+<span ng-show="ctrl.panel.mode == 'markdown'">
+	(This area uses <a target="_blank" href="http://en.wikipedia.org/wiki/Markdown">Markdown</a>. HTML is not supported)
+</span>
+
+<textarea class="gf-form-input" ng-model="ctrl.panel.content" rows="20" style="width:95%" ng-change="ctrl.render()" ng-model-onblur>
+</textarea>

+ 2 - 1
public/app/plugins/panel/text/module.html

@@ -1 +1,2 @@
-<p ng-bind-html="ctrl.content" ng-show="ctrl.content"></p>
+<p class="panel-text-content" ng-bind-html="ctrl.content" ng-show="ctrl.content">
+</p>

+ 1 - 0
public/app/system.conf.js

@@ -2,6 +2,7 @@ System.config({
   defaultJSExtenions: true,
   baseURL: 'public',
   paths: {
+    'tether': 'vendor/npm/tether/dist/js/tether.js',
     'moment': 'vendor/moment.js',
     "jquery": "vendor/jquery/dist/jquery.js",
     'lodash-src': 'vendor/lodash.js',

BIN
public/img/angle_gradient_light_rev.png


BIN
public/img/angle_gradient_rev.png


BIN
public/img/checkbox.png


BIN
public/img/transparent.png


+ 80 - 26
public/sass/.sass-lint.yml

@@ -1,33 +1,87 @@
+options:
+  formatter: stylish
+
 files:
   include: '**/*.s+(a|c)ss'
   ignore:
+    - './utils/*.scss'
 
 rules:
+  # Extends
+  extends-before-mixins: 0
+  extends-before-declarations: 0
+  placeholder-in-extend: 0
+
+  # Mixins
+  mixins-before-declarations: 0
+
+  # Line Spacing
+  one-declaration-per-line: 0
   empty-line-between-blocks: 0
+  single-line-per-selector: 0
+
+  # Disallows
+  no-color-keywords: 0
+  no-color-literals: 0
+  no-css-comments: 0
+  no-debug: 0
+  no-duplicate-properties: 0
   no-empty-rulesets: 1
-  # Consistency Rules
-  # extends-before-mixins: 1
-  # extends-before-declarations: 1
-  # mixins-before-declarations: 1
-  # Require an empty line between blocks
-  # final-newline: 1
-  # no-ids: 1
-  # indentation: 0
-  # leading-zero: 1
-  # name-format: 0
-  # nesting-depth: 1
-  # placeholder-in-extend: 1
-  # property-sort-order: 1
-  # property-spelling: 0
-  # shorthand: 1
-  # one-declaration-per-line: 1
-  # single-line-per-selector: 1
-  # space-after-comma: 1
-  # # Space surrounding colons
-  # space-before-colon: 1
-  # space-after-colon: 1
-  #
-  # space-before-brace: 1
-  #
-  # space-between-parens: 1
-  # trailing-semicolon: 1
+  no-extends: 0
+  no-ids: 0
+  no-important: 0
+  no-invalid-hex: 0
+  no-mergeable-selectors: 0
+  no-misspelled-properties: 0
+  no-qualifying-elements: 0
+  no-trailing-zero: 0
+  no-transition-all: 0
+  no-url-protocols: 0
+  no-vendor-prefixes: 0
+  no-warn: 0
+  property-units: 0
+
+  # Nesting
+  force-attribute-nesting: 0
+  force-element-nesting: 0
+  force-pseudo-nesting: 0
+
+  # Name Formats
+  class-name-format: 0
+  function-name-format: 0
+  id-name-format: 0
+  mixin-name-format: 0
+  placeholder-name-format: 0
+  variable-name-format: 0
+
+  # Style Guide
+  bem-depth: 0
+  border-zero: 0
+  brace-style: 0
+  clean-import-paths: 0
+  empty-args: 0
+  hex-length: 0
+  hex-notation: 0
+  indentation: 0
+  leading-zero: 0
+  nesting-depth: 0
+  property-sort-order: 0
+  quotes: 0
+  shorthand-values: 0
+  url-quotes: 0
+  variable-for-property: 0
+  zero-unit: 0
+
+  # Inner Spacing
+  space-after-comma: 0
+  space-before-colon: 0
+  space-after-colon: 0
+  space-before-brace: 0
+  space-before-bang: 0
+  space-after-bang: 0
+  space-between-parens: 0
+  space-around-operator: 0
+
+  # Final Items
+  trailing-semicolon: 0
+  final-newline: 0

+ 3 - 0
public/sass/_grafana.scss

@@ -1,5 +1,6 @@
 // MIXINS
 @import "mixins/mixins";
+@import "mixins/animations";
 @import "mixins/buttons";
 @import "mixins/breakpoints";
 @import "mixins/grid";
@@ -21,6 +22,7 @@
 @import "utils/validation";
 @import "utils/angular";
 @import "utils/spacings";
+@import "utils/widths";
 
 // LAYOUTS
 @import "layout/page";
@@ -37,6 +39,7 @@
 @import "components/panel_dashlist";
 @import "components/panel_singlestat";
 @import "components/panel_table";
+@import "components/panel_text";
 @import "components/tagsinput";
 @import "components/tables_lists";
 @import "components/search";

+ 5 - 5
public/sass/_old_responsive.scss

@@ -7,8 +7,8 @@
 // ---------------------
 @include media-breakpoint-down(sm) {
   div.panel {
-    width: 100% !important;
-    padding: 0px !important;
+    width: 100%;
+    padding: 0px;
   }
   .panel-margin {
     margin-right: 0;
@@ -17,7 +17,7 @@
   body {
     padding: 0;
   }
-  .dashnav-dashboards-btn a {
+  .page-dashboard .navbar-page-btn {
     max-width: 200px;
   }
   .gf-timepicker-nav-btn {
@@ -34,7 +34,7 @@
 
 // form styles
 @include media-breakpoint-up(md) {
-  .dashnav-dashboards-btn a {
+  .page-dashboard .navbar-page-btn {
     max-width: 180px;
   }
   .gf-timepicker-nav-btn {
@@ -51,7 +51,7 @@
 }
 
 @include media-breakpoint-up(lg) {
-  .dashnav-dashboards-btn a {
+  .page-dashboard .navbar-page-btn {
     max-width: 290px;
   }
   .gf-timepicker-nav-btn {

+ 26 - 25
public/sass/_variables.dark.scss

@@ -8,17 +8,17 @@ $black:                 #000;
 
 // -------------------------
 $black:            #000;
-$dark-1:           #141414; // (body)
-$dark-2:           #1f1d1d; // ($gray-darker)
-$dark-3:           #262626; // ($gray-dark)
+$dark-1:           #141414;
+$dark-2:           #1f1d1d;
+$dark-3:           #292929;
 $dark-4:           #333333;
 $dark-5:           #444444;
 $gray-1:           #555555;
-$gray-2:           #6e7580;
+$gray-2:           #7B7B7B;
 $gray-3:           #b3b3b3;
-$gray-4:           #cfd4d9;
-$gray-5:           #e8edf0;
-$gray-6:           #f7f9fa;
+$gray-4:           #D8D9DA;
+$gray-5:           #ECECEC;
+$gray-6:           #f4f5f8;
 $gray-7:           #fbfbfb;
 
 $white:            #fff;
@@ -92,8 +92,8 @@ $modal-background: $black;
 $code-tag-bg: #444;
 
 // Lists
-$grafanaListBackground:  	$dark-3;
-$grafanaListAccent:      	lighten($dark-2, 2%);
+$grafanaListBackground:  	  $dark-3;
+$grafanaListAccent:      	  lighten($dark-2, 2%);
 $grafanaListBorderTop:      $dark-3;
 $grafanaListBorderBottom:   $black;
 $grafanaListHighlight:      #333;
@@ -117,20 +117,20 @@ $table-border:                $dark-3; // table and cell border
 $btn-primary-bg:              $brand-primary;
 $btn-primary-bg-hl:           lighten($brand-primary, 8%);
 
-$btn-info-bg:                 lighten($purple, 3%);
-$btn-info-bg-hl:     		  darken($purple, 3%);
+$btn-secondary-bg:            $blue-dark;
+$btn-secondary-bg-hl:     		lighten($blue-dark, 3%);
 
-$btn-success-bg-hl:     	  darken($green, 3%);
+$btn-success-bg-hl:     	    darken($green, 3%);
 $btn-success-bg:              lighten($green, 3%);
 
 $btn-warning-bg:              $brand-warning;
-$btn-warning-bg-hl:     	  lighten($brand-warning, 8%);
+$btn-warning-bg-hl:     	    lighten($brand-warning, 8%);
 
 $btn-danger-bg:               lighten($red, 3%);
 $btn-danger-bg-hl:            darken($red, 3%);
 
 $btn-inverse-bg:              $dark-3;
-$btn-inverse-bg-hl:     	  lighten($dark-3, 1%);
+$btn-inverse-bg-hl:     	    lighten($dark-3, 1%);
 $btn-inverse-text-color:      $link-color;
 
 $btn-link-color:              $gray-3;
@@ -139,19 +139,19 @@ $iconContainerBackground:	  $black;
 
 // Forms
 // -------------------------
-$input-bg:                       lighten($dark-3,5%);
-$input-bg-disabled:              #555;
+$input-bg:                       $dark-4;
+$input-bg-disabled:              $dark-3;
 
 $input-color:                    $gray-4;
-$input-border-color:             lighten($dark-2,5%);
-$input-box-shadow:               inset 0 1px 1px rgba(0,0,0,.075) !default;
+$input-border-color:             $dark-4;
+$input-box-shadow:               inset 1px 0px 0.5rem 0px rgba(200, 200, 200, 0.10);
 $input-border-focus:             $input-border-color !default;
 $input-box-shadow-focus:         rgba(102,175,233,.6) !default;
-$input-color-placeholder:        #999 !default;
-$input-label-bg:				 $dark-3;
+$input-color-placeholder:        $gray-1 !default;
+$input-label-bg:				         $dark-3;
 
 // Search
-$searchShadow: 0 0 35px 0 $body-bg;
+$search-shadow: 0 0 35px 0 $body-bg;
 
 // Dropdowns
 // -------------------------
@@ -166,8 +166,8 @@ $dropdownLinkColor:             $text-color;
 $dropdownLinkColorHover:        $white;
 $dropdownLinkColorActive:       $white;
 
-$dropdownLinkBackgroundActive:  $blue-dark;
-$dropdownLinkBackgroundHover:   $blue-dark;
+$dropdownLinkBackgroundActive:  $dark-4;
+$dropdownLinkBackgroundHover:   $dark-4;
 
 
 // COMPONENT VARIABLES
@@ -206,7 +206,8 @@ $navbarButtonBackgroundHighlight: lighten($navbarBackground, 5%);
 // Sidemenu
 // -------------------------
 $side-menu-bg:            $body-bg;
-$side-menu-item-hover-bg: $dark-4;
+$side-menu-item-hover-bg: $dark-3;
+$side-menu-opacity:		  0.97;
 
 // Pagination
 // -------------------------
@@ -226,7 +227,7 @@ $successText:             #468847;
 $successBackground:       $btn-success-bg;
 
 $infoText:                $blue-dark;
-$infoBackground:          $btn-info-bg;
+$infoBackground:          $blue-dark;
 
 // Tooltips and popovers
 // -------------------------

+ 16 - 17
public/sass/_variables.light.scss

@@ -13,10 +13,10 @@ $black:                 #000;
 
 // -------------------------
 $black:            #000;
-$dark-1:           #141414; // (body)
-$dark-2:           #1f1d1d; // ($gray-darker)
-$dark-3:           #262626; // ($gray-dark)
-$dark-4:           #333333;
+$dark-1:           #141414;
+$dark-2:           #1f1d1d;
+$dark-3:           #292929;
+$dark-4:           #373737;
 $dark-5:           #444444;
 $gray-1:           #555555;
 $gray-2:           #7B7B7B;
@@ -31,7 +31,7 @@ $white:            #fff;
 // Accent colors
 // -------------------------
 $blue:             	   	#2AB2E4;
-$blue-dark:             #75CAEB;
+$blue-dark:             #3CAAD6;
 $green:                 #28B62C;
 $red:                   #FF4136;
 $yellow:                #FF851B;
@@ -125,8 +125,8 @@ $scrollbarBorder:       $gray-4;
 $btn-primary-bg:        $brand-primary;
 $btn-primary-bg-hl:     lighten($brand-primary, 8%);
 
-$btn-info-bg:           lighten($purple, 3%);
-$btn-info-bg-hl:        darken($purple, 3%);
+$btn-secondary-bg:      $blue-dark;
+$btn-secondary-bg-hl:   lighten($blue-dark, 4%);
 
 $btn-success-bg:        lighten($green, 3%);
 $btn-success-bg-hl:     darken($green, 3%);
@@ -156,15 +156,16 @@ $input-box-shadow:               none;
 $input-border-focus:             $blue !default;
 $input-box-shadow-focus:         $blue !default;
 $input-color-placeholder:        $gray-4 !default;
-$input-label-bg:	        	     $gray-5;
+$input-label-bg:	        	     $gray-6;
 
 // Sidemenu
 // -------------------------
 $side-menu-bg:            $body-bg;
 $side-menu-item-hover-bg: $gray-6;
+$side-menu-opacity:		  0.97;
 
 // search
-$searchShadow: 0 5px 30px 0 lighten($gray-2, 30%);
+$search-shadow: 0 5px 30px 0 lighten($gray-2, 30%);
 
 // Dropdowns
 // -------------------------
@@ -176,11 +177,11 @@ $dropdownDivider:               $dropdownDividerTop;
 $dropdownTitle:                 $dark-5;
 
 $dropdownLinkColor:             $dark-3;
-$dropdownLinkColorHover:        $white;
-$dropdownLinkColorActive:       $white;
+$dropdownLinkColorHover:        $link-color;
+$dropdownLinkColorActive:       $link-color;
 
-$dropdownLinkBackgroundActive:  $blue;
-$dropdownLinkBackgroundHover:   $blue;
+$dropdownLinkBackgroundActive:  $gray-6;
+$dropdownLinkBackgroundHover:   $gray-6;
 
 
 // COMPONENT VARIABLES
@@ -250,8 +251,8 @@ $successText:             lighten($green, 10%);
 $successBackground:       $green;
 $successBorder:           transparent;
 
-$infoText:                lighten($purple,10%);
-$infoBackground:          $purple;
+$infoText:                $blue;
+$infoBackground:          $blue-dark;
 $infoBorder:              transparent;
 
 
@@ -274,5 +275,3 @@ $popoverArrowOuterColor:  rgba(0,0,0,.25);
 
 // images
 $checkboxImageUrl: '../img/checkbox_white.png';
-
-

+ 9 - 15
public/sass/_variables.scss

@@ -77,17 +77,6 @@ $grid-gutter-width: 30px !default;
 
 $enable-flex: false;
 
-$form-sizes: (
-  xs: 5.7rem,
-  sm: 7rem,
-  md: 8.5rem,
-  lg: 10rem,
-  xl: 14rem,
-  xxl: 21rem,
-  xxxl: 28rem
-) !default;
-
-
 // Typography
 // -------------------------
 
@@ -170,9 +159,13 @@ $input-padding-y:                .6rem !default;
 $input-line-height:              1.35rem !default;
 
 $input-btn-border-width:         1px;
-$input-border-radius:            $border-radius !default;
-$input-border-radius-lg:         $border-radius-lg !default;
-$input-border-radius-sm:         $border-radius-sm !default;
+$input-border-radius:            0 $border-radius $border-radius 0 !default;
+$input-border-radius-lg:         0 $border-radius-lg $border-radius-lg 0 !default;
+$input-border-radius-sm:         0 $border-radius-sm $border-radius-sm 0 !default;
+
+$label-border-radius:            $border-radius 0 0 $border-radius !default;
+$label-border-radius-lg:         $border-radius-lg 0 0 $border-radius-lg !default;
+$label-border-radius-sm:         $border-radius-sm 0 0 $border-radius-sm !default;
 
 $input-padding-x-sm:             .5rem !default;
 $input-padding-y-sm:             .25rem !default;
@@ -185,6 +178,7 @@ $input-height-lg:                (($font-size-lg * $line-height-lg) + ($input-pa
 $input-height-sm:                (($font-size-sm * $line-height-sm) + ($input-padding-y-sm * 2)) !default;
 
 $form-group-margin-bottom:       $spacer-y !default;
+$gf-form-margin: 0.2rem;
 
 $cursor-disabled:                not-allowed !default;
 
@@ -221,4 +215,4 @@ $btn-padding-y-lg:               .75rem !default;
 $btn-border-radius:              3px;
 
 // sidemenu
-$side-menu-width:  15rem;
+$side-menu-width:  14rem;

+ 8 - 0
public/sass/base/_fonts.scss

@@ -24,6 +24,14 @@
     -moz-osx-font-smoothing: grayscale;
 }
 
+.inline-icon-gf {
+    vertical-align: middle;
+}
+
+
+
+
+
 .icon-gf-raintank_wordmark:before {
     content: "\e600";
 }

+ 2 - 2
public/sass/base/_forms.scss

@@ -178,7 +178,7 @@ input[type="checkbox"].cr1 {
 
 label.cr1 {
   display: inline-block;
-  height: 19px;
+  height: 18px;
   position: relative;
   clear: none;
   text-indent: 2px;
@@ -190,7 +190,7 @@ label.cr1 {
 }
 
 input[type="checkbox"]:checked+label {
-  background: url($checkboxImageUrl) 0px -21px no-repeat;
+  background: url($checkboxImageUrl) 0px -18px no-repeat;
 }
 
 .gf-fluid-input {

+ 0 - 2
public/sass/base/_reboot.scss

@@ -270,11 +270,9 @@ th {
 //
 // Forms
 //
-
 label {
   // Allow labels to use `margin` for spacing.
   display: inline-block;
-  margin-bottom: .5rem;
 }
 
 // Work around a Firefox/IE bug where the transparent `button` background

+ 4 - 4
public/sass/components/_alerts.scss

@@ -48,17 +48,17 @@
   position: absolute;
   top: -4px;
   right: -2px;
-  width: 18px;
-  height: 18px;
+  width: 16px;
+  height: 16px;
   padding: 0;
   background: $white;
   border-radius: 50%;
   border: none;
   font-size: 1.1rem;
-  color: $gray-2;
+  color: $dark-4;
   .fa {
     position: relative;
-    top: -2px;
+    top: -3px;
   }
 }
 

+ 9 - 6
public/sass/components/_buttons.scss

@@ -65,7 +65,7 @@
 }
 
 .btn-link {
-  color: $btn-link-color;   
+  color: $btn-link-color;
 }
 
 // Set the backgrounds
@@ -73,6 +73,9 @@
 .btn-primary {
   @include buttonBackground($btn-primary-bg, $btn-primary-bg-hl);
 }
+.btn-secondary {
+  @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl);
+}
 // Warning appears are orange
 .btn-warning {
   @include buttonBackground($btn-warning-bg, $btn-warning-bg-hl);
@@ -86,8 +89,8 @@
   @include buttonBackground($btn-success-bg, $btn-success-bg-hl);
 }
 // Info appears as a neutral blue
-.btn-info {
-  @include buttonBackground($btn-info-bg, $btn-info-bg-hl);
+.btn-secondary {
+  @include buttonBackground($btn-secondary-bg, $btn-secondary-bg-hl);
 }
 // Inverse appears as dark gray
 .btn-inverse {
@@ -100,10 +103,10 @@
   @include button-outline-variant($btn-primary-bg);
 }
 .btn-outline-secondary {
-  @include button-outline-variant($btn-inverse-bg);
+  @include button-outline-variant($btn-secondary-bg);
 }
-.btn-outline-info {
-  @include button-outline-variant($btn-info-bg);
+.btn-outline-inverse {
+  @include button-outline-variant($btn-inverse-bg);
 }
 .btn-outline-success {
   @include button-outline-variant($btn-success-bg);

+ 4 - 4
public/sass/components/_dashboard.scss

@@ -58,7 +58,7 @@
 
 .row-open {
   margin-top: 5px;
-  left: -28px;
+  left: -30px;
   position: absolute;
   z-index: 100;
   transition: .10s left;
@@ -233,12 +233,12 @@ div.flot-text {
 }
 
 .panel-highlight  {
-  @include box-shadow("inset 0 1px 1px rgba(0,0,0,.075), 0 0 5px rgba(82,168,236, 0.8)");
+  box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 5px rgba(82,168,236,10.8)
 }
 
 .on-drag-hover {
   .panel-container {
-    @include box-shadow("inset 0 1px 1px rgba(0,0,0,.075), 0 0 5px rgba(82,168,236, 0.8)");
+    box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 5px rgba(82,168,236,10.8)
   }
 }
 
@@ -281,7 +281,7 @@ div.flot-text {
     border-top: 0;
     border-right: 0;
     border-left: 0;
-    border-bottom: 2px solid transparent;
+    border-bottom: 1px solid transparent;
     padding: 1.2rem .5rem .4rem .5rem;
   }
 }

+ 32 - 16
public/sass/components/_gf-form.scss

@@ -1,4 +1,5 @@
-$gf-form-margin: 0.4rem;
+$gf-form-margin: 0.25rem;
+$gf-form-label-margin: 0.25rem;
 
 .gf-form {
   margin-bottom: $gf-form-margin;
@@ -49,26 +50,16 @@ $gf-form-margin: 0.4rem;
   background-color: $input-label-bg;
   display: block;
   font-size: $font-size-sm;
-  margin-right: $gf-form-margin;
+  margin-right: $gf-form-label-margin;
+
   border: $input-btn-border-width solid transparent;
+  @include border-radius($label-border-radius-sm);
 }
 
 .gf-form-checkbox {
   flex-shrink: 0;
 }
 
-@each $size, $value in $form-sizes {
-  .gf-size-#{$size} { width: $value; }
-
-  .gf-size-max-#{$size} {
-    flex-grow: 1;
-    max-width: $value;
-  }
-}
-
-.gf-size-max { width: 100%; }
-.gf-size-auto { width: auto; }
-
 .gf-form-input {
   display: block;
   width: 100%;
@@ -80,7 +71,7 @@ $gf-form-margin: 0.4rem;
   background-image: none;
   background-clip: padding-box;
   border: $input-btn-border-width solid $input-border-color;
-  @include border-radius($input-border-radius);
+  @include border-radius($input-border-radius-sm);
   @include box-shadow($input-box-shadow);
   transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
 
@@ -96,7 +87,6 @@ $gf-form-margin: 0.4rem;
   // Placeholder
   &::placeholder {
     color: $input-color-placeholder;
-    // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526.
     opacity: 1;
   }
 
@@ -114,6 +104,32 @@ $gf-form-margin: 0.4rem;
   &.gf-size-auto { width: auto; }
 }
 
+.gf-form-select-wrapper {
+  position: relative;
+
+  select.gf-form-input {
+    padding-right: $input-padding-x*2;
+    -webkit-appearance: none;
+    -moz-appearance: menulist-text; // was set to "window" and caused odd display on windos and linux.
+    appearance: none;
+
+    &:-moz-focusring {
+      color: transparent;
+    }
+  }
+
+  &:after {
+    position: absolute;
+    top: 35%;
+    right: $input-padding-x/2;
+    background-color: transparent;
+    color: $input-color;
+    font: normal normal normal $font-size-sm/1 FontAwesome;
+    content: '\f0d7';
+    pointer-events: none;
+  }
+}
+
 .gf-form-btn {
   padding: $input-padding-y $input-padding-x;
   line-height: $input-line-height;

+ 31 - 66
public/sass/components/_navbar.scss

@@ -56,52 +56,30 @@
   background-color: $navbarLinkBackgroundActive;
 }
 
-
-.fa.top-nav-breadcrumb-icon {
-  margin: 18px 0 6px 5px;
-  float: left;
-  font-size: 120%;
-  color: $text-color;
-}
-
-.top-nav-btn {
+.navbar-brand-btn {
   display: block;
   position: relative;
   float: left;
   margin: 0;
-  font-size: 1.4em;
   border-right: 1px solid $tight-form-border;
+  background-color: $navbarButtonBackground;
+  padding: 0.6rem 1.0rem 0.5rem 1rem;
 
   .fa-caret-down {
-    font-size: 55%;
-    position: absolute;
-    right: 8px;
-    top: 23px;
-    margin-right: 2px;
+    font-size: 70%;
   }
 
-  a {
-    color: darken($link-color, 5%);
-    background-color: $navbarButtonBackground;
-    display: inline-block;
-    &:hover {
-      background: $navbarButtonBackgroundHighlight;
-      color: $link-color;
-      // border-bottom: 1px solid $blue;
-    }
+  &:hover {
+    background: $navbarButtonBackgroundHighlight;
   }
-}
 
-.top-nav-menu-btn {
-  a {
-     padding: 7px 20px 6px 13px;
-  }
   img {
     width: 30px;
     position: relative;
     top: -2px;
   }
-  .top-nav-logo-background {
+
+  .navbar-brand-btn-background {
     display: inline-block;
     border: 1px solid $body-bg;
     padding: 4px;
@@ -110,6 +88,7 @@
     width: 40px;
     height: 40px;
   }
+
   .icon-gf-grafana_wordmark {
     font-size: 21px;
     position: relative;
@@ -119,13 +98,28 @@
   }
 }
 
-.dashnav-dashboards-btn {
-  a {
-    text-overflow: ellipsis;
-    overflow: hidden;
-    white-space: nowrap;
-    display: block;
-    padding: 11px 17px 12px 13px;
+.navbar-page-btn {
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+  float: left;
+  display: block;
+  margin: 0;
+  font-size: 1.4em;
+  border-right: 1px solid $tight-form-border;
+  color: darken($link-color, 5%);
+  background-color: $navbarButtonBackground;
+  font-size: $font-size-lg;
+  padding: 1rem 0.8rem;
+
+  &:hover {
+    background: $navbarButtonBackgroundHighlight;
+    color: $link-color;
+  }
+
+  .fa-caret-down {
+    font-size: 60%;
+    padding-left: 0.2rem;
   }
 
   .fa-th-large {
@@ -141,32 +135,3 @@
   }
 }
 
-.dashboard-title {
-  padding: 0 0.4rem 0 0.5rem;
-  font-size: $font-size-lg;
-}
-
-.top-nav-icon {
-  margin: 5px 0px 0 11px;
-  line-height: 41px;
-  float: left;
-}
-
-.top-nav-section {
-  display: block;
-  float: left;
-  padding: 19px 9px 8px 0;
-  font-weight: bold;
-  i {
-    padding-left: 8px;
-  }
-}
-
-.top-nav-title {
-  display: block;
-  float: left;
-  font-size: 17px;
-  padding: 18px 10px 10px 13px;
-  color: $link-color;
-}
-

+ 6 - 0
public/sass/components/_panel_text.scss

@@ -0,0 +1,6 @@
+
+.panel-text-content {
+  ul {
+    margin: 0 0 $spacer $spacer * 1.5;
+  }
+}

+ 2 - 1
public/sass/components/_search.scss

@@ -5,7 +5,7 @@
   z-index: 1000;
   position: absolute;
   width: 700px;
-  box-shadow: $searchShadow;
+  box-shadow: $search-shadow;
   padding: 10px;
   background-color: $panel-bg;
   border: $panel-border;
@@ -73,6 +73,7 @@
     white-space: nowrap;
     background-color: $grafanaListBackground;
     margin-bottom: 4px;
+
     .search-result-icon:before {
       content: "\f009";
     }

+ 50 - 24
public/sass/components/_sidemenu.scss

@@ -7,7 +7,7 @@
   top: 52px;
   left: 0;
   width: $side-menu-width;
-  background-color: $side-menu-bg;
+  background-color: rgba($side-menu-bg,$side-menu-opacity);
   z-index: 101;
   transform: translate3d(0, -100%, 0);
   visibility: hidden;
@@ -30,16 +30,15 @@
     min-height: calc(100% - 54px);
   }
   .dashboard-container {
-    padding-left: $side-menu-width;
+    padding-left: $side-menu-width + 0.2rem;
   }
   .page-container {
     margin-left: $side-menu-width;
   }
-  .top-nav-menu-btn {
-    a {
-      background-color: $page-bg;
-      width: $side-menu-width;
-    }
+  .navbar-brand-btn {
+    background-color: $page-bg;
+    width: $side-menu-width;
+
     .icon-gf-grafana_wordmark  {
       display: inline-block;
     }
@@ -47,9 +46,6 @@
       display: none;
     }
   }
-  .sidemenu-org {
-    box-shadow: none;
-  }
   .search-container {
     left: auto;
   }
@@ -60,20 +56,43 @@
   margin: 0;
   padding: 0;
 
-  li {
+  > li {
     position: relative;
+    @include left-brand-border();
 
     &:hover {
+      background-color: $side-menu-item-hover-bg;
+      @include left-brand-border-gradient();
+
       .dropdown-menu {
         display: block;
+        opacity: 0;
         top: 0px;
         left: $side-menu-width;
-        background-color: $side-menu-bg;
+        background-color: rgba($side-menu-bg,$side-menu-opacity);
+        @include animation('dropdown-anim 100ms ease-in-out 100ms forwards');
+        z-index: -9999;
       }
     }
   }
 }
 
+@include keyframes(dropdown-anim) {
+  0% {
+    display: none;
+    opacity: 0;
+  }
+  1% {
+    display: block;
+    opacity: 0;
+    transform: translate3d(-5%,0,0);
+  }
+  100% {
+    opacity: 1;
+    transform: translate3d(0,0,0);
+  }
+}
+
 .sidemenu-main-link {
   font-size: 16px;
 }
@@ -110,10 +129,8 @@
   line-height: 47px;
   padding: 0px 10px 0px 10px;
   display: block;
+  border-left: 1px solid transparent;
 
-  &:hover {
-    background-color: $side-menu-item-hover-bg;
-  }
 
   .sidemenu-item-text {
     padding-left: 11px;
@@ -165,9 +182,6 @@
   padding: 17px 10px 15px 14px;
   box-sizing: border-box;
   cursor: pointer;
-  &:hover {
-    background-color: $side-menu-item-hover-bg;
-  }
   display: table;
   position: relative;
   width: 100%;
@@ -176,7 +190,7 @@
 .sidemenu .fa-caret-right {
   position: absolute;
   top: 38%;
-  right: 25px;
+  right: 6px;
   font-size: 14px;
   color: $link-color;
 }
@@ -188,14 +202,26 @@
 }
 
 .sidemenu-org-avatar {
-  width: 44px;
-}
-
-.sidemenu-org-avatar > img {
   width: 40px;
   height: 40px;
+  background-color: $gray-2;
   border-radius: 50%;
-  position: absolute;
+  text-align: center;
+
+  >img {
+    position: absolute;
+    width: 40px;
+    height: 40px;
+    border-radius: 50%;
+    left: 14px;
+  }
+}
+
+.sidemenu-org-avatar--missing {
+  color: $gray-4;
+  text-shadow: 0 1px 0 $dark-1;
+  line-height: 40px;
+  font-size: $font-size-lg;
 }
 
 .sidemenu-org-details {

+ 1 - 3
public/sass/components/_timepicker.scss

@@ -13,7 +13,7 @@
 }
 
 .gf-timepicker-dropdown {
-  margin: -5px -10px 10px 5px;
+  margin: -8px -10px 10px 5px;
   padding: 10px 20px;
   float: right;
   background-color: $panel-bg;
@@ -107,13 +107,11 @@
 }
 
 .glyphicon-chevron-right {
-
   @extend .fa;
   @extend .fa-chevron-right;
 }
 
 .glyphicon-chevron-left {
-
   @extend .fa;
   @extend .fa-chevron-left;
 }

+ 2 - 4
public/sass/layout/_page.scss

@@ -20,17 +20,15 @@
 
 .page-container {
   background-color: $page-bg;
-  position: relative;
   padding: ($spacer * 2) ($spacer * 4);
   max-width: 1060px;
-  margin-left: 0;
   min-height: calc(100% - 54px);
   padding-bottom: $spacer * 5;
+  background-image: linear-gradient(60deg, transparent 70%, darken($page-bg, 4%) 98%)
 }
 
 .page-header {
   padding: $spacer 0 $spacer/2 0;
-
   display: flex;
   justify-content: flex-end;
   align-items: flex-end;
@@ -40,7 +38,7 @@
   border-top: 0;
   border-right: 0;
   border-left: 0;
-  border-bottom: 4px solid transparent;
+  border-bottom: 1px solid transparent;
 
   h1 {
     font-style: italic;

+ 25 - 0
public/sass/mixins/_animations.scss

@@ -0,0 +1,25 @@
+@mixin keyframes($animation-name) {
+    @-webkit-keyframes #{$animation-name} {
+        @content;
+    }
+    @-moz-keyframes #{$animation-name} {
+        @content;
+    }
+    @-ms-keyframes #{$animation-name} {
+        @content;
+    }
+    @-o-keyframes #{$animation-name} {
+        @content;
+    }
+    @keyframes #{$animation-name} {
+        @content;
+    }
+}
+
+@mixin animation($str) {
+  -webkit-animation: #{$str};
+  -moz-animation: #{$str};
+  -ms-animation: #{$str};
+  -o-animation: #{$str};
+  animation: #{$str};
+}

+ 11 - 0
public/sass/mixins/_mixins.scss

@@ -331,4 +331,15 @@
   background-image: linear-gradient($angle, rgba(255,255,255,.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,.15) 50%, rgba(255,255,255,.15) 75%, transparent 75%, transparent);
 }
 
+@mixin left-brand-border() {
+  border-left: 2px solid transparent;
+}
 
+@mixin left-brand-border-gradient() {
+  border-image: linear-gradient(rgba(255,213,0,1) 0%, rgba(255,68,0,1) 99%, rgba(255,68,0,1) 100%);
+  border-image-slice: 1;
+  border-top: 0;
+  border-right: 0;
+  border-bottom: 0;
+  border-left: 2px solid transparent;
+}

+ 12 - 2
public/sass/pages/_login.scss

@@ -1,3 +1,12 @@
+.login-container {
+  background-position: left;
+  background-size: 60%;
+  background-repeat: no-repeat;
+  min-width: 100%;
+  margin-left: 0;
+  margin-top: -26px; /* BAD HACK - experiement to see how it looks */
+  padding-top: $spacer * 5; /* BAD HACK - experiement to see how it looks */
+}
 
 .login-form {
   display: inline-block;
@@ -6,11 +15,12 @@
 
 .login-box {
   max-width: 700px;
-  margin: $spacer * 2 auto 0 auto;
+  margin: 0 auto; /* was $spacer * 2 auto 0 auto; */
 }
 
 .login-box-logo {
   text-align: center;
+  margin-bottom: $spacer * 2;
   img {
     width: 6rem;
   }
@@ -173,7 +183,7 @@
       width: 125px;
     }
     .icon-gf-grafana_wordmark {
-      top: -90px;
+      top: -5px;
       font-size: 3rem;
     }
   }

+ 1 - 0
public/sass/pages/_styleguide.scss

@@ -16,6 +16,7 @@
 .style-guide-button-list {
   padding: $spacer;
   button {
+    display: block;
     margin: 0 $spacer $spacer 0;
   }
 }

+ 1 - 0
public/sass/utils/_spacings.scss

@@ -28,6 +28,7 @@
   }
 }
 
+
 // Positioning
 
 .pos-f-t {

+ 3 - 1
public/sass/utils/_utils.scss

@@ -9,13 +9,15 @@
 // Close icons
 // --------------------------------------------------
 .close {
+  @include opacity(20);
+
   float: right;
   font-size: 20px;
   font-weight: bold;
   line-height: $line-height-base;
   color: $black;
   text-shadow: 0 1px 0 rgba(255,255,255,1);
-  @include opacity(20);
+
   &:hover,
   &:focus {
     color: $black;

+ 19 - 0
public/sass/utils/_widths.scss

@@ -0,0 +1,19 @@
+
+
+.max-width { width: 100%; }
+.width-auto { width: auto; }
+
+// widths
+@for $i from 1 through 30 {
+  .width-#{$i} {
+    width: ($spacer * $i) - $gf-form-margin;
+  }
+}
+
+@for $i from 1 through 30 {
+  .max-width-#{$i} {
+    max-width: ($spacer * $i) - $gf-form-margin;
+    flex-grow: 1;
+  }
+}
+

+ 1 - 1
public/vendor/angular/angular.js

@@ -30458,4 +30458,4 @@ $provide.value("$locale", {
 
 })(window, document);
 
-!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
+!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');

+ 1 - 1
tasks/default_task.js

@@ -2,7 +2,7 @@
 module.exports = function(grunt) {
   'use strict';
 
-  grunt.registerTask('css', ['sass', 'concat:cssDark', 'concat:cssLight', 'styleguide']);
+  grunt.registerTask('css', ['sass', 'concat:cssDark', 'concat:cssLight', 'styleguide', 'sasslint']);
   grunt.registerTask('default', [
     'jscs',
     'jshint',

+ 2 - 0
tasks/options/copy.js

@@ -31,6 +31,8 @@ module.exports = function(config) {
         'reflect-metadata/*.ts',
         'reflect-metadata/*.d.ts',
         'rxjs/**/*',
+        'tether/**/*',
+        'tether-drop/**/*',
       ],
       dest: '<%= srcDir %>/vendor/npm'
     }

+ 4 - 2
tasks/options/sasslint.js

@@ -1,10 +1,12 @@
 module.exports = function(config) {
   'use strict';
-
   return {
     options: {
       configFile: 'public/sass/.sass-lint.yml',
     },
-    target: ['public/sass/**/*.scss', '!public/sass/base/_normalize.scss']
+    target: [
+      'public/sass/*.scss',
+      'public/sass/components/*.scss',
+    ]
   };
 };

+ 0 - 1
tslint.json

@@ -29,7 +29,6 @@
     "no-shadowed-variable": false,
     "no-string-literal": false,
     "no-switch-case-fall-through": false,
-    "no-trailing-comma": true,
     "no-trailing-whitespace": true,
     "no-unused-expression": false,
     "no-unused-variable": false,