PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.3
Tutor LMS – eLearning and online course solution v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / components / DateFilter.php
tutor / components Last commit date
Constants 3 weeks ago Accordion.php 6 days ago Alert.php 6 days ago AttachmentCard.php 6 days ago Avatar.php 6 days ago Badge.php 6 days ago BaseComponent.php 3 weeks ago Button.php 6 days ago ConfirmationModal.php 6 days ago CourseFilter.php 6 days ago DateFilter.php 6 days ago DropdownFilter.php 6 days ago EmptyState.php 6 days ago FileUploader.php 6 days ago InputField.php 6 days ago Modal.php 6 days ago Nav.php 6 days ago Pagination.php 6 days ago Popover.php 6 days ago PreviewTrigger.php 6 days ago Progress.php 6 days ago SearchFilter.php 6 days ago Sorting.php 6 days ago StarRating.php 6 days ago StarRatingInput.php 6 days ago StatusSelect.php 6 days ago SvgIcon.php 6 days ago Table.php 6 days ago Tabs.php 6 days ago Tooltip.php 6 days ago WPEditor.php 6 days ago
DateFilter.php
464 lines
1 <?php
2 /**
3 * Date Filter Component.
4 *
5 * @package Tutor\Components
6 * @author Themeum
7 * @link https://themeum.com
8 * @since 4.0.0
9 */
10
11 namespace Tutor\Components;
12
13 use TUTOR\Icon;
14 use TUTOR\Input;
15 use Tutor\Components\Constants\Size;
16 use Tutor\Components\Popover;
17
18 defined( 'ABSPATH' ) || exit;
19
20 /**
21 * DateFilter Component Class.
22 *
23 * Example usage:
24 *
25 * ```php
26 * // Single-date picker (bottom-start)
27 * DateFilter::make()
28 * ->type( DateFilter::TYPE_SINGLE )
29 * ->render();
30 *
31 * // Date-range picker (bottom-start, default)
32 * DateFilter::make()
33 * ->type( DateFilter::TYPE_RANGE )
34 * ->render();
35 *
36 * // Date-range picker positioned at bottom-end
37 * DateFilter::make()
38 * ->type( DateFilter::TYPE_RANGE )
39 * ->placement( DateFilter::PLACEMENT_BOTTOM_END )
40 * ->render();
41 *
42 * // With a custom button label
43 * DateFilter::make()
44 * ->type( DateFilter::TYPE_RANGE )
45 * ->label( 'Filter by date' )
46 * ->render();
47 *
48 * // Hide the initial label (show only icon until a range is selected)
49 * DateFilter::make()
50 * ->type( DateFilter::TYPE_RANGE )
51 * ->hide_initial_label()
52 * ->render();
53 *
54 * // Suppress label text entirely (icon-only button)
55 * DateFilter::make()
56 * ->type( DateFilter::TYPE_RANGE )
57 * ->show_label( false )
58 * ->render();
59 *
60 * // Custom trigger button size and icon size
61 * DateFilter::make()
62 * ->type( DateFilter::TYPE_RANGE )
63 * ->trigger_size( Size::X_SMALL )
64 * ->icon_size( 14 )
65 * ->render();
66 *
67 * // Clear related query params when the filter changes (e.g. reset pagination)
68 * DateFilter::make()
69 * ->type( DateFilter::TYPE_RANGE )
70 * ->clear_params( array( 'current_page' ) )
71 * ->render();
72 *
73 * // Extra attributes on the wrapper
74 * DateFilter::make()
75 * ->type( DateFilter::TYPE_SINGLE )
76 * ->attr( 'id', 'my-date-filter' )
77 * ->render();
78 * ```
79 *
80 * @since 4.0.0
81 */
82 class DateFilter extends BaseComponent {
83
84 /**
85 * Date Filter Type Single
86 */
87 const TYPE_SINGLE = 'single';
88
89 /**
90 * Date Filter Type Range
91 */
92 const TYPE_RANGE = 'range';
93
94 /**
95 * Placement Bottom Start
96 */
97 const PLACEMENT_BOTTOM_START = 'bottom-start';
98
99 /**
100 * Placement Bottom End
101 */
102 const PLACEMENT_BOTTOM_END = 'bottom-end';
103
104 /**
105 * Component Type
106 *
107 * @var string
108 */
109 protected $type = self::TYPE_SINGLE;
110
111 /**
112 * Button Label
113 *
114 * @var string
115 */
116 protected $label = '';
117
118 /**
119 * Popover placement
120 *
121 * @var string
122 */
123 protected $placement = self::PLACEMENT_BOTTOM_START;
124
125 /**
126 * Button size.
127 *
128 * @var string
129 */
130 protected $size = Size::SMALL;
131
132 /**
133 * Icon size.
134 *
135 * @var integer
136 */
137 protected $icon_size = Size::SIZE_16;
138
139 /**
140 * Whether to display the label text.
141 *
142 * @since 4.0.0
143 *
144 * @var bool
145 */
146 protected $show_label = true;
147
148 /**
149 * Whether to hide the initial label when no date range is selected.
150 *
151 * @since 4.0.0
152 *
153 * @var bool
154 */
155 protected $hide_initial_label = false;
156
157 /**
158 * Query params to clear when a date filter is applied or cleared (e.g. 'current_page').
159 *
160 * @since 4.0.0
161 *
162 * @var array
163 */
164 protected $clear_params = array();
165
166 /**
167 * Set filter type.
168 *
169 * @param string $type Filter type (single|range).
170 *
171 * @return self
172 */
173 public function type( string $type ): self {
174 $this->type = $type;
175 return $this;
176 }
177
178 /**
179 * Set Button Size.
180 *
181 * @since 4.0.0
182 *
183 * @param string $size the size type (x-small|small|medium|large).
184 *
185 * @return self
186 */
187 public function trigger_size( string $size ): self {
188 $allowed_sizes = array( Size::X_SMALL, Size::SMALL, Size::MEDIUM, Size::LARGE );
189 if ( in_array( $size, $allowed_sizes, true ) ) {
190 $this->size = $size;
191 }
192 return $this;
193 }
194
195 /**
196 * Set the button icon size.
197 *
198 * @since 4.0.0
199 *
200 * @param integer $size the icon size.
201 *
202 * @return self
203 */
204 public function icon_size( int $size ): self {
205 $this->icon_size = $size;
206 return $this;
207 }
208
209 /**
210 * Set button label.
211 *
212 * @param string $label Button label.
213 *
214 * @return self
215 */
216 public function label( string $label ): self {
217 $this->label = $label;
218 return $this;
219 }
220
221 /**
222 * Set popover placement.
223 *
224 * @param string $placement Popover placement.
225 *
226 * @return self
227 */
228 public function placement( string $placement ): self {
229 $this->placement = $placement;
230 return $this;
231 }
232
233 /**
234 * Enable or disable the display of the label text.
235 *
236 * @since 4.0.0
237 *
238 * @param bool $show_label True to show the label, false to hide it.
239 *
240 * @return $this
241 */
242 public function show_label( bool $show_label ) {
243 $this->show_label = $show_label;
244 return $this;
245 }
246
247 /**
248 * Hide or show the initial label text before a date range is selected.
249 *
250 * @since 4.0.0
251 *
252 * @return self
253 */
254 public function hide_initial_label(): self {
255 $this->hide_initial_label = true;
256 return $this;
257 }
258
259 /**
260 * Set query params to clear when a date filter is applied or cleared.
261 *
262 * Useful for resetting pagination (e.g. 'current_page') or other
263 * context-specific params whenever the date selection changes.
264 *
265 * @since 4.0.0
266 *
267 * @param array $params List of query param keys to remove on apply/clear.
268 *
269 * @return self
270 */
271 public function clear_params( array $params ): self {
272 $this->clear_params = $params;
273 return $this;
274 }
275
276 /**
277 * Render the component.
278 *
279 * @return string
280 */
281 public function get(): string {
282 $is_range = self::TYPE_RANGE === $this->type;
283
284 if ( empty( $this->label ) ) {
285 $this->label = $this->calculate_label();
286 }
287
288 // Default settings based on type.
289 $calendar_options = array(
290 'type' => 'default',
291 'clearParams' => $this->clear_params,
292 );
293
294 $button_classes = 'tutor-btn tutor-btn-outline';
295
296 if ( $this->size ) {
297 $button_classes .= ' tutor-btn-' . $this->size;
298 }
299
300 $popover_classes = 'tutor-popover';
301 $icon = Icon::CALENDAR_2;
302
303 if ( $is_range ) {
304 $calendar_options = array(
305 'type' => 'multiple',
306 'selectionDatesMode' => 'multiple-ranged',
307 'clearParams' => $this->clear_params,
308 );
309 $popover_classes .= ' tutor-range-calendar-popover';
310 }
311
312 if ( empty( $this->label ) ) {
313 $button_classes .= ' tutor-btn-icon';
314 } else {
315 $button_classes .= ' tutor-gap-2';
316 }
317
318 $options_json = wp_json_encode( $calendar_options );
319
320 $origin = Popover::TRANSFORM_ORIGIN_MAP[ $this->placement ] ?? 'center.top';
321
322 ob_start();
323 ?>
324 <div x-data="tutorPopover({ placement: '<?php echo esc_attr( $this->placement ); ?>' })" <?php echo $this->get_attributes_string(); //phpcs:ignore -- Sanitization is performed inside get_attributes_string. ?>>
325
326 <button
327 type="button"
328 x-ref="trigger"
329 @click="toggle()"
330 class="<?php echo esc_attr( $button_classes ); ?>"
331 <?php if ( empty( $this->label ) ) : ?>
332 aria-label="<?php echo $is_range ? esc_attr__( 'Filter by date range', 'tutor' ) : esc_attr__( 'Filter by date', 'tutor' ); ?>"
333 <?php endif; ?>
334 >
335 <?php SvgIcon::make()->name( $icon )->size( $this->icon_size )->render(); ?>
336 <?php if ( ! empty( $this->label ) ) : ?>
337 <span><?php echo esc_html( $this->label ); ?></span>
338 <?php endif; ?>
339
340 <?php if ( $this->has_selection() ) : ?>
341 <span @click.stop="$dispatch('tutor-calendar:clear')" class="tutor-cursor-pointer tutor-icon-secondary tutor-flex tutor-align-center">
342 <?php SvgIcon::make()->name( Icon::CROSS_2 )->render(); ?>
343 </span>
344 <?php endif; ?>
345 </button>
346
347 <div
348 x-ref="content"
349 x-show="open"
350 x-cloak
351 x-transition.origin.<?php echo esc_attr( $origin ); ?>
352 @click.outside="handleClickOutside()"
353 class="<?php echo esc_attr( $popover_classes ); ?>"
354 >
355 <div x-data="tutorCalendar({ options: <?php echo esc_attr( $options_json ); ?>, hidePopover: () => hide() })"></div>
356 </div>
357 </div>
358 <?php
359 return ob_get_clean();
360 }
361
362 /**
363 * Check if filter has active selection.
364 *
365 * @return bool
366 */
367 protected function has_selection(): bool {
368 if ( self::TYPE_SINGLE === $this->type ) {
369 return ! empty( Input::get( 'date' ) );
370 }
371 return ! empty( Input::get( 'start_date' ) ) && ! empty( Input::get( 'end_date' ) );
372 }
373
374 /**
375 * Calculate dynamic label based on URL parameters.
376 *
377 * @return string
378 */
379 protected function calculate_label(): string {
380 if ( self::TYPE_SINGLE === $this->type ) {
381 return Input::get( 'date', '' );
382 }
383
384 if ( ! $this->show_label ) {
385 return '';
386 }
387
388 $start_date = Input::get( 'start_date' );
389 $end_date = Input::get( ( 'end_date' ) );
390
391 if ( ! $start_date || ! $end_date ) {
392 return $this->hide_initial_label ? '' : __( 'All Time', 'tutor' );
393 }
394
395 $presets = array(
396 'today' => __( 'Today', 'tutor' ),
397 'yesterday' => __( 'Yesterday', 'tutor' ),
398 'last-7' => __( 'Last 7 Days', 'tutor' ),
399 'last-14' => __( 'Last 14 Days', 'tutor' ),
400 'last-30' => __( 'Last 30 Days', 'tutor' ),
401 'this-month' => __( 'This Month', 'tutor' ),
402 'last-month' => __( 'Last Month', 'tutor' ),
403 'last-year' => __( 'Last Year', 'tutor' ),
404 );
405
406 $current_time = time();
407
408 foreach ( $presets as $key => $label ) {
409 $range = $this->get_preset_dates( $key, $current_time );
410 if ( $range && $range[0] === $start_date && $range[1] === $end_date ) {
411 return $label;
412 }
413 }
414
415 $start_format = date_i18n( 'M j', strtotime( $start_date ) );
416 $end_format = date_i18n( 'M j', strtotime( $end_date ) );
417
418 return sprintf( '%s - %s', $start_format, $end_format );
419 }
420
421 /**
422 * Get preset dates.
423 *
424 * @param string $preset Preset key.
425 * @param int $current_time Current timestamp.
426 *
427 * @return array|null
428 */
429 protected function get_preset_dates( $preset, $current_time ) {
430 $today = gmdate( 'Y-m-d', $current_time );
431
432 switch ( $preset ) {
433 case 'today':
434 return array( $today, $today );
435 case 'yesterday':
436 $d = gmdate( 'Y-m-d', strtotime( '-1 day', $current_time ) );
437 return array( $d, $d );
438 case 'last-7':
439 $start = gmdate( 'Y-m-d', strtotime( '-6 days', $current_time ) );
440 return array( $start, $today );
441 case 'last-14':
442 $start = gmdate( 'Y-m-d', strtotime( '-13 days', $current_time ) );
443 return array( $start, $today );
444 case 'last-30':
445 $start = gmdate( 'Y-m-d', strtotime( '-29 days', $current_time ) );
446 return array( $start, $today );
447 case 'this-month':
448 $start = gmdate( 'Y-m-01', $current_time );
449 $end = gmdate( 'Y-m-t', $current_time );
450 return array( $start, $end );
451 case 'last-month':
452 $first_day_last_month = strtotime( 'first day of previous month', $current_time );
453 $start = gmdate( 'Y-m-d', $first_day_last_month );
454 $end = gmdate( 'Y-m-t', $first_day_last_month );
455 return array( $start, $end );
456 case 'last-year':
457 $start = gmdate( 'Y-01-01', strtotime( '-1 year', $current_time ) );
458 $end = gmdate( 'Y-12-31', strtotime( '-1 year', $current_time ) );
459 return array( $start, $end );
460 }
461 return null;
462 }
463 }
464