ControlSidebar.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /**
  2. * --------------------------------------------
  3. * AdminLTE ControlSidebar.js
  4. * License MIT
  5. * --------------------------------------------
  6. */
  7. import $ from 'jquery'
  8. /**
  9. * Constants
  10. * ====================================================
  11. */
  12. const NAME = 'ControlSidebar'
  13. const DATA_KEY = 'lte.controlsidebar'
  14. const EVENT_KEY = `.${DATA_KEY}`
  15. const JQUERY_NO_CONFLICT = $.fn[NAME]
  16. const EVENT_COLLAPSED = `collapsed${EVENT_KEY}`
  17. const EVENT_COLLAPSED_DONE = `collapsed-done${EVENT_KEY}`
  18. const EVENT_EXPANDED = `expanded${EVENT_KEY}`
  19. const SELECTOR_CONTROL_SIDEBAR = '.control-sidebar'
  20. const SELECTOR_CONTROL_SIDEBAR_CONTENT = '.control-sidebar-content'
  21. const SELECTOR_DATA_TOGGLE = '[data-widget="control-sidebar"]'
  22. const SELECTOR_HEADER = '.main-header'
  23. const SELECTOR_FOOTER = '.main-footer'
  24. const CLASS_NAME_CONTROL_SIDEBAR_ANIMATE = 'control-sidebar-animate'
  25. const CLASS_NAME_CONTROL_SIDEBAR_OPEN = 'control-sidebar-open'
  26. const CLASS_NAME_CONTROL_SIDEBAR_SLIDE = 'control-sidebar-slide-open'
  27. const CLASS_NAME_LAYOUT_FIXED = 'layout-fixed'
  28. const CLASS_NAME_NAVBAR_FIXED = 'layout-navbar-fixed'
  29. const CLASS_NAME_NAVBAR_SM_FIXED = 'layout-sm-navbar-fixed'
  30. const CLASS_NAME_NAVBAR_MD_FIXED = 'layout-md-navbar-fixed'
  31. const CLASS_NAME_NAVBAR_LG_FIXED = 'layout-lg-navbar-fixed'
  32. const CLASS_NAME_NAVBAR_XL_FIXED = 'layout-xl-navbar-fixed'
  33. const CLASS_NAME_FOOTER_FIXED = 'layout-footer-fixed'
  34. const CLASS_NAME_FOOTER_SM_FIXED = 'layout-sm-footer-fixed'
  35. const CLASS_NAME_FOOTER_MD_FIXED = 'layout-md-footer-fixed'
  36. const CLASS_NAME_FOOTER_LG_FIXED = 'layout-lg-footer-fixed'
  37. const CLASS_NAME_FOOTER_XL_FIXED = 'layout-xl-footer-fixed'
  38. const Default = {
  39. controlsidebarSlide: true,
  40. scrollbarTheme: 'os-theme-light',
  41. scrollbarAutoHide: 'l',
  42. target: SELECTOR_CONTROL_SIDEBAR,
  43. animationSpeed: 300
  44. }
  45. /**
  46. * Class Definition
  47. * ====================================================
  48. */
  49. class ControlSidebar {
  50. constructor(element, config) {
  51. this._element = element
  52. this._config = config
  53. }
  54. // Public
  55. collapse() {
  56. const $body = $('body')
  57. const $html = $('html')
  58. // Show the control sidebar
  59. if (this._config.controlsidebarSlide) {
  60. $html.addClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE)
  61. $body.removeClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE).delay(300).queue(function () {
  62. $(SELECTOR_CONTROL_SIDEBAR).hide()
  63. $html.removeClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE)
  64. $(this).dequeue()
  65. })
  66. } else {
  67. $body.removeClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN)
  68. }
  69. $(this._element).trigger($.Event(EVENT_COLLAPSED))
  70. setTimeout(() => {
  71. $(this._element).trigger($.Event(EVENT_COLLAPSED_DONE))
  72. }, this._config.animationSpeed)
  73. }
  74. show(toggle = false) {
  75. const $body = $('body')
  76. const $html = $('html')
  77. if (toggle) {
  78. $(SELECTOR_CONTROL_SIDEBAR).hide()
  79. }
  80. // Collapse the control sidebar
  81. if (this._config.controlsidebarSlide) {
  82. $html.addClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE)
  83. $(this._config.target).show().delay(10).queue(function () {
  84. $body.addClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE).delay(300).queue(function () {
  85. $html.removeClass(CLASS_NAME_CONTROL_SIDEBAR_ANIMATE)
  86. $(this).dequeue()
  87. })
  88. $(this).dequeue()
  89. })
  90. } else {
  91. $body.addClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN)
  92. }
  93. this._fixHeight()
  94. this._fixScrollHeight()
  95. $(this._element).trigger($.Event(EVENT_EXPANDED))
  96. }
  97. toggle() {
  98. const $body = $('body')
  99. const { target } = this._config
  100. const notVisible = !$(target).is(':visible')
  101. const shouldClose = ($body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN) ||
  102. $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE))
  103. const shouldToggle = notVisible && ($body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN) ||
  104. $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE))
  105. if (notVisible || shouldToggle) {
  106. // Open the control sidebar
  107. this.show(notVisible)
  108. } else if (shouldClose) {
  109. // Close the control sidebar
  110. this.collapse()
  111. }
  112. }
  113. // Private
  114. _init() {
  115. const $body = $('body')
  116. const shouldNotHideAll = $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN) ||
  117. $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE)
  118. if (shouldNotHideAll) {
  119. $(SELECTOR_CONTROL_SIDEBAR).not(this._config.target).hide()
  120. $(this._config.target).css('display', 'block')
  121. } else {
  122. $(SELECTOR_CONTROL_SIDEBAR).hide()
  123. }
  124. this._fixHeight()
  125. this._fixScrollHeight()
  126. $(window).resize(() => {
  127. this._fixHeight()
  128. this._fixScrollHeight()
  129. })
  130. $(window).scroll(() => {
  131. const $body = $('body')
  132. const shouldFixHeight = $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_OPEN) ||
  133. $body.hasClass(CLASS_NAME_CONTROL_SIDEBAR_SLIDE)
  134. if (shouldFixHeight) {
  135. this._fixScrollHeight()
  136. }
  137. })
  138. }
  139. _isNavbarFixed() {
  140. const $body = $('body')
  141. return (
  142. $body.hasClass(CLASS_NAME_NAVBAR_FIXED) ||
  143. $body.hasClass(CLASS_NAME_NAVBAR_SM_FIXED) ||
  144. $body.hasClass(CLASS_NAME_NAVBAR_MD_FIXED) ||
  145. $body.hasClass(CLASS_NAME_NAVBAR_LG_FIXED) ||
  146. $body.hasClass(CLASS_NAME_NAVBAR_XL_FIXED)
  147. )
  148. }
  149. _isFooterFixed() {
  150. const $body = $('body')
  151. return (
  152. $body.hasClass(CLASS_NAME_FOOTER_FIXED) ||
  153. $body.hasClass(CLASS_NAME_FOOTER_SM_FIXED) ||
  154. $body.hasClass(CLASS_NAME_FOOTER_MD_FIXED) ||
  155. $body.hasClass(CLASS_NAME_FOOTER_LG_FIXED) ||
  156. $body.hasClass(CLASS_NAME_FOOTER_XL_FIXED)
  157. )
  158. }
  159. _fixScrollHeight() {
  160. const $body = $('body')
  161. const $controlSidebar = $(this._config.target)
  162. if (!$body.hasClass(CLASS_NAME_LAYOUT_FIXED)) {
  163. return
  164. }
  165. const heights = {
  166. scroll: $(document).height(),
  167. window: $(window).height(),
  168. header: $(SELECTOR_HEADER).outerHeight(),
  169. footer: $(SELECTOR_FOOTER).outerHeight()
  170. }
  171. const positions = {
  172. bottom: Math.abs((heights.window + $(window).scrollTop()) - heights.scroll),
  173. top: $(window).scrollTop()
  174. }
  175. const navbarFixed = this._isNavbarFixed() && $(SELECTOR_HEADER).css('position') === 'fixed'
  176. const footerFixed = this._isFooterFixed() && $(SELECTOR_FOOTER).css('position') === 'fixed'
  177. const $controlsidebarContent = $(`${this._config.target}, ${this._config.target} ${SELECTOR_CONTROL_SIDEBAR_CONTENT}`)
  178. if (positions.top === 0 && positions.bottom === 0) {
  179. $controlSidebar.css({
  180. bottom: heights.footer,
  181. top: heights.header
  182. })
  183. $controlsidebarContent.css('height', heights.window - (heights.header + heights.footer))
  184. } else if (positions.bottom <= heights.footer) {
  185. if (footerFixed === false) {
  186. const top = heights.header - positions.top
  187. $controlSidebar.css('bottom', heights.footer - positions.bottom).css('top', top >= 0 ? top : 0)
  188. $controlsidebarContent.css('height', heights.window - (heights.footer - positions.bottom))
  189. } else {
  190. $controlSidebar.css('bottom', heights.footer)
  191. }
  192. } else if (positions.top <= heights.header) {
  193. if (navbarFixed === false) {
  194. $controlSidebar.css('top', heights.header - positions.top)
  195. $controlsidebarContent.css('height', heights.window - (heights.header - positions.top))
  196. } else {
  197. $controlSidebar.css('top', heights.header)
  198. }
  199. } else if (navbarFixed === false) {
  200. $controlSidebar.css('top', 0)
  201. $controlsidebarContent.css('height', heights.window)
  202. } else {
  203. $controlSidebar.css('top', heights.header)
  204. }
  205. if (footerFixed && navbarFixed) {
  206. $controlsidebarContent.css('height', '100%')
  207. $controlSidebar.css('height', '')
  208. } else if (footerFixed || navbarFixed) {
  209. $controlsidebarContent.css('height', '100%')
  210. $controlsidebarContent.css('height', '')
  211. }
  212. }
  213. _fixHeight() {
  214. const $body = $('body')
  215. const $controlSidebar = $(`${this._config.target} ${SELECTOR_CONTROL_SIDEBAR_CONTENT}`)
  216. if (!$body.hasClass(CLASS_NAME_LAYOUT_FIXED)) {
  217. $controlSidebar.attr('style', '')
  218. return
  219. }
  220. const heights = {
  221. window: $(window).height(),
  222. header: $(SELECTOR_HEADER).outerHeight(),
  223. footer: $(SELECTOR_FOOTER).outerHeight()
  224. }
  225. let sidebarHeight = heights.window - heights.header
  226. if (this._isFooterFixed() && $(SELECTOR_FOOTER).css('position') === 'fixed') {
  227. sidebarHeight = heights.window - heights.header - heights.footer
  228. }
  229. $controlSidebar.css('height', sidebarHeight)
  230. if (typeof $.fn.overlayScrollbars !== 'undefined') {
  231. $controlSidebar.overlayScrollbars({
  232. className: this._config.scrollbarTheme,
  233. sizeAutoCapable: true,
  234. scrollbars: {
  235. autoHide: this._config.scrollbarAutoHide,
  236. clickScrolling: true
  237. }
  238. })
  239. }
  240. }
  241. // Static
  242. static _jQueryInterface(config) {
  243. return this.each(function () {
  244. let data = $(this).data(DATA_KEY)
  245. const _config = $.extend({}, Default, typeof config === 'object' ? config : $(this).data())
  246. if (!data) {
  247. data = new ControlSidebar($(this), _config)
  248. $(this).data(DATA_KEY, data)
  249. data._init()
  250. } else if (typeof config === 'string') {
  251. if (typeof data[config] === 'undefined') {
  252. throw new TypeError(`No method named "${config}"`)
  253. }
  254. data[config]()
  255. } else if (typeof config === 'undefined') {
  256. data._init()
  257. }
  258. })
  259. }
  260. }
  261. /**
  262. *
  263. * Data Api implementation
  264. * ====================================================
  265. */
  266. $(document).on('click', SELECTOR_DATA_TOGGLE, function (event) {
  267. event.preventDefault()
  268. ControlSidebar._jQueryInterface.call($(this), 'toggle')
  269. })
  270. $(document).ready(() => {
  271. ControlSidebar._jQueryInterface.call($(SELECTOR_DATA_TOGGLE), '_init')
  272. })
  273. /**
  274. * jQuery API
  275. * ====================================================
  276. */
  277. $.fn[NAME] = ControlSidebar._jQueryInterface
  278. $.fn[NAME].Constructor = ControlSidebar
  279. $.fn[NAME].noConflict = function () {
  280. $.fn[NAME] = JQUERY_NO_CONFLICT
  281. return ControlSidebar._jQueryInterface
  282. }
  283. export default ControlSidebar