1/* 2 # How to use ZoeyActionList. 3 4 ## Classes Used 5 #### JavaScript Classes 6 * .zoey-action-list__js__element - Used to scope the action list. 7 * .zoey-action-list__js__element-trigger - Used to attach click events to trigger hide/show. 8 * .zoey-action-list__js__element-list - Used to add styles to hide/show. 9 10 #### CSS Classes 11 * .zoey-actions-list-element - No Styles applied. 12 * .zoey-actions-list-element-trigger - Used to style the correct pointer. 13 * .zoey-actions-list-element-list - Used to style the list. 14 15 #### Dyamic CSS Classes 16 * .zoey-action-list-element-trigger-active - Added by the model when list is active. 17 18 ## Examples 19 ```js 20 var element = document.getElementById('#whatever-your-id-is'); 21 22 // `model` will be direct access to the ZoeyActionList model. 23 var model = ZoeyActionList(element); 24 25 // access to the API. API will remain on element even after destroy. This allows for re-init; 26 jQuery(element).data('ZoeyActionList') 27 ``` 28 29 ```html 30 <div class="zoey-action-list__js__element"> <!-- Container Element --> 31 <div class="zoey-action-list__js__element-trigger">This is the trigger</div> <!-- Trigger Element --> 32 <ul class="zoey-action-list__js__element-list"> <!-- List Element --> 33 <li>Item 1</li> 34 <li>Item 2</li> 35 </ul> 36 </div> 37 ``` 38*/ 39window.Zoey.require(['jQuery', 'pix.globalEvents'], function($, globalEvents) { 40 "use strict"; 41 42 window.pix = window.pix || {}; 43 window.pix.modules = window.pix.modules || {}; 44 45 var idCounter = 0; 46 var $body = null; 47 48 /** 49 * Used to create an actions list. 50 * @param {HTMLElement} element 51 */ 52 function ZoeyActionList(element) { 53 this._$element = $(element); 54 this._$list = this._$element.find('.'+this.ACTION_ELEMENT_LIST_CLASS).first(); 55 this._$trigger = this._$element.find('.'+this.ACTION_ELEMENT_TRIGGER_CLASS).first(); 56 this._$element.data('ZoeyActionList', this.getApi()); 57 this._$list.data('ZoeyActionList', this.getApi()); 58 this._$trigger.data('ZoeyActionList', this.getApi()); 59 this.init(); 60 } 61 62 ZoeyActionList.prototype = { 63 ACTION_ELEMENT_CLASS: 'zoey-action-list__js__element', 64 ACTION_ELEMENT_TRIGGER_CLASS: 'zoey-action-list__js__element-trigger', 65 ACTION_ELEMENT_LIST_CLASS: 'zoey-action-list__js__element-list', 66 ACTION_ELEMENT_ID: 'zoey-action-list-element-id', 67 ACTION_ELEMENT_TRIGGER_ACTIVE_CLASS: 'zoey-action-list-element-trigger-active', 68 69 /** 70 * Used to build the elements and set events. 71 * @return {ZoeyActionList} 72 */ 73 init: function() { 74 if(this._isInit) return this; 75 this._isInit = true; 76 var elementId = this._$element.attr('id'); 77 if(!elementId) { 78 elementId = this.ACTION_ELEMENT_ID + '__' + Date.now() + idCounter; 79 idCounter++; 80 this._$element.attr('id', elementId); 81 } 82 83 this._$list.attr(this.ACTION_ELEMENT_ID, elementId); 84 this._$trigger.attr(this.ACTION_ELEMENT_ID, elementId); 85 86 this._componentId = this._$list.parents('.pix-design-component').first().attr('id'); 87 this._$list.css('display', 'none'); 88 if(this._componentId){ 89 this._$listContainer = jQuery('<div style="width: 0; padding: 0; margin: 0; height: 0; border: 0; outline: 0;">'); 90 this._$listContainer.addClass(this._componentId); 91 this._$listContainer.append(this._$list); 92 $body.append(this._$listContainer); 93 } else { 94 // remove the list `ul` from the container and place it in the bottom of the body. 95 $body.append(this._$list); 96 } 97 98 // set click event. 99 this._boundTriggerCallback = this.toggle.bind(this); 100 this._$trigger.on('click', this._boundTriggerCallback); 101 102 // set body click event. 103 this._bodyClickBoundCallback = function(evt) { 104 var element = this._$element.get(0); 105 var listElement = this._$list.get(0); 106 if(!element || !listElement){ 107 this.destroy(); 108 } else if( this.isShown() && !$.contains(element, evt.target) && !$.contains(listElement, evt.target) ){ 109 this.hide(); 110 } 111 }.bind(this); 112 globalEvents.add('bodyClick', this._bodyClickBoundCallback); 113 this._boundHide = this.hide.bind(this); 114 globalEvents.add('smartresize', this._boundHide); 115 }, 116 117 /** 118 * Is the list currently showing. 119 * @return {Boolean} 120 */ 121 isShown: function() { 122 return this._$list.css('display') !== 'none'; 123 }, 124 125 /** 126 * Used to show the list. 127 * @return {ZoeyActionList} 128 */ 129 show: function() { 130 // get position of trigger 131 var listStyles = this._$trigger.offset(); 132 this._$list.css('display', 'inline-block'); 133 listStyles.width = this._$list.outerWidth(); 134 listStyles.height = this._$list.outerHeight(); 135 this._$list.css('display', 'block'); 136 137 if(listStyles.left < (window.outerWidth / 2)) { 138 // open to the right of the trigger. 139 listStyles.left += (this._$trigger.outerWidth() - (this._$trigger.outerWidth() / 2)); 140 } else { 141 // open to the left of the trigger. 142 // listStyles.left -= this._$trigger.outerWidth(); 143 listStyles.left -= (listStyles.width - (this._$trigger.outerWidth() / 2)); 144 } 145 146 var scrollTop = $(document).scrollTop(); 147 var scrollBottom = (scrollTop + window.outerHeight); 148 149 if(listStyles.top > (scrollBottom - scrollTop) / 2) { 150 // open to the top of the trigger. 151 listStyles.top -= listStyles.height; 152 } else { 153 // open to the bottom of the trigger. 154 listStyles.top += this._$trigger.outerHeight(); 155 } 156 157 listStyles.position = 'absolute'; 158 // same zindex as the drawer 159 listStyles.zIndex = '9999999999'; 160 this._$list.css(listStyles); 161 // NOTE zindex should be higher than the FE drawer's z-index (adding another 9 to make it higher doesn't work) 162 // instead append the list again to the parent so that it's the last item 163 this._$list.parent().append(this._$list); 164 this._$trigger.addClass(this.ACTION_ELEMENT_TRIGGER_ACTIVE_CLASS); 165 166 return this; 167 }, 168 169 /** 170 * Used to hide the list. 171 * @return {ZoeyActionList} 172 */ 173 hide: function() { 174 this._$list.css({ 175 position: '', 176 top: '', 177 left: '', 178 display: 'none', 179 }); 180 this._$trigger.removeClass(this.ACTION_ELEMENT_TRIGGER_ACTIVE_CLASS); 181 182 return this; 183 }, 184 185 /** 186 * Used to toggle the list. 187 * @return {ZoeyActionList} 188 */ 189 toggle: function() { 190 if(this.isShown()) { 191 this.hide(); 192 } else { 193 this.show(); 194 } 195 }, 196 197 /** 198 * Used to destroy the model and remove events. 199 * @return {ZoeyActionList} 200 */ 201 destroy: function() { 202 // remove all events. 203 if(this._bodyClickBoundCallback) globalEvents.remove('bodyClick', this._bodyClickBoundCallback); 204 if(this._boundTriggerCallback) this._$trigger.off('click', this._boundTriggerCallback); 205 if(this._boundHide) globalEvents.remove('smartresize', this._boundHide); 206 207 // put everything back the way it was. 208 this.hide(); 209 this._$list.css('display', ''); 210 this._$element.append(this._$list); 211 if(this._$listContainer){ 212 this._$listContainer.remove(); 213 this._$listContainer = null; 214 } 215 216 var elementId = this._$element.attr('id'); 217 if(elementId.indexOf(this.ACTION_ELEMENT_ID) > -1) { 218 this._$element.removeAttr('id'); 219 } 220 this._$list.removeAttr(this.ACTION_ELEMENT_ID); 221 this._$trigger.removeAttr(this.ACTION_ELEMENT_ID); 222 223 this._isInit = false;
224 return this; 225 }, 226 227 /** 228 * Used to return an API. 229 * @return {Object} 230 */ 231 getApi: function() { 232 var self = this; 233 return { 234 show: function() { 235 self.show(); 236 return this; 237 }, 238 hide: function() { 239 self.hide(); 240 return this; 241 }, 242 toggle: function() { 243 self.toggle(); 244 return this; 245 }, 246 isActive: this.isShown.bind(this), 247 destroy: function() { 248 self.destroy(); 249 return this; 250 }, 251 init: function() { 252 self.init(); 253 return this; 254 }, 255 }; 256 }, 257 }; 258 259 // Adding it as a module. 260 window.pix.modules.ZoeyActionList = ZoeyActionList; 261 262 function initModule(elements){ 263 var elementsToInit = elements instanceof Array ? $(elements) : $(document.body); 264 $body = $(document.body); 265 266 // init all action-lists 267 elementsToInit.find('.'+ZoeyActionList.prototype.ACTION_ELEMENT_CLASS).each(function(index, element) { 268 new ZoeyActionList(element); 269 }); 270 } 271 272 globalEvents.add('layoutUpdate', initModule); 273 globalEvents.add('documentLoaded', initModule); 274 275 window.Zoey.export('pix.modules.ZoeyActionList'); 276});
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.