Constants
17 hours ago
Accordion.php
17 hours ago
Alert.php
17 hours ago
AttachmentCard.php
17 hours ago
Avatar.php
17 hours ago
Badge.php
17 hours ago
BaseComponent.php
17 hours ago
Button.php
17 hours ago
ConfirmationModal.php
17 hours ago
CourseFilter.php
17 hours ago
DateFilter.php
17 hours ago
DropdownFilter.php
17 hours ago
EmptyState.php
17 hours ago
FileUploader.php
17 hours ago
InputField.php
17 hours ago
Modal.php
17 hours ago
Nav.php
17 hours ago
Pagination.php
17 hours ago
Popover.php
17 hours ago
PreviewTrigger.php
17 hours ago
Progress.php
17 hours ago
SearchFilter.php
17 hours ago
Sorting.php
17 hours ago
StarRating.php
17 hours ago
StarRatingInput.php
17 hours ago
StatusSelect.php
17 hours ago
SvgIcon.php
17 hours ago
Table.php
17 hours ago
Tabs.php
17 hours ago
Tooltip.php
17 hours ago
WPEditor.php
17 hours ago
Accordion.php
267 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Tutor Component: Accordion |
| 4 | * |
| 5 | * Provides a fluent builder for rendering accordion items with |
| 6 | * Alpine.js integration for expand/collapse functionality. |
| 7 | * |
| 8 | * @package Tutor\Components |
| 9 | * @author Themeum |
| 10 | * @link https://themeum.com |
| 11 | * @since 4.0.0 |
| 12 | */ |
| 13 | |
| 14 | namespace Tutor\Components; |
| 15 | |
| 16 | use TUTOR\Icon; |
| 17 | |
| 18 | defined( 'ABSPATH' ) || exit; |
| 19 | |
| 20 | /** |
| 21 | * Accordion Component Class. |
| 22 | * |
| 23 | * Example usage: |
| 24 | * ``` |
| 25 | * // Single item accordion |
| 26 | * Accordion::make() |
| 27 | * ->add_item( 'About Course', '<p>Description...</p>' ) |
| 28 | * ->render(); |
| 29 | * |
| 30 | * // Multiple items |
| 31 | * Accordion::make() |
| 32 | * ->add_item( 'About Course', '<p>Description...</p>' ) |
| 33 | * ->add_item( 'Requirements', '<p>Prerequisites...</p>' ) |
| 34 | * ->add_item( 'Instructor', '<p>Meet your instructor...</p>' ) |
| 35 | * ->default_open( array( 0 ) ) |
| 36 | * ->render(); |
| 37 | * |
| 38 | * // With custom icon and template |
| 39 | * Accordion::make() |
| 40 | * ->add_item( 'Details', '', 'path/to/template.php', 'custom-icon' ) |
| 41 | * ->allow_multiple( false ) |
| 42 | * ->render(); |
| 43 | * ``` |
| 44 | * |
| 45 | * @since 4.0.0 |
| 46 | */ |
| 47 | class Accordion extends BaseComponent { |
| 48 | |
| 49 | /** |
| 50 | * Accordion items array. |
| 51 | * |
| 52 | * @since 4.0.0 |
| 53 | * |
| 54 | * @var array |
| 55 | */ |
| 56 | protected $items = array(); |
| 57 | |
| 58 | /** |
| 59 | * Whether multiple items can be open. |
| 60 | * |
| 61 | * @since 4.0.0 |
| 62 | * |
| 63 | * @var bool |
| 64 | */ |
| 65 | protected $multiple = true; |
| 66 | |
| 67 | /** |
| 68 | * Default open item indices. |
| 69 | * |
| 70 | * @since 4.0.0 |
| 71 | * |
| 72 | * @var array |
| 73 | */ |
| 74 | protected $default_open = array(); |
| 75 | |
| 76 | /** |
| 77 | * Add accordion item. |
| 78 | * |
| 79 | * @since 4.0.0 |
| 80 | * |
| 81 | * @param string $title Item title. |
| 82 | * @param string $content Item content HTML. |
| 83 | * @param string $template Optional. Template path instead of content. |
| 84 | * @param string $icon Optional. Custom icon name. |
| 85 | * |
| 86 | * @return $this |
| 87 | */ |
| 88 | public function add_item( $title, $content = '', $template = '', $icon = '' ) { |
| 89 | $this->items[] = array( |
| 90 | 'title' => $title, |
| 91 | 'content' => $content, |
| 92 | 'template' => $template, |
| 93 | 'icon' => $icon, |
| 94 | ); |
| 95 | return $this; |
| 96 | } |
| 97 | |
| 98 | /** |
| 99 | * Set whether multiple items can be open. |
| 100 | * |
| 101 | * @since 4.0.0 |
| 102 | * |
| 103 | * @param bool $multiple Whether to allow multiple open items. |
| 104 | * |
| 105 | * @return $this |
| 106 | */ |
| 107 | public function allow_multiple( $multiple = true ) { |
| 108 | $this->multiple = (bool) $multiple; |
| 109 | return $this; |
| 110 | } |
| 111 | |
| 112 | /** |
| 113 | * Set default open item indices. |
| 114 | * |
| 115 | * @since 4.0.0 |
| 116 | * |
| 117 | * @param array $indices Array of item indices to open by default. |
| 118 | * |
| 119 | * @return $this |
| 120 | */ |
| 121 | public function default_open( $indices ) { |
| 122 | $this->default_open = is_array( $indices ) ? $indices : array( $indices ); |
| 123 | return $this; |
| 124 | } |
| 125 | |
| 126 | /** |
| 127 | * Render accordion item content. |
| 128 | * |
| 129 | * @since 4.0.0 |
| 130 | * |
| 131 | * @param array $item Item data. |
| 132 | * |
| 133 | * @return string Content HTML. |
| 134 | */ |
| 135 | protected function render_item_content( $item ) { |
| 136 | // If template is provided, use it. |
| 137 | if ( ! empty( $item['template'] ) && file_exists( $item['template'] ) ) { |
| 138 | ob_start(); |
| 139 | include $item['template']; |
| 140 | return ob_get_clean(); |
| 141 | } |
| 142 | |
| 143 | // Otherwise return content. |
| 144 | return ! empty( $item['content'] ) ? $item['content'] : ''; |
| 145 | } |
| 146 | |
| 147 | /** |
| 148 | * Render default chevron icon. |
| 149 | * |
| 150 | * @since 4.0.0 |
| 151 | * |
| 152 | * @return string Icon SVG. |
| 153 | */ |
| 154 | protected function render_default_icon() { |
| 155 | return SvgIcon::make()->name( Icon::CHEVRON_DOWN )->size( 24 )->get(); |
| 156 | } |
| 157 | |
| 158 | /** |
| 159 | * Render custom icon. |
| 160 | * |
| 161 | * @since 4.0.0 |
| 162 | * |
| 163 | * @param string $icon Icon name. |
| 164 | * |
| 165 | * @return string Icon SVG. |
| 166 | */ |
| 167 | protected function render_icon( $icon ) { |
| 168 | if ( empty( $icon ) ) { |
| 169 | return $this->render_default_icon(); |
| 170 | } |
| 171 | |
| 172 | return SvgIcon::make()->name( $icon )->size( 24 )->get(); |
| 173 | } |
| 174 | |
| 175 | /** |
| 176 | * Get the accordion HTML. |
| 177 | * |
| 178 | * @since 4.0.0 |
| 179 | * |
| 180 | * @return string HTML output. |
| 181 | */ |
| 182 | public function get(): string { |
| 183 | if ( empty( $this->items ) ) { |
| 184 | return ''; |
| 185 | } |
| 186 | |
| 187 | // Build Alpine.js config. |
| 188 | $alpine_config = array( |
| 189 | 'multiple' => $this->multiple, |
| 190 | 'defaultOpen' => $this->default_open, |
| 191 | ); |
| 192 | $alpine_json = wp_json_encode( $alpine_config ); |
| 193 | |
| 194 | // Merge custom classes. |
| 195 | $wrapper_classes = 'tutor-accordion'; |
| 196 | if ( ! empty( $this->attributes['class'] ) ) { |
| 197 | $wrapper_classes .= ' ' . $this->attributes['class']; |
| 198 | unset( $this->attributes['class'] ); |
| 199 | } |
| 200 | |
| 201 | $this->attributes['x-data'] = sprintf( 'tutorAccordion(%s)', $alpine_json ); |
| 202 | $this->attributes['class'] = $wrapper_classes; |
| 203 | |
| 204 | $wrapper_attrs = $this->get_attributes_string(); |
| 205 | |
| 206 | // Build items HTML. |
| 207 | $items_html = ''; |
| 208 | foreach ( $this->items as $index => $item ) { |
| 209 | $title = isset( $item['title'] ) ? $item['title'] : ''; |
| 210 | $icon = isset( $item['icon'] ) ? $item['icon'] : ''; |
| 211 | $panel_id = 'tutor-acc-panel-' . $index; |
| 212 | $trigger_id = 'tutor-acc-trigger-' . $index; |
| 213 | |
| 214 | $icon_html = $this->render_icon( $icon ); |
| 215 | $content = $this->render_item_content( $item ); |
| 216 | |
| 217 | $items_html .= sprintf( |
| 218 | '<div class="tutor-accordion-item"> |
| 219 | <button |
| 220 | @click="toggle(%d)" |
| 221 | @keydown="handleKeydown($event, %d)" |
| 222 | :aria-expanded="isOpen(%d)" |
| 223 | class="tutor-accordion-header tutor-accordion-trigger" |
| 224 | aria-controls="%s" |
| 225 | id="%s" |
| 226 | > |
| 227 | <span class="tutor-accordion-title">%s</span> |
| 228 | <span class="tutor-accordion-icon" aria-hidden="true"> |
| 229 | %s |
| 230 | </span> |
| 231 | </button> |
| 232 | <div |
| 233 | id="%s" |
| 234 | role="region" |
| 235 | aria-labelledby="%s" |
| 236 | class="tutor-accordion-content" |
| 237 | x-show="isOpen(%d)" |
| 238 | x-collapse.duration.350ms |
| 239 | > |
| 240 | <div class="tutor-accordion-body"> |
| 241 | %s |
| 242 | </div> |
| 243 | </div> |
| 244 | </div>', |
| 245 | $index, |
| 246 | $index, |
| 247 | $index, |
| 248 | esc_attr( $panel_id ), |
| 249 | esc_attr( $trigger_id ), |
| 250 | esc_html( $title ), |
| 251 | $icon_html, |
| 252 | esc_attr( $panel_id ), |
| 253 | esc_attr( $trigger_id ), |
| 254 | $index, |
| 255 | wp_kses_post( $content ) |
| 256 | ); |
| 257 | } |
| 258 | |
| 259 | return sprintf( |
| 260 | '<div %s>%s</div>', |
| 261 | $wrapper_attrs, |
| 262 | $items_html |
| 263 | ); |
| 264 | } |
| 265 | |
| 266 | } |
| 267 |