' . '' . '' . '', esc_attr( $stroke ), esc_attr( $fill ) ); } // ── Public API ───────────────────────────────────────────────────────────── /** * Render a view count component. * * @param int $style 1–4 * @param int $views Raw view count * @param array $options { * variant?: string 'outline' | 'filled' | 'accent' (Style 1 only) * accent?: string CSS colour for accent variant, e.g. '#7c6fef' * } */ public static function render( int $style, int $views, array $options = [] ): string { self::maybe_enqueue_css(); $count = self::format_count( $views ); /** * Filter: modify the formatted view count string before output. * * @param string $count e.g. "24.8K" * @param int $views Raw integer * @param int $style Style number 1–4 * @param array $options Render options * @since 1.0.0 */ $count = (string) apply_filters( 'pulseviews/format_count', $count, $views, $style, $options ); $variant = sanitize_key( $options['variant'] ?? 'outline' ); // Label: shortcode attr > global setting > default 'views' $settings = get_option( 'pulseviews_settings', [] ); $label = sanitize_text_field( $options['label'] ?? $settings['views_label'] ?? 'views' ); // Trim and fallback $label = '' !== trim( $label ) ? trim( $label ) : 'views'; return match ( $style ) { 2 => self::style_block( $count, $variant, $label ), 3 => self::style_inline( $count, $label ), 4 => self::style_compact( $count, $label ), default => self::style_pill( $count, $variant, $label ), }; } // ── Style renderers ──────────────────────────────────────────────────────── /** * Style 1 — Pill. * Variant: 'outline' | 'filled' | 'accent' */ private static function style_pill( string $count, string $variant, string $label ): string { $class = 'pv-pill'; $icon_color = '#111'; $icon_fill = '#111'; if ( 'filled' === $variant ) { $class .= ' pv-pill-filled'; $icon_color = '#fff'; $icon_fill = '#fff'; } elseif ( 'accent' === $variant ) { $class .= ' pv-pill-accent'; $icon_color = '#fff'; $icon_fill = '#fff'; } $aria = $count . ' ' . $label; return sprintf( '' . '' . '%s' . '%s' . '%s' . '' . '', esc_attr( $class ), esc_attr( $aria ), self::icon_eye( $icon_color, $icon_fill ), esc_html( $count ), esc_html( $label ) ); } /** * Style 2 — Bold Block. * Standalone high-contrast block with large number and label. * Variant: 'filled' (black/white) | 'accent' */ private static function style_block( string $count, string $variant, string $label ): string { $class = 'pv-block'; $icon_color = '#fff'; $icon_fill = '#fff'; if ( 'accent' === $variant ) { $class .= ' pv-block-accent'; } $aria = $count . ' ' . $label; return sprintf( '' . '' . '%s%s' . '%s' . '' . '', esc_attr( $class ), esc_attr( $aria ), self::icon_eye( $icon_color, $icon_fill ), esc_html( $count ), esc_html( ucfirst( $label ) ) ); } /** * Style 3 — Inline. * Pure text. Zero decoration. Inherits theme font and colour entirely. */ private static function style_inline( string $count, string $label ): string { $aria = $count . ' ' . $label; return sprintf( '' . '' . '%s' . '%s' . '%s' . '' . '', esc_attr( $aria ), self::icon_eye( 'currentColor', 'currentColor' ), esc_html( $count ), esc_html( $label ) ); } /** * Style 4 — Compact Badge. * Icon + number only. Minimal border. Mobile-first. */ private static function style_compact( string $count, string $label ): string { $aria = $count . ' ' . $label; return sprintf( '' . '' . '%s' . '' . '' . '', esc_attr( $aria ), self::icon_eye( 'currentColor', 'currentColor' ), esc_html( $count ) ); } // ── Helpers ──────────────────────────────────────────────────────────────── /** * Format a raw count to a compact string. * 999 → "999" * 1,234 → "1.2K" * 10,500 → "10K" * 1,234,567→ "1.2M" */ public static function format_count( int $views ): string { if ( $views >= 1_000_000 ) { return number_format_i18n( $views / 1_000_000, 1 ) . 'M'; } if ( $views >= 10_000 ) { return number_format_i18n( $views / 1_000, 0 ) . 'K'; } if ( $views >= 1_000 ) { return number_format_i18n( $views / 1_000, 1 ) . 'K'; } return number_format_i18n( $views ); } private static function maybe_enqueue_css(): void { if ( self::$css_enqueued || is_admin() ) { return; } self::$css_enqueued = true; wp_enqueue_style( 'pulseviews', PULSEVIEWS_URL . 'assets/frontend/styles.css', [], PULSEVIEWS_VERSION ); } }