multi-select.directive.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import angular from 'angular';
  2. import _ from 'lodash';
  3. export class MultiSelectDropdownCtrl {
  4. dropdownVisible: boolean;
  5. highlightIndex: number;
  6. linkText: string;
  7. options: Array<{ selected: boolean; text: string; value: string }>;
  8. selectedValues: Array<{ text: string; value: string }>;
  9. initialValues: string[];
  10. onUpdated: any;
  11. show() {
  12. this.highlightIndex = -1;
  13. this.options = this.options;
  14. this.selectedValues = this.options.filter(({ selected }) => selected);
  15. this.dropdownVisible = true;
  16. }
  17. hide() {
  18. this.dropdownVisible = false;
  19. }
  20. updateLinkText() {
  21. this.linkText =
  22. this.selectedValues.length === 1 ? this.selectedValues[0].text : `(${this.selectedValues.length}) selected`;
  23. }
  24. clearSelections() {
  25. this.selectedValues = _.filter(this.options, { selected: true });
  26. if (this.selectedValues.length > 1) {
  27. _.each(this.options, option => {
  28. option.selected = false;
  29. });
  30. } else {
  31. _.each(this.options, option => {
  32. option.selected = true;
  33. });
  34. }
  35. this.selectionsChanged();
  36. }
  37. selectValue(option: any) {
  38. if (!option) {
  39. return;
  40. }
  41. option.selected = !option.selected;
  42. this.selectionsChanged();
  43. }
  44. selectionsChanged() {
  45. this.selectedValues = _.filter(this.options, { selected: true });
  46. if (!this.selectedValues.length && this.options.length) {
  47. this.selectedValues = this.options.slice(0, 1);
  48. }
  49. this.updateLinkText();
  50. this.onUpdated({ values: this.selectedValues.map(({ value }) => value) });
  51. }
  52. onClickOutside() {
  53. this.selectedValues = _.filter(this.options, { selected: true });
  54. if (this.selectedValues.length === 0) {
  55. this.options[0].selected = true;
  56. this.selectionsChanged();
  57. }
  58. this.dropdownVisible = false;
  59. }
  60. init() {
  61. if (!this.options) {
  62. return;
  63. }
  64. this.options = this.options.map(o => ({
  65. ...o,
  66. selected: this.initialValues.includes(o.value),
  67. }));
  68. this.selectedValues = _.filter(this.options, { selected: true });
  69. if (!this.selectedValues.length) {
  70. this.options = this.options.map(o => ({
  71. ...o,
  72. selected: true,
  73. }));
  74. }
  75. this.updateLinkText();
  76. }
  77. updateSelection() {
  78. this.selectedValues = _.filter(this.options, { selected: true });
  79. if (!this.selectedValues.length && this.options.length) {
  80. this.options = this.options.map(o => ({
  81. ...o,
  82. selected: true,
  83. }));
  84. this.selectedValues = _.filter(this.options, { selected: true });
  85. this.selectionsChanged();
  86. }
  87. this.updateLinkText();
  88. }
  89. }
  90. /** @ngInject */
  91. export function multiSelectDropdown($window: any, $timeout: any) {
  92. return {
  93. scope: { onUpdated: '&', options: '=', initialValues: '=' },
  94. templateUrl: 'public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/multi-select.directive.html',
  95. controller: MultiSelectDropdownCtrl,
  96. controllerAs: 'vm',
  97. bindToController: true,
  98. link: (scope: any, elem: any) => {
  99. const bodyEl = angular.element($window.document.body);
  100. const linkEl = elem.find('.variable-value-link');
  101. const inputEl = elem.find('input');
  102. function openDropdown() {
  103. inputEl.css('width', Math.max(linkEl.width(), 80) + 'px');
  104. inputEl.show();
  105. linkEl.hide();
  106. inputEl.focus();
  107. $timeout(
  108. () => {
  109. bodyEl.on('click', () => {
  110. bodyEl.on('click', bodyOnClick);
  111. });
  112. },
  113. 0,
  114. false
  115. );
  116. }
  117. function switchToLink() {
  118. inputEl.hide();
  119. linkEl.show();
  120. bodyEl.off('click', bodyOnClick);
  121. }
  122. function bodyOnClick(e: any) {
  123. if (elem.has(e.target).length === 0) {
  124. scope.$apply(() => {
  125. scope.vm.onClickOutside();
  126. });
  127. }
  128. }
  129. scope.$watch('vm.options', (newValue: any) => {
  130. if (newValue) {
  131. scope.vm.updateSelection(newValue);
  132. }
  133. });
  134. scope.$watch('vm.dropdownVisible', (newValue: any) => {
  135. if (newValue) {
  136. openDropdown();
  137. } else {
  138. switchToLink();
  139. }
  140. });
  141. scope.vm.init();
  142. },
  143. };
  144. }
  145. angular.module('grafana.directives').directive('multiSelect', multiSelectDropdown);