diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js deleted file mode 100644 index a2bae1c34d3..00000000000 --- a/public/app/core/directives/value_select_dropdown.js +++ /dev/null @@ -1,283 +0,0 @@ -define([ - 'angular', - 'lodash', - '../core_module', -], -function (angular, _, coreModule) { - 'use strict'; - - coreModule.default.controller('ValueSelectDropdownCtrl', function($q) { - var vm = this; - - vm.show = function() { - vm.oldVariableText = vm.variable.current.text; - vm.highlightIndex = -1; - - vm.options = vm.variable.options; - vm.selectedValues = _.filter(vm.options, {selected: true}); - - vm.tags = _.map(vm.variable.tags, function(value) { - var tag = { text: value, selected: false }; - _.each(vm.variable.current.tags, function(tagObj) { - if (tagObj.text === value) { - tag = tagObj; - } - }); - return tag; - }); - - vm.search = { - query: '', - options: vm.options.slice(0, Math.min(vm.options.length, 1000)) - }; - - vm.dropdownVisible = true; - }; - - vm.updateLinkText = function() { - var current = vm.variable.current; - - if (current.tags && current.tags.length) { - // filer out values that are in selected tags - var selectedAndNotInTag = _.filter(vm.variable.options, function(option) { - if (!option.selected) { return false; } - for (var i = 0; i < current.tags.length; i++) { - var tag = current.tags[i]; - if (_.indexOf(tag.values, option.value) !== -1) { - return false; - } - } - return true; - }); - - // convert values to text - var currentTexts = _.map(selectedAndNotInTag, 'text'); - - // join texts - vm.linkText = currentTexts.join(' + '); - if (vm.linkText.length > 0) { - vm.linkText += ' + '; - } - } else { - vm.linkText = vm.variable.current.text; - } - }; - - vm.clearSelections = function() { - _.each(vm.options, function(option) { - option.selected = false; - }); - - vm.selectionsChanged(false); - }; - - vm.selectTag = function(tag) { - tag.selected = !tag.selected; - var tagValuesPromise; - if (!tag.values) { - tagValuesPromise = vm.variable.getValuesForTag(tag.text); - } else { - tagValuesPromise = $q.when(tag.values); - } - - tagValuesPromise.then(function(values) { - tag.values = values; - tag.valuesText = values.join(' + '); - _.each(vm.options, function(option) { - if (_.indexOf(tag.values, option.value) !== -1) { - option.selected = tag.selected; - } - }); - - vm.selectionsChanged(false); - }); - }; - - vm.keyDown = function (evt) { - if (evt.keyCode === 27) { - vm.hide(); - } - if (evt.keyCode === 40) { - vm.moveHighlight(1); - } - if (evt.keyCode === 38) { - vm.moveHighlight(-1); - } - if (evt.keyCode === 13) { - if (vm.search.options.length === 0) { - vm.commitChanges(); - } else { - vm.selectValue(vm.search.options[vm.highlightIndex], {}, true, false); - } - } - if (evt.keyCode === 32) { - vm.selectValue(vm.search.options[vm.highlightIndex], {}, false, false); - } - }; - - vm.moveHighlight = function(direction) { - vm.highlightIndex = (vm.highlightIndex + direction) % vm.search.options.length; - }; - - vm.selectValue = function(option, event, commitChange, excludeOthers) { - if (!option) { return; } - - option.selected = vm.variable.multi ? !option.selected: true; - - commitChange = commitChange || false; - excludeOthers = excludeOthers || false; - - var setAllExceptCurrentTo = function(newValue) { - _.each(vm.options, function(other) { - if (option !== other) { other.selected = newValue; } - }); - }; - - // commit action (enter key), should not deselect it - if (commitChange) { - option.selected = true; - } - - if (option.text === 'All' || excludeOthers) { - setAllExceptCurrentTo(false); - commitChange = true; - } - else if (!vm.variable.multi) { - setAllExceptCurrentTo(false); - commitChange = true; - } else if (event.ctrlKey || event.metaKey || event.shiftKey) { - commitChange = true; - setAllExceptCurrentTo(false); - } - - vm.selectionsChanged(commitChange); - }; - - vm.selectionsChanged = function(commitChange) { - vm.selectedValues = _.filter(vm.options, {selected: true}); - - if (vm.selectedValues.length > 1) { - if (vm.selectedValues[0].text === 'All') { - vm.selectedValues[0].selected = false; - vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length); - } - } - - // validate selected tags - _.each(vm.tags, function(tag) { - if (tag.selected) { - _.each(tag.values, function(value) { - if (!_.find(vm.selectedValues, {value: value})) { - tag.selected = false; - } - }); - } - }); - - vm.selectedTags = _.filter(vm.tags, {selected: true}); - vm.variable.current.value = _.map(vm.selectedValues, 'value'); - vm.variable.current.text = _.map(vm.selectedValues, 'text').join(' + '); - vm.variable.current.tags = vm.selectedTags; - - if (!vm.variable.multi) { - vm.variable.current.value = vm.selectedValues[0].value; - } - - if (commitChange) { - vm.commitChanges(); - } - }; - - vm.commitChanges = function() { - // if we have a search query and no options use that - if (vm.search.options.length === 0 && vm.search.query.length > 0) { - vm.variable.current = {text: vm.search.query, value: vm.search.query}; - } - else if (vm.selectedValues.length === 0) { - // make sure one option is selected - vm.options[0].selected = true; - vm.selectionsChanged(false); - } - - vm.dropdownVisible = false; - vm.updateLinkText(); - - if (vm.variable.current.text !== vm.oldVariableText) { - vm.onUpdated(); - } - }; - - vm.queryChanged = function() { - vm.highlightIndex = -1; - vm.search.options = _.filter(vm.options, function(option) { - return option.text.toLowerCase().indexOf(vm.search.query.toLowerCase()) !== -1; - }); - - vm.search.options = vm.search.options.slice(0, Math.min(vm.search.options.length, 1000)); - }; - - vm.init = function() { - vm.selectedTags = vm.variable.current.tags || []; - vm.updateLinkText(); - }; - - }); - - coreModule.default.directive('valueSelectDropdown', function($compile, $window, $timeout, $rootScope) { - return { - scope: { variable: "=", onUpdated: "&"}, - templateUrl: 'public/app/partials/valueSelectDropdown.html', - controller: 'ValueSelectDropdownCtrl', - controllerAs: 'vm', - bindToController: true, - link: function(scope, elem) { - var bodyEl = angular.element($window.document.body); - var linkEl = elem.find('.variable-value-link'); - var inputEl = elem.find('input'); - - function openDropdown() { - inputEl.css('width', Math.max(linkEl.width(), 80) + 'px'); - - inputEl.show(); - linkEl.hide(); - - inputEl.focus(); - $timeout(function() { bodyEl.on('click', bodyOnClick); }, 0, false); - } - - function switchToLink() { - inputEl.hide(); - linkEl.show(); - bodyEl.off('click', bodyOnClick); - } - - function bodyOnClick (e) { - if (elem.has(e.target).length === 0) { - scope.$apply(function() { - scope.vm.commitChanges(); - }); - } - } - - scope.$watch('vm.dropdownVisible', function(newValue) { - if (newValue) { - openDropdown(); - } else { - switchToLink(); - } - }); - - var cleanUp = $rootScope.$on('template-variable-value-updated', function() { - scope.vm.updateLinkText(); - }); - - scope.$on("$destroy", function() { - cleanUp(); - }); - - scope.vm.init(); - }, - }; - }); - -}); diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts new file mode 100644 index 00000000000..628b1480a4f --- /dev/null +++ b/public/app/core/directives/value_select_dropdown.ts @@ -0,0 +1,303 @@ +import angular from 'angular'; +import _ from 'lodash'; +import coreModule from '../core_module'; + +export class ValueSelectDropdownCtrl { + dropdownVisible: any; + highlightIndex: any; + linkText: any; + oldVariableText: any; + options: any; + search: any; + selectedTags: any; + selectedValues: any; + tags: any; + variable: any; + + hide: any; + onUpdated: any; + + constructor(private $q) {} + + show() { + this.oldVariableText = this.variable.current.text; + this.highlightIndex = -1; + + this.options = this.variable.options; + this.selectedValues = _.filter(this.options, { selected: true }); + + this.tags = _.map(this.variable.tags, function(value) { + let tag = { text: value, selected: false }; + _.each(this.variable.current.tags, function(tagObj) { + if (tagObj.text === value) { + tag = tagObj; + } + }); + return tag; + }); + + this.search = { + query: '', + options: this.options.slice(0, Math.min(this.options.length, 1000)), + }; + + this.dropdownVisible = true; + } + + updateLinkText() { + let current = this.variable.current; + + if (current.tags && current.tags.length) { + // filer out values that are in selected tags + let selectedAndNotInTag = _.filter(this.variable.options, function(option) { + if (!option.selected) { + return false; + } + for (let i = 0; i < current.tags.length; i++) { + let tag = current.tags[i]; + if (_.indexOf(tag.values, option.value) !== -1) { + return false; + } + } + return true; + }); + + // convert values to text + let currentTexts = _.map(selectedAndNotInTag, 'text'); + + // join texts + this.linkText = currentTexts.join(' + '); + if (this.linkText.length > 0) { + this.linkText += ' + '; + } + } else { + this.linkText = this.variable.current.text; + } + } + + clearSelections() { + _.each(this.options, function(option) { + option.selected = false; + }); + + this.selectionsChanged(false); + } + + selectTag(tag) { + tag.selected = !tag.selected; + let tagValuesPromise; + if (!tag.values) { + tagValuesPromise = this.variable.getValuesForTag(tag.text); + } else { + tagValuesPromise = this.$q.when(tag.values); + } + + tagValuesPromise.then(function(values) { + tag.values = values; + tag.valuesText = values.join(' + '); + _.each(this.options, function(option) { + if (_.indexOf(tag.values, option.value) !== -1) { + option.selected = tag.selected; + } + }); + + this.selectionsChanged(false); + }); + } + + keyDown(evt) { + if (evt.keyCode === 27) { + this.hide(); + } + if (evt.keyCode === 40) { + this.moveHighlight(1); + } + if (evt.keyCode === 38) { + this.moveHighlight(-1); + } + if (evt.keyCode === 13) { + if (this.search.options.length === 0) { + this.commitChanges(); + } else { + this.selectValue(this.search.options[this.highlightIndex], {}, true, false); + } + } + if (evt.keyCode === 32) { + this.selectValue(this.search.options[this.highlightIndex], {}, false, false); + } + } + + moveHighlight(direction) { + this.highlightIndex = (this.highlightIndex + direction) % this.search.options.length; + } + + selectValue(option, event, commitChange, excludeOthers) { + if (!option) { + return; + } + + option.selected = this.variable.multi ? !option.selected : true; + + commitChange = commitChange || false; + excludeOthers = excludeOthers || false; + + let setAllExceptCurrentTo = function(newValue) { + _.each(this.options, function(other) { + if (option !== other) { + other.selected = newValue; + } + }); + }; + + // commit action (enter key), should not deselect it + if (commitChange) { + option.selected = true; + } + + if (option.text === 'All' || excludeOthers) { + setAllExceptCurrentTo(false); + commitChange = true; + } else if (!this.variable.multi) { + setAllExceptCurrentTo(false); + commitChange = true; + } else if (event.ctrlKey || event.metaKey || event.shiftKey) { + commitChange = true; + setAllExceptCurrentTo(false); + } + + this.selectionsChanged(commitChange); + } + + selectionsChanged(commitChange) { + this.selectedValues = _.filter(this.options, { selected: true }); + + if (this.selectedValues.length > 1) { + if (this.selectedValues[0].text === 'All') { + this.selectedValues[0].selected = false; + this.selectedValues = this.selectedValues.slice(1, this.selectedValues.length); + } + } + + // validate selected tags + _.each(this.tags, function(tag) { + if (tag.selected) { + _.each(tag.values, function(value) { + if (!_.find(this.selectedValues, { value: value })) { + tag.selected = false; + } + }); + } + }); + + this.selectedTags = _.filter(this.tags, { selected: true }); + this.variable.current.value = _.map(this.selectedValues, 'value'); + this.variable.current.text = _.map(this.selectedValues, 'text').join(' + '); + this.variable.current.tags = this.selectedTags; + + if (!this.variable.multi) { + this.variable.current.value = this.selectedValues[0].value; + } + + if (commitChange) { + this.commitChanges(); + } + } + + commitChanges() { + // if we have a search query and no options use that + if (this.search.options.length === 0 && this.search.query.length > 0) { + this.variable.current = { text: this.search.query, value: this.search.query }; + } else if (this.selectedValues.length === 0) { + // make sure one option is selected + this.options[0].selected = true; + this.selectionsChanged(false); + } + + this.dropdownVisible = false; + this.updateLinkText(); + + if (this.variable.current.text !== this.oldVariableText) { + this.onUpdated(); + } + } + + queryChanged() { + this.highlightIndex = -1; + this.search.options = _.filter(this.options, function(option) { + return option.text.toLowerCase().indexOf(this.search.query.toLowerCase()) !== -1; + }); + + this.search.options = this.search.options.slice(0, Math.min(this.search.options.length, 1000)); + } + + init() { + this.selectedTags = this.variable.current.tags || []; + this.updateLinkText(); + } +} + +export function valueSelectDropdown($compile, $window, $timeout, $rootScope) { + return { + scope: { variable: '=', onUpdated: '&' }, + templateUrl: 'public/app/partials/valueSelectDropdown.html', + controller: 'ValueSelectDropdownCtrl', + controllerAs: 'vm', + bindToController: true, + link: function(scope, elem) { + let bodyEl = angular.element($window.document.body); + let linkEl = elem.find('.variable-value-link'); + let inputEl = elem.find('input'); + + function openDropdown() { + inputEl.css('width', Math.max(linkEl.width(), 80) + 'px'); + + inputEl.show(); + linkEl.hide(); + + inputEl.focus(); + $timeout( + function() { + bodyEl.on('click', bodyOnClick); + }, + 0, + false + ); + } + + function switchToLink() { + inputEl.hide(); + linkEl.show(); + bodyEl.off('click', bodyOnClick); + } + + function bodyOnClick(e) { + if (elem.has(e.target).length === 0) { + scope.$apply(function() { + scope.vm.commitChanges(); + }); + } + } + + scope.$watch('vm.dropdownVisible', function(newValue) { + if (newValue) { + openDropdown(); + } else { + switchToLink(); + } + }); + + let cleanUp = $rootScope.$on('template-variable-value-updated', function() { + scope.vm.updateLinkText(); + }); + + scope.$on('$destroy', function() { + cleanUp(); + }); + + scope.vm.init(); + }, + }; +} + +coreModule.controller('ValueSelectDropdownCtrl', ValueSelectDropdownCtrl); +coreModule.directive('valueSelectDropdown', valueSelectDropdown);