core.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /* ------------------------------------------------------------------------------
  2. *
  3. * # Template JS core
  4. *
  5. * Includes minimum required JS code for proper template functioning
  6. *
  7. * ---------------------------------------------------------------------------- */
  8. // Setup module
  9. // ------------------------------
  10. const App = function () {
  11. // Utils
  12. // -------------------------
  13. //
  14. // Transitions
  15. //
  16. // Disable all transitions
  17. const transitionsDisabled = function() {
  18. $('body').addClass('no-transitions');
  19. };
  20. // Enable all transitions
  21. const transitionsEnabled = function() {
  22. $('body').removeClass('no-transitions');
  23. };
  24. //
  25. // Detect OS to apply custom scrollbars
  26. //
  27. // Custom scrollbar style is controlled by CSS. This function is needed to keep default
  28. // scrollbars on MacOS and avoid usage of extra JS libraries
  29. const detectOS = function() {
  30. const platform = window.navigator.platform,
  31. windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],
  32. customScrollbarsClass = 'custom-scrollbars';
  33. // Add class if OS is windows
  34. windowsPlatforms.indexOf(platform) != -1 && $('body').addClass(customScrollbarsClass);
  35. };
  36. // Sidebars
  37. // -------------------------
  38. //
  39. // On desktop
  40. //
  41. // Resize main sidebar
  42. const sidebarMainResize = function() {
  43. // Elements
  44. const sidebarMainElement = $('.sidebar-main'),
  45. sidebarMainToggler = $('.sidebar-main-resize'),
  46. resizeClass = 'sidebar-main-resized',
  47. unfoldClass = 'sidebar-main-unfold';
  48. // Define variables
  49. const unfoldDelay = 150;
  50. let timerStart,
  51. timerFinish;
  52. // Toggle classes on click
  53. sidebarMainToggler.on('click', function(e) {
  54. sidebarMainElement.toggleClass(resizeClass);
  55. !sidebarMainElement.hasClass(resizeClass) && sidebarMainElement.removeClass(unfoldClass);
  56. });
  57. // Add class on mouse enter
  58. sidebarMainElement.on('mouseenter', function() {
  59. clearTimeout(timerFinish);
  60. timerStart = setTimeout(function() {
  61. sidebarMainElement.hasClass(resizeClass) && sidebarMainElement.addClass(unfoldClass);
  62. }, unfoldDelay);
  63. });
  64. // Remove class on mouse leave
  65. sidebarMainElement.on('mouseleave', function() {
  66. clearTimeout(timerStart);
  67. timerFinish = setTimeout(function() {
  68. sidebarMainElement.removeClass(unfoldClass);
  69. }, unfoldDelay);
  70. });
  71. };
  72. // Toggle main sidebar
  73. const sidebarMainToggle = function() {
  74. // Elements
  75. const sidebarMainElement = $('.sidebar-main'),
  76. sidebarMainRestElements = $('.sidebar:not(.sidebar-main):not(.sidebar-component)'),
  77. sidebarMainDesktopToggler = $('.sidebar-main-toggle'),
  78. sidebarMainMobileToggler = $('.sidebar-mobile-main-toggle'),
  79. sidebarCollapsedClass = 'sidebar-collapsed',
  80. sidebarMobileExpandedClass = 'sidebar-mobile-expanded';
  81. $('#sidebar-main').on('click', function(e) {
  82. e.preventDefault();
  83. if (matchMedia('screen and (max-width: 1200px)').matches) {
  84. sidebarMainElement.toggleClass(sidebarMobileExpandedClass);
  85. sidebarMainRestElements.removeClass(sidebarMobileExpandedClass);
  86. } else {
  87. sidebarMainElement.toggleClass(sidebarCollapsedClass);
  88. }
  89. })
  90. // // On desktop
  91. // sidebarMainDesktopToggler.on('click', function(e) {
  92. // e.preventDefault();
  93. // sidebarMainElement.toggleClass(sidebarCollapsedClass);
  94. // });
  95. // On mobile
  96. sidebarMainMobileToggler.on('click', function(e) {
  97. e.preventDefault();
  98. sidebarMainElement.toggleClass(sidebarMobileExpandedClass);
  99. sidebarMainRestElements.removeClass(sidebarMobileExpandedClass);
  100. });
  101. };
  102. // Toggle secondary sidebar
  103. const sidebarSecondaryToggle = function() {
  104. // Elements
  105. const sidebarSecondaryElement = $('.sidebar-secondary'),
  106. sidebarSecondaryRestElements = $('.sidebar:not(.sidebar-secondary):not(.sidebar-component)'),
  107. sidebarSecondaryDesktopToggler = $('.sidebar-secondary-toggle'),
  108. sidebarSecondaryMobileToggler = $('.sidebar-mobile-secondary-toggle'),
  109. sidebarCollapsedClass = 'sidebar-collapsed',
  110. sidebarMobileExpandedClass = 'sidebar-mobile-expanded';
  111. // On desktop
  112. sidebarSecondaryDesktopToggler.on('click', function(e) {
  113. e.preventDefault();
  114. sidebarSecondaryElement.toggleClass(sidebarCollapsedClass);
  115. });
  116. // On mobile
  117. sidebarSecondaryMobileToggler.on('click', function(e) {
  118. e.preventDefault();
  119. sidebarSecondaryElement.toggleClass(sidebarMobileExpandedClass);
  120. sidebarSecondaryRestElements.removeClass(sidebarMobileExpandedClass);
  121. });
  122. };
  123. // Toggle right sidebar
  124. const sidebarRightToggle = function() {
  125. // Elements
  126. const sidebarRightElement = $('.sidebar-right'),
  127. sidebarRightRestElements = $('.sidebar:not(.sidebar-right):not(.sidebar-component)'),
  128. sidebarRightDesktopToggler = $('.sidebar-right-toggle'),
  129. sidebarRightMobileToggler = $('.sidebar-mobile-right-toggle'),
  130. sidebarCollapsedClass = 'sidebar-collapsed',
  131. sidebarMobileExpandedClass = 'sidebar-mobile-expanded';
  132. $(sidebarRightDesktopToggler).on('click', function(e) {
  133. e.preventDefault();
  134. if (matchMedia('screen and (max-width: 1200px)').matches) {
  135. sidebarRightElement.toggleClass(sidebarMobileExpandedClass);
  136. sidebarRightRestElements.removeClass(sidebarMobileExpandedClass);
  137. } else {
  138. sidebarRightElement.toggleClass(sidebarCollapsedClass);
  139. }
  140. });
  141. // // On desktop
  142. // sidebarRightDesktopToggler.on('click', function(e) {
  143. // e.preventDefault();
  144. // sidebarRightElement.toggleClass(sidebarCollapsedClass);
  145. // });
  146. // // On mobile
  147. // sidebarRightMobileToggler.on('click', function(e) {
  148. // e.preventDefault();
  149. // sidebarRightElement.toggleClass(sidebarMobileExpandedClass);
  150. // sidebarRightRestElements.removeClass(sidebarMobileExpandedClass);
  151. // });
  152. };
  153. // Toggle component sidebar
  154. const sidebarComponentToggle = function() {
  155. // Elements
  156. const sidebarComponentElement = $('.sidebar-component'),
  157. sidebarComponentMobileToggler = $('.sidebar-mobile-component-toggle'),
  158. sidebarMobileExpandedClass = 'sidebar-mobile-expanded';
  159. // Toggle classes
  160. sidebarComponentMobileToggler.on('click', function(e) {
  161. e.preventDefault();
  162. sidebarComponentElement.toggleClass(sidebarMobileExpandedClass);
  163. });
  164. };
  165. // Navigations
  166. // -------------------------
  167. // Sidebar navigation
  168. const navigationSidebar = function() {
  169. // Define default class names and options
  170. var navClass = 'nav-sidebar',
  171. navItemClass = 'nav-item',
  172. navItemOpenClass = 'nav-item-open',
  173. navLinkClass = 'nav-link',
  174. navSubmenuClass = 'nav-group-sub',
  175. navScrollSpyClass = 'nav-scrollspy',
  176. navSlidingSpeed = 250;
  177. // Configure collapsible functionality
  178. $('.' + navClass + ':not(.' + navScrollSpyClass + ')').each(function() {
  179. $(this).find('.' + navItemClass).has('.' + navSubmenuClass).children('.' + navItemClass + ' > ' + '.' + navLinkClass).not('.disabled').on('click', function (e) {
  180. e.preventDefault();
  181. // Simplify stuff
  182. var $target = $(this);
  183. // Collapsible
  184. if($target.parent('.' + navItemClass).hasClass(navItemOpenClass)) {
  185. $target.parent('.' + navItemClass).removeClass(navItemOpenClass).children('.' + navSubmenuClass).slideUp(navSlidingSpeed);
  186. }
  187. else {
  188. $target.parent('.' + navItemClass).addClass(navItemOpenClass).children('.' + navSubmenuClass).slideDown(navSlidingSpeed);
  189. }
  190. // Accordion
  191. if ($target.parents('.' + navClass).data('nav-type') == 'accordion') {
  192. $target.parent('.' + navItemClass).siblings(':has(.' + navSubmenuClass + ')').removeClass(navItemOpenClass).children('.' + navSubmenuClass).slideUp(navSlidingSpeed);
  193. }
  194. });
  195. });
  196. // Disable click in disabled navigation items
  197. $(document).on('click', '.' + navClass + ' .disabled', function(e) {
  198. e.preventDefault();
  199. });
  200. };
  201. // Navbar navigation
  202. const navigationNavbar = function() {
  203. // Prevent dropdown from closing on click
  204. $(document).on('click', '.dropdown-content', function(e) {
  205. e.stopPropagation();
  206. });
  207. // Disabled links
  208. $('.navbar-nav .disabled a, .nav-item-levels .disabled').on('click', function(e) {
  209. e.preventDefault();
  210. e.stopPropagation();
  211. });
  212. // Show tabs inside dropdowns
  213. $('.dropdown-content a[data-toggle="tab"]').on('click', function() {
  214. $(this).tab('show');
  215. });
  216. };
  217. // Components
  218. // -------------------------
  219. // Tooltip
  220. const componentTooltip = function() {
  221. $('[data-popup="tooltip"]').tooltip({
  222. boundary: '.page-content'
  223. });
  224. };
  225. // Popover
  226. const componentPopover = function() {
  227. $('[data-popup="popover"]').popover({
  228. boundary: '.page-content'
  229. });
  230. };
  231. // "Go to top" button
  232. const componentToTopButton = function() {
  233. // Elements
  234. const toTopContainer = $('.content-wrapper'),
  235. scrollableContainer = $('.content-inner'),
  236. scrollableDistance = 250;
  237. // Append only if container exists
  238. if (scrollableContainer) {
  239. // Create button
  240. toTopContainer.append($('<div class="btn-to-top"><button type="button" class="btn btn-dark btn-icon rounded-pill"><i class="icon-arrow-up8"></i></button></div>'));
  241. // Show and hide on scroll
  242. const to_top_button = $('.btn-to-top'),
  243. add_class_on_scroll = function() {
  244. to_top_button.addClass('btn-to-top-visible');
  245. },
  246. remove_class_on_scroll = function() {
  247. to_top_button.removeClass('btn-to-top-visible');
  248. };
  249. scrollableContainer.on('scroll', function() {
  250. const scrollpos = scrollableContainer.scrollTop();
  251. if (scrollpos >= scrollableDistance) {
  252. add_class_on_scroll();
  253. }
  254. else {
  255. remove_class_on_scroll();
  256. }
  257. });
  258. // Scroll to top on click
  259. $('.btn-to-top .btn').on('click', function() {
  260. scrollableContainer.scrollTop(0);
  261. });
  262. }
  263. };
  264. // Card actions
  265. // -------------------------
  266. // Reload card (uses BlockUI extension)
  267. const cardActionReload = function() {
  268. // Elements
  269. const buttonElement = $('[data-action=reload]'),
  270. overlayContainer = '.card',
  271. overlayClass = 'card-overlay',
  272. spinnerClass = 'icon-spinner9 spinner text-body',
  273. overlayAnimationClass = 'card-overlay-fadeout';
  274. // Configure
  275. buttonElement.on('click', function(e) {
  276. e.preventDefault();
  277. // Create overlay with spinner
  278. $(this).parents(overlayContainer).append($('<div class="' + overlayClass + '"><i class="' + spinnerClass + '"></i></div>'));
  279. // Remove overlay after 2.5s, for demo only
  280. setTimeout(function() {
  281. $('.' + overlayClass).addClass(overlayAnimationClass).on('animationend animationcancel', function() {
  282. $(this).remove();
  283. });
  284. }, 2500);
  285. });
  286. };
  287. // Collapse card
  288. const cardActionCollapse = function() {
  289. // Elements
  290. const buttonElement = $('[data-action=collapse]'),
  291. cardContainer = '.card',
  292. cardCollapsedClass = 'card-collapsed';
  293. // Configure
  294. buttonElement.on('click', function(e) {
  295. e.preventDefault();
  296. const parentContainer = $(this).parents('.card'),
  297. collapsibleContainer = parentContainer.find('> .collapse');
  298. if (parentContainer.hasClass(cardCollapsedClass)) {
  299. parentContainer.removeClass(cardCollapsedClass);
  300. collapsibleContainer.collapse('show');
  301. }
  302. else {
  303. parentContainer.addClass(cardCollapsedClass);
  304. collapsibleContainer.collapse('hide');
  305. }
  306. });
  307. };
  308. // Remove card
  309. const cardActionRemove = function() {
  310. // Elements
  311. const buttonElement = $('[data-action=remove]'),
  312. cardContainer = '.card';
  313. // Configure
  314. buttonElement.on('click', function(e) {
  315. e.preventDefault();
  316. $(this).parents(cardContainer).slideUp(150);
  317. });
  318. };
  319. // Card fullscreen mode
  320. const cardActionFullscreen = function() {
  321. // Elements
  322. const buttonElement = '[data-action=fullscreen]',
  323. buttonClass = 'list-icons-item',
  324. buttonContainerClass = 'list-icons',
  325. cardFullscreenClass = 'card-fullscreen',
  326. collapsedClass = 'collapsed-in-fullscreen',
  327. scrollableContainerClass = 'content-inner',
  328. fullscreenAttr = 'data-fullscreen';
  329. // Configure
  330. $(buttonElement).on('click', function(e) {
  331. e.preventDefault();
  332. const button = $(this);
  333. // Get closest card container
  334. const cardFullscreen = button.parents('.card');
  335. // Toggle required classes
  336. cardFullscreen.toggleClass(cardFullscreenClass);
  337. // Toggle classes depending on state
  338. if (!cardFullscreen.hasClass(cardFullscreenClass)) {
  339. button.removeAttr(fullscreenAttr);
  340. cardFullscreen.find('.' + collapsedClass).removeClass('show');
  341. $('.' + scrollableContainerClass).removeClass('overflow-hidden');
  342. button.parents('.' + buttonContainerClass).find('.' + buttonClass + ':not(' + buttonElement + ')').removeClass('d-none');
  343. }
  344. else {
  345. button.attr(fullscreenAttr, 'active');
  346. cardFullscreen.removeAttr('style');
  347. cardFullscreen.find('.collapse:not(.show)').addClass('show ' + collapsedClass);
  348. $('.' + scrollableContainerClass).addClass('overflow-hidden');
  349. button.parents('.' + buttonContainerClass).find('.' + buttonClass + ':not(' + buttonElement + ')').addClass('d-none');
  350. }
  351. });
  352. };
  353. // Misc
  354. // -------------------------
  355. // Re-declare dropdown boundary for app container
  356. const dropdownMenus = function() {
  357. $.fn.dropdown.Constructor.Default.boundary = '.page-content';
  358. };
  359. // Dropdown submenus. Trigger on click
  360. const dropdownSubmenu = function() {
  361. // All parent levels require .dropdown-toggle class
  362. $('.dropdown-menu').find('.dropdown-submenu').not('.disabled').find('.dropdown-toggle').on('click', function(e) {
  363. e.stopPropagation();
  364. e.preventDefault();
  365. const button = $(this);
  366. // Remove "show" class in all siblings
  367. button.parent().siblings().removeClass('show').find('.show').removeClass('show');
  368. // Toggle submenu
  369. button.parent().toggleClass('show').children('.dropdown-menu').toggleClass('show');
  370. // Hide all levels when parent dropdown is closed
  371. button.parents('.show').on('hidden.bs.dropdown', function(e) {
  372. $('.dropdown-submenu .show, .dropdown-submenu.show').removeClass('show');
  373. });
  374. });
  375. };
  376. // Header elements toggler
  377. const componentHeaderElements = function() {
  378. // Toggle visible state of header elements
  379. $('.header-elements-toggle').on('click', function(e) {
  380. e.preventDefault();
  381. $(this).parents('[class*=header-elements-]:not(.header-elements-toggle)').find('.header-elements').toggleClass('d-none');
  382. });
  383. // Toggle visible state of footer elements
  384. $('.footer-elements-toggle').on('click', function(e) {
  385. e.preventDefault();
  386. $(this).parents('.card-footer').find('.footer-elements').toggleClass('d-none');
  387. });
  388. };
  389. //
  390. // Return objects assigned to module
  391. //
  392. return {
  393. // Disable transitions before page is fully loaded
  394. initBeforeLoad: function() {
  395. detectOS();
  396. transitionsDisabled();
  397. },
  398. // Enable transitions when page is fully loaded
  399. initAfterLoad: function() {
  400. transitionsEnabled();
  401. },
  402. // Initialize all components
  403. initComponents: function() {
  404. componentTooltip();
  405. componentPopover();
  406. componentToTopButton();
  407. componentHeaderElements();
  408. },
  409. // Initialize all sidebars
  410. initSidebars: function() {
  411. sidebarMainResize();
  412. sidebarMainToggle();
  413. sidebarSecondaryToggle();
  414. sidebarRightToggle();
  415. sidebarComponentToggle();
  416. },
  417. // Initialize all navigations
  418. initNavigations: function() {
  419. navigationSidebar();
  420. navigationNavbar();
  421. },
  422. // Initialize all card actions
  423. initCardActions: function() {
  424. cardActionReload();
  425. cardActionCollapse();
  426. cardActionRemove();
  427. cardActionFullscreen();
  428. },
  429. // Dropdown submenu
  430. initDropdowns: function() {
  431. dropdownMenus();
  432. dropdownSubmenu();
  433. },
  434. // Initialize core
  435. initCore: function() {
  436. App.initBeforeLoad();
  437. App.initSidebars();
  438. App.initNavigations();
  439. App.initComponents();
  440. App.initCardActions();
  441. App.initDropdowns();
  442. }
  443. }
  444. }();
  445. // Initialize module
  446. // ------------------------------
  447. // When content is loaded
  448. document.addEventListener('DOMContentLoaded', function() {
  449. App.initCore();
  450. });
  451. // When page is fully loaded
  452. window.addEventListener('load', function() {
  453. App.initAfterLoad();
  454. });