1. /**
  2. * @module meteoJS/timeline/visualisation/bsDropdown
  3. */
  4. import $ from 'jquery';
  5. import Visualisation from '../Visualisation.js';
  6. import Text from './Text.js';
  7. /**
  8. * Options for constructor.
  9. *
  10. * @typedef {module:meteoJS/timeline/visualisation~options}
  11. * module:meteoJS/timeline/visualisation/bsDropdown~options
  12. * @param {string|undefined} format
  13. * Format string for dropdown items, used for 'getTimeText'.
  14. * @param {string|undefined} buttonFormat
  15. * Format string for dropdown button, used for 'getTimeText'.
  16. * @param {string} grouping
  17. * 'daily', 'hourly' or a format string. Defines if items will be grouped and
  18. * be title depending on groupingDivider and groupingFormat.
  19. * @param {boolean} groupingDivider Show dropdown divider above an item group.
  20. * @param {undefined|string} groupingFormat
  21. * Format string for a dropdown header above an item group.
  22. * @param {undefined|string} classMain Class for the main node.
  23. * @param {undefined|string} classDropdownMenu Class for the dropdown node.
  24. * @param {undefined|string} classDropdownItem Class for a dropdown item.
  25. * @param {undefined|string} classItemActive Class for an active item.
  26. * @param {undefined|string} classItemNotEnabled Class for a not enabled item.
  27. * @param {undefined|string} classItemEnabled Class for an enabled item.
  28. * @param {undefined|string} classItemAllEnabled Class for an all enabled item.
  29. * @param {undefined|string} classDropdownHeader Class for dropdown header.
  30. * @param {undefined|string} classDropdownDivider Class for dropdown divider.
  31. * @param {undefined|string} classDropdownButton Class for the dropdown button.
  32. * @param {undefined|string} classButtonNotEnabled
  33. * Class for the dropdown button if the selected time is not enabled.
  34. * @param {undefined|string} classButtonEnabled
  35. * Class for the dropdown button if the selected time is enabled.
  36. * @param {undefined|string} classButtonAllEnabled
  37. * Class for the dropdown button if the selected time is all enabled.
  38. */
  39. /**
  40. * Show timeline in a dropdown menu. The menu will be build according to
  41. * bootstrap.
  42. *
  43. * <pre><code>import bsDropdown from 'meteojs/timeline/visualisation/bsDropdown';</code></pre>
  44. *
  45. * @extends module:meteoJS/timeline/visualisation.Visualisation
  46. */
  47. export class bsDropdown extends Visualisation {
  48. /**
  49. * @param {module:meteoJS/timeline/visualisation/bsDropdown~options} options - Options.
  50. */
  51. constructor({
  52. format = 'HH:mm',
  53. buttonFormat = 'DD. MMMM YYYY HH:mm',
  54. grouping = 'daily',
  55. groupingDivider = true,
  56. groupingFormat = 'ddd, DD. MMMM YYYY',
  57. classMain = 'dropdown',
  58. classDropdownMenu = 'dropdown-menu',
  59. classDropdownItem = 'dropdown-item',
  60. classItemActive = 'active',
  61. classItemNotEnabled = 'disabled',
  62. classItemEnabled = undefined,
  63. classItemAllEnabled = undefined,
  64. classDropdownHeader = 'dropdown-header',
  65. classDropdownDivider = 'dropdown-divider',
  66. classDropdownButton = 'btn dropdown-toggle',
  67. classButtonNotEnabled = undefined,
  68. classButtonEnabled = undefined,
  69. classButtonAllEnabled = undefined,
  70. ...rest
  71. } = {}) {
  72. super(rest);
  73. this.options.format = format;
  74. this.options.buttonFormat = buttonFormat;
  75. this.options.grouping = grouping;
  76. this.options.groupingDivider = groupingDivider;
  77. this.options.groupingFormat = groupingFormat;
  78. this.options.classMain = classMain;
  79. this.options.classDropdownMenu = classDropdownMenu;
  80. this.options.classDropdownItem = classDropdownItem;
  81. this.options.classItemActive = classItemActive;
  82. this.options.classItemNotEnabled = classItemNotEnabled;
  83. this.options.classItemEnabled = classItemEnabled;
  84. this.options.classItemAllEnabled = classItemAllEnabled;
  85. this.options.classDropdownHeader = classDropdownHeader;
  86. this.options.classDropdownDivider = classDropdownDivider;
  87. this.options.classDropdownButton = classDropdownButton;
  88. this.options.classButtonNotEnabled = classButtonNotEnabled;
  89. this.options.classButtonEnabled = classButtonEnabled;
  90. this.options.classButtonAllEnabled = classButtonAllEnabled;
  91. /**
  92. * @member {module:meteoJS/timeline/visualisation/text.Text}
  93. * @private
  94. */
  95. this.visualisationButtonText = new Text({
  96. timeline: this.options.timeline,
  97. format: this.options.buttonFormat,
  98. textInvalid: this.options.textInvalid,
  99. outputTimezone: this.options.outputTimezone,
  100. getTimeText: this.options.getTimeText
  101. });
  102. /**
  103. * @member {external:jQuery|undefined}
  104. * @private
  105. */
  106. this.dropdownNode = undefined;
  107. this.setNode(this.options.node);
  108. }
  109. /**
  110. * @inheritdoc
  111. */
  112. setOutputTimezone(outputTimezone) {
  113. super.setOutputTimezone(outputTimezone);
  114. this.visualisationButtonText.setOutputTimezone(outputTimezone);
  115. return this;
  116. }
  117. /**
  118. * @inheritdoc
  119. */
  120. onChangeTime() {
  121. if (this.dropdownNode === undefined)
  122. return;
  123. var time = this.options.timeline.getSelectedTime();
  124. this.options.node.children('li').children('button')
  125. .removeClass(this.options.classButtonActive)
  126. .removeClass(this.options.classButtonNotEnabled)
  127. .removeClass(this.options.classButtonEnabled)
  128. .removeClass(this.options.classButtonAllEnabled);
  129. if (this.options.timeline.isTimeAllEnabled(time))
  130. this.options.node.children('button')
  131. .addClass(this.options.classButtonAllEnabled);
  132. else if (this.options.timeline.isTimeEnabled(time))
  133. this.options.node.children('button')
  134. .addClass(this.options.classButtonEnabled);
  135. else
  136. this.options.node.children('button')
  137. .addClass(this.options.classButtonNotEnabled);
  138. var that = this;
  139. this.dropdownNode
  140. .children('li')
  141. .children('button.'+this.options.classDropdownItem)
  142. .each(function () {
  143. var t = new Date(+$(this).data('time'));
  144. $(this)
  145. .removeClass(that.options.classItemActive)
  146. .removeClass(that.options.classItemNotEnabled)
  147. .removeClass(that.options.classItemEnabled)
  148. .removeClass(that.options.classItemAllEnabled);
  149. if (time.valueOf() == t.valueOf())
  150. $(this).addClass(that.options.classItemActive);
  151. else if (that.options.timeline.isTimeAllEnabled(t))
  152. $(this).addClass(that.options.classItemAllEnabled);
  153. else if (that.options.timeline.isTimeEnabled(t))
  154. $(this).addClass(that.options.classItemEnabled);
  155. else
  156. $(this).addClass(that.options.classItemNotEnabled);
  157. });
  158. }
  159. /**
  160. * @inheritdoc
  161. */
  162. onChangeTimes() {
  163. if (this.dropdownNode === undefined)
  164. this.dropdownNode = $('<div>');
  165. this.dropdownNode.empty();
  166. var groupingFormat =
  167. (this.options.grouping == 'daily') ? 'YYYY-MM-DD' :
  168. (this.options.grouping == 'hourly') ? 'YYYY-MM-DD HH' :
  169. this.options.grouping;
  170. var lastGroupTimeStr = undefined;
  171. this.getTimelineTimes().forEach(function (time) {
  172. if (lastGroupTimeStr === undefined ||
  173. lastGroupTimeStr != this.timeToText(time, groupingFormat)) {
  174. if (lastGroupTimeStr !== undefined && // No divider at the beginning
  175. this.options.groupingDivider)
  176. this.dropdownNode
  177. .append($('<div>')
  178. .addClass(this.options.classDropdownDivider));
  179. if (this.options.groupingFormat !== undefined)
  180. this.dropdownNode
  181. .append($('<h6>')
  182. .addClass(this.options.classDropdownHeader)
  183. .text(this.timeToText(time, this.options.groupingFormat)));
  184. lastGroupTimeStr = this.timeToText(time, groupingFormat);
  185. }
  186. var btn = $('<button>')
  187. .addClass(this.options.classDropdownItem)
  188. .attr('type', 'button')
  189. .text(this.timeToText(time, this.options.format))
  190. .data('time', time.valueOf());
  191. var that = this;
  192. btn.click(function () {
  193. that.options.timeline.setSelectedTime(new Date(+$(this).data('time')));
  194. that.trigger('input');
  195. });
  196. this.dropdownNode.append($('<li>').append(btn));
  197. }, this);
  198. }
  199. /**
  200. * @inheritdoc
  201. */
  202. emptyNode() {
  203. if (this.visualisationButtonText !== undefined)
  204. this.visualisationButtonText.setNode(undefined);
  205. this.dropdownNode = undefined;
  206. this.options.node.empty();
  207. }
  208. /**
  209. * @inheritdoc
  210. */
  211. onInitNode() {
  212. var id = 'dropdownMenuButton';
  213. var i=0;
  214. while (document.getElementById(id) != null) {
  215. id = 'dropdownMenuButton'+(++i);
  216. }
  217. var button = $('<button>')
  218. .addClass(this.options.classDropdownButton)
  219. .attr('type', 'button')
  220. .attr('id', id)
  221. .attr('data-bs-toggle', 'dropdown')
  222. .attr('aria-haspopup', true)
  223. .attr('aria-expanded', false);
  224. this.visualisationButtonText.setNode(button);
  225. this.dropdownNode = $('<ul>')
  226. .addClass(this.options.classDropdownMenu)
  227. .attr('aria-labelledby', id);
  228. this.options.node
  229. .addClass(this.options.classMain)
  230. .append(button)
  231. .append(this.dropdownNode);
  232. }
  233. }
  234. export default bsDropdown;