[], // Widget slugs that are not registered. 'smooth_scroll' => '1', // Lenis smooth scrolling on pages that use a Ston Motion widget. 'smooth_duration' => '1.2', // Seconds. 'scroll_refresh' => '1', // Pro: re-measure scroll animations when the page height changes. ]; } public static function get( $key = null ) { static $settings = null; if ( null === $settings ) { $saved = get_option( self::OPTION, [] ); $settings = wp_parse_args( is_array( $saved ) ? $saved : [], self::defaults() ); $settings['disabled_widgets'] = array_values( array_filter( (array) $settings['disabled_widgets'], 'is_string' ) ); } return null === $key ? $settings : ( isset( $settings[ $key ] ) ? $settings[ $key ] : null ); } public static function is_widget_enabled( $slug ) { return ! in_array( $slug, self::get( 'disabled_widgets' ), true ); } /** * Widget catalogue: slug => [ title, description, icon URL ]. * * @return array */ public static function widgets() { $icons = STON_MOTION_URL . 'assets/icons/'; $widgets = [ 'split-text' => [ __( 'Split Text Reveal', 'ston-motion' ), __( 'Reveal text by character, word or line as it scrolls into view.', 'ston-motion' ), $icons . 'split-text.svg' ], 'marquee' => [ __( 'Marquee', 'ston-motion' ), __( 'Seamless infinite ticker for logos, text or images.', 'ston-motion' ), $icons . 'marquee.svg' ], 'counter' => [ __( 'Scroll Counter', 'ston-motion' ), __( 'Count a number up on view or scrub it with scroll.', 'ston-motion' ), $icons . 'counter.svg' ], 'magnetic-button' => [ __( 'Magnetic Button', 'ston-motion' ), __( 'A button that pulls toward the cursor.', 'ston-motion' ), $icons . 'magnetic-button.svg' ], 'custom-cursor' => [ __( 'Custom Cursor', 'ston-motion' ), __( 'Replace the cursor with a dot or icon, site-wide.', 'ston-motion' ), $icons . 'custom-cursor.svg' ], 'pulse-badge' => [ __( 'Pulse Badge', 'ston-motion' ), __( 'A label with a pulsing dot: eyebrows, status and live tags.', 'ston-motion' ), $icons . 'pulse-badge.svg' ], 'text-rotator' => [ __( 'Text Rotator', 'ston-motion' ), __( 'A headline whose last words cycle through a list.', 'ston-motion' ), $icons . 'text-rotator.svg' ], 'tag-pills' => [ __( 'Tag Pills', 'ston-motion' ), __( 'A row of outlined tags with hover and staggered entrance.', 'ston-motion' ), $icons . 'tag-pills.svg' ], 'connection-flow' => [ __( 'Connection Flow', 'ston-motion' ), __( 'Two nodes joined by a line with a travelling dot.', 'ston-motion' ), $icons . 'connection-flow.svg' ], 'status-list' => [ __( 'Status List', 'ston-motion' ), __( 'Cards with an icon, a title and a status badge.', 'ston-motion' ), $icons . 'status-list.svg' ], 'ripple-button' => [ __( 'Ripple Button', 'ston-motion' ), __( 'A round icon button with spreading rings.', 'ston-motion' ), $icons . 'ripple-button.svg' ], 'lock-preview' => [ __( 'Page Mockup', 'ston-motion' ), __( 'A browser window with page blocks, tags and a cursor that moves, edits or clicks them.', 'ston-motion' ), $icons . 'lock-preview.svg' ], ]; /** * Filters the widget list on Ston Motion → Widgets. * * @param array $widgets slug => [ title, description, icon URL ]. */ return (array) apply_filters( 'ston_motion/admin_widgets', $widgets ); } /** Whether an add-on extends Ston Motion (it then shows its own panels instead of the Pro info). */ private static function has_addon() { return (bool) has_action( 'ston_motion/admin_help' ); } /* ------------------------------------------------------------------ * Menu * ---------------------------------------------------------------- */ /** Menu icon: the Ston Motion mark, recoloured by WordPress to match the admin colour scheme. */ private static function menu_icon() { $svg = ''; return 'data:image/svg+xml;base64,' . base64_encode( $svg ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode } public static function menu() { self::$hook = add_menu_page( __( 'Ston Motion', 'ston-motion' ), __( 'Ston Motion', 'ston-motion' ), self::CAP, self::SLUG, [ __CLASS__, 'render' ], self::menu_icon(), 58.7 ); add_submenu_page( self::SLUG, __( 'Widgets', 'ston-motion' ), __( 'Widgets', 'ston-motion' ), self::CAP, self::SLUG, [ __CLASS__, 'render' ] ); add_submenu_page( self::SLUG, __( 'Settings', 'ston-motion' ), __( 'Settings', 'ston-motion' ), self::CAP, self::SLUG . '&tab=settings', '__return_null' ); add_submenu_page( self::SLUG, __( 'Help', 'ston-motion' ), __( 'Help', 'ston-motion' ), self::CAP, self::SLUG . '&tab=help', '__return_null' ); } public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'ston-motion' ) . '' ); return $links; } public static function assets( $hook ) { if ( $hook !== self::$hook ) return; wp_enqueue_style( 'ston-motion-admin', STON_MOTION_URL . 'assets/css/admin.css', [], STON_MOTION_VERSION ); } /* ------------------------------------------------------------------ * Save * ---------------------------------------------------------------- */ public static function save() { if ( ! current_user_can( self::CAP ) ) { wp_die( esc_html__( 'You are not allowed to change these settings.', 'ston-motion' ), 403 ); } check_admin_referer( 'ston_motion_save' ); $tab = isset( $_POST['tab'] ) ? sanitize_key( wp_unslash( $_POST['tab'] ) ) : 'widgets'; $settings = self::get(); if ( 'widgets' === $tab ) { $enabled = isset( $_POST['enabled'] ) ? array_map( 'sanitize_key', (array) wp_unslash( $_POST['enabled'] ) ) : []; $listed = array_keys( self::widgets() ); // Widgets not listed right now (e.g. an add-on is off) keep their saved state. $kept = array_diff( $settings['disabled_widgets'], $listed ); $settings['disabled_widgets'] = array_values( array_merge( $kept, array_diff( $listed, $enabled ) ) ); } elseif ( 'settings' === $tab ) { $settings['smooth_scroll'] = empty( $_POST['smooth_scroll'] ) ? '0' : '1'; $duration = isset( $_POST['smooth_duration'] ) ? (float) sanitize_text_field( wp_unslash( $_POST['smooth_duration'] ) ) : 1.2; $settings['smooth_duration'] = (string) max( 0.3, min( 3, $duration ) ); /** * Filters the Settings tab values before they are saved (the nonce is already checked). * Add-ons use it to save the rows they add with `ston_motion/settings_rows`. * * @param array $settings All Ston Motion settings. */ $settings = (array) apply_filters( 'ston_motion/save_settings', $settings ); } update_option( self::OPTION, $settings ); // Elementor caches widget lists and generated CSS. if ( class_exists( '\Elementor\Plugin' ) && isset( \Elementor\Plugin::$instance->files_manager ) ) { \Elementor\Plugin::$instance->files_manager->clear_cache(); } wp_safe_redirect( add_query_arg( [ 'page' => self::SLUG, 'tab' => $tab, 'updated' => '1' ], admin_url( 'admin.php' ) ) ); exit; } /* ------------------------------------------------------------------ * Page * ---------------------------------------------------------------- */ public static function render() { $tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'widgets'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $tabs = [ 'widgets' => __( 'Widgets', 'ston-motion' ), 'settings' => __( 'Settings', 'ston-motion' ), 'help' => __( 'Help', 'ston-motion' ), ]; $tab = isset( $tabs[ $tab ] ) ? $tab : 'widgets'; ?>