1 2import { debounce, random } from './../helpers.js' 3 4const NAME = 'navscrollbar' 5const VERSION = '1.0.0' 6const UNIQID = random() 7const DATA_KEY = `jtl.${NAME}` 8const EVENT_KEY = `.${DATA_KEY}#${UNIQID}` 9const JQUERY_NO_CONFLICT = $.fn[NAME] 10 11const Default = { 12 classes: { 13 scrollbarInner : 'nav-scrollbar-inner', 14 scrollbarItems : 'nav-scrollbar-item', 15 scrollbarArrow : 'nav-scrollbar-arrow' 16 }, 17 templates: { 18 arrowLeft: ` 19 <div class="left"> 20 <span class="fas fa-chevron-left"></span> 21 </div> 22 `, 23 arrowRight: ` 24 <div class="right"> 25 <span class="fas fa-chevron-right"></span> 26 </div> 27 ` 28 }, 29 showArrowOffset: 0, 30 clickTransitionDuration: 500, 31 clickScrollLength: 70, // percentage 32 enableDrag: false, 33} 34 35const Event = { 36 CLICK : `click${EVENT_KEY}`, 37 MOUSEDOWN : `mousedown${EVENT_KEY}`, 38 MOUSEMOVE : `mousemove${EVENT_KEY}`, 39 MOUSEUP : `mouseup${EVENT_KEY}`, 40 RESIZE : `resize${EVENT_KEY}`, 41 SCROLL : `scroll${EVENT_KEY}` 42} 43 44let $document = $(document) 45let $window = $(window) 46 47 48export default class NavScrollbar { 49 constructor(element, config = {}) { 50 let self = this; 51 this.element = $(element) 52 this.config = $.extend(true, {}, Default, config) 53 54 this._isDragging = false 55 this._start = 0 56 this._startClientX = 0 57 this._distance = 0 58 this._itemWidth = 0 59 60 this.$scrollBarInner = this.element.find(`.${this.config.classes.scrollbarInner}`) 61 this.$scrollBarItems = this.element.find(`.${this.config.classes.scrollbarItems}`) 62 63 this.$scrollBarArrowLeft = $(this.config.templates.arrowLeft).addClass(`${this.config.classes.scrollbarArrow} disabled`) 64 this.$scrollBarArrowRight = $(this.config.templates.arrowRight).addClass(`${this.config.classes.scrollbarArrow} disabled`) 65 66 this.element.prepend(this.$scrollBarArrowLeft) 67 this.element.append(this.$scrollBarArrowRight) 68 69 setTimeout(function () { 70 self.update(); 71 self._bindEvents(); 72 }, 100); 73 } 74 75 /* Public */ 76 77 scrollToPrev() { 78 this.$scrollBarInner.animate({ 79 scrollLeft: this.$scrollBarInner.scrollLeft() - (this.$scrollBarInner.width() / 100 * this.config.clickScrollLength - this.$scrollBarArrowLeft.width()) 80 }, { duration: this.config.clickTransitionDuration }) 81 } 82 83 scrollToNext() { 84 this.$scrollBarInner.animate({ 85 scrollLeft: this.$scrollBarInner.scrollLeft() + (this.$scrollBarInner.width() / 100 * this.config.clickScrollLength - this.$scrollBarArrowLeft.width()) 86 }, { duration: this.config.clickTransitionDuration }) 87 } 88 89 update() { 90 this._updateItemWidth() 91 92 let widthDifference = Math.round(this._itemWidth - this.$scrollBarInner.width()) 93 let scrolledFromLeft = this.$scrollBarInner.scrollLeft() 94 95 if(widthDifference <= 0) { 96 this.$scrollBarArrowLeft.addClass('disabled') 97 this.$scrollBarArrowRight.addClass('disabled') 98 return 99 } 100 101 this.$scrollBarArrowLeft[scrolledFromLeft > this.config.showArrowOffset ? 'removeClass' : 'addClass']('disabled') 102 this.$scrollBarArrowRight[scrolledFromLeft + this.config.showArrowOffset < (widthDifference - 8) ? 'removeClass' : 'addClass']('disabled') 103 } 104 105 destroy() { 106 this.element.removeData(DATA_KEY) 107 this.element = null 108 this.$scrollBarInner.off(EVENT_KEY) 109 this.$scrollBarArrowLeft.off(EVENT_KEY).remove() 110 this.$scrollBarArrowRight.off(EVENT_KEY).remove() 111 $document.off(EVENT_KEY) 112 $window.off(EVENT_KEY) 113 } 114 115 /* Private */ 116 117 _dragStart(e) { 118 e.preventDefault() 119 120 this._startClientX = e.clientX 121 122 this._isDragging = true 123 this._start = e.clientX + this.$scrollBarInner.scrollLeft() 124 } 125 126 _dragMove(e) { 127 if(!this._isDragging) 128 return 129 130 this.$scrollBarInner.scrollLeft(this._start - e.clientX) 131 } 132 133 _dragEnd(e) { 134 if(!this._isDragging) 135 return 136 137 this._isDragging = false 138 139 if($.contains(this.element.get(0), e.target)) { 140 e.target.removeEventListener('click', this._preventDragClick) 141 142 if(this._startClientX - e.clientX != 0) 143 e.target.addEventListener('click', this._preventDragClick) 144 } 145 146 this.$scrollBarInner.scrollLeft(this._start - e.clientX) 147 } 148 149 _preventDragClick(e) { 150 e.preventDefault() 151 e.stopImmediatePropagation() 152 } 153 154 _updateItemWidth() { 155 this._itemWidth = 0 156 157 $.each(this.$scrollBarItems, (i, element) => { 158 this._itemWidth += Math.round(element.offsetWidth) 159 }) 160 } 161 162 _bindEvents() { 163 this.$scrollBarInner.on(Event.SCROLL, debounce(() => this.update())) 164 165 this.$scrollBarArrowLeft.on(Event.CLICK, () => this.scrollToPrev()) 166 this.$scrollBarArrowRight.on(Event.CLICK, () => this.scrollToNext()) 167 168 if(this.config.enableDrag) { 169 this.$scrollBarInner.on(Event.MOUSEDOWN, (e) => this._dragStart(e)) 170 $document.on(Event.MOUSEMOVE, (e) => this._dragMove(e)) 171 $document.on(Event.MOUSEUP, (e) => this._dragEnd(e)) 172 } 173 174 $window.on(Event.RESIZE, debounce(() => this.update())) 175 } 176 177 /* Static */ 178 179 static _jQueryInterface(config = {}) { 180 let _arguments = arguments || null 181 182 return this.each(function() { 183 const $element = $(this) 184 let data = $element.data(DATA_KEY) 185 186 if(!data) { 187 if(typeof config === 'object') { 188 data = new NavScrollbar(this, config) 189 $element.data(DATA_KEY, data) 190 } else { 191 $.error(`cannot call methods on ${NAME} prior to initialization`) 192 } 193 } else { 194 if(typeof data[config] === 'function') { 195 data[config].apply(data, Array.prototype.slice.call(_arguments, 1)) 196 } else { 197 $.error(`method ${config} does not exist.`) 198 } 199 } 200 }) 201 } 202} 203 204/** 205 * ------------------------------------------------------------------------ 206 * jQuery 207 * ------------------------------------------------------------------------ 208 */ 209 210$.fn[NAME] = NavScrollbar._jQueryInterface 211$.fn[NAME].Constructor = NavScrollbar 212$.fn[NAME].noConflict = () => { 213 $.fn[NAME] = JQUERY_NO_CONFLICT 214 return NavScrollbar._jQueryInterface 215}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.