Mercurial > hg > isophonics-drupal-site
comparison core/modules/toolbar/js/views/ToolbarVisualView.es6.js @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 /** | |
2 * @file | |
3 * A Backbone view for the toolbar element. Listens to mouse & touch. | |
4 */ | |
5 | |
6 (function ($, Drupal, drupalSettings, Backbone) { | |
7 Drupal.toolbar.ToolbarVisualView = Backbone.View.extend(/** @lends Drupal.toolbar.ToolbarVisualView# */{ | |
8 | |
9 /** | |
10 * Event map for the `ToolbarVisualView`. | |
11 * | |
12 * @return {object} | |
13 * A map of events. | |
14 */ | |
15 events() { | |
16 // Prevents delay and simulated mouse events. | |
17 const touchEndToClick = function (event) { | |
18 event.preventDefault(); | |
19 event.target.click(); | |
20 }; | |
21 | |
22 return { | |
23 'click .toolbar-bar .toolbar-tab .trigger': 'onTabClick', | |
24 'click .toolbar-toggle-orientation button': 'onOrientationToggleClick', | |
25 'touchend .toolbar-bar .toolbar-tab .trigger': touchEndToClick, | |
26 'touchend .toolbar-toggle-orientation button': touchEndToClick, | |
27 }; | |
28 }, | |
29 | |
30 /** | |
31 * Backbone view for the toolbar element. Listens to mouse & touch. | |
32 * | |
33 * @constructs | |
34 * | |
35 * @augments Backbone.View | |
36 * | |
37 * @param {object} options | |
38 * Options for the view object. | |
39 * @param {object} options.strings | |
40 * Various strings to use in the view. | |
41 */ | |
42 initialize(options) { | |
43 this.strings = options.strings; | |
44 | |
45 this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented change:isTrayToggleVisible', this.render); | |
46 this.listenTo(this.model, 'change:mqMatches', this.onMediaQueryChange); | |
47 this.listenTo(this.model, 'change:offsets', this.adjustPlacement); | |
48 this.listenTo(this.model, 'change:activeTab change:orientation change:isOriented', this.updateToolbarHeight); | |
49 | |
50 // Add the tray orientation toggles. | |
51 this.$el | |
52 .find('.toolbar-tray .toolbar-lining') | |
53 .append(Drupal.theme('toolbarOrientationToggle')); | |
54 | |
55 // Trigger an activeTab change so that listening scripts can respond on | |
56 // page load. This will call render. | |
57 this.model.trigger('change:activeTab'); | |
58 }, | |
59 | |
60 /** | |
61 * Update the toolbar element height. | |
62 * | |
63 * @constructs | |
64 * | |
65 * @augments Backbone.View | |
66 */ | |
67 updateToolbarHeight() { | |
68 const toolbarTabOuterHeight = $('#toolbar-bar').find('.toolbar-tab').outerHeight() || 0; | |
69 const toolbarTrayHorizontalOuterHeight = $('.is-active.toolbar-tray-horizontal').outerHeight() || 0; | |
70 this.model.set('height', toolbarTabOuterHeight + toolbarTrayHorizontalOuterHeight); | |
71 | |
72 $('body').css({ | |
73 'padding-top': this.model.get('height'), | |
74 }); | |
75 | |
76 this.triggerDisplace(); | |
77 }, | |
78 | |
79 // Trigger a recalculation of viewport displacing elements. Use setTimeout | |
80 // to ensure this recalculation happens after changes to visual elements | |
81 // have processed. | |
82 triggerDisplace() { | |
83 _.defer(() => { | |
84 Drupal.displace(true); | |
85 }); | |
86 }, | |
87 | |
88 /** | |
89 * @inheritdoc | |
90 * | |
91 * @return {Drupal.toolbar.ToolbarVisualView} | |
92 * The `ToolbarVisualView` instance. | |
93 */ | |
94 render() { | |
95 this.updateTabs(); | |
96 this.updateTrayOrientation(); | |
97 this.updateBarAttributes(); | |
98 | |
99 $('body').removeClass('toolbar-loading'); | |
100 | |
101 // Load the subtrees if the orientation of the toolbar is changed to | |
102 // vertical. This condition responds to the case that the toolbar switches | |
103 // from horizontal to vertical orientation. The toolbar starts in a | |
104 // vertical orientation by default and then switches to horizontal during | |
105 // initialization if the media query conditions are met. Simply checking | |
106 // that the orientation is vertical here would result in the subtrees | |
107 // always being loaded, even when the toolbar initialization ultimately | |
108 // results in a horizontal orientation. | |
109 // | |
110 // @see Drupal.behaviors.toolbar.attach() where admin menu subtrees | |
111 // loading is invoked during initialization after media query conditions | |
112 // have been processed. | |
113 if (this.model.changed.orientation === 'vertical' || this.model.changed.activeTab) { | |
114 this.loadSubtrees(); | |
115 } | |
116 | |
117 return this; | |
118 }, | |
119 | |
120 /** | |
121 * Responds to a toolbar tab click. | |
122 * | |
123 * @param {jQuery.Event} event | |
124 * The event triggered. | |
125 */ | |
126 onTabClick(event) { | |
127 // If this tab has a tray associated with it, it is considered an | |
128 // activatable tab. | |
129 if (event.target.hasAttribute('data-toolbar-tray')) { | |
130 const activeTab = this.model.get('activeTab'); | |
131 const clickedTab = event.target; | |
132 | |
133 // Set the event target as the active item if it is not already. | |
134 this.model.set('activeTab', (!activeTab || clickedTab !== activeTab) ? clickedTab : null); | |
135 | |
136 event.preventDefault(); | |
137 event.stopPropagation(); | |
138 } | |
139 }, | |
140 | |
141 /** | |
142 * Toggles the orientation of a toolbar tray. | |
143 * | |
144 * @param {jQuery.Event} event | |
145 * The event triggered. | |
146 */ | |
147 onOrientationToggleClick(event) { | |
148 const orientation = this.model.get('orientation'); | |
149 // Determine the toggle-to orientation. | |
150 const antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical'; | |
151 const locked = antiOrientation === 'vertical'; | |
152 // Remember the locked state. | |
153 if (locked) { | |
154 localStorage.setItem('Drupal.toolbar.trayVerticalLocked', 'true'); | |
155 } | |
156 else { | |
157 localStorage.removeItem('Drupal.toolbar.trayVerticalLocked'); | |
158 } | |
159 // Update the model. | |
160 this.model.set({ | |
161 locked, | |
162 orientation: antiOrientation, | |
163 }, { | |
164 validate: true, | |
165 override: true, | |
166 }); | |
167 | |
168 event.preventDefault(); | |
169 event.stopPropagation(); | |
170 }, | |
171 | |
172 /** | |
173 * Updates the display of the tabs: toggles a tab and the associated tray. | |
174 */ | |
175 updateTabs() { | |
176 const $tab = $(this.model.get('activeTab')); | |
177 // Deactivate the previous tab. | |
178 $(this.model.previous('activeTab')) | |
179 .removeClass('is-active') | |
180 .prop('aria-pressed', false); | |
181 // Deactivate the previous tray. | |
182 $(this.model.previous('activeTray')) | |
183 .removeClass('is-active'); | |
184 | |
185 // Activate the selected tab. | |
186 if ($tab.length > 0) { | |
187 $tab | |
188 .addClass('is-active') | |
189 // Mark the tab as pressed. | |
190 .prop('aria-pressed', true); | |
191 const name = $tab.attr('data-toolbar-tray'); | |
192 // Store the active tab name or remove the setting. | |
193 const id = $tab.get(0).id; | |
194 if (id) { | |
195 localStorage.setItem('Drupal.toolbar.activeTabID', JSON.stringify(id)); | |
196 } | |
197 // Activate the associated tray. | |
198 const $tray = this.$el.find(`[data-toolbar-tray="${name}"].toolbar-tray`); | |
199 if ($tray.length) { | |
200 $tray.addClass('is-active'); | |
201 this.model.set('activeTray', $tray.get(0)); | |
202 } | |
203 else { | |
204 // There is no active tray. | |
205 this.model.set('activeTray', null); | |
206 } | |
207 } | |
208 else { | |
209 // There is no active tray. | |
210 this.model.set('activeTray', null); | |
211 localStorage.removeItem('Drupal.toolbar.activeTabID'); | |
212 } | |
213 }, | |
214 | |
215 /** | |
216 * Update the attributes of the toolbar bar element. | |
217 */ | |
218 updateBarAttributes() { | |
219 const isOriented = this.model.get('isOriented'); | |
220 if (isOriented) { | |
221 this.$el.find('.toolbar-bar').attr('data-offset-top', ''); | |
222 } | |
223 else { | |
224 this.$el.find('.toolbar-bar').removeAttr('data-offset-top'); | |
225 } | |
226 // Toggle between a basic vertical view and a more sophisticated | |
227 // horizontal and vertical display of the toolbar bar and trays. | |
228 this.$el.toggleClass('toolbar-oriented', isOriented); | |
229 }, | |
230 | |
231 /** | |
232 * Updates the orientation of the active tray if necessary. | |
233 */ | |
234 updateTrayOrientation() { | |
235 const orientation = this.model.get('orientation'); | |
236 | |
237 // The antiOrientation is used to render the view of action buttons like | |
238 // the tray orientation toggle. | |
239 const antiOrientation = (orientation === 'vertical') ? 'horizontal' : 'vertical'; | |
240 | |
241 // Toggle toolbar's parent classes before other toolbar classes to avoid | |
242 // potential flicker and re-rendering. | |
243 $('body') | |
244 .toggleClass('toolbar-vertical', (orientation === 'vertical')) | |
245 .toggleClass('toolbar-horizontal', (orientation === 'horizontal')); | |
246 | |
247 const removeClass = (antiOrientation === 'horizontal') ? 'toolbar-tray-horizontal' : 'toolbar-tray-vertical'; | |
248 const $trays = this.$el.find('.toolbar-tray') | |
249 .removeClass(removeClass) | |
250 .addClass(`toolbar-tray-${orientation}`); | |
251 | |
252 // Update the tray orientation toggle button. | |
253 const iconClass = `toolbar-icon-toggle-${orientation}`; | |
254 const iconAntiClass = `toolbar-icon-toggle-${antiOrientation}`; | |
255 const $orientationToggle = this.$el.find('.toolbar-toggle-orientation') | |
256 .toggle(this.model.get('isTrayToggleVisible')); | |
257 $orientationToggle.find('button') | |
258 .val(antiOrientation) | |
259 .attr('title', this.strings[antiOrientation]) | |
260 .text(this.strings[antiOrientation]) | |
261 .removeClass(iconClass) | |
262 .addClass(iconAntiClass); | |
263 | |
264 // Update data offset attributes for the trays. | |
265 const dir = document.documentElement.dir; | |
266 const edge = (dir === 'rtl') ? 'right' : 'left'; | |
267 // Remove data-offset attributes from the trays so they can be refreshed. | |
268 $trays.removeAttr('data-offset-left data-offset-right data-offset-top'); | |
269 // If an active vertical tray exists, mark it as an offset element. | |
270 $trays.filter('.toolbar-tray-vertical.is-active').attr(`data-offset-${edge}`, ''); | |
271 // If an active horizontal tray exists, mark it as an offset element. | |
272 $trays.filter('.toolbar-tray-horizontal.is-active').attr('data-offset-top', ''); | |
273 }, | |
274 | |
275 /** | |
276 * Sets the tops of the trays so that they align with the bottom of the bar. | |
277 */ | |
278 adjustPlacement() { | |
279 const $trays = this.$el.find('.toolbar-tray'); | |
280 if (!this.model.get('isOriented')) { | |
281 $trays.removeClass('toolbar-tray-horizontal').addClass('toolbar-tray-vertical'); | |
282 } | |
283 }, | |
284 | |
285 /** | |
286 * Calls the endpoint URI that builds an AJAX command with the rendered | |
287 * subtrees. | |
288 * | |
289 * The rendered admin menu subtrees HTML is cached on the client in | |
290 * localStorage until the cache of the admin menu subtrees on the server- | |
291 * side is invalidated. The subtreesHash is stored in localStorage as well | |
292 * and compared to the subtreesHash in drupalSettings to determine when the | |
293 * admin menu subtrees cache has been invalidated. | |
294 */ | |
295 loadSubtrees() { | |
296 const $activeTab = $(this.model.get('activeTab')); | |
297 const orientation = this.model.get('orientation'); | |
298 // Only load and render the admin menu subtrees if: | |
299 // (1) They have not been loaded yet. | |
300 // (2) The active tab is the administration menu tab, indicated by the | |
301 // presence of the data-drupal-subtrees attribute. | |
302 // (3) The orientation of the tray is vertical. | |
303 if (!this.model.get('areSubtreesLoaded') && typeof $activeTab.data('drupal-subtrees') !== 'undefined' && orientation === 'vertical') { | |
304 const subtreesHash = drupalSettings.toolbar.subtreesHash; | |
305 const theme = drupalSettings.ajaxPageState.theme; | |
306 const endpoint = Drupal.url(`toolbar/subtrees/${subtreesHash}`); | |
307 const cachedSubtreesHash = localStorage.getItem(`Drupal.toolbar.subtreesHash.${theme}`); | |
308 const cachedSubtrees = JSON.parse(localStorage.getItem(`Drupal.toolbar.subtrees.${theme}`)); | |
309 const isVertical = this.model.get('orientation') === 'vertical'; | |
310 // If we have the subtrees in localStorage and the subtree hash has not | |
311 // changed, then use the cached data. | |
312 if (isVertical && subtreesHash === cachedSubtreesHash && cachedSubtrees) { | |
313 Drupal.toolbar.setSubtrees.resolve(cachedSubtrees); | |
314 } | |
315 // Only make the call to get the subtrees if the orientation of the | |
316 // toolbar is vertical. | |
317 else if (isVertical) { | |
318 // Remove the cached menu information. | |
319 localStorage.removeItem(`Drupal.toolbar.subtreesHash.${theme}`); | |
320 localStorage.removeItem(`Drupal.toolbar.subtrees.${theme}`); | |
321 // The AJAX response's command will trigger the resolve method of the | |
322 // Drupal.toolbar.setSubtrees Promise. | |
323 Drupal.ajax({ url: endpoint }).execute(); | |
324 // Cache the hash for the subtrees locally. | |
325 localStorage.setItem(`Drupal.toolbar.subtreesHash.${theme}`, subtreesHash); | |
326 } | |
327 } | |
328 }, | |
329 }); | |
330 }(jQuery, Drupal, drupalSettings, Backbone)); |