Mercurial > hg > cmmr2012-drupal-site
comparison core/misc/collapse.es6.js @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 /** | |
2 * @file | |
3 * Polyfill for HTML5 details elements. | |
4 */ | |
5 | |
6 (function ($, Modernizr, Drupal) { | |
7 /** | |
8 * The collapsible details object represents a single details element. | |
9 * | |
10 * @constructor Drupal.CollapsibleDetails | |
11 * | |
12 * @param {HTMLElement} node | |
13 * The details element. | |
14 */ | |
15 function CollapsibleDetails(node) { | |
16 this.$node = $(node); | |
17 this.$node.data('details', this); | |
18 // Expand details if there are errors inside, or if it contains an | |
19 // element that is targeted by the URI fragment identifier. | |
20 const anchor = location.hash && location.hash !== '#' ? `, ${location.hash}` : ''; | |
21 if (this.$node.find(`.error${anchor}`).length) { | |
22 this.$node.attr('open', true); | |
23 } | |
24 // Initialize and setup the summary, | |
25 this.setupSummary(); | |
26 // Initialize and setup the legend. | |
27 this.setupLegend(); | |
28 } | |
29 | |
30 $.extend(CollapsibleDetails, /** @lends Drupal.CollapsibleDetails */{ | |
31 | |
32 /** | |
33 * Holds references to instantiated CollapsibleDetails objects. | |
34 * | |
35 * @type {Array.<Drupal.CollapsibleDetails>} | |
36 */ | |
37 instances: [], | |
38 }); | |
39 | |
40 $.extend(CollapsibleDetails.prototype, /** @lends Drupal.CollapsibleDetails# */{ | |
41 | |
42 /** | |
43 * Initialize and setup summary events and markup. | |
44 * | |
45 * @fires event:summaryUpdated | |
46 * | |
47 * @listens event:summaryUpdated | |
48 */ | |
49 setupSummary() { | |
50 this.$summary = $('<span class="summary"></span>'); | |
51 this.$node | |
52 .on('summaryUpdated', $.proxy(this.onSummaryUpdated, this)) | |
53 .trigger('summaryUpdated'); | |
54 }, | |
55 | |
56 /** | |
57 * Initialize and setup legend markup. | |
58 */ | |
59 setupLegend() { | |
60 // Turn the summary into a clickable link. | |
61 const $legend = this.$node.find('> summary'); | |
62 | |
63 $('<span class="details-summary-prefix visually-hidden"></span>') | |
64 .append(this.$node.attr('open') ? Drupal.t('Hide') : Drupal.t('Show')) | |
65 .prependTo($legend) | |
66 .after(document.createTextNode(' ')); | |
67 | |
68 // .wrapInner() does not retain bound events. | |
69 $('<a class="details-title"></a>') | |
70 .attr('href', `#${this.$node.attr('id')}`) | |
71 .prepend($legend.contents()) | |
72 .appendTo($legend); | |
73 | |
74 $legend | |
75 .append(this.$summary) | |
76 .on('click', $.proxy(this.onLegendClick, this)); | |
77 }, | |
78 | |
79 /** | |
80 * Handle legend clicks. | |
81 * | |
82 * @param {jQuery.Event} e | |
83 * The event triggered. | |
84 */ | |
85 onLegendClick(e) { | |
86 this.toggle(); | |
87 e.preventDefault(); | |
88 }, | |
89 | |
90 /** | |
91 * Update summary. | |
92 */ | |
93 onSummaryUpdated() { | |
94 const text = $.trim(this.$node.drupalGetSummary()); | |
95 this.$summary.html(text ? ` (${text})` : ''); | |
96 }, | |
97 | |
98 /** | |
99 * Toggle the visibility of a details element using smooth animations. | |
100 */ | |
101 toggle() { | |
102 const isOpen = !!this.$node.attr('open'); | |
103 const $summaryPrefix = this.$node.find('> summary span.details-summary-prefix'); | |
104 if (isOpen) { | |
105 $summaryPrefix.html(Drupal.t('Show')); | |
106 } | |
107 else { | |
108 $summaryPrefix.html(Drupal.t('Hide')); | |
109 } | |
110 // Delay setting the attribute to emulate chrome behavior and make | |
111 // details-aria.js work as expected with this polyfill. | |
112 setTimeout(() => { | |
113 this.$node.attr('open', !isOpen); | |
114 }, 0); | |
115 }, | |
116 }); | |
117 | |
118 /** | |
119 * Polyfill HTML5 details element. | |
120 * | |
121 * @type {Drupal~behavior} | |
122 * | |
123 * @prop {Drupal~behaviorAttach} attach | |
124 * Attaches behavior for the details element. | |
125 */ | |
126 Drupal.behaviors.collapse = { | |
127 attach(context) { | |
128 if (Modernizr.details) { | |
129 return; | |
130 } | |
131 const $collapsibleDetails = $(context).find('details').once('collapse').addClass('collapse-processed'); | |
132 if ($collapsibleDetails.length) { | |
133 for (let i = 0; i < $collapsibleDetails.length; i++) { | |
134 CollapsibleDetails.instances.push(new CollapsibleDetails($collapsibleDetails[i])); | |
135 } | |
136 } | |
137 }, | |
138 }; | |
139 | |
140 /** | |
141 * Open parent details elements of a targeted page fragment. | |
142 * | |
143 * Opens all (nested) details element on a hash change or fragment link click | |
144 * when the target is a child element, in order to make sure the targeted | |
145 * element is visible. Aria attributes on the summary | |
146 * are set by triggering the click event listener in details-aria.js. | |
147 * | |
148 * @param {jQuery.Event} e | |
149 * The event triggered. | |
150 * @param {jQuery} $target | |
151 * The targeted node as a jQuery object. | |
152 */ | |
153 const handleFragmentLinkClickOrHashChange = (e, $target) => { | |
154 $target.parents('details').not('[open]').find('> summary').trigger('click'); | |
155 }; | |
156 | |
157 /** | |
158 * Binds a listener to handle fragment link clicks and URL hash changes. | |
159 */ | |
160 $('body').on('formFragmentLinkClickOrHashChange.details', handleFragmentLinkClickOrHashChange); | |
161 | |
162 // Expose constructor in the public space. | |
163 Drupal.CollapsibleDetails = CollapsibleDetails; | |
164 }(jQuery, Modernizr, Drupal)); |