|
@ -2,7 +2,7 @@ import ingest from './server/routes/api/ingest';
|
|||
|
||||
module.exports = function (kibana) {
|
||||
return new kibana.Plugin({
|
||||
|
||||
id: 'kibana',
|
||||
config: function (Joi) {
|
||||
return Joi.object({
|
||||
enabled: Joi.boolean().default(true),
|
||||
|
@ -13,7 +13,9 @@ module.exports = function (kibana) {
|
|||
|
||||
uiExports: {
|
||||
app: {
|
||||
id: 'kibana',
|
||||
title: 'Kibana',
|
||||
listed: false,
|
||||
description: 'the kibana you know and love',
|
||||
//icon: 'plugins/kibana/settings/sections/about/barcode.svg',
|
||||
main: 'plugins/kibana/kibana',
|
||||
|
@ -32,8 +34,39 @@ module.exports = function (kibana) {
|
|||
return {
|
||||
kbnDefaultAppId: config.get('kibana.defaultAppId')
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
links: [
|
||||
{
|
||||
title: 'Discover',
|
||||
order: -1003,
|
||||
url: '/app/kibana#/discover',
|
||||
description: 'interactively explore your data',
|
||||
icon: 'plugins/kibana/assets/discover.svg',
|
||||
},
|
||||
{
|
||||
title: 'Visualize',
|
||||
order: -1002,
|
||||
url: '/app/kibana#/visualize',
|
||||
description: 'design data visualizations',
|
||||
icon: 'plugins/kibana/assets/visualize.svg',
|
||||
},
|
||||
{
|
||||
title: 'Dashboard',
|
||||
order: -1001,
|
||||
url: '/app/kibana#/dashboard',
|
||||
description: 'compose visualizations for much win',
|
||||
icon: 'plugins/kibana/assets/dashboard.svg',
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
order: 1000,
|
||||
url: '/app/kibana#/settings',
|
||||
description: 'define index patterns, change config, and more',
|
||||
icon: 'plugins/kibana/assets/settings.svg',
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
init: function (server, options) {
|
||||
|
|
4
src/plugins/kibana/public/assets/dashboard.svg
Executable file
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
|
||||
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 1c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5zm-1.66 1a.5.5 0 0 0-.19.84l.91.91c-.02.08-.06.16-.06.25 0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1c-.09 0-.17.04-.25.06l-.91-.91a.5.5 0 0 0-.44-.16.5.5 0 0 0-.06 0zm3.16 0c-.28 0-.5.22-.5.5s.22.5.5.5.5-.22.5-.5-.22-.5-.5-.5z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 486 B |
3
src/plugins/kibana/public/assets/discover.svg
Executable file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
|
||||
<path d="M4 0c-2.2 0-4 1.8-4 4s1.8 4 4 4 4-1.8 4-4-1.8-4-4-4zm0 1c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm2 1l-3 1-1 3 3-1 1-3zm-2 1.5c.28 0 .5.22.5.5s-.22.5-.5.5-.5-.22-.5-.5.22-.5.5-.5z" />
|
||||
</svg>
|
After Width: | Height: | Size: 293 B |
3
src/plugins/kibana/public/assets/logout.svg
Executable file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
|
||||
<path d="M3 0v1h4v5h-4v1h5v-7h-5zm-1 2l-2 1.5 2 1.5v-1h4v-1h-4v-1z" />
|
||||
</svg>
|
After Width: | Height: | Size: 159 B |
4
src/plugins/kibana/public/assets/search.svg
Executable file
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
|
||||
<path d="M3.5 0c-1.93 0-3.5 1.57-3.5 3.5s1.57 3.5 3.5 3.5c.59 0 1.17-.14 1.66-.41a1 1 0 0 0 .13.13l1 1a1.02 1.02 0 1 0 1.44-1.44l-1-1a1 1 0 0 0-.16-.13c.27-.49.44-1.06.44-1.66 0-1.93-1.57-3.5-3.5-3.5zm0 1c1.39 0 2.5 1.11 2.5 2.5 0 .66-.24 1.27-.66 1.72-.01.01-.02.02-.03.03a1 1 0 0 0-.13.13c-.44.4-1.04.63-1.69.63-1.39 0-2.5-1.11-2.5-2.5s1.11-2.5 2.5-2.5z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 450 B |
4
src/plugins/kibana/public/assets/settings.svg
Executable file
|
@ -0,0 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
|
||||
<path d="M3.5 0l-.5 1.19c-.1.03-.19.08-.28.13l-1.19-.5-.72.72.5 1.19c-.05.1-.09.18-.13.28l-1.19.5v1l1.19.5c.04.1.08.18.13.28l-.5 1.19.72.72 1.19-.5c.09.04.18.09.28.13l.5 1.19h1l.5-1.19c.09-.04.19-.08.28-.13l1.19.5.72-.72-.5-1.19c.04-.09.09-.19.13-.28l1.19-.5v-1l-1.19-.5c-.03-.09-.08-.19-.13-.28l.5-1.19-.72-.72-1.19.5c-.09-.04-.19-.09-.28-.13l-.5-1.19h-1zm.5 2.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5-1.5-.67-1.5-1.5.67-1.5 1.5-1.5z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 528 B |
3
src/plugins/kibana/public/assets/visualize.svg
Executable file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8">
|
||||
<path d="M0 0v7h8v-1h-7v-6h-1zm5 0v5h2v-5h-2zm-3 2v3h2v-3h-2z" />
|
||||
</svg>
|
After Width: | Height: | Size: 154 B |
|
@ -28,13 +28,13 @@ routes
|
|||
|
||||
chrome
|
||||
.setBrand({
|
||||
'logo': 'url(' + kibanaLogoUrl + ') center / 160px 70px no-repeat #e8488b',
|
||||
'smallLogo': 'url(' + kibanaLogoUrl + ') center / 160px 70px no-repeat #e8488b'
|
||||
'logo': 'url(' + kibanaLogoUrl + ') 6px 10px / 140px 50px no-repeat #e8488b',
|
||||
'smallLogo': 'url(' + kibanaLogoUrl + ') 6px 10px / 140px 50px no-repeat #e8488b'
|
||||
})
|
||||
.setNavBackground('#222222')
|
||||
.setTabDefaults({
|
||||
resetWhenActive: true,
|
||||
lastUrlStore: window.sessionStore,
|
||||
lastUrlStore: window.sessionStorage,
|
||||
activeIndicatorColor: '#656a76'
|
||||
})
|
||||
.setTabs([
|
||||
|
|
|
@ -54,3 +54,8 @@ const chrome = require('ui/chrome')
|
|||
|
||||
ui.refresh();
|
||||
});
|
||||
|
||||
require('ui/modules').get('kibana')
|
||||
.config(function (appSwitcherEnsureNavigationProvider) {
|
||||
appSwitcherEnsureNavigationProvider.forceNavigation(true);
|
||||
});
|
||||
|
|
|
@ -71,7 +71,7 @@ module.exports = async (kbnServer, server, config) => {
|
|||
server.decorate('reply', 'renderApp', function (app) {
|
||||
const payload = {
|
||||
app: app,
|
||||
nav: uiExports.apps,
|
||||
nav: uiExports.navLinks.inOrder,
|
||||
version: kbnServer.version,
|
||||
buildNum: config.get('pkg.buildNum'),
|
||||
buildSha: config.get('pkg.buildSha'),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import notify from 'ui/notify';
|
||||
import _ from 'lodash';
|
||||
import { escapeRegExp as reEsc } from 'lodash';
|
||||
import { parse, format } from 'url';
|
||||
|
@ -28,7 +29,16 @@ export default class Tab {
|
|||
|
||||
this.lastUrlStoreKey = `lastUrl:${this.id}`;
|
||||
this.lastUrlStore = spec.lastUrlStore;
|
||||
this.lastUrl = this.lastUrlStore ? this.lastUrlStore.getItem(this.lastUrlStoreKey) : null;
|
||||
|
||||
this.lastUrl = null;
|
||||
if (this.lastUrlStore) {
|
||||
this.lastUrl = this.lastUrlStore.getItem(this.lastUrlStoreKey);
|
||||
if (this.lastUrl && !this.lastUrl.startsWith(this.rootUrl)) {
|
||||
notify.log(`Found invalid lastUrl for tab with root url ${this.rootUrl}: "${this.lastUrl}"`);
|
||||
this.lastUrl = null;
|
||||
this.lastUrlStore.removeItem(this.lastUrlStoreKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
href() {
|
||||
|
@ -54,7 +64,8 @@ export default class Tab {
|
|||
let lastUrl = this.getLastUrl();
|
||||
|
||||
if (!lastUrl.startsWith(rootUrl)) {
|
||||
throw new Error(`Tab "${id}" has invalid root "${rootUrl}" for last url "${lastUrl}"`);
|
||||
notify.log(`Tab "${id}" has invalid root "${rootUrl}" for last url "${lastUrl}"`);
|
||||
lastUrl = rootUrl;
|
||||
}
|
||||
|
||||
return lastUrl.slice(rootUrl.length);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import sinon from 'auto-release-sinon';
|
||||
import Tab from '../Tab';
|
||||
import expect from 'expect.js';
|
||||
import TabFakeStore from './_TabFakeStore';
|
||||
|
@ -89,13 +90,24 @@ describe('Chrome Tab', function () {
|
|||
it('discovers the lastUrl', function () {
|
||||
const lastUrlStore = new TabFakeStore();
|
||||
const tab = new Tab({ id: 'foo', lastUrlStore });
|
||||
expect(tab.lastUrl).to.not.equal('bar');
|
||||
expect(tab.lastUrl).to.not.equal('/foo/bar');
|
||||
|
||||
tab.setLastUrl('bar');
|
||||
expect(tab.lastUrl).to.equal('bar');
|
||||
tab.setLastUrl('/foo/bar');
|
||||
expect(tab.lastUrl).to.equal('/foo/bar');
|
||||
|
||||
const tab2 = new Tab({ id: 'foo', lastUrlStore });
|
||||
expect(tab2.lastUrl).to.equal('bar');
|
||||
expect(tab2.lastUrl).to.equal('/foo/bar');
|
||||
});
|
||||
|
||||
it('logs a warning about last urls that do not match the rootUrl', function () {
|
||||
const lastUrlStore = new TabFakeStore();
|
||||
const tab = new Tab({ id: 'foo', baseUrl: '/bar', lastUrlStore });
|
||||
tab.setLastUrl('/bar/foo/1');
|
||||
|
||||
const stub = sinon.stub(console, 'log');
|
||||
const tab2 = new Tab({ id: 'foo', baseUrl: '/baz', lastUrlStore });
|
||||
sinon.assert.calledOnce(stub);
|
||||
expect(tab2.lastUrl).to.equal(null);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -167,14 +179,14 @@ describe('Chrome Tab', function () {
|
|||
expect(tab.getLastPath()).to.equal('/index');
|
||||
});
|
||||
|
||||
it('throws an error if the lastUrl does not extend the root url', function () {
|
||||
expect(function () {
|
||||
const baseUrl = 'http://local:5601/app/visualize#';
|
||||
const tab = new Tab({ baseUrl });
|
||||
it('logs a warning if the lastUrl does not extend the root url', function () {
|
||||
const baseUrl = 'http://local:5601/app/visualize#';
|
||||
const tab = new Tab({ baseUrl });
|
||||
sinon.stub(console, 'log');
|
||||
|
||||
tab.setLastUrl('http://local:5601/');
|
||||
tab.getLastPath();
|
||||
}).to.throwError(/invalid.*root/);
|
||||
tab.setLastUrl('http://local:5601/');
|
||||
tab.getLastPath();
|
||||
sinon.assert.calledOnce(console.log);// eslint-disable-line no-console
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ export default class TabFakeStore {
|
|||
constructor() { this[store] = new Map(); }
|
||||
getItem(k) { return this[store].get(k); }
|
||||
setItem(k, v) { return this[store].set(k, v); }
|
||||
removeItem(k) { return this[store].delete(k); }
|
||||
getKeys() { return [ ...this[store].keys() ]; }
|
||||
getValues() { return [ ...this[store].values() ]; }
|
||||
}
|
||||
|
|
|
@ -86,11 +86,11 @@ describe('Chrome API :: apps', function () {
|
|||
describe('#getAppUrl()', function () {
|
||||
it('returns the resolved url of the current app', function () {
|
||||
const chrome = {};
|
||||
const app = { url: '/foo' };
|
||||
const app = { navLink: { url: '/foo' } };
|
||||
setup(chrome, { app });
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', app.url);
|
||||
a.setAttribute('href', app.navLink.url);
|
||||
expect(chrome.getAppUrl()).to.equal(a.href);
|
||||
});
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import { resolve } from 'url';
|
|||
|
||||
module.exports = function (chrome, internals) {
|
||||
|
||||
if (internals.app) {
|
||||
internals.app.url = resolve(window.location.href, internals.app.url);
|
||||
if (get(internals, 'app.navLink.url')) {
|
||||
internals.app.navLink.url = resolve(window.location.href, internals.app.navLink.url);
|
||||
}
|
||||
|
||||
internals.appUrlStore = internals.appUrlStore || window.sessionStorage;
|
||||
|
@ -35,7 +35,7 @@ module.exports = function (chrome, internals) {
|
|||
};
|
||||
|
||||
chrome.getAppUrl = function () {
|
||||
return get(internals, ['app', 'url']);
|
||||
return get(internals, ['app', 'navLink', 'url']);
|
||||
};
|
||||
|
||||
chrome.getInjected = function (name, def) {
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
import { parse, format } from 'url';
|
||||
import { startsWith, isString } from 'lodash';
|
||||
import { startsWith, isString, find } from 'lodash';
|
||||
|
||||
export default function (chrome, internals) {
|
||||
chrome.getNavLinks = function () {
|
||||
return internals.nav;
|
||||
};
|
||||
|
||||
chrome.getLastSubUrlFor = function (url) {
|
||||
return internals.appUrlStore.getItem(`lastSubUrl:${url}`);
|
||||
};
|
||||
|
||||
chrome.getBasePath = function () {
|
||||
return internals.basePath || '';
|
||||
};
|
||||
|
@ -34,26 +30,46 @@ export default function (chrome, internals) {
|
|||
});
|
||||
};
|
||||
|
||||
function lastSubUrlKey(link) {
|
||||
return `lastSubUrl:${link.url}`;
|
||||
}
|
||||
|
||||
function setLastUrl(link, url) {
|
||||
link.lastSubUrl = url;
|
||||
internals.appUrlStore.setItem(lastSubUrlKey(link), url);
|
||||
}
|
||||
|
||||
function refreshLastUrl(link) {
|
||||
link.lastSubUrl = internals.appUrlStore.getItem(lastSubUrlKey(link));
|
||||
}
|
||||
|
||||
internals.trackPossibleSubUrl = function (url) {
|
||||
for (const link of internals.nav) {
|
||||
if (startsWith(url, link.url)) {
|
||||
link.lastSubUrl = url;
|
||||
internals.appUrlStore.setItem(`lastSubUrl:${link.url}`, url);
|
||||
link.active = startsWith(url, link.url);
|
||||
|
||||
if (link.active) {
|
||||
setLastUrl(link, url);
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchingTab = find(internals.tabs, { rootUrl: link.url });
|
||||
if (matchingTab) {
|
||||
setLastUrl(link, matchingTab.getLastUrl());
|
||||
continue;
|
||||
}
|
||||
|
||||
refreshLastUrl(link);
|
||||
}
|
||||
};
|
||||
|
||||
internals.nav.forEach(link => {
|
||||
// convert all link urls to absolute urls
|
||||
|
||||
var a = document.createElement('a');
|
||||
a.setAttribute('href', link.url);
|
||||
link.url = a.href;
|
||||
link.lastSubUrl = chrome.getLastSubUrlFor(link.url);
|
||||
|
||||
if (link.url === chrome.getAppUrl()) {
|
||||
link.active = true;
|
||||
}
|
||||
});
|
||||
|
||||
// simulate a possible change in url to initialize the
|
||||
// link.active and link.lastUrl properties
|
||||
internals.trackPossibleSubUrl(document.location.href);
|
||||
};
|
||||
|
|
|
@ -43,51 +43,53 @@
|
|||
</nav>
|
||||
|
||||
<div class="app-wrapper">
|
||||
<kbn-notifications list="notifList"></kbn-notifications>
|
||||
<nav
|
||||
ng-style="::{ background: chrome.getNavBackground() }"
|
||||
ng-class="{ show: chrome.getVisible() }"
|
||||
class="hide navbar navbar-inverse navbar-static-top">
|
||||
<div class="app-wrapper-panel">
|
||||
<kbn-notifications list="notifList"></kbn-notifications>
|
||||
<nav
|
||||
ng-style="::{ background: chrome.getNavBackground() }"
|
||||
ng-class="{ show: chrome.getVisible() }"
|
||||
class="hide navbar navbar-inverse navbar-static-top">
|
||||
|
||||
<!-- Mobile navbar -->
|
||||
<div class="navbar-header">
|
||||
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<span class="visible-xs">
|
||||
<span ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</span>
|
||||
<span ng-if="chrome.getActiveTabTitle()" class="navbar-brand">{{ chrome.getActiveTabTitle() }}</span>
|
||||
<span ng-show="chrome.httpActive.length" class="spinner"></span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /Mobile navbar -->
|
||||
<!-- Mobile navbar -->
|
||||
<div class="navbar-header">
|
||||
<button ng-click="showCollapsed = !showCollapsed" type="button" class="navbar-toggle">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<span class="visible-xs">
|
||||
<span ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</span>
|
||||
<span ng-if="chrome.getActiveTabTitle()" class="navbar-brand">{{ chrome.getActiveTabTitle() }}</span>
|
||||
<span ng-show="chrome.httpActive.length" class="spinner"></span>
|
||||
</span>
|
||||
</div>
|
||||
<!-- /Mobile navbar -->
|
||||
|
||||
<!-- Full navbar -->
|
||||
<div collapse="!showCollapsed" class="navbar-collapse" kbn-chrome-append-nav-controls>
|
||||
<ul class="nav navbar-nav" role="navigation">
|
||||
<!-- Full navbar -->
|
||||
<div collapse="!showCollapsed" class="navbar-collapse" kbn-chrome-append-nav-controls>
|
||||
<ul class="nav navbar-nav" role="navigation">
|
||||
|
||||
<li ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</li>
|
||||
<li ng-if="chrome.getBrand('title')" class="navbar-brand">{{ chrome.getBrand('title') }}</li>
|
||||
|
||||
<li ng-repeat="tab in chrome.getTabs()" ng-class="{ active: tab.active }">
|
||||
<a ng-href="{{ tab.href() }}" ng-style="{ 'border-bottom-color': tab.activeIndicatorColor }">
|
||||
{{ tab.title }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- /Full navbar -->
|
||||
</nav>
|
||||
<li ng-repeat="tab in chrome.getTabs()" ng-class="{ active: tab.active }">
|
||||
<a ng-href="{{ tab.href() }}" ng-style="{ 'border-bottom-color': tab.activeIndicatorColor }">
|
||||
{{ tab.title }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- /Full navbar -->
|
||||
</nav>
|
||||
|
||||
<config
|
||||
ng-show="timefilter.enabled"
|
||||
config-template="pickerTemplate"
|
||||
config-object="timefilter"
|
||||
config-close="pickerTemplate.close">
|
||||
</config>
|
||||
<config
|
||||
ng-show="timefilter.enabled"
|
||||
config-template="pickerTemplate"
|
||||
config-object="timefilter"
|
||||
config-close="pickerTemplate.close">
|
||||
</config>
|
||||
|
||||
<div class="application" ng-class="'tab-' + chrome.getActiveTabId('-none-') + ' ' + chrome.getApplicationClasses()" ng-view></div>
|
||||
<div class="application" ng-class="'tab-' + chrome.getActiveTabId('-none-') + ' ' + chrome.getApplicationClasses()" ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="app-links">
|
||||
<div
|
||||
class="app-link"
|
||||
ng-repeat="link in switcher.getNavLinks() | orderBy:'title'"
|
||||
ng-repeat="link in switcher.getNavLinks()"
|
||||
ng-class="{ active: link.active }">
|
||||
|
||||
<a
|
||||
|
@ -9,8 +9,8 @@
|
|||
ng-href="{{ link.active ? link.url : (link.lastSubUrl || link.url) }}"
|
||||
data-test-subj="appLink">
|
||||
|
||||
<div ng-if="link.icon" ng-style="{ 'background-image': 'url(../' + link.icon + ')' }" class="app-icon"></div>
|
||||
<div ng-if="!link.icon" class="app-icon app-icon-missing">{{ link.title[0] }}</div>
|
||||
<div ng-if="link.icon" class="app-icon"><img ng-src="{{'../' + link.icon}}"></div>
|
||||
<div ng-if="!link.icon" class="app-icon-missing">{{ link.title[0] }}</div>
|
||||
|
||||
<div class="app-title">{{ link.title }}</div>
|
||||
</a>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import DomLocationProvider from 'ui/domLocation';
|
||||
import { parse } from 'url';
|
||||
import { bindKey } from 'lodash';
|
||||
|
@ -8,14 +7,45 @@ import appSwitcherTemplate from './app_switcher.html';
|
|||
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.provider('appSwitcherEnsureNavigation', function () {
|
||||
let forceNavigation = false;
|
||||
|
||||
this.forceNavigation = function (val) {
|
||||
forceNavigation = !!val;
|
||||
};
|
||||
|
||||
this.$get = ['Private', function (Private) {
|
||||
const domLocation = Private(DomLocationProvider);
|
||||
|
||||
return function (event) {
|
||||
if (!forceNavigation || event.isDefaultPrevented() || event.altKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
const toParsed = parse(event.delegateTarget.href);
|
||||
const fromParsed = parse(domLocation.href);
|
||||
const sameProto = toParsed.protocol === fromParsed.protocol;
|
||||
const sameHost = toParsed.host === fromParsed.host;
|
||||
const samePath = toParsed.path === fromParsed.path;
|
||||
|
||||
if (sameProto && sameHost && samePath) {
|
||||
toParsed.hash && domLocation.reload();
|
||||
|
||||
// event.preventDefault() keeps the browser from seeing the new url as an update
|
||||
// and even setting window.location does not mimic that behavior, so instead
|
||||
// we use stopPropagation() to prevent angular from seeing the click and
|
||||
// starting a digest cycle/attempting to handle it in the router.
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
}];
|
||||
})
|
||||
.directive('appSwitcher', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: appSwitcherTemplate,
|
||||
controllerAs: 'switcher',
|
||||
controller: function ($scope, Private) {
|
||||
var domLocation = Private(DomLocationProvider);
|
||||
|
||||
controller($scope, appSwitcherEnsureNavigation) {
|
||||
// since we render this in an isolate scope we can't "require: ^chrome", but
|
||||
// rather than remove all helpfull checks we can just check here.
|
||||
if (!$scope.chrome || !$scope.chrome.getNavLinks) {
|
||||
|
@ -26,28 +56,7 @@ uiModules
|
|||
|
||||
// links don't cause full-navigation events in certain scenarios
|
||||
// so we force them when needed
|
||||
this.ensureNavigation = function (event, app) {
|
||||
if (event.isDefaultPrevented() || event.altKey || event.metaKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
var toParsed = parse(event.delegateTarget.href);
|
||||
var fromParsed = parse(domLocation.href);
|
||||
var sameProto = toParsed.protocol === fromParsed.protocol;
|
||||
var sameHost = toParsed.host === fromParsed.host;
|
||||
var samePath = toParsed.path === fromParsed.path;
|
||||
|
||||
if (sameProto && sameHost && samePath) {
|
||||
toParsed.hash && domLocation.reload();
|
||||
|
||||
// event.preventDefault() keeps the browser from seeing the new url as an update
|
||||
// and even setting window.location does not mimic that behavior, so instead
|
||||
// we use stopPropagation() to prevent angular from seeing the click and
|
||||
// starting a digest cycle/attempting to handle it in the router.
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
this.ensureNavigation = appSwitcherEnsureNavigation;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
|
||||
// as - App Switcher
|
||||
@as-open-width: 170px;
|
||||
@as-open-width: 160px;
|
||||
@as-closed-width: 53px;
|
||||
@app-icon-height: 30px;
|
||||
@transition-time: .65s;
|
||||
@transition-delay: .2s;
|
||||
@transition-time: .35s;
|
||||
@transition-delay: .1s;
|
||||
@app-links-wrapper-background: #3caed2;
|
||||
|
||||
body { overflow-x: hidden; }
|
||||
|
||||
|
@ -17,7 +18,7 @@ body { overflow-x: hidden; }
|
|||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
background-color: #3caed2;
|
||||
background-color: @app-links-wrapper-background;
|
||||
overflow: hidden;
|
||||
transition: width @transition-time;
|
||||
transition-delay: @transition-delay;
|
||||
|
@ -36,7 +37,11 @@ body { overflow-x: hidden; }
|
|||
width: @as-open-width;
|
||||
list-style-type: none;
|
||||
&.kibana {
|
||||
background: url("~ui/images/kibana.svg") center / 160px 70px no-repeat #e8488b;
|
||||
background-image: url("~ui/images/kibana.svg");
|
||||
background-position: 6px 10px;
|
||||
background-size: 140px 50px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #e8488b;
|
||||
}
|
||||
}
|
||||
.bottom-apps {
|
||||
|
@ -57,6 +62,11 @@ body { overflow-x: hidden; }
|
|||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
|
||||
&-panel {
|
||||
.flex-parent(@shrink: 0);
|
||||
box-shadow: -4px 0px 3px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.navbar-right {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
@ -67,12 +77,36 @@ body { overflow-x: hidden; }
|
|||
.app-link {
|
||||
width: @as-open-width;
|
||||
height: @app-icon-height;
|
||||
line-height: 24px;
|
||||
|
||||
> a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: lighten(@app-links-wrapper-background, 7.5%);
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
float: left;
|
||||
filter: invert(100%);
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 1.7em;
|
||||
display: inline-block;
|
||||
height: @app-icon-height;
|
||||
width: @as-closed-width;
|
||||
> i {
|
||||
}
|
||||
|
||||
> img {
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.app-icon-missing {
|
||||
float: left;
|
||||
text-align: center;
|
||||
font-size: 1.7em;
|
||||
|
@ -81,8 +115,7 @@ body { overflow-x: hidden; }
|
|||
width: @as-closed-width;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
> i {
|
||||
}
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
|
@ -90,16 +123,20 @@ body { overflow-x: hidden; }
|
|||
display: inline-block;
|
||||
float: right;
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
text-align: left;
|
||||
padding-left: 3px;
|
||||
line-height: @app-icon-height;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #73c6e0;
|
||||
background-color: lighten(@app-links-wrapper-background, 10%);
|
||||
> a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
img {
|
||||
filter: invert(100%);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ export default function (chrome, internals) {
|
|||
const onRouteChange = function () {
|
||||
let { href } = window.location;
|
||||
let persist = chrome.getVisible();
|
||||
internals.trackPossibleSubUrl(href);
|
||||
internals.tabs.consumeRouteUpdate(href, persist);
|
||||
internals.trackPossibleSubUrl(href);
|
||||
};
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', onRouteChange);
|
||||
|
|
|
@ -1 +1,110 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 141.37 51"><defs><style>.cls-1,.cls-2,.cls-3,.cls-4{fill:#fff;}.cls-1{opacity:0.6;}.cls-2{opacity:0.4;}.cls-3{opacity:0.9;}</style></defs><title>Kibana-Full-Logo</title><path class="cls-1" d="M409.68,273.59a38.94,38.94,0,0,1,18.71,4.76L448.2,254.5H408.94v19.11Z" transform="translate(-408.94 -254.5)"/><path class="cls-1" d="M428.39,278.35l-19.45,23.42v3.73h39.2A39.2,39.2,0,0,0,428.39,278.35Z" transform="translate(-408.94 -254.5)"/><path class="cls-2" d="M428.39,278.35l-19.45,23.42v3.73h7l18.87-22.77s-1.25-1.06-3-2.3C430.41,279.46,428.39,278.35,428.39,278.35Z" transform="translate(-408.94 -254.5)"/><path class="cls-3" d="M409.68,273.59l-0.74,0v28.17l19.45-23.42A38.94,38.94,0,0,0,409.68,273.59Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M457.06,291.29H453.2V266.23h3.86v14.48l2.2-.21,4.18-6.9h4.32l-5.1,8.28,5.38,9.42h-4.36l-4.32-7.47-2.3.25v7.22Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M470.86,270.58v-4.07h3.86v4.07h-3.86Zm0,20.71v-17.7h3.86v17.7h-3.86Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M487.64,273.2q3.65,0,5.26,2t1.61,7.2q0,5.17-1.91,7.21t-6.73,2q-1.66,0-5.27-.32l-1.2-.11v-25h3.82v8A11.25,11.25,0,0,1,487.64,273.2Zm-1.77,15q2.83,0,3.77-1.33a8.11,8.11,0,0,0,.94-4.51,9.08,9.08,0,0,0-.8-4.48,2.82,2.82,0,0,0-2.6-1.29,11.33,11.33,0,0,0-3.4.53l-0.57.18v10.76Q485.17,288.25,485.87,288.25Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M511.47,279v8.07a1.68,1.68,0,0,0,.41,1.15,2,2,0,0,0,1.15.48l-0.11,2.94a7.45,7.45,0,0,1-4.71-1.31,13.47,13.47,0,0,1-5.7,1.31q-5.28,0-5.28-5.63a4.81,4.81,0,0,1,1.43-3.89,7.81,7.81,0,0,1,4.41-1.45l4.57-.39V279a2.15,2.15,0,0,0-2.44-2.55q-2.27,0-5.66.28l-1.13.07-0.14-2.73a30.77,30.77,0,0,1,7.1-.92q3.24,0,4.67,1.4A6,6,0,0,1,511.47,279Zm-7.93,4.39a2.38,2.38,0,0,0-2.44,2.66q0,2.44,2.16,2.44A13.67,13.67,0,0,0,507,288l0.64-.21V283Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M520.18,291.29h-3.86v-17.7h3.82v1.1A9.85,9.85,0,0,1,525,273.2q3.54,0,4.83,2t1.29,6.6v9.49h-3.82v-9.38a9.58,9.58,0,0,0-.62-4.07,2.57,2.57,0,0,0-2.53-1.2,8.77,8.77,0,0,0-3.47.71l-0.53.21v13.74Z" transform="translate(-408.94 -254.5)"/><path class="cls-4" d="M548.75,279v8.07a1.68,1.68,0,0,0,.41,1.15,2,2,0,0,0,1.15.48l-0.11,2.94a7.45,7.45,0,0,1-4.71-1.31,13.48,13.48,0,0,1-5.7,1.31q-5.28,0-5.28-5.63a4.82,4.82,0,0,1,1.43-3.89,7.81,7.81,0,0,1,4.41-1.45l4.57-.39V279a2.15,2.15,0,0,0-2.44-2.55q-2.27,0-5.66.28l-1.13.07-0.14-2.73a30.78,30.78,0,0,1,7.1-.92q3.24,0,4.67,1.4A6,6,0,0,1,548.75,279Zm-7.93,4.39a2.38,2.38,0,0,0-2.44,2.66q0,2.44,2.16,2.44a13.66,13.66,0,0,0,3.75-.57l0.64-.21V283Z" transform="translate(-408.94 -254.5)"/></svg>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="Layer_1"
|
||||
data-name="Layer 1"
|
||||
viewBox="0 0 141.37 51"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="kibana.svg">
|
||||
<metadata
|
||||
id="metadata31">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>Kibana-Full-Logo</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
pagecolor="#515151"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="1"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1796"
|
||||
inkscape:window-height="1079"
|
||||
id="namedview29"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.0725756"
|
||||
inkscape:cx="70.684998"
|
||||
inkscape:cy="25.5"
|
||||
inkscape:window-x="349"
|
||||
inkscape:window-y="210"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<defs
|
||||
id="defs3">
|
||||
<style
|
||||
id="style5">.cls-1,.cls-2,.cls-3,.cls-4{fill:#fff;}.cls-1{opacity:0.6;}.cls-2{opacity:0.4;}.cls-3{opacity:0.9;}</style>
|
||||
</defs>
|
||||
<title
|
||||
id="title7">Kibana-Full-Logo</title>
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M409.68,273.59a38.94,38.94,0,0,1,18.71,4.76L448.2,254.5H408.94v19.11Z"
|
||||
transform="translate(-408.94 -254.5)"
|
||||
id="path9" />
|
||||
<path
|
||||
class="cls-1"
|
||||
d="M428.39,278.35l-19.45,23.42v3.73h39.2A39.2,39.2,0,0,0,428.39,278.35Z"
|
||||
transform="translate(-408.94 -254.5)"
|
||||
id="path11" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M428.39,278.35l-19.45,23.42v3.73h7l18.87-22.77s-1.25-1.06-3-2.3C430.41,279.46,428.39,278.35,428.39,278.35Z"
|
||||
transform="translate(-408.94 -254.5)"
|
||||
id="path13" />
|
||||
<path
|
||||
class="cls-3"
|
||||
d="M409.68,273.59l-0.74,0v28.17l19.45-23.42A38.94,38.94,0,0,0,409.68,273.59Z"
|
||||
transform="translate(-408.94 -254.5)"
|
||||
id="path15" />
|
||||
<g
|
||||
style="font-style:normal;font-weight:normal;font-size:33.04906845px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:0.92528737;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text4159"
|
||||
transform="translate(0,-13.509761)">
|
||||
<path
|
||||
d="m 53.346838,50.53812 0,-6.74201 2.14819,-0.231343 4.031986,6.973353 4.065036,0 -5.023459,-8.791052 4.759066,-7.733482 -4.031986,0 -3.89979,6.444568 -2.049043,0.198295 0,-13.517069 -3.602348,0 0,23.39874 3.602348,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
|
||||
id="path4891"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 66.269024,50.53812 3.602349,0 0,-16.524534 -3.602349,0 0,16.524534 z m 0,-19.333705 3.602349,0 0,-3.800643 -3.602349,0 0,3.800643 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
|
||||
id="path4893"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 81.973529,33.650046 c -1.850747,0 -4.131133,0.991472 -4.131133,0.991472 l 0,-7.502138 -3.5693,0 0,23.365691 c 0,0 4.296379,0.396589 6.04798,0.396589 5.915783,0 8.063973,-2.015993 8.063973,-8.85715 0,-6.180176 -1.916846,-8.394464 -6.41152,-8.394464 z M 80.321076,47.6959 c -0.660981,0 -2.47868,-0.132196 -2.47868,-0.132196 l 0,-10.046917 c 0,0 1.949895,-0.660981 3.701495,-0.660981 2.214288,0 3.172711,1.454159 3.172711,5.188704 0,4.031986 -0.727079,5.65139 -4.395526,5.65139 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
|
||||
id="path4895"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 104.18766,39.103143 c 0,-3.767594 -1.65245,-5.453097 -5.684435,-5.453097 -3.040515,0 -6.642863,0.859276 -6.642863,0.859276 l 0.132196,2.544778 c 0,0 3.998938,-0.33049 6.213225,-0.33049 1.619405,0 2.412587,0.561834 2.412587,2.379533 l 0,1.189766 -4.263335,0.36354 c -3.53625,0.297441 -5.453096,1.487208 -5.453096,4.990409 0,3.437103 1.652453,5.254802 4.924311,5.254802 2.676975,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45416,-1.520257 l 0,-7.535187 z m -3.56929,3.734544 0,4.395526 c 0,0 -2.214292,0.72708 -4.098089,0.72708 -1.388061,0 -2.015993,-0.925374 -2.015993,-2.412582 0,-1.487208 0.760128,-2.214288 2.280385,-2.346484 l 3.833697,-0.36354 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
|
||||
id="path4897"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 112.28004,50.53812 0,-12.823038 c 0,0 1.8177,-0.859276 3.73454,-0.859276 2.51173,0 2.94137,1.619404 2.94137,4.924311 l 0,8.758003 3.5693,0 0,-8.85715 c 0,-5.420047 -1.12367,-8.030924 -5.71749,-8.030924 -2.14819,0 -4.56077,1.388061 -4.56077,1.388061 l 0,-1.024521 -3.5693,0 0,16.524534 3.60235,0 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
|
||||
id="path4899"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
d="m 138.91501,39.103143 c 0,-3.767594 -1.65246,-5.453097 -5.68444,-5.453097 -3.04052,0 -6.64287,0.859276 -6.64287,0.859276 l 0.1322,2.544778 c 0,0 3.99894,-0.33049 6.21322,-0.33049 1.61941,0 2.41259,0.561834 2.41259,2.379533 l 0,1.189766 -4.26333,0.36354 c -3.53625,0.297441 -5.4531,1.487208 -5.4531,4.990409 0,3.437103 1.65245,5.254802 4.92431,5.254802 2.67698,0 5.3209,-1.222816 5.3209,-1.222816 1.22282,0.958423 2.37953,1.222816 4.39553,1.222816 l 0.0991,-2.743073 c -0.95842,-0.132196 -1.38806,-0.528785 -1.45415,-1.520257 l 0,-7.535187 z m -3.5693,3.734544 0,4.395526 c 0,0 -2.21429,0.72708 -4.09809,0.72708 -1.38806,0 -2.01599,-0.925374 -2.01599,-2.412582 0,-1.487208 0.76013,-2.214288 2.28038,-2.346484 l 3.8337,-0.36354 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#ffffff;fill-opacity:0.92528737"
|
||||
id="path4901"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 6.9 KiB |
|
@ -1,5 +1,4 @@
|
|||
import _ from 'lodash';
|
||||
import { join } from 'path';
|
||||
import { chain, get, noop, once, pick } from 'lodash';
|
||||
|
||||
class UiApp {
|
||||
constructor(uiExports, spec) {
|
||||
|
@ -8,6 +7,7 @@ class UiApp {
|
|||
|
||||
this.id = this.spec.id;
|
||||
if (!this.id) {
|
||||
console.log('-----------Spec Without ID:', this.spec);
|
||||
throw new Error('Every app must specify it\'s id');
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,24 @@ class UiApp {
|
|||
this.title = this.spec.title;
|
||||
this.description = this.spec.description;
|
||||
this.icon = this.spec.icon;
|
||||
this.hidden = this.spec.hidden;
|
||||
this.hidden = !!this.spec.hidden;
|
||||
this.listed = this.spec.listed == null ? !this.hidden : this.spec.listed;
|
||||
this.templateName = this.spec.templateName || 'ui_app';
|
||||
this.url = `${spec.urlBasePath || ''}${this.spec.url || `/app/${this.id}`}`;
|
||||
|
||||
if (!this.hidden) {
|
||||
// any non-hidden app has a url, so it gets a "navLink"
|
||||
this.navLink = this.uiExports.navLinks.new({
|
||||
title: this.title,
|
||||
description: this.description,
|
||||
icon: this.icon,
|
||||
url: this.spec.url || `/app/${this.id}`
|
||||
});
|
||||
|
||||
if (!this.listed) {
|
||||
// unlisted apps remove their navLinks from the uiExports collection though
|
||||
this.uiExports.navLinks.delete(this.navLink);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.spec.autoload) {
|
||||
console.warn(
|
||||
|
@ -27,15 +42,15 @@ class UiApp {
|
|||
}
|
||||
|
||||
// once this resolves, no reason to run it again
|
||||
this.getModules = _.once(this.getModules);
|
||||
this.getModules = once(this.getModules);
|
||||
|
||||
// variables that are injected into the browser, must serialize to JSON
|
||||
this.getInjectedVars = this.spec.injectVars || _.noop;
|
||||
this.getInjectedVars = this.spec.injectVars || noop;
|
||||
}
|
||||
|
||||
getModules() {
|
||||
return _.chain([
|
||||
this.uiExports.find(_.get(this, 'spec.uses', [])),
|
||||
return chain([
|
||||
this.uiExports.find(get(this, 'spec.uses', [])),
|
||||
this.uiExports.find(['chromeNavControls', 'sledgehammers']),
|
||||
])
|
||||
.flatten()
|
||||
|
@ -45,7 +60,7 @@ class UiApp {
|
|||
}
|
||||
|
||||
toJSON() {
|
||||
return _.pick(this, ['id', 'title', 'description', 'icon', 'main', 'url']);
|
||||
return pick(this, ['id', 'title', 'description', 'icon', 'main', 'navLink']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ module.exports = class UiAppCollection extends Collection {
|
|||
}
|
||||
|
||||
new(spec) {
|
||||
console.log('--------NewAppSpec:', spec);
|
||||
if (this.hidden && spec.hidden) {
|
||||
return this.hidden.new(spec);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@ import _ from 'lodash';
|
|||
import minimatch from 'minimatch';
|
||||
|
||||
import UiAppCollection from './ui_app_collection';
|
||||
import UiNavLinkCollection from './ui_nav_link_collection';
|
||||
|
||||
class UiExports {
|
||||
constructor({ urlBasePath }) {
|
||||
this.navLinks = new UiNavLinkCollection(this);
|
||||
this.apps = new UiAppCollection(this);
|
||||
this.aliases = {};
|
||||
this.urlBasePath = urlBasePath;
|
||||
|
@ -48,15 +50,21 @@ class UiExports {
|
|||
case 'app':
|
||||
case 'apps':
|
||||
return (plugin, specs) => {
|
||||
const id = plugin.id;
|
||||
for (let spec of [].concat(specs || [])) {
|
||||
let app = this.apps.new(_.defaults({}, spec, {
|
||||
id: plugin.id,
|
||||
urlBasePath: this.urlBasePath
|
||||
}));
|
||||
const app = this.apps.new({ id, ...spec });
|
||||
plugin.apps.add(app);
|
||||
}
|
||||
};
|
||||
|
||||
case 'link':
|
||||
case 'links':
|
||||
return (plugin, spec) => {
|
||||
for (const spec of [].concat(spec || [])) {
|
||||
this.navLinks.new(spec);
|
||||
}
|
||||
};
|
||||
|
||||
case 'visTypes':
|
||||
case 'fieldFormats':
|
||||
case 'spyModes':
|
||||
|
|
16
src/ui/ui_nav_link.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { pick } from 'lodash';
|
||||
import { join } from 'path';
|
||||
|
||||
export default class UiNavLink {
|
||||
constructor(uiExports, spec) {
|
||||
this.title = spec.title;
|
||||
this.order = spec.order || 0;
|
||||
this.url = `${uiExports.urlBasePath || ''}${spec.url}`;
|
||||
this.description = spec.description;
|
||||
this.icon = spec.icon;
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return pick(this, ['title', 'url', 'order', 'description', 'icon']);
|
||||
}
|
||||
}
|
29
src/ui/ui_nav_link_collection.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { sortBy } from 'lodash';
|
||||
import UiNavLink from './ui_nav_link';
|
||||
import Collection from '../utils/Collection';
|
||||
|
||||
const inOrderCache = Symbol('inOrder');
|
||||
|
||||
export default class UiNavLinkCollection extends Collection {
|
||||
|
||||
constructor(uiExports, parent) {
|
||||
super();
|
||||
this.uiExports = uiExports;
|
||||
}
|
||||
|
||||
new(spec) {
|
||||
const link = new UiNavLink(this.uiExports, spec);
|
||||
this[inOrderCache] = null;
|
||||
this.add(link);
|
||||
return link;
|
||||
}
|
||||
|
||||
get inOrder() {
|
||||
if (!this[inOrderCache]) {
|
||||
this[inOrderCache] = sortBy([...this], 'order');
|
||||
}
|
||||
|
||||
return this[inOrderCache];
|
||||
}
|
||||
|
||||
};
|
|
@ -5,6 +5,7 @@ import { includes } from 'lodash';
|
|||
export default function filesToCommit(path) {
|
||||
const simpleGit = new SimpleGit(path);
|
||||
const gitDiff = promisify(simpleGit.diff, simpleGit);
|
||||
const pathsToIgnore = ['webpackShims'];
|
||||
|
||||
return gitDiff(['--name-status', '--cached'])
|
||||
.then(output => {
|
||||
|
@ -12,6 +13,9 @@ export default function filesToCommit(path) {
|
|||
.split('\n')
|
||||
.map(line => line.trim().split('\t'))
|
||||
.filter(parts => parts.length === 2)
|
||||
.filter(parts => {
|
||||
return pathsToIgnore.reduce((prev, curr) => {(prev === false) ? prev : parts[1].indexOf(curr) === -1;}, true);
|
||||
})
|
||||
.map(parts => {
|
||||
const status = parts.shift();
|
||||
const name = parts.join('\t').trim();
|
||||
|
|
|
@ -154,11 +154,11 @@ define(function (require) {
|
|||
var self = this;
|
||||
|
||||
return self.remote.setFindTimeout(defaultTimeout)
|
||||
.findByCssSelector('.app-wrapper > .application')
|
||||
.findByCssSelector('.app-wrapper .application')
|
||||
.then(function () {
|
||||
return self.runScript(function () {
|
||||
var $ = window.$;
|
||||
var $scope = $('.app-wrapper > .application').scope();
|
||||
var $scope = $('.app-wrapper .application').scope();
|
||||
return $scope ? $scope.chrome.getApp() : {};
|
||||
});
|
||||
});
|
||||
|
|