jquery-asBreadcrumbs.es.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /**
  2. * jQuery asBreadcrumbs v0.2.3
  3. * https://github.com/amazingSurge/jquery-asBreadcrumbs
  4. *
  5. * Copyright (c) amazingSurge
  6. * Released under the LGPL-3.0 license
  7. */
  8. import $ from 'jquery';
  9. var DEFAULTS = {
  10. namespace: 'breadcrumb',
  11. overflow: "left",
  12. responsive: true,
  13. ellipsisText: "…",
  14. ellipsisClass: null,
  15. hiddenClass: 'is-hidden',
  16. dropdownClass: null,
  17. dropdownMenuClass: null,
  18. dropdownItemClass: null,
  19. dropdownItemDisableClass: 'disabled',
  20. toggleClass: null,
  21. toggleIconClass: 'caret',
  22. getItems: function($parent) {
  23. return $parent.children();
  24. },
  25. getItemLink: function($item) {
  26. return $item.find('a');
  27. },
  28. // templates
  29. ellipsis: function(classes, label) {
  30. return `<li class="${classes.ellipsisClass}">${label}</li>`;
  31. },
  32. dropdown: function(classes) {
  33. const dropdownClass = 'dropdown';
  34. let dropdownMenuClass = 'dropdown-menu';
  35. if (this.options.overflow === 'right') {
  36. dropdownMenuClass += ' dropdown-menu-right';
  37. }
  38. return `<li class="${dropdownClass} ${classes.dropdownClass}">
  39. <a href="javascript:void(0);" class="${classes.toggleClass}" data-toggle="dropdown">
  40. <i class="${classes.toggleIconClass}"></i>
  41. </a>
  42. <ul class="${dropdownMenuClass} ${classes.dropdownMenuClass}"></ul>
  43. </li>`;
  44. },
  45. dropdownItem: function(classes, label, href) {
  46. if(!href) {
  47. return `<li class="${classes.dropdownItemClass} ${classes.dropdownItemDisableClass}"><a href="#">${label}</a></li>`;
  48. }
  49. return `<li class="${classes.dropdownItemClass}"><a href="${href}">${label}</a></li>`;
  50. },
  51. // callbacks
  52. onInit: null,
  53. onReady: null
  54. };
  55. const NAMESPACE = 'asBreadcrumbs';
  56. let instanceId = 0;
  57. /**
  58. * Plugin constructor
  59. **/
  60. class asBreadcrumbs {
  61. constructor(element, options) {
  62. this.element = element;
  63. this.$element = $(element);
  64. this.options = $.extend({}, DEFAULTS, options, this.$element.data());
  65. this.namespace = this.options.namespace;
  66. this.$element.addClass(this.namespace);
  67. this.classes = {
  68. toggleClass: this.options.toggleClass? this.options.toggleClass: `${this.namespace}-toggle`,
  69. toggleIconClass: this.options.toggleIconClass,
  70. dropdownClass: this.options.dropdownClass? this.options.dropdownClass: `${this.namespace}-dropdown`,
  71. dropdownMenuClass: this.options.dropdownMenuClass? this.options.dropdownMenuClass: `${this.namespace}-dropdown-menu`,
  72. dropdownItemClass: this.options.dropdownItemClass? this.options.dropdownItemClass: '',
  73. dropdownItemDisableClass: this.options.dropdownItemDisableClass? this.options.dropdownItemDisableClass: '',
  74. ellipsisClass: this.options.ellipsisClass? this.options.ellipsisClass: `${this.namespace}-ellipsis`,
  75. hiddenClass: this.options.hiddenClass
  76. };
  77. // flag
  78. this.initialized = false;
  79. this.instanceId = (++instanceId);
  80. this.$children = this.options.getItems(this.$element);
  81. this.$firstChild = this.$children.eq(0);
  82. this.$dropdown = null;
  83. this.$dropdownMenu = null;
  84. this.gap = 6;
  85. this.items = [];
  86. this._trigger('init');
  87. this.init();
  88. }
  89. init() {
  90. this.$element.addClass(`${this.namespace}-${this.options.overflow}`);
  91. this._prepareItems();
  92. this._createDropdown();
  93. this._createEllipsis();
  94. this.render();
  95. if (this.options.responsive) {
  96. $(window).on(this.eventNameWithId('resize'), this._throttle(() => {
  97. this.resize();
  98. }, 250));
  99. }
  100. this.initialized = true;
  101. this._trigger('ready');
  102. }
  103. _prepareItems() {
  104. const that = this;
  105. this.$children.each(function(){
  106. const $this = $(this);
  107. const $link = that.options.getItemLink($this);
  108. const $dropdownItem = $(that.options.dropdownItem.call(that, that.classes, $this.text(), $link.attr('href')));
  109. that.items.push({
  110. $this,
  111. outerWidth: $this.outerWidth(),
  112. $item: $dropdownItem
  113. });
  114. });
  115. if (this.options.overflow === "left") {
  116. this.items.reverse();
  117. }
  118. }
  119. _createDropdown() {
  120. this.$dropdown = $(this.options.dropdown.call(this, this.classes)).addClass(this.classes.hiddenClass).appendTo(this.$element);
  121. this.$dropdownMenu = this.$dropdown.find(`.${this.classes.dropdownMenuClass}`);
  122. this._createDropdownItems();
  123. if (this.options.overflow === 'right') {
  124. this.$dropdown.appendTo(this.$element);
  125. } else {
  126. this.$dropdown.prependTo(this.$element);
  127. }
  128. }
  129. _createDropdownItems() {
  130. for (let i = 0; i < this.items.length; i++) {
  131. this.items[i].$item.appendTo(this.$dropdownMenu).addClass(this.classes.hiddenClass);
  132. }
  133. }
  134. _createEllipsis() {
  135. if (!this.options.ellipsisText) {
  136. return;
  137. }
  138. this.$ellipsis = $(this.options.ellipsis.call(this, this.classes, this.options.ellipsisText)).addClass(this.classes.hiddenClass);
  139. if (this.options.overflow === 'right') {
  140. this.$ellipsis.insertBefore(this.$dropdown);
  141. } else {
  142. this.$ellipsis.insertAfter(this.$dropdown);
  143. }
  144. }
  145. render() {
  146. const dropdownWidth = this.getDropdownWidth();
  147. let childrenWidthTotal = 0;
  148. let containerWidth = this.getConatinerWidth();
  149. let showDropdown = false;
  150. for (let i = 0; i < this.items.length; i++) {
  151. childrenWidthTotal += this.items[i].outerWidth;
  152. if (childrenWidthTotal + dropdownWidth > containerWidth) {
  153. showDropdown = true;
  154. this._showDropdownItem(i);
  155. } else {
  156. this._hideDropdownItem(i);
  157. }
  158. }
  159. if(showDropdown) {
  160. this.$ellipsis.removeClass(this.classes.hiddenClass);
  161. this.$dropdown.removeClass(this.classes.hiddenClass);
  162. } else {
  163. this.$ellipsis.addClass(this.classes.hiddenClass);
  164. this.$dropdown.addClass(this.classes.hiddenClass);
  165. }
  166. this._trigger('update');
  167. }
  168. resize() {
  169. this.render();
  170. }
  171. getDropdownWidth() {
  172. return this.$dropdown.outerWidth() + (this.options.ellipsisText? this.$ellipsis.outerWidth() : 0);
  173. }
  174. getConatinerWidth() {
  175. let width = 0;
  176. const that = this;
  177. this.$element.children().each(function() {
  178. if ($(this).css('display') === 'inline-block' && $(this).css('float') === 'none') {
  179. width += that.gap;
  180. }
  181. });
  182. return this.$element.width() - width;
  183. }
  184. _showDropdownItem(i) {
  185. this.items[i].$item.removeClass(this.classes.hiddenClass);
  186. this.items[i].$this.addClass(this.classes.hiddenClass);
  187. }
  188. _hideDropdownItem(i) {
  189. this.items[i].$this.removeClass(this.classes.hiddenClass);
  190. this.items[i].$item.addClass(this.classes.hiddenClass);
  191. }
  192. _trigger(eventType, ...params) {
  193. let data = [this].concat(params);
  194. // event
  195. this.$element.trigger(`${NAMESPACE}::${eventType}`, data);
  196. // callback
  197. eventType = eventType.replace(/\b\w+\b/g, (word) => {
  198. return word.substring(0, 1).toUpperCase() + word.substring(1);
  199. });
  200. let onFunction = `on${eventType}`;
  201. if (typeof this.options[onFunction] === 'function') {
  202. this.options[onFunction].apply(this, params);
  203. }
  204. }
  205. eventName(events) {
  206. if (typeof events !== 'string' || events === '') {
  207. return `.${this.options.namespace}`;
  208. }
  209. events = events.split(' ');
  210. let length = events.length;
  211. for (let i = 0; i < length; i++) {
  212. events[i] = `${events[i]}.${this.options.namespace}`;
  213. }
  214. return events.join(' ');
  215. }
  216. eventNameWithId(events) {
  217. if (typeof events !== 'string' || events === '') {
  218. return `.${this.options.namespace}-${this.instanceId}`;
  219. }
  220. events = events.split(' ');
  221. let length = events.length;
  222. for (let i = 0; i < length; i++) {
  223. events[i] = `${events[i]}.${this.options.namespace}-${this.instanceId}`;
  224. }
  225. return events.join(' ');
  226. }
  227. _throttle(func, wait) {
  228. const _now = Date.now || function() {
  229. return new Date().getTime();
  230. };
  231. let timeout;
  232. let context;
  233. let args;
  234. let result;
  235. let previous = 0;
  236. let later = function() {
  237. previous = _now();
  238. timeout = null;
  239. result = func.apply(context, args);
  240. if (!timeout) {
  241. context = args = null;
  242. }
  243. };
  244. return (...params) => {
  245. /*eslint consistent-this: "off"*/
  246. let now = _now();
  247. let remaining = wait - (now - previous);
  248. context = this;
  249. args = params;
  250. if (remaining <= 0 || remaining > wait) {
  251. if (timeout) {
  252. clearTimeout(timeout);
  253. timeout = null;
  254. }
  255. previous = now;
  256. result = func.apply(context, args);
  257. if (!timeout) {
  258. context = args = null;
  259. }
  260. } else if (!timeout) {
  261. timeout = setTimeout(later, remaining);
  262. }
  263. return result;
  264. };
  265. }
  266. destroy() {
  267. this.$element.children().removeClass(this.classes.hiddenClass);
  268. this.$dropdown.remove();
  269. if (this.options.ellipsisText) {
  270. this.$ellipsis.remove();
  271. }
  272. this.initialized = false;
  273. this.$element.data(NAMESPACE, null);
  274. $(window).off(this.eventNameWithId('resize'));
  275. this._trigger('destroy');
  276. }
  277. static setDefaults(options) {
  278. $.extend(DEFAULTS, $.isPlainObject(options) && options);
  279. }
  280. }
  281. var info = {
  282. version:'0.2.3'
  283. };
  284. const NAME = 'asBreadcrumbs';
  285. const OtherAsBreadcrumbs = $.fn.asBreadcrumbs;
  286. const jQueryAsBreadcrumbs = function(options, ...args) {
  287. if (typeof options === 'string') {
  288. let method = options;
  289. if (/^_/.test(method)) {
  290. return false;
  291. } else if ((/^(get)/.test(method))) {
  292. let instance = this.first().data(NAME);
  293. if (instance && typeof instance[method] === 'function') {
  294. return instance[method](...args);
  295. }
  296. } else {
  297. return this.each(function() {
  298. let instance = $.data(this, NAME);
  299. if (instance && typeof instance[method] === 'function') {
  300. instance[method](...args);
  301. }
  302. });
  303. }
  304. }
  305. return this.each(function() {
  306. if (!$(this).data(NAME)) {
  307. $(this).data(NAME, new asBreadcrumbs(this, options));
  308. }
  309. });
  310. };
  311. $.fn.asBreadcrumbs = jQueryAsBreadcrumbs;
  312. $.asBreadcrumbs = $.extend({
  313. setDefaults: asBreadcrumbs.setDefaults,
  314. noConflict: function() {
  315. $.fn.asBreadcrumbs = OtherAsBreadcrumbs;
  316. return jQueryAsBreadcrumbs;
  317. }
  318. }, info);