-
-
+
+
+
+
+
+
+
+
+
-
-
-
`;
+ `;
},
- controller: ['$scope', '$compile', '$attrs', function ($scope, $compile, $attrs) {
- const ctrlObj = this;
- // toggleCurrTemplate(false) to turn it off
- ctrlObj.toggleCurrTemplate = function (which) {
- if (ctrlObj.curr === which || !which) {
- ctrlObj.curr = null;
- } else {
- ctrlObj.curr = which;
- }
-
- const templateToCompile = ctrlObj.templates[ctrlObj.curr] || false;
-
- if ($scope.kbnTopNav.currTemplate) {
- $scope.kbnTopNav.currTemplateScope.$destroy();
- $scope.kbnTopNav.currTemplate.remove();
- }
-
- if (templateToCompile) {
- $scope.kbnTopNav.currTemplateScope = $scope.$new();
- $scope.kbnTopNav.currTemplate = $compile(templateToCompile)($scope.kbnTopNav.currTemplateScope);
- } else {
- $scope.kbnTopNav.currTemplateScope = null;
- $scope.kbnTopNav.currTemplate = false;
- }
- };
- const normalizeOpts = _.partial(optionsNormalizer, (item) => {
- ctrlObj.toggleCurrTemplate(item.key);
- });
-
- const niceMenuItems = _.compact(_.get($scope, $attrs.config, []).map(normalizeOpts));
- ctrlObj.templates = _.assign({
- interval: intervalTemplate,
- filter: filterTemplate,
- }, getTemplatesMap(niceMenuItems));
+ controller($scope, $compile, $attrs, $element) {
+ const KbnTopNavController = Private(KbnTopNavControllerProvider);
- $scope.kbnTopNav = {
- menuItems: niceMenuItems.filter(item => !item.noButton),
- currTemplate: false,
- is: which => { return ctrlObj.curr === which; },
- close: () => { ctrlObj.toggleCurrTemplate(false); },
- toggle: ctrlObj.toggleCurrTemplate,
- open: which => {
- if (ctrlObj.curr !== which) {
- ctrlObj.toggleCurrTemplate(which);
- }
- }
- };
- }],
- link: function ($scope, element, attr, configCtrl) {
- $scope.$watch('kbnTopNav.currTemplate', newVal => {
- element.find('#template_wrapper').html(newVal);
- });
+ $scope.kbnTopNav = new KbnTopNavController(_.get($scope, $attrs.config));
+ $scope.kbnTopNav._link($scope, $element);
+ return $scope.kbnTopNav;
}
};
});
diff --git a/src/ui/public/kbn_top_nav/kbn_top_nav_controller.js b/src/ui/public/kbn_top_nav/kbn_top_nav_controller.js
new file mode 100644
index 0000000000000..34e2d61fa128c
--- /dev/null
+++ b/src/ui/public/kbn_top_nav/kbn_top_nav_controller.js
@@ -0,0 +1,95 @@
+import { defaults, capitalize } from 'lodash';
+
+import uiModules from 'ui/modules';
+import filterTemplate from 'ui/chrome/config/filter.html';
+import intervalTemplate from 'ui/chrome/config/interval.html';
+
+export default function ($compile) {
+ return class KbnTopNavController {
+ constructor(opts = []) {
+ if (opts instanceof KbnTopNavController) {
+ return opts;
+ }
+
+ this.opts = [];
+ this.menuItems = [];
+ this.currentKey = null;
+ this.templates = {
+ interval: intervalTemplate,
+ filter: filterTemplate,
+ };
+
+ opts.forEach(rawOpt => {
+ const opt = this._applyOptDefault(rawOpt);
+ if (!opt.key) throw new TypeError('KbnTopNav: menu items must have a key');
+ this.opts.push(opt);
+ if (!opt.hideButton) this.menuItems.push(opt);
+ if (opt.template) this.templates[opt.key] = opt.template;
+ });
+ }
+
+ // change the current key and rerender
+ set(key) {
+ if (key && !this.templates.hasOwnProperty(key)) {
+ throw new TypeError(`KbnTopNav: unknown template key "${key}"`);
+ }
+
+ this.currentKey = key || null;
+ this._render();
+ }
+
+ // little usability helpers
+ which() { return this.currentKey; }
+ is(key) { return this.which() === key; }
+ open(key) { this.set(key); }
+ close(key) { (!key || this.is(key)) && this.set(null); }
+ toggle(key) { this.set(this.is(key) ? null : key); }
+
+ // apply the defaults to individual options
+ _applyOptDefault(opt = {}) {
+ return defaults({}, opt, {
+ label: capitalize(opt.key),
+ hasFunction: !!opt.run,
+ description: opt.run ? opt.key : `Toggle ${opt.key} view`,
+ hideButton: !!opt.hideButton,
+ run: (item) => this.toggle(item.key)
+ });
+ }
+
+ // enable actual rendering
+ _link($scope, $element) {
+ this.$scope = $scope;
+ this.$element = $element;
+ this._render();
+ }
+
+ // render the current template to the $element if possible
+ // function is idempotent
+ _render() {
+ const { $scope, $element, rendered, currentKey } = this;
+ const templateToRender = currentKey && this.templates[currentKey];
+
+ if (rendered) {
+ if (rendered.key !== currentKey) {
+ // we have an invalid render, clear it
+ rendered.$childScope.$destroy();
+ rendered.$el.remove();
+ this.rendered = null;
+ } else {
+ // our previous render is still valid, keep it
+ return;
+ }
+ }
+
+ if (!templateToRender || !$scope || !$element) {
+ // we either have nothing to render, or we can't render
+ return;
+ }
+
+ const $childScope = $scope.$new();
+ const $el = $compile(templateToRender)($childScope);
+ $element.find('#template_wrapper').html($el);
+ this.rendered = { $childScope, $el, key: currentKey };
+ }
+ };
+}