PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / trunk
Subscriptions for WooCommerce with Stripe Recurring Payments vtrunk
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 trunk, at includes/Admin/views/subscription-list.php

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