comparison core/misc/collapse.es6.js @ 17:129ea1e6d783

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