'fa-arrow-down', 'active' => 'fa-arrow-down', ); case 'template-2': return array( 'inactive' => 'fa-angle-down', 'active' => 'fa-angle-down', ); default: return array( 'inactive' => 'fa-plus', 'active' => 'fa-minus', ); } } /** * Templates 1 ("Accent Border") and 5 ("Boxed List") show the * published date stacked above the title instead of alongside it; * template-5 was changed to match on request. */ public static function date_before_title( string $layout ): bool { return in_array( $layout, array( 'template-1', 'template-5' ), true ); } public static function icon_class( string $icon ): string { $icon = trim( $icon ); if ( '' === $icon ) { return ''; } return sanitize_html_class( $icon ); } /** * Safely reads a string field out of one `faqs[]` item. Block * attributes are freeform JSON from the REST API, so a field could in * theory arrive as an array/object/number rather than the string * every partial expects -- reading through this instead of * `$item[ $key ]` directly means a malformed value quietly becomes an * empty string instead of a fatal type error or an "Array" being * passed to esc_html()/wp_kses_post(). */ public static function field( array $item, string $key ): string { return isset( $item[ $key ] ) && is_string( $item[ $key ] ) ? $item[ $key ] : ''; } /** * Whether $index is the FAQ item that starts open -- the single rule * item_classes(), header.php's aria-expanded and toggle.php's initial * glyph all need to agree on. */ public static function is_first( int $index ): bool { return 0 === $index; } public static function format_date( string $raw_date ): string { if ( '' === trim( $raw_date ) ) { return ''; } $timestamp = strtotime( $raw_date ); if ( false === $timestamp ) { return $raw_date; } return date_i18n( get_option( 'date_format', 'F j, Y' ), $timestamp ); } /** * Maps the block's color attributes to the CSS custom properties every * _template-*.scss reads: * * - --cfbk-faq-border: the template's resting/default border or * persistent accent fill (e.g. Accent Border's border-left, Rounded * Card's card border, Boxed List's box + toggle border, Pill * Badge's icon badge). Skipped entirely for templates with no such * element. * - --cfbk-faq-active: whatever that template changes to when an item * is hovered or open -- an active border color (Accent Border, * Sidebar Highlight, Rounded Card), an inverted background (Pill * Badge), or the active title/date/toggle color (Boxed List). * * Both are independent of Title/Background/Icon, so setting either * never also changes the title text, background, or icon color. * Leaving everything unset keeps that template's own default look * completely unchanged. Returns an empty string when no colors are * set, so render.php never emits an empty style="" attribute. */ public static function color_style_vars( array $attributes ): string { $map = array( '--cfbk-faq-border' => $attributes['borderColor'] ?? '', '--cfbk-faq-active' => $attributes['activeColor'] ?? '', '--cfbk-faq-title' => $attributes['titleColor'] ?? '', '--cfbk-faq-background' => $attributes['backgroundColor'] ?? '', '--cfbk-faq-icon' => $attributes['iconColor'] ?? '', ); $declarations = array(); foreach ( $map as $property => $value ) { $value = self::sanitize_color_value( (string) $value ); if ( '' !== $value ) { $declarations[] = $property . ':' . $value; } } return implode( ';', $declarations ); } /** * Class list for a single .cfbk-faq-item, including the "open by * default" state (first item) that both the server-rendered markup * and view.js's click handler need to agree on. */ public static function item_classes( int $index ): string { $classes = array( 'cfbk-faq-item' ); if ( self::is_first( $index ) ) { $classes[] = 'cfbk-faq-active'; } return implode( ' ', $classes ); } /** * Class list for the .cfbk-faq-title-wrap bar, shared by header.php so * the modifier logic isn't repeated as inline ternaries there. */ public static function title_wrap_classes( bool $has_icon, bool $date_before_title ): string { $classes = array( 'cfbk-faq-title-wrap' ); if ( $has_icon ) { $classes[] = 'cfbk-faq-has-icon'; } if ( $date_before_title ) { $classes[] = 'cfbk-faq-date-above-title'; } return implode( ' ', $classes ); } }