PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 1.11.2
Subscriptions for WooCommerce with Stripe Recurring Payments v1.11.2
2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
subscription / includes / Admin / views / subscription-list.php

subscription-list.php in Subscriptions for WooCommerce with Stripe Recurring Payments 1.11.2, at includes/Admin/views/subscription-list.php

529 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Subscription admin list view.
4 *
5 * @package SpringDevs\Subscription\Admin
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit;
10 }
11
12 if ( ! isset( $date_filter ) ) {
13 $date_filter = '';
14 }
15
16 $filters_active = ! empty( $status ) || ! empty( $date_filter ) || ! empty( $search );
17 $months = array();
18 for ( $i = 0; $i < 12; $i++ ) {
19 $month = strtotime( "-$i month" );
20 $months[ date( 'Y-m', $month ) ] = date( 'F Y', $month );
21 }
22 ?>
23 <div class="wp-subscription-admin-content list-page subscrpt-subs-list">
24
25 <?php
26 // Getting started card — hidden once a subscription product exists.
27 require __DIR__ . '/subscription-gsc.php';
28 ?>
29
30 <!-- Page header -->
31 <div style="margin-bottom:20px;">
32 <h1 style="font-size:1.375rem;font-weight:700;color:var(--wpsubs-text);margin:0 0 6px;line-height:1.2;"><?php esc_html_e( 'Subscriptions', 'subscription' ); ?></h1>
33 <p style="font-size:13px;color:var(--wpsubs-text-muted);margin:0 0 12px;line-height:1.5;"><?php esc_html_e( 'Manage all your WooCommerce subscriptions.', 'subscription' ); ?></p>
34 <div style="border-top:1px dashed #d0d3d7;"></div>
35 </div>
36
37 <form method="post" id="subscriptions-form">
38 <?php wp_nonce_field( 'subscrpt_list_action' ); ?>
39 <input type="hidden" name="page" value="wp-subscription" />
40
41 <!-- Toolbar -->
42 <div class="wpsubs-toolbar">
43
44 <?php
45 wpsubs_render_adv_select(
46 array(
47 'name' => 'subscrpt_status',
48 'placeholder' => __( 'All Status', 'subscription' ),
49 'value' => $status,
50 'options' => array(
51 array(
52 'value' => 'active',
53 'label' => __( 'Active', 'subscription' ),
54 ),
55 array(
56 'value' => 'pending',
57 'label' => __( 'Pending', 'subscription' ),
58 ),
59 array(
60 'value' => 'pe_cancelled',
61 'label' => __( 'Pending Cancellation', 'subscription' ),
62 ),
63 array(
64 'value' => 'cancelled',
65 'label' => __( 'Cancelled', 'subscription' ),
66 ),
67 array(
68 'value' => 'expired',
69 'label' => __( 'Expired', 'subscription' ),
70 ),
71 array(
72 'value' => 'trash',
73 'label' => __( 'Trash', 'subscription' ),
74 ),
75 ),
76 )
77 );
78 ?>
79
80 <?php
81 $date_options = array();
82 foreach ( $months as $val => $label ) {
83 $date_options[] = array(
84 'value' => $val,
85 'label' => $label,
86 );
87 }
88 wpsubs_render_adv_select(
89 array(
90 'name' => 'date_filter',
91 'placeholder' => __( 'All Dates', 'subscription' ),
92 'value' => $date_filter,
93 'options' => $date_options,
94 )
95 );
96 ?>
97
98 <div class="wpsubs-search">
99 <div class="wpsubs-input-wrap wpsubs-input-wrap--icon-l">
100 <svg class="wpsubs-input-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-4.35-4.35M17 11A6 6 0 1 1 5 11a6 6 0 0 1 12 0z"/></svg>
101 <input type="search" name="s" value="<?php echo esc_attr( $search ); ?>" placeholder="<?php esc_attr_e( 'Search subscriptions...', 'subscription' ); ?>" class="wpsubs-input" />
102 </div>
103 </div>
104
105 <button type="submit" name="filter_action" value="filter" class="wpsubs-btn wpsubs-btn--outline">
106 <?php esc_html_e( 'Filter', 'subscription' ); ?>
107 </button>
108
109 <?php if ( $filters_active ) : ?>
110 <a href="<?php echo esc_url( remove_query_arg( array( 'subscrpt_status', 'date_filter', 's', 'title', 'paged' ) ) ); ?>" class="wpsubs-btn wpsubs-btn--outline">
111 <?php esc_html_e( 'Clear', 'subscription' ); ?>
112 </a>
113 <?php endif; ?>
114
115 <div class="wpsubs-toolbar__spacer"></div>
116
117 <?php
118 $current_per_page = isset( $_GET['per_page'] ) ? intval( wp_unslash( $_GET['per_page'] ) ) : 20;
119 wpsubs_render_adv_select(
120 array(
121 'name' => 'per_page',
122 'value' => (string) $current_per_page,
123 'options' => array(
124 array(
125 'value' => '10',
126 'label' => __( '10 / page', 'subscription' ),
127 ),
128 array(
129 'value' => '20',
130 'label' => __( '20 / page', 'subscription' ),
131 ),
132 array(
133 'value' => '50',
134 'label' => __( '50 / page', 'subscription' ),
135 ),
136 array(
137 'value' => '100',
138 'label' => __( '100 / page', 'subscription' ),
139 ),
140 ),
141 'align' => 'right',
142 'id' => 'wpsubs-per-page-select',
143 )
144 );
145 ?>
146
147 <?php
148 // Bulk action advanced-select + hidden submit.
149 $bulk_options = 'trash' === $status
150 ? array(
151 array(
152 'value' => 'restore',
153 'label' => __( 'Restore selected', 'subscription' ),
154 ),
155 array(
156 'value' => 'delete',
157 'label' => __( 'Delete permanently', 'subscription' ),
158 'danger' => true,
159 'confirm' => __( 'Delete selected subscriptions permanently? This cannot be undone.', 'subscription' ),
160 ),
161 )
162 : array(
163 array(
164 'value' => 'trash',
165 'label' => __( 'Move to trash', 'subscription' ),
166 'danger' => true,
167 'confirm' => __( 'Move selected subscriptions to trash?', 'subscription' ),
168 ),
169 );
170
171 wpsubs_render_adv_select(
172 array(
173 'name' => 'action',
174 'placeholder' => __( 'Select Action', 'subscription' ),
175 'value' => '-1',
176 'options' => $bulk_options,
177 'id' => 'wpsubs-bulk-action-select',
178 'align' => 'right',
179 )
180 );
181 ?>
182 <button type="submit" name="bulk_action" value="1" id="wpsubs-bulk-action-submit" style="display:none;" aria-hidden="true"></button>
183
184 <?php
185 if ( 'trash' === $status && ! empty( $subscriptions ) ) :
186 $empty_trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=clean_trash&sub_id=all' ), 'wpsubs_action_clean_trash' );
187 ?>
188 <a href="<?php echo esc_url( $empty_trash_url ); ?>" class="wpsubs-btn wpsubs-btn--danger" onclick="return confirm('<?php esc_attr_e( 'Permanently delete all trash items? This cannot be undone.', 'subscription' ); ?>')">
189 <?php esc_html_e( 'Empty Trash', 'subscription' ); ?>
190 </a>
191 <?php endif; ?>
192
193 </div><!-- /.wpsubs-toolbar -->
194
195 <!-- Table card -->
196 <h2 class="screen-reader-text"><?php esc_html_e( 'Subscriptions list', 'subscription' ); ?></h2>
197 <div class="wpsubs-table-card">
198 <table class="wpsubs-table">
199 <thead>
200 <tr>
201 <th class="wpsubs-col--check">
202 <input type="checkbox" id="cb-select-all" class="wpsubs-checkbox">
203 </th>
204 <th style="width:1%;white-space:nowrap;"><?php esc_html_e( 'Status', 'subscription' ); ?></th>
205 <th><?php esc_html_e( 'Subscription', 'subscription' ); ?></th>
206 <th><?php esc_html_e( 'Customer', 'subscription' ); ?></th>
207 <th><?php esc_html_e( 'Amount', 'subscription' ); ?></th>
208 <th><?php esc_html_e( 'Started', 'subscription' ); ?></th>
209 <th><?php esc_html_e( 'Next Renewal', 'subscription' ); ?></th>
210 <th class="wpsubs-col--actions"><?php esc_html_e( 'Actions', 'subscription' ); ?></th>
211 </tr>
212 </thead>
213 <tbody>
214
215 <?php if ( ! empty( $subscriptions ) ) : ?>
216 <?php
217 foreach ( $subscriptions as $subscription ) :
218 $subscription_id = $subscription->ID;
219 $subscription_data = SpringDevs\Subscription\Illuminate\Helper::get_subscription_data( $subscription_id );
220
221 $subscrpt_status = $subscription_data['status'] ?? '';
222 $subscrpt_status = empty( $subscrpt_status ) ? get_post_status( $subscription_id ) : $subscrpt_status;
223
224 $order_id = $subscription_data['order']['order_id'] ?? 0;
225 $order_item_id = $subscription_data['order']['order_item_id'] ?? 0;
226 $order = $order_id ? wc_get_order( $order_id ) : null;
227 $order_item = $order ? $order->get_item( $order_item_id ) : null;
228 $product_name = $order_item ? $order_item->get_name() : '-';
229
230 $product_id = $subscription_data['product']['variation_id'] ?: ( $subscription_data['product']['product_id'] ?? 0 );
231 $product_url = $product_id ? get_edit_post_link( $product_id ) : '';
232
233 $customer = $order ? $order->get_formatted_billing_full_name() : '-';
234 $customer_id = $order ? $order->get_customer_id() : 0;
235 $customer_url = $customer_id ? admin_url( 'user-edit.php?user_id=' . $customer_id ) : '';
236 $customer_email = $order ? $order->get_billing_email() : '';
237
238 $start_date = $subscription_data['start_date'] ? strtotime( $subscription_data['start_date'] ) : 0;
239 $renewal_date = $subscription_data['next_date'] ? strtotime( $subscription_data['next_date'] ) : 0;
240
241 $is_trash = 'trash' === $subscription->post_status;
242 $is_grace_period = isset( $subscription_data['grace_period'] );
243 $grace_remaining = $subscription_data['grace_period']['remaining_days'] ?? 0;
244
245 // Amount + timing. Net of any discount that carries into renewals.
246 $quantity = $order_item ? (int) $order_item->get_quantity() : 1;
247 $amount_totals = SpringDevs\Subscription\Illuminate\Helper::get_subscription_display_totals( $subscription_id, $order_item );
248 $price = '' !== ( $subscription_data['price'] ?? '' ) ? (float) $amount_totals['total'] : '';
249 $price_full = (float) $amount_totals['full_excl'] + $amount_totals['tax'] + $amount_totals['discount_tax'];
250 $has_discount = (bool) $amount_totals['has_discount'];
251 $price_per_unit = '' !== $price ? $price / max( 1, $quantity ) : '';
252 $timing_per = $subscription_data['schedule']['timing_per'] ?? '';
253 $timing_option = $subscription_data['schedule']['timing_option'] ?? '';
254 $timing_label = '';
255 if ( $timing_per && $timing_option ) {
256 $timing_label = ( (int) $timing_per > 1 )
257 ? $timing_per . ' ' . $timing_option . 's'
258 : $timing_option;
259 }
260
261 // Avatar
262 $name_parts = array_values( array_filter( explode( ' ', trim( $customer ) ) ) );
263 $initials = '?';
264 if ( $name_parts ) {
265 $initials = '';
266 foreach ( array_slice( $name_parts, 0, 2 ) as $part ) {
267 $initials .= strtoupper( $part[0] );
268 }
269 }
270 $color_slot = ord( strtolower( $initials[0] ?? 'a' ) ) % 8;
271
272 // Action URLs
273 $nonce_action = 'wpsubs_action_' . $subscription->ID;
274 $view_url = admin_url( 'admin.php?page=wp-subscription-details&id=' . $subscription->ID );
275 $trash_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=trash&sub_id=' . $subscription->ID ), $nonce_action );
276 $delete_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=delete&sub_id=' . $subscription->ID ), $nonce_action );
277 $restore_url = wp_nonce_url( admin_url( 'admin.php?page=wp-subscription&action=restore&sub_id=' . $subscription->ID ), $nonce_action );
278
279 // Status badge
280 $badge_mod_map = array(
281 'active' => 'active',
282 'pending' => 'pending',
283 'pe_cancelled' => 'pending-cancel',
284 'cancelled' => 'cancelled',
285 'expired' => 'expired',
286 'draft' => 'draft',
287 'trash' => 'trash',
288 );
289 $badge_mod = $badge_mod_map[ $subscrpt_status ] ?? 'expired';
290 $verbose_status = SpringDevs\Subscription\Illuminate\Helper::get_verbose_status( $subscrpt_status );
291
292 if ( $is_grace_period && $grace_remaining > 0 ) {
293 $badge_mod = 'active';
294 $verbose_status = __( 'Active', 'subscription' );
295 }
296 ?>
297 <tr>
298 <!-- Checkbox -->
299 <td class="wpsubs-col--check">
300 <input type="checkbox" name="subscription_ids[]" value="<?php echo esc_attr( $subscription->ID ); ?>" class="wpsubs-checkbox wpsubs-row-check">
301 </td>
302
303 <!-- Status badge -->
304 <td style="white-space:nowrap;">
305 <span class="wpsubs-badge wpsubs-badge--<?php echo esc_attr( $badge_mod ); ?>">
306 <?php echo esc_html( $verbose_status ); ?>
307 <?php if ( $is_grace_period && $grace_remaining > 0 ) : ?>
308 <span class="dashicons dashicons-warning" style="font-size:11px;width:11px;height:11px;color:#d97706;" title="<?php echo esc_attr( sprintf( __( '%d days remaining in grace period', 'subscription' ), $grace_remaining ) ); ?>"></span>
309 <?php endif; ?>
310 </span>
311 </td>
312
313 <!-- Subscription: product name + subscription ID -->
314 <td>
315 <a href="<?php echo esc_url( $view_url ); ?>" class="wpsubs-cell-title" style="font-weight:600;"><?php echo esc_html( $product_name ); ?></a>
316 <span class="wpsubs-cell-id">#<?php echo (int) $subscription->ID; ?></span>
317 </td>
318
319 <!-- Customer avatar + name + email -->
320 <td>
321 <div class="wpsubs-customer">
322 <div class="wpsubs-avatar" data-color="<?php echo (int) $color_slot; ?>">
323 <?php echo esc_html( $initials ); ?>
324 </div>
325 <div class="wpsubs-customer__info">
326 <?php if ( $customer_url ) : ?>
327 <a href="<?php echo esc_url( $customer_url ); ?>" class="wpsubs-customer__name"><?php echo esc_html( $customer ); ?></a>
328 <?php else : ?>
329 <span class="wpsubs-customer__name"><?php echo esc_html( $customer ); ?></span>
330 <?php endif; ?>
331 <?php if ( $customer_email ) : ?>
332 <span class="wpsubs-customer__sub"><?php echo esc_html( $customer_email ); ?></span>
333 <?php endif; ?>
334 </div>
335 </div>
336 </td>
337
338 <!-- Subscription amount + timing -->
339 <td style="white-space:nowrap;">
340 <?php if ( '' !== $price ) : ?>
341 <span class="wpsubs-cell-title" style="font-variant-numeric:tabular-nums;"><?php echo wp_kses_post( wc_price( $price ) ); ?></span>
342 <?php if ( $has_discount ) : ?>
343 <span class="wpsubs-cell-id"><del aria-hidden="true"><?php echo wp_kses_post( wc_price( $price_full ) ); ?></del></span>
344 <?php endif; ?>
345 <?php if ( $quantity > 1 ) : ?>
346 <span class="wpsubs-cell-id"><?php echo wp_kses_post( wc_price( (float) $price_per_unit ) ); ?> &times; <?php echo (int) $quantity; ?></span>
347 <?php endif; ?>
348 <?php if ( $timing_label ) : ?>
349 <span class="wpsubs-cell-id">/ <?php echo esc_html( $timing_label ); ?></span>
350 <?php endif; ?>
351 <?php else : ?>
352 <span class="wpsubs-cell--muted">&#8212;</span>
353 <?php endif; ?>
354 </td>
355
356 <!-- Start date -->
357 <td class="wpsubs-col--nowrap">
358 <?php if ( $start_date ) : ?>
359 <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $start_date ) ); ?></span>
360 <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $start_date ) ); ?></span>
361 <?php else : ?>
362 <span class="wpsubs-cell--muted">&#8212;</span>
363 <?php endif; ?>
364 </td>
365
366 <!-- Renewal date -->
367 <td class="wpsubs-col--nowrap">
368 <?php if ( $renewal_date ) : ?>
369 <span class="wpsubs-cell-title"><?php echo esc_html( wp_date( get_option( 'date_format' ), $renewal_date ) ); ?></span>
370 <span class="wpsubs-cell-id"><?php echo esc_html( wp_date( get_option( 'time_format' ), $renewal_date ) ); ?></span>
371 <?php else : ?>
372 <span class="wpsubs-cell--muted">&#8212;</span>
373 <?php endif; ?>
374 </td>
375
376 <!-- Actions -->
377 <td class="wpsubs-cell--actions">
378 <div class="wpsubs-row-actions">
379 <button type="button" class="wpsubs-row-actions__trigger" aria-label="<?php esc_attr_e( 'Row actions', 'subscription' ); ?>">···</button>
380 <div class="wpsubs-dropdown">
381 <a href="<?php echo esc_url( $view_url ); ?>" class="wpsubs-dropdown__item"><?php esc_html_e( 'View / Edit', 'subscription' ); ?></a>
382 <div class="wpsubs-dropdown__divider"></div>
383 <?php if ( ! $is_trash ) : ?>
384 <a href="<?php echo esc_url( $trash_url ); ?>" class="wpsubs-dropdown__item wpsubs-dropdown__item--danger" onclick="return confirm('<?php esc_attr_e( 'Move this subscription to trash?', 'subscription' ); ?>')"><?php esc_html_e( 'Trash', 'subscription' ); ?></a>
385 <?php else : ?>
386 <a href="<?php echo esc_url( $restore_url ); ?>" class="wpsubs-dropdown__item"><?php esc_html_e( 'Restore', 'subscription' ); ?></a>
387 <a href="<?php echo esc_url( $delete_url ); ?>" class="wpsubs-dropdown__item wpsubs-dropdown__item--danger" onclick="return confirm('<?php esc_attr_e( 'Delete permanently? This cannot be undone.', 'subscription' ); ?>')"><?php esc_html_e( 'Delete Permanently', 'subscription' ); ?></a>
388 <?php endif; ?>
389 </div>
390 </div>
391 </td>
392 </tr>
393 <?php endforeach; ?>
394
395 <?php else : ?>
396 <tr>
397 <td colspan="8">
398 <div class="wpsubs-empty">
399 <div class="wpsubs-empty__icon">📋</div>
400 <div class="wpsubs-empty__title"><?php esc_html_e( 'No subscriptions found', 'subscription' ); ?></div>
401 <div class="wpsubs-empty__desc"><?php esc_html_e( 'Subscriptions will appear here once customers complete checkout.', 'subscription' ); ?></div>
402 </div>
403 </td>
404 </tr>
405 <?php endif; ?>
406
407 </tbody>
408 </table>
409
410 <!-- Pagination inside table card -->
411 <?php
412 wpsubs_render_pager(
413 array(
414 'current' => (int) $paged,
415 'total' => max( 1, (int) $max_num_pages ),
416 'info' => true,
417 'per_page' => (int) $per_page,
418 'item_count' => (int) $total,
419 'base_url' => remove_query_arg( 'paged' ),
420 'link_mode' => 'url',
421 // translators: %1$s = first item number, %2$s = last item number, %3$s = total items.
422 'info_format' => __( 'Showing %1$s–%2$s of %3$s subscriptions', 'subscription' ),
423 'context' => 'subscription-list',
424 )
425 );
426 ?>
427
428 </div><!-- /.wpsubs-table-card -->
429
430 </form>
431
432 </div>
433
434 <script>
435 ( function () {
436 var selectAll = document.getElementById( 'cb-select-all' );
437 var form = document.getElementById( 'subscriptions-form' );
438
439 function rowChecks() {
440 return form ? Array.from( form.querySelectorAll( '.wpsubs-row-check' ) ) : [];
441 }
442
443 function syncSelectAll() {
444 if ( ! selectAll ) return;
445 var checks = rowChecks();
446 var checked = checks.filter( function ( cb ) { return cb.checked; } );
447 checks.forEach( function ( cb ) {
448 var row = cb.closest( 'tr' );
449 if ( row ) row.classList.toggle( 'wpsubs-row--selected', cb.checked );
450 } );
451 selectAll.indeterminate = checked.length > 0 && checked.length < checks.length;
452 selectAll.checked = checks.length > 0 && checked.length === checks.length;
453 }
454
455 if ( selectAll ) {
456 selectAll.addEventListener( 'change', function () {
457 rowChecks().forEach( function ( cb ) { cb.checked = selectAll.checked; } );
458 syncSelectAll();
459 } );
460 }
461
462 if ( form ) {
463 form.addEventListener( 'change', function ( e ) {
464 if ( e.target.classList.contains( 'wpsubs-row-check' ) ) syncSelectAll();
465 } );
466 }
467
468 // ── Advanced-select event handlers ──────────────────────────
469 var bulkSubmit = document.getElementById( 'wpsubs-bulk-action-submit' );
470 document.addEventListener( 'wpsubs:select', function ( e ) {
471 var id = e.target && e.target.id;
472
473 // Bulk action: submit via hidden button
474 if ( id === 'wpsubs-bulk-action-select' && bulkSubmit ) {
475 bulkSubmit.click();
476 return;
477 }
478
479 // Per-page: auto-submit as a filter action
480 if ( id === 'wpsubs-per-page-select' && form ) {
481 var fi = document.createElement( 'input' );
482 fi.type = 'hidden';
483 fi.name = 'filter_action';
484 fi.value = 'filter';
485 form.appendChild( fi );
486 form.submit();
487 }
488 } );
489
490 // ── Row action dropdowns ─────────────────────────────────────
491 function closeAllRowMenus() {
492 document.querySelectorAll( '.wpsubs-row-actions--open' ).forEach( function ( el ) {
493 el.classList.remove( 'wpsubs-row-actions--open' );
494 var d = el.querySelector( '.wpsubs-dropdown' );
495 if ( d ) d.classList.remove( 'wpsubs-dropdown--open' );
496 } );
497 }
498
499 document.addEventListener( 'click', function ( e ) {
500 var trigger = e.target.closest( '.wpsubs-row-actions__trigger' );
501
502 document.querySelectorAll( '.wpsubs-row-actions--open' ).forEach( function ( el ) {
503 if ( el !== ( trigger && trigger.closest( '.wpsubs-row-actions' ) ) ) {
504 el.classList.remove( 'wpsubs-row-actions--open' );
505 var d = el.querySelector( '.wpsubs-dropdown' );
506 if ( d ) d.classList.remove( 'wpsubs-dropdown--open' );
507 }
508 } );
509
510 if ( ! trigger ) return;
511
512 e.stopPropagation();
513 var wrapper = trigger.closest( '.wpsubs-row-actions' );
514 var dropdown = wrapper && wrapper.querySelector( '.wpsubs-dropdown' );
515 if ( ! wrapper || ! dropdown ) return;
516
517 var isOpen = wrapper.classList.contains( 'wpsubs-row-actions--open' );
518 wrapper.classList.toggle( 'wpsubs-row-actions--open', ! isOpen );
519 dropdown.classList.toggle( 'wpsubs-dropdown--open', ! isOpen );
520 } );
521
522 document.addEventListener( 'keydown', function ( e ) {
523 if ( e.key === 'Escape' ) closeAllRowMenus();
524 } );
525
526 syncSelectAll();
527 }() );
528 </script>
529