Files
OTSSigns-Website/theme/inc/theme-settings.php

733 lines
28 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Theme Settings - Custom admin page for configuring colours, fonts,
* spacing, and border-radius design tokens.
*
* Appearance → Theme Design Settings
*
* @package OTS_Theme
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/* ── Register the admin page ───────────────────────────────────── */
add_action( 'admin_menu', function () {
add_theme_page(
__( 'Theme Design Settings', 'ots-theme' ),
__( 'Theme Design', 'ots-theme' ),
'edit_theme_options',
'oribi-theme-settings',
'oribi_render_settings_page'
);
} );
/* ── Enqueue admin-only assets ─────────────────────────────────── */
add_action( 'admin_enqueue_scripts', function ( $hook ) {
if ( 'appearance_page_oribi-theme-settings' !== $hook ) {
return;
}
// WordPress colour picker.
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
// Google Fonts for preview.
wp_enqueue_style(
'oribi-admin-google-fonts',
'https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Roboto:wght@400;700&family=Open+Sans:wght@400;700&family=Poppins:wght@400;600;700&family=Lato:wght@400;700&family=Montserrat:wght@400;600;700&family=Source+Sans+3:wght@400;600;700&family=Nunito:wght@400;600;700&family=Raleway:wght@400;600;700&family=DM+Sans:wght@400;500;700&family=Work+Sans:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap',
[],
null
);
// Inline CSS + JS for the settings page.
wp_add_inline_style( 'wp-color-picker', oribi_admin_inline_css() );
wp_add_inline_script( 'wp-color-picker', oribi_admin_inline_js(), 'after' );
} );
/* ── Handle form submission ────────────────────────────────────── */
add_action( 'admin_init', function () {
if (
! isset( $_POST['oribi_settings_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['oribi_settings_nonce'] ) ), 'oribi_save_settings' )
) {
return;
}
if ( ! current_user_can( 'edit_theme_options' ) ) {
return;
}
$defaults = oribi_get_theme_defaults();
// Determine action - save or reset.
$action = isset( $_POST['oribi_action'] ) ? sanitize_text_field( wp_unslash( $_POST['oribi_action'] ) ) : 'save';
if ( 'reset' === $action ) {
foreach ( $defaults as $key => $value ) {
set_theme_mod( 'oribi_' . $key, $value );
}
} else {
// Sanitize & save each setting.
foreach ( $defaults as $key => $default ) {
$posted = isset( $_POST[ 'oribi_' . $key ] )
? wp_unslash( $_POST[ 'oribi_' . $key ] ) // phpcs:ignore
: $default;
// Determine sanitisation method by key prefix/type.
if ( strpos( $key, 'color_' ) === 0 || strpos( $key, 'dark_' ) === 0 ) {
// Colour values (hex or rgba).
$posted = oribi_sanitize_color( $posted );
} elseif ( strpos( $key, 'font_' ) === 0 ) {
$posted = sanitize_text_field( $posted );
} elseif ( strpos( $key, 'radius_' ) === 0 || strpos( $key, 'container_' ) === 0 || $key === 'wide_size' ) {
$posted = sanitize_text_field( $posted );
} else {
$posted = sanitize_text_field( $posted );
}
set_theme_mod( 'oribi_' . $key, $posted );
}
}
// Regenerate CSS.
$result = oribi_write_generated_css();
if ( is_wp_error( $result ) ) {
add_settings_error( 'oribi_settings', 'css_error', $result->get_error_message(), 'error' );
} else {
$msg = 'reset' === $action
? __( 'Settings reset to defaults. CSS regenerated.', 'ots-theme' )
: __( 'Settings saved. CSS regenerated.', 'ots-theme' );
add_settings_error( 'oribi_settings', 'saved', $msg, 'success' );
}
// Store errors/notices in transient so they survive the redirect.
set_transient( 'oribi_settings_notices', get_settings_errors( 'oribi_settings' ), 30 );
// PRG redirect.
wp_safe_redirect( admin_url( 'themes.php?page=oribi-theme-settings' ) );
exit;
} );
/**
* Sanitize a colour value (hex or rgba).
*
* @param string $value Raw colour value.
* @return string Sanitized colour.
*/
function oribi_sanitize_color( $value ) {
$value = trim( $value );
// Allow rgba(...) values (used for dark mode light tints).
if ( preg_match( '/^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(,\s*[\d.]+\s*)?\)$/', $value ) ) {
return $value;
}
// Standard hex.
return sanitize_hex_color( $value ) ?? '#000000';
}
/* ── Render the settings page ──────────────────────────────────── */
function oribi_render_settings_page() {
// Seed defaults on first visit.
oribi_maybe_seed_defaults();
// Show any saved notices.
$notices = get_transient( 'oribi_settings_notices' );
if ( $notices ) {
delete_transient( 'oribi_settings_notices' );
foreach ( $notices as $notice ) {
printf(
'<div class="notice notice-%s is-dismissible"><p>%s</p></div>',
esc_attr( $notice['type'] ),
esc_html( $notice['message'] )
);
}
}
$s = 'oribi_get_setting';
$fonts = oribi_get_available_fonts();
?>
<div class="wrap oribi-settings-wrap">
<h1><?php esc_html_e( 'Theme Design Settings', 'ots-theme' ); ?></h1>
<p class="description" style="margin-bottom:24px;">
<?php esc_html_e( 'Customise colours, typography, spacing, and border radii. Changes are applied site-wide via a generated CSS file.', 'ots-theme' ); ?>
</p>
<form method="post" id="oribi-settings-form">
<?php wp_nonce_field( 'oribi_save_settings', 'oribi_settings_nonce' ); ?>
<input type="hidden" name="oribi_action" id="oribi-action-field" value="save" />
<!-- Tabs -->
<nav class="oribi-tabs" role="tablist">
<button type="button" class="oribi-tab active" data-tab="colors" role="tab"><?php esc_html_e( 'Colours', 'ots-theme' ); ?></button>
<button type="button" class="oribi-tab" data-tab="dark" role="tab"><?php esc_html_e( 'Dark Mode', 'ots-theme' ); ?></button>
<button type="button" class="oribi-tab" data-tab="typography" role="tab"><?php esc_html_e( 'Typography', 'ots-theme' ); ?></button>
<button type="button" class="oribi-tab" data-tab="spacing" role="tab"><?php esc_html_e( 'Spacing & Layout', 'ots-theme' ); ?></button>
<button type="button" class="oribi-tab" data-tab="radius" role="tab"><?php esc_html_e( 'Border Radius', 'ots-theme' ); ?></button>
</nav>
<div class="oribi-panels-wrap">
<div class="oribi-panels">
<!-- ═══ COLOURS ══════════════════════════════════════ -->
<div class="oribi-panel active" data-panel="colors">
<h2><?php esc_html_e( 'Light Mode Colour Palette', 'ots-theme' ); ?></h2>
<p class="description"><?php esc_html_e( 'These colours apply when dark mode is off.', 'ots-theme' ); ?></p>
<div class="oribi-color-grid">
<?php
$light_colors = [
'color_primary' => __( 'Primary', 'ots-theme' ),
'color_primary_dk' => __( 'Primary Dark', 'ots-theme' ),
'color_primary_lt' => __( 'Primary Light', 'ots-theme' ),
'color_accent' => __( 'Accent', 'ots-theme' ),
'color_accent_dk' => __( 'Accent Dark', 'ots-theme' ),
'color_accent_lt' => __( 'Accent Light', 'ots-theme' ),
'color_dark' => __( 'Dark', 'ots-theme' ),
'color_dark_2' => __( 'Dark 2', 'ots-theme' ),
'color_text' => __( 'Text', 'ots-theme' ),
'color_text_muted' => __( 'Text Muted', 'ots-theme' ),
'color_border' => __( 'Border', 'ots-theme' ),
'color_bg' => __( 'Background', 'ots-theme' ),
'color_bg_alt' => __( 'Background Alt', 'ots-theme' ),
];
foreach ( $light_colors as $key => $label ) :
$val = $s( $key );
?>
<div class="oribi-color-field">
<label for="oribi_<?php echo esc_attr( $key ); ?>">
<?php echo esc_html( $label ); ?>
</label>
<input
type="text"
id="oribi_<?php echo esc_attr( $key ); ?>"
name="oribi_<?php echo esc_attr( $key ); ?>"
value="<?php echo esc_attr( $val ); ?>"
class="oribi-color-picker"
data-default-color="<?php echo esc_attr( oribi_get_theme_defaults()[ $key ] ); ?>"
/>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- ═══ DARK MODE ════════════════════════════════════ -->
<div class="oribi-panel" data-panel="dark">
<h2><?php esc_html_e( 'Dark Mode Colour Palette', 'ots-theme' ); ?></h2>
<p class="description"><?php esc_html_e( 'These colours apply when dark mode is active. Some values support rgba() notation.', 'ots-theme' ); ?></p>
<div class="oribi-color-grid">
<?php
$dark_colors = [
'dark_primary' => __( 'Primary', 'ots-theme' ),
'dark_primary_dk' => __( 'Primary Dark', 'ots-theme' ),
'dark_primary_lt' => __( 'Primary Light', 'ots-theme' ),
'dark_accent' => __( 'Accent', 'ots-theme' ),
'dark_accent_dk' => __( 'Accent Dark', 'ots-theme' ),
'dark_accent_lt' => __( 'Accent Light', 'ots-theme' ),
'dark_dark' => __( 'Dark (Text)', 'ots-theme' ),
'dark_dark_2' => __( 'Dark 2 (Text)', 'ots-theme' ),
'dark_text' => __( 'Body Text', 'ots-theme' ),
'dark_text_muted' => __( 'Text Muted', 'ots-theme' ),
'dark_border' => __( 'Border', 'ots-theme' ),
'dark_bg' => __( 'Background', 'ots-theme' ),
'dark_bg_alt' => __( 'Background Alt', 'ots-theme' ),
'dark_bg_dark' => __( 'Background Darker', 'ots-theme' ),
'dark_heading' => __( 'Heading', 'ots-theme' ),
'dark_card_bg' => __( 'Card Background', 'ots-theme' ),
];
foreach ( $dark_colors as $key => $label ) :
$val = $s( $key );
$is_rgba = ( strpos( $val, 'rgba' ) !== false );
?>
<div class="oribi-color-field">
<label for="oribi_<?php echo esc_attr( $key ); ?>">
<?php echo esc_html( $label ); ?>
</label>
<?php if ( $is_rgba ) : ?>
<input
type="text"
id="oribi_<?php echo esc_attr( $key ); ?>"
name="oribi_<?php echo esc_attr( $key ); ?>"
value="<?php echo esc_attr( $val ); ?>"
class="regular-text oribi-rgba-input"
placeholder="rgba(r,g,b,a)"
/>
<span class="oribi-color-swatch" style="background:<?php echo esc_attr( $val ); ?>;"></span>
<?php else : ?>
<input
type="text"
id="oribi_<?php echo esc_attr( $key ); ?>"
name="oribi_<?php echo esc_attr( $key ); ?>"
value="<?php echo esc_attr( $val ); ?>"
class="oribi-color-picker"
data-default-color="<?php echo esc_attr( oribi_get_theme_defaults()[ $key ] ); ?>"
/>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- ═══ TYPOGRAPHY ═══════════════════════════════════ -->
<div class="oribi-panel" data-panel="typography">
<h2><?php esc_html_e( 'Typography', 'ots-theme' ); ?></h2>
<p class="description">
<?php
printf(
/* translators: %s = link to Font Library */
esc_html__( 'Select from available fonts below. To add more fonts, use the %s in the Site Editor.', 'ots-theme' ),
'<a href="' . esc_url( admin_url( 'site-editor.php' ) ) . '">' . esc_html__( 'Font Library', 'ots-theme' ) . '</a>'
);
?>
</p>
<table class="form-table" role="presentation">
<tr>
<th scope="row">
<label for="oribi_font_family"><?php esc_html_e( 'Body Font', 'ots-theme' ); ?></label>
</th>
<td>
<select id="oribi_font_family" name="oribi_font_family" class="oribi-font-select">
<?php foreach ( $fonts as $f ) : ?>
<option
value="<?php echo esc_attr( $f['slug'] ); ?>"
data-font-family="<?php echo esc_attr( $f['fontFamily'] ); ?>"
<?php selected( $s( 'font_family' ), $f['slug'] ); ?>
>
<?php echo esc_html( $f['name'] ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'Applied to body text, paragraphs, and UI elements.', 'ots-theme' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="oribi_font_heading"><?php esc_html_e( 'Heading Font', 'ots-theme' ); ?></label>
</th>
<td>
<select id="oribi_font_heading" name="oribi_font_heading" class="oribi-font-select">
<option value=""><?php esc_html_e( '- Same as body font -', 'ots-theme' ); ?></option>
<?php foreach ( $fonts as $f ) : ?>
<option
value="<?php echo esc_attr( $f['slug'] ); ?>"
data-font-family="<?php echo esc_attr( $f['fontFamily'] ); ?>"
<?php selected( $s( 'font_heading' ), $f['slug'] ); ?>
>
<?php echo esc_html( $f['name'] ); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description"><?php esc_html_e( 'Applied to h1h6 headings. Leave blank to use the body font.', 'ots-theme' ); ?></p>
</td>
</tr>
</table>
<!-- Font preview -->
<div class="oribi-font-preview" id="oribi-font-preview">
<h3 style="margin:0 0 8px;"><?php esc_html_e( 'Font Preview', 'ots-theme' ); ?></h3>
<div class="oribi-font-preview-heading" id="oribi-preview-heading">
The quick brown fox jumps over the lazy dog
</div>
<div class="oribi-font-preview-body" id="oribi-preview-body">
The quick brown fox jumps over the lazy dog. 0123456789
</div>
</div>
</div>
<!-- ═══ SPACING & LAYOUT ═════════════════════════════ -->
<div class="oribi-panel" data-panel="spacing">
<h2><?php esc_html_e( 'Spacing & Layout', 'ots-theme' ); ?></h2>
<table class="form-table" role="presentation">
<tr>
<th scope="row">
<label for="oribi_container_max"><?php esc_html_e( 'Container Max Width (px)', 'ots-theme' ); ?></label>
</th>
<td>
<input type="number" id="oribi_container_max" name="oribi_container_max"
value="<?php echo esc_attr( $s( 'container_max' ) ); ?>"
min="600" max="2400" step="10" class="small-text" />
<p class="description"><?php esc_html_e( 'Maximum width of the main content area (default: 1200).', 'ots-theme' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<label for="oribi_wide_size"><?php esc_html_e( 'Wide Block Width (px)', 'ots-theme' ); ?></label>
</th>
<td>
<input type="number" id="oribi_wide_size" name="oribi_wide_size"
value="<?php echo esc_attr( $s( 'wide_size' ) ); ?>"
min="800" max="3000" step="10" class="small-text" />
<p class="description"><?php esc_html_e( 'Width of "wide" aligned blocks (default: 1536).', 'ots-theme' ); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php esc_html_e( 'Horizontal Padding', 'ots-theme' ); ?></th>
<td>
<label>
<?php esc_html_e( 'Min:', 'ots-theme' ); ?>
<input type="number" name="oribi_container_pad_min"
value="<?php echo esc_attr( $s( 'container_pad_min' ) ); ?>"
min="0" max="10" step="0.25" class="small-text" /> rem
</label>
&nbsp;&nbsp;
<label>
<?php esc_html_e( 'Max:', 'ots-theme' ); ?>
<input type="number" name="oribi_container_pad_max"
value="<?php echo esc_attr( $s( 'container_pad_max' ) ); ?>"
min="0" max="10" step="0.25" class="small-text" /> rem
</label>
<p class="description"><?php esc_html_e( 'Responsive horizontal padding using clamp(min, 5vw, max). Default: 1rem 2rem.', 'ots-theme' ); ?></p>
</td>
</tr>
</table>
</div>
<!-- ═══ BORDER RADIUS ════════════════════════════════ -->
<div class="oribi-panel" data-panel="radius">
<h2><?php esc_html_e( 'Border Radius', 'ots-theme' ); ?></h2>
<p class="description"><?php esc_html_e( 'Four preset sizes used across all blocks. Values in pixels.', 'ots-theme' ); ?></p>
<table class="form-table" role="presentation">
<?php
$radii = [
'radius_sm' => [ 'SM', __( 'Buttons, badges, small elements', 'ots-theme' ) ],
'radius_md' => [ 'MD', __( 'Cards, inputs, mid-size components', 'ots-theme' ) ],
'radius_lg' => [ 'LG', __( 'Sections, larger surfaces', 'ots-theme' ) ],
'radius_xl' => [ 'XL', __( 'Pills, fully rounded elements', 'ots-theme' ) ],
];
foreach ( $radii as $key => $info ) :
?>
<tr>
<th scope="row">
<label for="oribi_<?php echo esc_attr( $key ); ?>">
<?php echo esc_html( $info[0] ); ?>
</label>
</th>
<td>
<input type="number" id="oribi_<?php echo esc_attr( $key ); ?>"
name="oribi_<?php echo esc_attr( $key ); ?>"
value="<?php echo esc_attr( $s( $key ) ); ?>"
min="0" max="100" step="1" class="small-text" /> px
<span class="oribi-radius-preview" style="border-radius:<?php echo esc_attr( $s( $key ) ); ?>px;"></span>
<p class="description"><?php echo esc_html( $info[1] ); ?></p>
</td>
</tr>
<?php endforeach; ?>
</table>
</div>
</div><!-- .oribi-panels -->
<!-- ═══ LIVE PREVIEW SIDEBAR ═════════════════════════ -->
<aside class="oribi-preview-sidebar" id="oribi-preview-sidebar">
<h3><?php esc_html_e( 'Preview', 'ots-theme' ); ?></h3>
<div class="oribi-preview-card" id="oribi-preview-card">
<div class="oribi-preview-hero" id="preview-hero">
<h2 id="preview-hero-title"><?php esc_html_e( 'Hero Heading', 'ots-theme' ); ?></h2>
<p id="preview-hero-text"><?php esc_html_e( 'Body text preview. The quick brown fox jumps over the lazy dog.', 'ots-theme' ); ?></p>
<button id="preview-btn-primary"><?php esc_html_e( 'Primary Button', 'ots-theme' ); ?></button>
<button id="preview-btn-accent"><?php esc_html_e( 'Accent Button', 'ots-theme' ); ?></button>
</div>
<div class="oribi-preview-section" id="preview-card-section">
<div class="oribi-prev-card" id="preview-card-1">
<h4><?php esc_html_e( 'Card Title', 'ots-theme' ); ?></h4>
<p><?php esc_html_e( 'Card body text with muted small text below.', 'ots-theme' ); ?></p>
<small><?php esc_html_e( 'Muted text', 'ots-theme' ); ?></small>
</div>
</div>
</div>
</aside>
</div><!-- .oribi-panels-wrap -->
<!-- Actions -->
<div class="oribi-actions">
<?php submit_button( __( 'Save Changes', 'ots-theme' ), 'primary', 'submit', false ); ?>
<button type="button" id="oribi-reset-btn" class="button button-secondary">
<?php esc_html_e( 'Reset to Defaults', 'ots-theme' ); ?>
</button>
</div>
</form>
</div>
<?php
}
/* ── Inline CSS for the admin page ─────────────────────────────── */
function oribi_admin_inline_css() {
return <<<'CSS'
/* ── Settings page layout ── */
.oribi-settings-wrap { max-width: 1400px; }
/* ── Tabs ── */
.oribi-tabs {
display: flex; gap: 0; border-bottom: 2px solid #c3c4c7;
margin-bottom: 0; background: #f0f0f1;
}
.oribi-tab {
padding: 12px 20px; border: none; background: transparent;
cursor: pointer; font-size: 14px; font-weight: 500;
border-bottom: 2px solid transparent; margin-bottom: -2px;
color: #50575e; transition: all .15s ease;
}
.oribi-tab:hover { color: #1d2327; background: #fff; }
.oribi-tab.active {
color: #1d2327; background: #fff;
border-bottom-color: #2271b1; font-weight: 600;
}
/* ── Panels + Preview sidebar ── */
.oribi-panels-wrap {
display: grid;
grid-template-columns: 1fr 320px;
gap: 24px;
margin-top: 0;
}
@media (max-width: 1100px) {
.oribi-panels-wrap { grid-template-columns: 1fr; }
.oribi-preview-sidebar { order: -1; }
}
.oribi-panel { display: none; padding: 24px; background: #fff; border: 1px solid #c3c4c7; border-top: none; }
.oribi-panel.active { display: block; }
.oribi-panel h2 { margin-top: 0; }
/* ── Colour grid ── */
.oribi-color-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px; margin-top: 16px;
}
.oribi-color-field { display: flex; flex-direction: column; gap: 4px; }
.oribi-color-field label { font-weight: 500; font-size: 13px; }
.oribi-rgba-input { font-family: monospace; font-size: 13px; }
.oribi-color-swatch {
display: inline-block; width: 28px; height: 28px;
border: 1px solid #ddd; border-radius: 4px; margin-top: 4px;
vertical-align: middle;
}
/* ── Font preview ── */
.oribi-font-preview {
margin-top: 24px; padding: 20px;
background: #f9f9f9; border: 1px solid #ddd; border-radius: 8px;
}
.oribi-font-preview-heading {
font-size: 28px; font-weight: 700; line-height: 1.3; margin-bottom: 8px;
}
.oribi-font-preview-body {
font-size: 16px; line-height: 1.65; color: #555;
}
/* ── Radius preview ── */
.oribi-radius-preview {
display: inline-block; width: 40px; height: 40px;
background: #2271b1; vertical-align: middle; margin-left: 12px;
}
/* ── Preview sidebar ── */
.oribi-preview-sidebar {
position: sticky; top: 32px; align-self: start;
padding: 20px; background: #fff; border: 1px solid #c3c4c7;
border-radius: 8px;
}
.oribi-preview-sidebar h3 { margin-top: 0; }
.oribi-preview-card { border-radius: 8px; overflow: hidden; }
.oribi-preview-hero {
padding: 24px 16px; text-align: center;
transition: all .2s ease;
}
.oribi-preview-hero h2 { margin: 0 0 8px; font-size: 22px; }
.oribi-preview-hero p { font-size: 14px; margin: 0 0 16px; }
.oribi-preview-hero button {
padding: 8px 16px; border: none; color: #fff;
border-radius: 6px; cursor: default; margin: 4px;
font-size: 13px; font-weight: 600;
}
.oribi-preview-section {
padding: 16px; transition: all .2s ease;
}
.oribi-prev-card {
padding: 16px; border: 1px solid #e2e8f0;
transition: all .2s ease;
}
.oribi-prev-card h4 { margin: 0 0 6px; font-size: 15px; }
.oribi-prev-card p { margin: 0 0 4px; font-size: 13px; }
.oribi-prev-card small { font-size: 12px; }
/* ── Actions bar ── */
.oribi-actions {
display: flex; align-items: center; gap: 12px;
margin-top: 24px; padding-top: 16px;
border-top: 1px solid #c3c4c7;
}
.oribi-actions .button-primary { margin: 0 !important; }
CSS;
}
/* ── Inline JS for the admin page ──────────────────────────────── */
function oribi_admin_inline_js() {
return <<<'JS'
document.addEventListener('DOMContentLoaded', function() {
/* ── Tabs ── */
document.querySelectorAll('.oribi-tab').forEach(function(tab) {
tab.addEventListener('click', function() {
document.querySelectorAll('.oribi-tab').forEach(function(t) { t.classList.remove('active'); });
document.querySelectorAll('.oribi-panel').forEach(function(p) { p.classList.remove('active'); });
tab.classList.add('active');
var panel = document.querySelector('[data-panel="' + tab.dataset.tab + '"]');
if (panel) panel.classList.add('active');
});
});
/* ── Colour pickers ── */
if (typeof jQuery !== 'undefined' && jQuery.fn.wpColorPicker) {
jQuery('.oribi-color-picker').wpColorPicker({
change: debounce(updatePreview, 150),
clear: debounce(updatePreview, 150)
});
}
/* ── Font preview ── */
document.querySelectorAll('.oribi-font-select').forEach(function(sel) {
sel.addEventListener('change', updateFontPreview);
});
updateFontPreview();
/* ── Reset button ── */
document.getElementById('oribi-reset-btn').addEventListener('click', function() {
if (confirm('Reset all settings to factory defaults? This cannot be undone.')) {
document.getElementById('oribi-action-field').value = 'reset';
document.getElementById('oribi-settings-form').submit();
}
});
/* ── Live preview updater ── */
function updatePreview() {
var get = function(id) {
var el = document.getElementById(id);
if (!el) return '';
// wpColorPicker stores hex in the text input
return el.value || '';
};
var hero = document.getElementById('preview-hero');
var card = document.getElementById('preview-card-1');
var section = document.getElementById('preview-card-section');
var btnP = document.getElementById('preview-btn-primary');
var btnA = document.getElementById('preview-btn-accent');
var heroTitle = document.getElementById('preview-hero-title');
var heroText = document.getElementById('preview-hero-text');
if (hero) {
hero.style.backgroundColor = get('oribi_color_dark') || '#111111';
if (heroTitle) heroTitle.style.color = '#fff';
if (heroText) heroText.style.color = 'rgba(255,255,255,.8)';
}
if (btnP) {
btnP.style.backgroundColor = get('oribi_color_primary') || '#D83302';
btnP.style.borderRadius = (get('oribi_radius_sm') || '6') + 'px';
}
if (btnA) {
btnA.style.backgroundColor = get('oribi_color_accent') || '#4CAF50';
btnA.style.borderRadius = (get('oribi_radius_sm') || '6') + 'px';
}
if (section) {
section.style.backgroundColor = get('oribi_color_bg_alt') || '#F5F5F5';
}
if (card) {
card.style.backgroundColor = get('oribi_color_bg') || '#fff';
card.style.borderColor = get('oribi_color_border') || '#E0E0E0';
card.style.borderRadius = (get('oribi_radius_md') || '10') + 'px';
var h4 = card.querySelector('h4');
if (h4) h4.style.color = get('oribi_color_dark') || '#111111';
var p = card.querySelector('p');
if (p) p.style.color = get('oribi_color_text') || '#333333';
var sm = card.querySelector('small');
if (sm) sm.style.color = get('oribi_color_text_muted') || '#666666';
}
// Radius previews.
document.querySelectorAll('.oribi-radius-preview').forEach(function(el) {
var inp = el.parentElement.querySelector('input[type="number"]');
if (inp) el.style.borderRadius = inp.value + 'px';
});
}
function updateFontPreview() {
var bodySelect = document.getElementById('oribi_font_family');
var headSelect = document.getElementById('oribi_font_heading');
var previewHead = document.getElementById('oribi-preview-heading');
var previewBody = document.getElementById('oribi-preview-body');
if (bodySelect && previewBody) {
var opt = bodySelect.options[bodySelect.selectedIndex];
previewBody.style.fontFamily = opt.dataset.fontFamily || 'system-ui';
}
if (headSelect && previewHead) {
var hOpt = headSelect.options[headSelect.selectedIndex];
if (hOpt.value) {
previewHead.style.fontFamily = hOpt.dataset.fontFamily || 'system-ui';
} else if (bodySelect) {
var bOpt = bodySelect.options[bodySelect.selectedIndex];
previewHead.style.fontFamily = bOpt.dataset.fontFamily || 'system-ui';
}
}
// Also update preview sidebar heading/body fonts.
var heroTitle = document.getElementById('preview-hero-title');
var heroText = document.getElementById('preview-hero-text');
var cardH4 = document.querySelector('.oribi-prev-card h4');
var cardP = document.querySelector('.oribi-prev-card p');
if (headSelect) {
var hf = headSelect.options[headSelect.selectedIndex];
var headingFont = hf.value
? (hf.dataset.fontFamily || 'system-ui')
: (bodySelect ? bodySelect.options[bodySelect.selectedIndex].dataset.fontFamily : 'system-ui');
if (heroTitle) heroTitle.style.fontFamily = headingFont;
if (cardH4) cardH4.style.fontFamily = headingFont;
}
if (bodySelect) {
var bf = bodySelect.options[bodySelect.selectedIndex];
var bodyFont = bf.dataset.fontFamily || 'system-ui';
if (heroText) heroText.style.fontFamily = bodyFont;
if (cardP) cardP.style.fontFamily = bodyFont;
}
}
function debounce(fn, ms) {
var timer;
return function() {
clearTimeout(timer);
timer = setTimeout(fn, ms);
};
}
// Initial preview render.
updatePreview();
// Attach change listeners to all inputs for live preview.
document.querySelectorAll('input[type="number"]').forEach(function(inp) {
inp.addEventListener('input', debounce(updatePreview, 100));
});
document.querySelectorAll('.oribi-rgba-input').forEach(function(inp) {
inp.addEventListener('input', debounce(updatePreview, 200));
});
});
JS;
}