1PROMTAI.RU » Как неудобство стандартных плагинов привело меня к созданию идеального конструктора таблиц для WordPress
1PROMTAI.RU

Как неудобство стандартных плагинов привело меня к созданию идеального конструктора таблиц для WordPress

Знакомо чувство, когда перепробовал кучу плагинов для создания таблиц в WordPress, а всё не то? То функционала не хватает, то интерфейс запутанный, то таблицы криво смотрятся на мобильных. Я через это прошёл.

Мне надоело постоянно искать компромиссы между красотой и функциональностью. Хотелось просто брать и создавать красивые, адаптивные таблицы с цветовым оформлением, без танцев с бубнами вокруг коротких кодов и настроек.

Решил — хватит это терпеть! Буду делать свой плагин, который будет делать именно то, что мне нужно.

Но я же не профессиональный разработчик. Знаю WordPress на уровне пользователя, с кодом на «вы». Поэтому обратился за помощью к DeepSeek — как к техническому напарнику.

И знаете, что удивительно? Процесс создания оказался не таким страшным, как я думал. Я описывал, что хочу видеть в интерфейсе, как должна работать цветовая палитра, какая нужна адаптивность для мобильных устройств. DeepSeek помогал с технической реализацией, предлагал решения, исправлял ошибки.

Мы работали в тандеме: я — как «продукт-менеджер» с пониманием потребностей, DeepSeek — как разработчик, который превращал мои идеи в рабочий код.

Результат превзошёл все мои ожидания! Получился плагин, в котором:

— Интуитивно понятный конструктор — никаких сложных настроек
— Красивые цветовые схемы на выбор
— Полная адаптивность — таблицы идеально смотрятся на любых устройствах
— Управление колонками и строками в пару кликов
— Встроенный предпросмотр перед публикацией

Самое приятное — когда проверил скорость сайта с плагином, оказалось что он не только удобный, но и не тормозит сайт! PageSpeed показывает 90+ баллов даже с тяжёлыми изображениями и рекламой.

Теперь я создаю таблицы за минуты, а не часы. И главное — получаю именно тот результат, который хотел.

Если вы тоже устали бороться с неудобными плагинами — возможно, стоит попробовать создать свой? С современными AI-инструментами это гораздо проще, чем кажется!

Это сам плагин:

PHP
<?php
/**
 * Plugin Name: Colorful Tables Pro
 * Description: Продвинутый конструктор таблиц с управлением колонками и строками
 * Version: 1.0.5
 * Author: Your Name
 * Text Domain: colorful-tables-pro
 */

if (!defined('ABSPATH')) {
    exit;
}

class ColorfulTablesPro {
    
    private $editing_table_id = null;
    private $storage_key = 'colorful_tables_pro_data';
    private $max_columns = 10;
    private $max_rows = 30;
    
    public function __construct() {
        add_action('init', array($this, 'init'));
        add_action('admin_menu', array($this, 'admin_menu'));
        add_shortcode('colorful_table', array($this, 'shortcode'));
        add_action('admin_init', array($this, 'check_compatibility'));
        
        register_activation_hook(__FILE__, array($this, 'activate'));
        register_deactivation_hook(__FILE__, array($this, 'deactivate'));
    }
    
    public function activate() {
        if (!current_user_can('activate_plugins')) {
            return;
        }
        
        // Initialize default options
        if (empty(get_option($this->storage_key))) {
            update_option($this->storage_key, array());
        }
    }
    
    public function deactivate() {
        // Clean up if needed
    }
    
    public function check_compatibility() {
        if (version_compare(get_bloginfo('version'), '5.0', '<')) {
            deactivate_plugins(plugin_basename(__FILE__));
            wp_die(__('Плагин требует WordPress версии 5.0 или выше', 'colorful-tables-pro'));
        }
    }
    
    public function init() {
        add_action('wp_enqueue_scripts', array($this, 'frontend_assets'));
        add_action('admin_enqueue_scripts', array($this, 'admin_assets'));
        load_plugin_textdomain('colorful-tables-pro', false, dirname(plugin_basename(__FILE__)) . '/languages');
    }
    
    public function admin_menu() {
        add_menu_page(
            __('Colorful Tables Pro', 'colorful-tables-pro'),
            __('Colorful Tables Pro', 'colorful-tables-pro'),
            'manage_options',
            'colorful-tables-pro',
            array($this, 'admin_page'),
            'dashicons-art',
            100
        );
    }
    
    public function frontend_assets() {
        wp_add_inline_style('wp-block-library', $this->get_frontend_css());
    }
    
    public function admin_assets($hook) {
        if ($hook !== 'toplevel_page_colorful-tables-pro') {
            return;
        }
        
        wp_enqueue_script('jquery');
        add_action('admin_head', array($this, 'output_admin_css'));
    }
    
    public function output_admin_css() {
        echo '<style>' . $this->get_admin_css() . '</style>';
    }
    
    public function admin_page() {
        if (!current_user_can('manage_options')) {
            wp_die(__('Недостаточно прав для доступа к этой странице.', 'colorful-tables-pro'));
        }
        
        // Process forms
        if (isset($_POST['save_table']) && check_admin_referer('colorful_tables_nonce')) {
            $this->save_table();
            echo '<div class="notice notice-success is-dismissible"><p>✅ ' . __('Таблица сохранена успешно!', 'colorful-tables-pro') . '</p></div>';
        }
        
        if (isset($_POST['delete_table']) && check_admin_referer('colorful_tables_nonce')) {
            $this->delete_table();
            echo '<div class="notice notice-success is-dismissible"><p>️ ' . __('Таблица удалена!', 'colorful-tables-pro') . '</p></div>';
        }
        
        $tables = get_option($this->storage_key, array());
        $edit_table_data = null;
        
        if (isset($_GET['edit_table'])) {
            $this->editing_table_id = intval($_GET['edit_table']);
            if (isset($tables[$this->editing_table_id])) {
                $edit_table_data = $tables[$this->editing_table_id];
            }
        }
        
        $edit_data_js = $edit_table_data ? $this->safe_json_encode($edit_table_data) : 'null';
        ?>
        
        <div class="wrap ct-wrapper <?php echo $this->editing_table_id ? 'ct-editing' : ''; ?>">
            <!-- Header -->
            <div class="ct-header">
                <div class="ct-header-content">
                    <h1>
                        <span class="ct-logo"></span>
                        <?php _e('Colorful Tables Pro', 'colorful-tables-pro'); ?>
                        <?php if ($this->editing_table_id): ?>
                            <span class="ct-badge"><?php _e('Режим редактирования', 'colorful-tables-pro'); ?></span>
                        <?php endif; ?>
                    </h1>
                    <p class="ct-subtitle"><?php _e('Продвинутый конструктор таблиц с полным контролем над колонками и строками', 'colorful-tables-pro'); ?></p>
                </div>
            </div>

            <div class="ct-container">
                <!-- Main Constructor -->
                <div class="ct-main-panel">
                    <div class="ct-panel ct-builder-panel">
                        <div class="ct-panel-header">
                            <h2>
                                <?php if ($this->editing_table_id): ?>
                                    <span class="ct-icon">✏️</span> <?php _e('Редактирование таблицы', 'colorful-tables-pro'); ?>
                                <?php else: ?>
                                    <span class="ct-icon"></span> <?php _e('Создать новую таблицу', 'colorful-tables-pro'); ?>
                                <?php endif; ?>
                            </h2>
                        </div>

                        <form method="post" id="ct-table-form">
                            <?php wp_nonce_field('colorful_tables_nonce'); ?>
                            
                            <?php if ($this->editing_table_id): ?>
                                <input type="hidden" name="edit_table_id" value="<?php echo esc_attr($this->editing_table_id); ?>">
                            <?php endif; ?>

                            <!-- Basic Settings -->
                            <div class="ct-section">
                                <div class="ct-section-header">
                                    <h3><span class="ct-icon">⚙️</span> <?php _e('Основные настройки', 'colorful-tables-pro'); ?></h3>
                                </div>
                                <div class="ct-section-content">
                                    <div class="ct-form-grid-2">
                                        <div class="ct-form-group">
                                            <label class="ct-label">
                                                <span class="ct-icon"></span> <?php _e('Название таблицы', 'colorful-tables-pro'); ?>
                                            </label>
                                            <input type="text" name="table_name" 
                                                   value="<?php echo $edit_table_data ? esc_attr($edit_table_data['name']) : __('Моя продвинутая таблица', 'colorful-tables-pro'); ?>" 
                                                   class="ct-input" required>
                                        </div>
                                        
                                        <div class="ct-form-group">
                                            <label class="ct-label">
                                                <span class="ct-icon"></span> <?php _e('Стиль оформления', 'colorful-tables-pro'); ?>
                                            </label>
                                            <select name="style" class="ct-select" id="ct-style-select">
                                                <option value="gradient" <?php selected('gradient', $edit_table_data ? $edit_table_data['style'] : 'gradient'); ?>><?php _e('Градиентный', 'colorful-tables-pro'); ?></option>
                                                <option value="modern" <?php selected('modern', $edit_table_data ? $edit_table_data['style'] : ''); ?>><?php _e('Современный', 'colorful-tables-pro'); ?></option>
                                                <option value="elegant" <?php selected('elegant', $edit_table_data ? $edit_table_data['style'] : ''); ?>><?php _e('Элегантный', 'colorful-tables-pro'); ?></option>
                                                <option value="vibrant" <?php selected('vibrant', $edit_table_data ? $edit_table_data['style'] : ''); ?>><?php _e('Яркий', 'colorful-tables-pro'); ?></option>
                                                <option value="minimal" <?php selected('minimal', $edit_table_data ? $edit_table_data['style'] : ''); ?>><?php _e('Минимализм', 'colorful-tables-pro'); ?></option>
                                            </select>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <!-- Table Structure -->
                            <div class="ct-section">
                                <div class="ct-section-header">
                                    <h3><span class="ct-icon">️</span> <?php _e('Структура таблицы', 'colorful-tables-pro'); ?></h3>
                                </div>
                                <div class="ct-section-content">
                                    <div class="ct-structure-grid">
                                        <div class="ct-structure-item">
                                            <h4><span class="ct-icon"></span> <?php _e('Управление колонками', 'colorful-tables-pro'); ?></h4>
                                            <div class="ct-control-group">
                                                <div class="ct-number-control">
                                                    <label><?php _e('Количество колонок:', 'colorful-tables-pro'); ?></label>
                                                    <div class="ct-number-buttons">
                                                        <button type="button" class="ct-number-btn" data-action="decrease" data-target="columns">−</button>
                                                        <input type="number" name="columns" id="ct-columns" 
                                                               value="<?php echo $edit_table_data ? $edit_table_data['columns'] : 3; ?>" 
                                                               min="1" max="<?php echo $this->max_columns; ?>" class="ct-number-input" readonly>
                                                        <button type="button" class="ct-number-btn" data-action="increase" data-target="columns">+</button>
                                                    </div>
                                                </div>
                                                <div class="ct-action-buttons-grid">
                                                    <button type="button" class="button ct-button-small" id="ct-add-column">
                                                        <span class="ct-icon">➕</span> <?php _e('Добавить колонку', 'colorful-tables-pro'); ?>
                                                    </button>
                                                    <button type="button" class="button ct-button-small ct-button-danger" id="ct-remove-column">
                                                        <span class="ct-icon">➖</span> <?php _e('Удалить колонку', 'colorful-tables-pro'); ?>
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                        
                                        <div class="ct-structure-item">
                                            <h4><span class="ct-icon"></span> <?php _e('Управление строками', 'colorful-tables-pro'); ?></h4>
                                            <div class="ct-control-group">
                                                <div class="ct-number-control">
                                                    <label><?php _e('Количество строк:', 'colorful-tables-pro'); ?></label>
                                                    <div class="ct-number-buttons">
                                                        <button type="button" class="ct-number-btn" data-action="decrease" data-target="rows">−</button>
                                                        <input type="number" name="rows" id="ct-rows" 
                                                               value="<?php echo $edit_table_data ? count($edit_table_data['rows']) : 5; ?>" 
                                                               min="1" max="<?php echo $this->max_rows; ?>" class="ct-number-input" readonly>
                                                        <button type="button" class="ct-number-btn" data-action="increase" data-target="rows">+</button>
                                                    </div>
                                                </div>
                                                <div class="ct-action-buttons-grid">
                                                    <button type="button" class="button ct-button-small" id="ct-add-row">
                                                        <span class="ct-icon">➕</span> <?php _e('Добавить строку', 'colorful-tables-pro'); ?>
                                                    </button>
                                                    <button type="button" class="button ct-button-small ct-button-danger" id="ct-remove-row">
                                                        <span class="ct-icon">➖</span> <?php _e('Удалить строку', 'colorful-tables-pro'); ?>
                                                    </button>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                    <div class="ct-structure-info" style="display: none;">
                                        <div class="ct-structure-stat"></div>
                                    </div>
                                </div>
                            </div>

                            <!-- Headers -->
                            <div class="ct-section">
                                <div class="ct-section-header">
                                    <h3><span class="ct-icon">️</span> <?php _e('Заголовки таблицы', 'colorful-tables-pro'); ?></h3>
                                    <div class="ct-color-presets-grid">
                                        <span><?php _e('Быстрые цвета:', 'colorful-tables-pro'); ?></span>
                                        <button type="button" class="ct-color-swatch" data-bg="#3498db" data-text="#ffffff" title="Синий" style="background: #3498db;"></button>
                                        <button type="button" class="ct-color-swatch" data-bg="#2ecc71" data-text="#ffffff" title="Зеленый" style="background: #2ecc71;"></button>
                                        <button type="button" class="ct-color-swatch" data-bg="#e74c3c" data-text="#ffffff" title="Красный" style="background: #e74c3c;"></button>
                                        <button type="button" class="ct-color-swatch" data-bg="#f39c12" data-text="#ffffff" title="Оранжевый" style="background: #f39c12;"></button>
                                        <button type="button" class="ct-color-swatch" data-bg="#9b59b6" data-text="#ffffff" title="Фиолетовый" style="background: #9b59b6;"></button>
                                    </div>
                                </div>
                                <div class="ct-section-content">
                                    <div id="ct-headers-container" class="ct-headers-grid">
                                        <!-- Headers generated by JS -->
                                    </div>
                                </div>
                            </div>

                            <!-- Table Data -->
                            <div class="ct-section">
                                <div class="ct-section-header">
                                    <h3><span class="ct-icon"></span> <?php _e('Данные таблицы', 'colorful-tables-pro'); ?></h3>
                                    <div class="ct-table-actions-grid">
                                        <div class="ct-color-presets-grid">
                                            <span><?php _e('Цвета ячеек:', 'colorful-tables-pro'); ?></span>
                                            <button type="button" class="ct-color-swatch" data-bg="#ecf0f1" data-text="#2c3e50" title="Светлый" style="background: #ecf0f1;"></button>
                                            <button type="button" class="ct-color-swatch" data-bg="#fff9e6" data-text="#e67e22" title="Кремовый" style="background: #fff9e6;"></button>
                                            <button type="button" class="ct-color-swatch" data-bg="#e8f6f3" data-text="#27ae60" title="Мятный" style="background: #e8f6f3;"></button>
                                        </div>
                                        <button type="button" class="button ct-button-small" id="ct-clear-all">
                                            <span class="ct-icon"></span> <?php _e('Очистить все', 'colorful-tables-pro'); ?>
                                        </button>
                                    </div>
                                </div>
                                <div class="ct-section-content">
                                    <div id="ct-rows-container" class="ct-rows-container">
                                        <!-- Rows generated by JS -->
                                    </div>
                                </div>
                            </div>

                            <!-- Actions -->
                            <div class="ct-actions">
                                <div class="ct-main-actions-grid">
                                    <button type="button" class="button ct-button-secondary" id="ct-preview">
                                        <span class="ct-icon">️</span> <?php _e('Предпросмотр', 'colorful-tables-pro'); ?>
                                    </button>
                                    <?php if ($this->editing_table_id): ?>
                                        <a href="<?php echo admin_url('admin.php?page=colorful-tables-pro'); ?>" class="button">
                                            <span class="ct-icon">↩️</span> <?php _e('Отмена', 'colorful-tables-pro'); ?>
                                        </a>
                                    <?php endif; ?>
                                    <button type="submit" name="save_table" class="button button-primary ct-button-primary">
                                        <span class="ct-icon"></span> <?php echo $this->editing_table_id ? __('Обновить таблицу', 'colorful-tables-pro') : __('Сохранить таблицу', 'colorful-tables-pro'); ?>
                                    </button>
                                </div>
                            </div>
                        </form>
                    </div>
                </div>

                <!-- Sidebar -->
                <div class="ct-sidebar">
                    <!-- Tables List -->
                    <div class="ct-panel ct-tables-panel">
                        <div class="ct-panel-header">
                            <h3><span class="ct-icon"></span> <?php _e('Мои таблицы', 'colorful-tables-pro'); ?></h3>
                        </div>
                        <div class="ct-panel-content">
                            <?php if (empty($tables)): ?>
                                <div class="ct-empty-state">
                                    <div class="ct-empty-icon"></div>
                                    <p><?php _e('Пока нет созданных таблиц', 'colorful-tables-pro'); ?></p>
                                    <p class="ct-empty-hint"><?php _e('Создайте свою первую таблицу!', 'colorful-tables-pro'); ?></p>
                                </div>
                            <?php else: ?>
                                <div class="ct-tables-grid">
                                    <?php foreach ($tables as $id => $table): ?>
                                        <div class="ct-table-card">
                                            <div class="ct-table-header-grid">
                                                <h4><?php echo esc_html($table['name']); ?></h4>
                                                <span class="ct-table-badge">#<?php echo $id; ?></span>
                                            </div>
                                            <div class="ct-table-meta-grid">
                                                <span class="ct-meta-item">
                                                    <span class="ct-icon"></span> <?php echo $table['columns']; ?>×<?php echo count($table['rows']); ?>
                                                </span>
                                                <span class="ct-meta-item">
                                                    <span class="ct-icon"></span> <?php echo $this->get_style_name($table['style']); ?>
                                                </span>
                                            </div>
                                            <div class="ct-table-shortcode-grid">
                                                <code><p>Неверный ID таблицы</p></code>
                                                <button type="button" class="ct-copy-btn" data-shortcode='[colorful_table id="<?php echo $id; ?>"]'>
                                                    <span class="ct-icon"></span>
                                                </button>
                                            </div>
                                            <div class="ct-table-actions-grid">
                                                <a href="<?php echo admin_url('admin.php?page=colorful-tables-pro&edit_table=' . $id); ?>" 
                                                   class="button ct-button-small">
                                                    <span class="ct-icon">✏️</span> <?php _e('Редактировать', 'colorful-tables-pro'); ?>
                                                </a>
                                                <form method="post" class="ct-delete-form">
                                                    <?php wp_nonce_field('colorful_tables_nonce'); ?>
                                                    <input type="hidden" name="delete_table" value="<?php echo $id; ?>">
                                                    <button type="submit" class="button ct-button-small ct-button-danger"
                                                            onclick="return confirm('<?php _e('Удалить таблицу?', 'colorful-tables-pro'); ?>')">
                                                        <span class="ct-icon">️</span>
                                                    </button>
                                                </form>
                                            </div>
                                        </div>
                                    <?php endforeach; ?>
                                </div>
                            <?php endif; ?>
                        </div>
                    </div>

                    <!-- Statistics -->
                    <div class="ct-panel ct-stats-panel">
                        <div class="ct-panel-header">
                            <h3><span class="ct-icon"></span> <?php _e('Статистика', 'colorful-tables-pro'); ?></h3>
                        </div>
                        <div class="ct-panel-content">
                            <div class="ct-stats-grid">
                                <div class="ct-stat-item">
                                    <div class="ct-stat-number"><?php echo count($tables); ?></div>
                                    <div class="ct-stat-label"><?php _e('Всего таблиц', 'colorful-tables-pro'); ?></div>
                                </div>
                                <div class="ct-stat-item">
                                    <div class="ct-stat-number">
                                        <?php
                                        $total_cells = 0;
                                        foreach ($tables as $table) {
                                            $total_cells += $table['columns'] * count($table['rows']);
                                        }
                                        echo $total_cells;
                                        ?>
                                    </div>
                                    <div class="ct-stat-label"><?php _e('Всего ячеек', 'colorful-tables-pro'); ?></div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Preview Modal -->
        <div id="ct-preview-modal" class="ct-modal" style="display: none;">
            <div class="ct-modal-content">
                <div class="ct-modal-header-grid">
                    <h3><span class="ct-icon">️</span> <?php _e('Предпросмотр таблицы', 'colorful-tables-pro'); ?></h3>
                    <button type="button" class="ct-modal-close">&times;</button>
                </div>
                <div class="ct-modal-body">
                    <div id="ct-preview-content"></div>
                </div>
            </div>
        </div>

        <script>
        jQuery(document).ready(function($) {
            var editTableData = <?php echo $edit_data_js; ?>;
            var maxColumns = <?php echo $this->max_columns; ?>;
            var maxRows = <?php echo $this->max_rows; ?>;
            
            // Initialize table builder
            initTableBuilder();
            
            // Event handlers
            $(document).on('click', '.ct-number-btn', handleNumberInput);
            $(document).on('click', '#ct-add-column', addColumn);
            $(document).on('click', '#ct-remove-column', removeColumn);
            $(document).on('click', '#ct-add-row', addRow);
            $(document).on('click', '#ct-remove-row', removeRow);
            $(document).on('click', '#ct-clear-all', clearAllCells);
            $(document).on('click', '.ct-color-swatch', applyColorPreset);
            $(document).on('click', '#ct-preview', showPreview);
            $(document).on('click', '.ct-modal-close', closePreview);
            $(document).on('click', '.ct-copy-btn', copyShortcode);
            $(document).on('click', '.ct-clear-row', clearRow);
            
            function initTableBuilder() {
                generateHeaders();
                generateRows();
                updateStructureInfo();
                initColorInputs();
            }
            
            function generateHeaders() {
                var columns = parseInt($('#ct-columns').val());
                var container = $('#ct-headers-container');
                var html = '';
                
                for (var i = 0; i < columns; i++) {
                    var headerData = getHeaderData(i);
                    html += `
                        <div class="ct-header-card" data-column="${i}">
                            <div class="ct-card-header-grid">
                                <label><?php _e('Колонка', 'colorful-tables-pro'); ?> ${i+1}</label>
                                <div class="ct-color-preview" style="background: ${headerData.bg_color}"></div>
                            </div>
                            <input type="text" class="ct-input" name="headers[${i}][content]" 
                                   value="${headerData.content}" placeholder="<?php _e('Заголовок колонки...', 'colorful-tables-pro'); ?>">
                            <div class="ct-color-controls-grid">
                                <div class="ct-color-group-grid">
                                    <label><?php _e('Фон', 'colorful-tables-pro'); ?></label>
                                    <div class="ct-color-input-grid">
                                        <input type="color" class="ct-color-input" name="headers[${i}][bg_color]" 
                                               value="${headerData.bg_color}">
                                        <input type="text" class="ct-color-hex" value="${headerData.bg_color}" 
                                               placeholder="#3498db">
                                    </div>
                                </div>
                                <div class="ct-color-group-grid">
                                    <label><?php _e('Текст', 'colorful-tables-pro'); ?></label>
                                    <div class="ct-color-input-grid">
                                        <input type="color" class="ct-color-input" name="headers[${i}][text_color]" 
                                               value="${headerData.text_color}">
                                        <input type="text" class="ct-color-hex" value="${headerData.text_color}" 
                                               placeholder="#ffffff">
                                    </div>
                                </div>
                            </div>
                        </div>
                    `;
                }
                
                container.html(html);
                initColorInputs();
            }
            
            function generateRows() {
                var columns = parseInt($('#ct-columns').val());
                var rows = parseInt($('#ct-rows').val());
                var container = $('#ct-rows-container');
                var html = '';
                
                for (var r = 0; r < rows; r++) {
                    html += `<div class="ct-row-card" data-row="${r}">`;
                    html += `<div class="ct-row-header-grid">
                                <span> <?php _e('Строка', 'colorful-tables-pro'); ?> ${r+1}</span>
                                <button type="button" class="ct-clear-row" data-row="${r}"><?php _e('Очистить строку', 'colorful-tables-pro'); ?></button>
                            </div>`;
                    html += `<div class="ct-cells-grid">`;
                    
                    for (var c = 0; c < columns; c++) {
                        var cellData = getCellData(r, c);
                        html += `
                            <div class="ct-cell-card" data-row="${r}" data-col="${c}">
                                <div class="ct-cell-header-grid">
                                    <label>${r+1}-${c+1}</label>
                                    <div class="ct-color-preview" style="background: ${cellData.bg_color || '#ffffff'}"></div>
                                </div>
                                <textarea name="rows[${r}][cells][${c}][content]" 
                                          placeholder="<?php _e('Введите данные...', 'colorful-tables-pro'); ?>" rows="2">${cellData.content}</textarea>
                                <div class="ct-color-controls-grid">
                                    <div class="ct-color-group-grid">
                                        <label><?php _e('Фон', 'colorful-tables-pro'); ?></label>
                                        <div class="ct-color-input-grid">
                                            <input type="color" class="ct-color-input" name="rows[${r}][cells][${c}][bg_color]" 
                                                   value="${cellData.bg_color || '#ffffff'}">
                                            <input type="text" class="ct-color-hex" value="${cellData.bg_color || '#ffffff'}" 
                                                   placeholder="#ffffff">
                                        </div>
                                    </div>
                                    <div class="ct-color-group-grid">
                                        <label><?php _e('Текст', 'colorful-tables-pro'); ?></label>
                                        <div class="ct-color-input-grid">
                                            <input type="color" class="ct-color-input" name="rows[${r}][cells][${c}][text_color]" 
                                                   value="${cellData.text_color || '#000000'}">
                                            <input type="text" class="ct-color-hex" value="${cellData.text_color || '#000000'}" 
                                                   placeholder="#000000">
                                        </div>
                                    </div>
                                </div>
                            </div>
                        `;
                    }
                    
                    html += `</div></div>`;
                }
                
                container.html(html);
                initColorInputs();
            }
            
            function getHeaderData(index) {
                if (editTableData && editTableData.headers && editTableData.headers[index]) {
                    return editTableData.headers[index];
                }
                var defaultColors = [
                    {bg: '#3498db', text: '#ffffff'},
                    {bg: '#2ecc71', text: '#ffffff'},
                    {bg: '#e74c3c', text: '#ffffff'},
                    {bg: '#f39c12', text: '#ffffff'},
                    {bg: '#9b59b6', text: '#ffffff'},
                    {bg: '#1abc9c', text: '#ffffff'},
                    {bg: '#34495e', text: '#ffffff'},
                    {bg: '#e67e22', text: '#ffffff'},
                    {bg: '#16a085', text: '#ffffff'},
                    {bg: '#8e44ad', text: '#ffffff'}
                ];
                var color = defaultColors[index] || defaultColors[0];
                return {
                    content: '<?php _e('Колонка', 'colorful-tables-pro'); ?> ' + (index + 1),
                    bg_color: color.bg,
                    text_color: color.text
                };
            }
            
            function getCellData(row, col) {
                if (editTableData && editTableData.rows && editTableData.rows[row] && 
                    editTableData.rows[row].cells && editTableData.rows[row].cells[col]) {
                    return editTableData.rows[row].cells[col];
                }
                return {
                    content: '',
                    bg_color: '#ffffff',
                    text_color: '#000000'
                };
            }
            
            function handleNumberInput() {
                var $btn = $(this);
                var action = $btn.data('action');
                var target = $btn.data('target');
                var $input = $('#ct-' + target);
                var current = parseInt($input.val());
                
                if (action === 'increase') {
                    if ((target === 'columns' && current < maxColumns) || 
                        (target === 'rows' && current < maxRows)) {
                        $input.val(current + 1);
                        if (target === 'columns') {
                            generateHeaders();
                            generateRows();
                        } else {
                            generateRows();
                        }
                        updateStructureInfo();
                    }
                } else if (action === 'decrease') {
                    if (current > 1) {
                        $input.val(current - 1);
                        if (target === 'columns') {
                            generateHeaders();
                            generateRows();
                        } else {
                            generateRows();
                        }
                        updateStructureInfo();
                    }
                }
            }
            
            function addColumn() {
                var $input = $('#ct-columns');
                var current = parseInt($input.val());
                if (current < maxColumns) {
                    $input.val(current + 1);
                    generateHeaders();
                    generateRows();
                    updateStructureInfo();
                    showNotification('<?php _e('Колонка добавлена', 'colorful-tables-pro'); ?>');
                } else {
                    showNotification('<?php printf(__('Достигнуто максимальное количество колонок (%d)', 'colorful-tables-pro'), $this->max_columns); ?>', 'error');
                }
            }
            
            function removeColumn() {
                var $input = $('#ct-columns');
                var current = parseInt($input.val());
                if (current > 1) {
                    $input.val(current - 1);
                    generateHeaders();
                    generateRows();
                    updateStructureInfo();
                    showNotification('<?php _e('Колонка удалена', 'colorful-tables-pro'); ?>');
                } else {
                    showNotification('<?php _e('Минимальное количество колонок - 1', 'colorful-tables-pro'); ?>', 'error');
                }
            }
            
            function addRow() {
                var $input = $('#ct-rows');
                var current = parseInt($input.val());
                if (current < maxRows) {
                    $input.val(current + 1);
                    generateRows();
                    updateStructureInfo();
                    showNotification('<?php _e('Строка добавлена', 'colorful-tables-pro'); ?>');
                } else {
                    showNotification('<?php printf(__('Достигнуто максимальное количество строк (%d)', 'colorful-tables-pro'), $this->max_rows); ?>', 'error');
                }
            }
            
            function removeRow() {
                var $input = $('#ct-rows');
                var current = parseInt($input.val());
                if (current > 1) {
                    $input.val(current - 1);
                    generateRows();
                    updateStructureInfo();
                    showNotification('<?php _e('Строка удалена', 'colorful-tables-pro'); ?>');
                } else {
                    showNotification('<?php _e('Минимальное количество строк - 1', 'colorful-tables-pro'); ?>', 'error');
                }
            }
            
            function clearAllCells() {
                if (confirm('<?php _e('Очистить все ячейки таблицы?', 'colorful-tables-pro'); ?>')) {
                    $('.ct-cell-card textarea').val('');
                    showNotification('<?php _e('Все ячейки очищены', 'colorful-tables-pro'); ?>');
                }
            }
            
            function clearRow() {
                var row = $(this).data('row');
                $(`.ct-cell-card[data-row="${row}"] textarea`).val('');
                showNotification('<?php _e('Строка', 'colorful-tables-pro'); ?> ' + (row + 1) + ' <?php _e('очищена', 'colorful-tables-pro'); ?>');
            }
            
            function applyColorPreset() {
                var $swatch = $(this);
                var bgColor = $swatch.data('bg');
                var textColor = $swatch.data('text');
                var isHeader = $swatch.closest('.ct-section-header').length;
                
                if (isHeader) {
                    $('.ct-header-card .ct-color-input[name$="[bg_color]"]').val(bgColor).trigger('change');
                    $('.ct-header-card .ct-color-input[name$="[text_color]"]').val(textColor).trigger('change');
                    showNotification('<?php _e('Цвета заголовков применены', 'colorful-tables-pro'); ?>');
                } else {
                    $('.ct-cell-card .ct-color-input[name$="[bg_color]"]').val(bgColor).trigger('change');
                    $('.ct-cell-card .ct-color-input[name$="[text_color]"]').val(textColor).trigger('change');
                    showNotification('<?php _e('Цвета ячеек применены', 'colorful-tables-pro'); ?>');
                }
            }
            
            function initColorInputs() {
                $('.ct-color-input').off('change').on('change', function() {
                    var $hex = $(this).siblings('.ct-color-hex');
                    $hex.val($(this).val());
                    updateColorPreview($(this));
                });
                
                $('.ct-color-hex').off('input').on('input', function() {
                    var $color = $(this).siblings('.ct-color-input');
                    var value = $(this).val();
                    if (/^#[0-9A-F]{6}$/i.test(value)) {
                        $color.val(value);
                        updateColorPreview($color);
                    }
                });
                
                // Update color previews
                $('.ct-color-input').each(function() {
                    updateColorPreview($(this));
                });
            }
            
            function updateColorPreview($input) {
                var color = $input.val();
                var $preview = $input.closest('.ct-color-controls-grid').siblings('.ct-card-header-grid').find('.ct-color-preview');
                if ($preview.length === 0) {
                    $preview = $input.closest('.ct-color-controls-grid').siblings('.ct-cell-header-grid').find('.ct-color-preview');
                }
                $preview.css('background-color', color);
            }
            
            function updateStructureInfo() {
                var columns = $('#ct-columns').val();
                var rows = $('#ct-rows').val();
                var totalCells = columns * rows;
                
                $('.ct-structure-info').show().find('.ct-structure-stat').html(
                    '<strong><?php _e('Размер таблицы:', 'colorful-tables-pro'); ?></strong> ' + columns + ' × ' + rows + ' = ' + totalCells + ' <?php _e('ячеек', 'colorful-tables-pro'); ?>'
                );
            }
            
            function showPreview() {
                var columns = $('#ct-columns').val();
                var style = $('#ct-style-select').val();
                
                // Collect data for preview
                var headers = [];
                $('.ct-header-card').each(function() {
                    headers.push({
                        content: $(this).find('input[type="text"]').val(),
                        bg_color: $(this).find('.ct-color-input[name$="[bg_color]"]').val(),
                        text_color: $(this).find('.ct-color-input[name$="[text_color]"]').val()
                    });
                });
                
                var tableRows = [];
                $('.ct-row-card').each(function() {
                    var cells = [];
                    $(this).find('.ct-cell-card').each(function() {
                        cells.push({
                            content: $(this).find('textarea').val(),
                            bg_color: $(this).find('.ct-color-input[name$="[bg_color]"]').val(),
                            text_color: $(this).find('.ct-color-input[name$="[text_color]"]').val()
                        });
                    });
                    tableRows.push({ cells: cells });
                });
                
                // Generate preview HTML
                var previewHtml = '<div class="ct-table-container style-' + style + '">';
                
                // Desktop version
                previewHtml += '<div class="ct-table-desktop" style="grid-template-columns: repeat(' + columns + ', 1fr)">';
                
                // Headers for desktop
                headers.forEach(function(header) {
                    var headerStyle = '';
                    if (header.bg_color) headerStyle += 'background-color: ' + header.bg_color + ';';
                    if (header.text_color) headerStyle += 'color: ' + header.text_color + ';';
                    
                    previewHtml += '<div class="ct-table-header" style="' + headerStyle + '">';
                    previewHtml += header.content || '<?php _e('Заголовок', 'colorful-tables-pro'); ?>';
                    previewHtml += '</div>';
                });
                
                // Cells for desktop
                tableRows.forEach(function(row) {
                    row.cells.forEach(function(cell) {
                        var cellStyle = '';
                        if (cell.bg_color) cellStyle += 'background-color: ' + cell.bg_color + ';';
                        if (cell.text_color) cellStyle += 'color: ' + cell.text_color + ';';
                        
                        previewHtml += '<div class="ct-table-cell" style="' + cellStyle + '">';
                        previewHtml += cell.content || '<?php _e('Данные', 'colorful-tables-pro'); ?>';
                        previewHtml += '</div>';
                    });
                });
                
                previewHtml += '</div>'; // .ct-table-desktop
                
                // Mobile version
                previewHtml += '<div class="ct-table-mobile">';
                
                tableRows.forEach(function(row, rowIndex) {
                    previewHtml += '<div class="ct-mobile-row">';
                    
                    row.cells.forEach(function(cell, cellIndex) {
                        if (headers[cellIndex]) {
                            var cellStyle = '';
                            if (cell.bg_color) cellStyle += 'background-color: ' + cell.bg_color + ';';
                            if (cell.text_color) cellStyle += 'color: ' + cell.text_color + ';';
                            
                            previewHtml += '<div class="ct-mobile-cell" style="' + cellStyle + '">';
                            previewHtml += '<div class="ct-mobile-header">' + (headers[cellIndex].content || '<?php _e('Заголовок', 'colorful-tables-pro'); ?>') + '</div>';
                            previewHtml += '<div class="ct-mobile-content">' + (cell.content || '<?php _e('Данные', 'colorful-tables-pro'); ?>') + '</div>';
                            previewHtml += '</div>';
                        }
                    });
                    
                    previewHtml += '</div>'; // .ct-mobile-row
                });
                
                previewHtml += '</div>'; // .ct-table-mobile
                previewHtml += '</div>'; // .ct-table-container
                
                $('#ct-preview-content').html(previewHtml);
                $('#ct-preview-modal').show();
            }
            
            function closePreview() {
                $('#ct-preview-modal').hide();
            }
            
            function copyShortcode() {
                var shortcode = $(this).data('shortcode');
                navigator.clipboard.writeText(shortcode).then(function() {
                    var $btn = $(this);
                    var originalHtml = $btn.html();
                    $btn.html('<span class="ct-icon">✅</span>');
                    
                    setTimeout(function() {
                        $btn.html(originalHtml);
                    }, 2000);
                    
                    showNotification('<?php _e('Шорткод скопирован в буфер обмена', 'colorful-tables-pro'); ?>');
                }.bind(this));
            }
            
            function showNotification(message, type = 'success') {
                var $notification = $('<div class="ct-notification ct-notification-' + type + '">' + message + '</div>');
                $('body').append($notification);
                
                setTimeout(function() {
                    $notification.fadeOut(300, function() {
                        $(this).remove();
                    });
                }, 3000);
            }
        });
        </script>
        <?php
    }
    
    private function safe_json_encode($data) {
        return wp_json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
    }
    
    private function get_style_name($style) {
        $styles = array(
            'gradient' => __('Градиентный', 'colorful-tables-pro'),
            'modern' => __('Современный', 'colorful-tables-pro'),
            'elegant' => __('Элегантный', 'colorful-tables-pro'),
            'vibrant' => __('Яркий', 'colorful-tables-pro'),
            'minimal' => __('Минимализм', 'colorful-tables-pro')
        );
        return isset($styles[$style]) ? $styles[$style] : $style;
    }
    
    private function sanitize_color($color) {
        if (empty($color)) return '';
        
        $color = sanitize_hex_color($color);
        return $color ?: '';
    }
    
    private function get_frontend_css() {
        ob_start(); ?>
        .ct-table-container {
            margin: 2rem 0;
            border-radius: 12px;
            box-shadow: 0 4px 20px rgba(0,0,0,0.1);
            background: white;
            overflow: hidden;
        }
        
        /* Desktop version */
        .ct-table-desktop {
            display: grid;
            width: 100%;
        }
        
        .ct-table-header {
            padding: 18px 20px;
            font-weight: 700;
            text-align: center;
            font-size: 16px;
            word-break: break-word;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.3s ease;
            min-height: 60px;
            box-sizing: border-box;
        }
        
        .ct-table-cell {
            padding: 16px 20px;
            background: #ffffff;
            min-height: 60px;
            display: flex;
            align-items: center;
            word-break: break-word;
            line-height: 1.5;
            transition: all 0.3s ease;
            box-sizing: border-box;
            border-bottom: 1px solid #f8f9fa;
        }
        
        .ct-table-cell:nth-child(odd) {
            background: #fafbfc;
        }
        
        /* Mobile version */
        .ct-table-mobile {
            display: none;
        }
        
        .ct-mobile-row {
            border-bottom: 1px solid #f0f2f5;
            padding: 0;
        }
        
        .ct-mobile-row:last-child {
            border-bottom: none;
        }
        
        .ct-mobile-cell {
            display: grid;
            grid-template-columns: 120px 1fr;
            gap: 15px;
            padding: 15px 20px;
            align-items: start;
            border-bottom: 1px solid #f8f9fa;
        }
        
        .ct-mobile-cell:last-child {
            border-bottom: none;
        }
        
        .ct-mobile-header {
            font-weight: 600;
            color: #2c3e50;
            font-size: 14px;
            word-break: break-word;
        }
        
        .ct-mobile-content {
            word-break: break-word;
            line-height: 1.5;
        }
        
        /* Styles for different themes */
        .ct-table-container.style-gradient .ct-table-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }
        
        .ct-table-container.style-modern .ct-table-desktop {
            border: 1px solid #e0e0e0;
        }
        
        .ct-table-container.style-modern .ct-table-header {
            background: #2c3e50;
            color: white;
        }
        
        .ct-table-container.style-elegant .ct-table-desktop {
            border: 1px solid #d4af37;
        }
        
        .ct-table-container.style-elegant .ct-table-header {
            background: #d4af37;
            color: #2c3e50;
            font-weight: 600;
        }
        
        .ct-table-container.style-vibrant .ct-table-header {
            background: #e74c3c;
            color: white;
            font-weight: 800;
        }
        
        .ct-table-container.style-minimal .ct-table-header {
            background: none;
            color: #2c3e50;
            border-bottom: 2px solid #3498db;
            font-weight: 600;
        }
        
        /* Responsive media queries */
        @media (max-width: 768px) {
            .ct-table-desktop {
                display: none;
            }
            
            .ct-table-mobile {
                display: block;
            }
            
            .ct-table-container {
                margin: 1rem 0;
                border-radius: 8px;
                box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            }
            
            .ct-mobile-cell {
                grid-template-columns: 100px 1fr;
                gap: 12px;
                padding: 12px 15px;
            }
            
            .ct-mobile-header {
                font-size: 13px;
            }
            
            .ct-mobile-content {
                font-size: 14px;
            }
        }
        
        @media (min-width: 769px) {
            .ct-table-mobile {
                display: none;
            }
            
            .ct-table-desktop {
                display: grid;
            }
        }
        <?php
        return ob_get_clean();
    }
    
    private function get_admin_css() {
        ob_start(); ?>
        .ct-wrapper {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
            background: #f8f9fa;
            min-height: 100vh;
        }
        
        .ct-header {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 30px 0;
            margin: 0 -20px 20px;
        }
        
        .ct-header-content {
            max-width: 1400px;
            margin: 0 auto;
            padding: 0 20px;
        }
        
        .ct-header h1 {
            font-size: 2em;
            margin: 0 0 8px 0;
            font-weight: 600;
            color: white;
        }
        
        .ct-subtitle {
            font-size: 1em;
            opacity: 0.9;
            margin: 0;
            color: white;
        }
        
        .ct-logo {
            font-size: 1.1em;
            margin-right: 8px;
        }
        
        .ct-badge {
            background: rgba(255,255,255,0.2);
            padding: 4px 10px;
            border-radius: 16px;
            font-size: 0.7em;
            margin-left: 12px;
            color: white;
        }
        
        .ct-container {
            max-width: 1400px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 1fr 350px;
            gap: 20px;
            padding: 0 20px;
        }
        
        .ct-main-panel {
            min-width: 0;
        }
        
        .ct-sidebar {
            display: grid;
            gap: 20px;
            grid-template-rows: auto auto;
        }
        
        .ct-panel {
            background: white;
            border-radius: 8px;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
            overflow: hidden;
        }
        
        .ct-panel-header {
            padding: 15px 20px;
            border-bottom: 1px solid #f0f2f5;
        }
        
        .ct-panel-header h2,
        .ct-panel-header h3 {
            margin: 0;
            font-weight: 600;
            color: #2c3e50;
            font-size: 1.1em;
        }
        
        .ct-panel-content {
            padding: 20px;
        }
        
        .ct-section {
            background: white;
            border-radius: 8px;
            margin-bottom: 20px;
            border: 1px solid #f0f2f5;
        }
        
        .ct-section-header {
            padding: 15px 20px;
            border-bottom: 1px solid #f0f2f5;
        }
        
        .ct-section-header h3 {
            margin: 0;
            font-weight: 600;
            color: #2c3e50;
            font-size: 1em;
        }
        
        .ct-section-content {
            padding: 20px;
        }
        
        .ct-form-grid-2 {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 15px;
        }
        
        .ct-form-group {
            margin-bottom: 15px;
        }
        
        .ct-label {
            display: block;
            margin-bottom: 6px;
            font-weight: 600;
            color: #2c3e50;
            font-size: 0.9em;
        }
        
        .ct-input, .ct-select, .ct-number-input {
            width: 100%;
            padding: 10px 12px;
            border: 1px solid #e9ecef;
            border-radius: 6px;
            font-size: 13px;
            transition: all 0.2s ease;
            box-sizing: border-box;
            color: #2c3e50;
            background: white;
        }
        
        .ct-input:focus, .ct-select:focus {
            border-color: #3498db;
            box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.1);
            outline: none;
        }
        
        .ct-structure-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }
        
        .ct-structure-item h4 {
            margin: 0 0 12px 0;
            color: #2c3e50;
            font-size: 0.95em;
        }
        
        .ct-control-group {
            display: grid;
            gap: 12px;
        }
        
        .ct-number-control {
            display: grid;
            gap: 8px;
        }
        
        .ct-number-control label {
            font-weight: 600;
            color: #2c3e50;
            margin: 0;
            font-size: 0.9em;
        }
        
        .ct-number-buttons {
            display: grid;
            grid-template-columns: auto 1fr auto;
            gap: 5px;
            align-items: center;
        }
        
        .ct-number-btn {
            background: #f8f9fa;
            border: 1px solid #e9ecef;
            width: 35px;
            height: 35px;
            border-radius: 6px;
            display: grid;
            place-items: center;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
            transition: all 0.2s ease;
            color: #2c3e50;
        }
        
        .ct-number-btn:hover {
            background: #3498db;
            color: white;
            border-color: #3498db;
        }
        
        .ct-number-input {
            text-align: center;
            background: #f8f9fa;
            height: 35px;
        }
        
        .ct-action-buttons-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
        }
        
        .ct-button-small {
            padding: 6px 10px;
            font-size: 11px;
            height: auto;
        }
        
        .ct-button-danger {
            background: #e74c3c;
            color: white;
            border: none;
        }
        
        .ct-button-danger:hover {
            background: #c0392b;
            color: white;
        }
        
        .ct-structure-info {
            grid-column: 1 / -1;
            margin-top: 15px;
            padding: 12px;
            background: #e8f6f3;
            border-radius: 6px;
            border: 1px solid #2ecc71;
            color: #2c3e50;
        }
        
        .ct-structure-stat {
            text-align: center;
            font-size: 13px;
            color: #27ae60;
        }
        
        .ct-color-presets-grid {
            display: flex;
            gap: 8px;
            align-items: center;
            font-size: 12px;
            color: #6c757d;
            flex-wrap: wrap;
        }
        
        .ct-color-presets-grid span {
            white-space: nowrap;
        }
        
        .ct-color-swatch {
            width: 18px;
            height: 18px;
            border: 2px solid white;
            border-radius: 3px;
            cursor: pointer;
            box-shadow: 0 1px 2px rgba(0,0,0,0.1);
            transition: all 0.2s ease;
        }
        
        .ct-color-swatch:hover {
            transform: scale(1.1);
        }
        
        .ct-table-actions-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 10px;
            align-items: center;
        }
        
        .ct-headers-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
            gap: 15px;
        }
        
        .ct-header-card {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            border: 1px solid transparent;
            transition: all 0.2s ease;
            color: #2c3e50;
        }
        
        .ct-header-card:hover {
            border-color: #3498db;
        }
        
        .ct-card-header-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 10px;
            align-items: center;
            margin-bottom: 12px;
        }
        
        .ct-card-header-grid label {
            font-size: 0.9em;
            font-weight: 600;
            color: #495057;
            margin: 0;
        }
        
        .ct-color-preview {
            width: 20px;
            height: 20px;
            border-radius: 4px;
            border: 2px solid white;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
        }
        
        .ct-color-controls-grid {
            display: grid;
            gap: 12px;
            margin-top: 12px;
        }
        
        .ct-color-group-grid {
            display: grid;
            grid-template-columns: auto 1fr;
            gap: 8px;
            align-items: center;
        }
        
        .ct-color-group-grid label {
            font-size: 11px;
            font-weight: 600;
            color: #6c757d;
            margin: 0;
            min-width: 40px;
        }
        
        .ct-color-input-grid {
            display: grid;
            grid-template-columns: auto 1fr;
            gap: 6px;
            align-items: center;
        }
        
        .ct-color-input {
            width: 40px !important;
            height: 32px;
            border: 1px solid #e9ecef;
            border-radius: 4px;
            cursor: pointer;
            padding: 0;
        }
        
        .ct-color-hex {
            padding: 6px 8px;
            border: 1px solid #e9ecef;
            border-radius: 4px;
            font-family: monospace;
            font-size: 11px;
            height: 32px;
            box-sizing: border-box;
            color: #2c3e50;
            background: white;
        }
        
        .ct-rows-container {
            max-height: 500px;
            overflow-y: auto;
            background: #fafbfc;
            padding: 15px;
            border-radius: 8px;
            border: 1px dashed #e9ecef;
        }
        
        .ct-row-card {
            background: white;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 15px;
            border: 1px solid #f0f2f5;
            transition: all 0.2s ease;
            color: #2c3e50;
        }
        
        .ct-row-card:hover {
            border-color: #3498db;
        }
        
        .ct-row-header-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 10px;
            align-items: center;
            margin-bottom: 12px;
        }
        
        .ct-row-header-grid span {
            font-weight: 600;
            color: #2c3e50;
            font-size: 0.9em;
        }
        
        .ct-clear-row {
            background: none;
            border: 1px solid #e74c3c;
            color: #e74c3c;
            padding: 3px 6px;
            border-radius: 3px;
            font-size: 10px;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        
        .ct-clear-row:hover {
            background: #e74c3c;
            color: white;
        }
        
        .ct-cells-grid {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
            gap: 12px;
        }
        
        .ct-cell-card {
            background: #f8f9fa;
            padding: 12px;
            border-radius: 6px;
            border: 1px solid transparent;
            transition: all 0.2s ease;
            color: #2c3e50;
        }
        
        .ct-cell-card:hover {
            border-color: #3498db;
        }
        
        .ct-cell-header-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 8px;
            align-items: center;
            margin-bottom: 8px;
        }
        
        .ct-cell-header-grid label {
            font-size: 10px;
            font-weight: 600;
            color: #6c757d;
            margin: 0;
        }
        
        .ct-cell-card textarea {
            width: 100%;
            min-height: 50px;
            padding: 8px;
            border: 1px solid #e9ecef;
            border-radius: 4px;
            resize: vertical;
            font-size: 12px;
            box-sizing: border-box;
            color: #2c3e50;
            background: white;
        }
        
        .ct-actions {
            margin-top: 20px;
            padding-top: 20px;
            border-top: 1px solid #f0f2f5;
        }
        
        .ct-main-actions-grid {
            display: grid;
            grid-template-columns: auto auto auto;
            gap: 8px;
            justify-content: end;
        }
        
        .ct-button-primary {
            background: #3498db;
            border: none;
            padding: 10px 20px;
            border-radius: 6px;
            font-weight: 600;
            font-size: 13px;
            transition: all 0.2s ease;
            color: white;
        }
        
        .ct-button-primary:hover {
            background: #2980b9;
            transform: translateY(-1px);
            color: white;
        }
        
        .ct-button-secondary {
            background: #f8f9fa;
            border: 1px solid #e9ecef;
            color: #2c3e50;
            padding: 8px 16px;
            border-radius: 6px;
            font-weight: 500;
            font-size: 13px;
            transition: all 0.2s ease;
        }
        
        .ct-button-secondary:hover {
            background: #3498db;
            color: white;
            border-color: #3498db;
        }
        
        .ct-icon {
            margin-right: 4px;
        }
        
        .ct-tables-grid {
            display: grid;
            gap: 12px;
        }
        
        .ct-table-card {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            border: 1px solid transparent;
            transition: all 0.2s ease;
            color: #2c3e50;
        }
        
        .ct-table-card:hover {
            border-color: #3498db;
        }
        
        .ct-table-header-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 8px;
            align-items: center;
            margin-bottom: 8px;
        }
        
        .ct-table-header-grid h4 {
            margin: 0;
            color: #2c3e50;
            font-size: 0.95em;
        }
        
        .ct-table-badge {
            background: #3498db;
            color: white;
            padding: 2px 6px;
            border-radius: 8px;
            font-size: 10px;
            font-weight: 600;
        }
        
        .ct-table-meta-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
            margin-bottom: 10px;
        }
        
        .ct-meta-item {
            font-size: 11px;
            color: #6c757d;
        }
        
        .ct-table-shortcode-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 8px;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .ct-table-shortcode-grid code {
            background: white;
            padding: 6px 8px;
            border-radius: 4px;
            font-size: 11px;
            border: 1px solid #e9ecef;
            overflow: hidden;
            text-overflow: ellipsis;
            color: #2c3e50;
        }
        
        .ct-copy-btn {
            background: #f8f9fa;
            border: 1px solid #e9ecef;
            padding: 6px;
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.2s ease;
            height: 30px;
            width: 30px;
            display: grid;
            place-items: center;
            color: #2c3e50;
        }
        
        .ct-copy-btn:hover {
            background: #3498db;
            color: white;
        }
        
        .ct-table-actions-grid {
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 6px;
        }
        
        .ct-delete-form {
            margin: 0;
        }
        
        .ct-empty-state {
            text-align: center;
            padding: 30px 15px;
            color: #6c757d;
        }
        
        .ct-empty-icon {
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        
        .ct-empty-hint {
            font-size: 0.8em;
            opacity: 0.7;
        }
        
        .ct-stats-panel .ct-panel-content {
            padding: 15px;
        }
        
        .ct-stats-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px;
        }
        
        .ct-stat-item {
            text-align: center;
            padding: 12px;
            background: #f8f9fa;
            border-radius: 6px;
            color: #2c3e50;
        }
        
        .ct-stat-number {
            font-size: 1.5em;
            font-weight: bold;
            color: #3498db;
            margin-bottom: 4px;
        }
        
        .ct-stat-label {
            font-size: 0.75em;
            color: #6c757d;
        }
        
        .ct-modal {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.5);
            z-index: 9999;
            display: none;
            place-items: center;
        }
        
        .ct-modal-content {
            background: white;
            border-radius: 8px;
            width: 90%;
            max-width: 700px;
            max-height: 70vh;
            overflow: hidden;
            box-shadow: 0 10px 30px rgba(0,0,0,0.3);
        }
        
        .ct-modal-header-grid {
            padding: 15px 20px;
            border-bottom: 1px solid #f0f2f5;
            display: grid;
            grid-template-columns: 1fr auto;
            gap: 15px;
            align-items: center;
        }
        
        .ct-modal-header-grid h3 {
            margin: 0;
            color: #2c3e50;
            font-size: 1em;
        }
        
        .ct-modal-close {
            background: none;
            border: none;
            font-size: 20px;
            cursor: pointer;
            color: #6c757d;
            padding: 0;
            width: 25px;
            height: 25px;
            display: grid;
            place-items: center;
        }
        
        .ct-modal-body {
            padding: 20px;
            max-height: 50vh;
            overflow-y: auto;
        }
        
        .ct-notification {
            position: fixed;
            top: 15px;
            right: 15px;
            background: #2ecc71;
            color: white;
            padding: 10px 15px;
            border-radius: 6px;
            box-shadow: 0 3px 10px rgba(0,0,0,0.2);
            z-index: 10000;
            font-weight: 500;
            font-size: 13px;
        }
        
        .ct-notification-error {
            background: #e74c3c;
        }
        
        /* Fix text colors in all elements */
        .ct-header-card input,
        .ct-cell-card textarea,
        .ct-color-hex,
        .ct-row-card span,
        .ct-table-card h4 {
            color: #2c3e50 !important;
        }
        
        @media (max-width: 1200px) {
            .ct-container {
                grid-template-columns: 1fr;
            }
            
            .ct-sidebar {
                grid-template-columns: 1fr 1fr;
                grid-template-rows: auto;
            }
            
            .ct-structure-grid {
                grid-template-columns: 1fr;
            }
        }
        
        @media (max-width: 768px) {
            .ct-header h1 {
                font-size: 1.6em;
            }
            
            .ct-form-grid-2 {
                grid-template-columns: 1fr;
            }
            
            .ct-headers-grid {
                grid-template-columns: 1fr;
            }
            
            .ct-cells-grid {
                grid-template-columns: 1fr;
            }
            
            .ct-main-actions-grid {
                grid-template-columns: 1fr;
                justify-content: stretch;
            }
            
            .ct-table-actions-grid {
                grid-template-columns: 1fr;
            }
            
            .ct-sidebar {
                grid-template-columns: 1fr;
            }
            
            .ct-color-presets-grid {
                grid-template-columns: 1fr;
            }
        }
        <?php
        return ob_get_clean();
    }
    
    private function save_table() {
        if (!current_user_can('manage_options')) {
            $this->log_error('Unauthorized table save attempt');
            return;
        }
        
        $table_id = isset($_POST['edit_table_id']) ? intval($_POST['edit_table_id']) : null;
        
        // Validate and sanitize data
        $table_data = array(
            'name' => sanitize_text_field($_POST['table_name']),
            'columns' => intval($_POST['columns']),
            'style' => sanitize_text_field($_POST['style']),
            'headers' => array(),
            'rows' => array()
        );
        
        // Sanitize headers
        if (isset($_POST['headers']) && is_array($_POST['headers'])) {
            foreach ($_POST['headers'] as $header) {
                $table_data['headers'][] = array(
                    'content' => sanitize_text_field($header['content']),
                    'bg_color' => $this->sanitize_color($header['bg_color']),
                    'text_color' => $this->sanitize_color($header['text_color'])
                );
            }
        }
        
        // Sanitize rows and cells
        if (isset($_POST['rows']) && is_array($_POST['rows'])) {
            foreach ($_POST['rows'] as $row) {
                $row_data = array('cells' => array());
                if (isset($row['cells']) && is_array($row['cells'])) {
                    foreach ($row['cells'] as $cell) {
                        $row_data['cells'][] = array(
                            'content' => wp_kses_post($cell['content']),
                            'bg_color' => $this->sanitize_color($cell['bg_color']),
                            'text_color' => $this->sanitize_color($cell['text_color'])
                        );
                    }
                }
                $table_data['rows'][] = $row_data;
            }
        }
        
        $tables = get_option($this->storage_key, array());
        
        if ($table_id && isset($tables[$table_id])) {
            $tables[$table_id] = $table_data;
        } else {
            $new_id = empty($tables) ? 1 : max(array_keys($tables)) + 1;
            $tables[$new_id] = $table_data;
        }
        
        update_option($this->storage_key, $tables);
        
        // Clear cache
        $this->clear_table_cache($table_id ?: $new_id);
    }
    
    private function delete_table() {
        if (!current_user_can('manage_options')) {
            $this->log_error('Unauthorized table delete attempt');
            return;
        }
        
        $table_id = intval($_POST['delete_table']);
        $tables = get_option($this->storage_key, array());
        
        if (isset($tables[$table_id])) {
            unset($tables[$table_id]);
            update_option($this->storage_key, $tables);
            $this->clear_table_cache($table_id);
        }
    }
    
    private function clear_table_cache($table_id) {
        delete_transient('colorful_table_' . $table_id);
    }
    
    private function log_error($message) {
        if (WP_DEBUG === true) {
            error_log('Colorful Tables Pro: ' . $message);
        }
    }
    
    public function shortcode($atts) {
        $atts = shortcode_atts(array('id' => '0'), $atts);
        $table_id = intval($atts['id']);
        
        if ($table_id <= 0) {
            return '<p>' . __('Неверный ID таблицы', 'colorful-tables-pro') . '</p>';
        }
        
        // Check cache
        $cache_key = 'colorful_table_' . $table_id;
        $cached = get_transient($cache_key);
        
        if ($cached !== false) {
            return $cached;
        }
        
        $tables = get_option($this->storage_key, array());
        
        if (!isset($tables[$table_id])) {
            return '<p>' . __('Таблица не найдена', 'colorful-tables-pro') . '</p>';
        }
        
        $output = $this->render_table($tables[$table_id]);
        
        // Cache for 1 hour
        set_transient($cache_key, $output, HOUR_IN_SECONDS);
        
        return $output;
    }
    
    private function render_table($table) {
        $columns = $table['columns'];
        $headers = $table['headers'];
        $rows = $table['rows'];
        $style = $table['style'];
        
        $output = '<div class="ct-table-container style-' . esc_attr($style) . '">';
        
        // Desktop version
        $output .= '<div class="ct-table-desktop" style="grid-template-columns: repeat(' . esc_attr($columns) . ', 1fr)">';
        
        // Headers for desktop
        foreach ($headers as $header) {
            $header_style = '';
            if (!empty($header['bg_color'])) $header_style .= 'background-color: ' . esc_attr($header['bg_color']) . ';';
            if (!empty($header['text_color'])) $header_style .= 'color: ' . esc_attr($header['text_color']) . ';';
            
            $output .= '<div class="ct-table-header" style="' . $header_style . '">';
            $output .= esc_html($header['content']);
            $output .= '</div>';
        }
        
        // Cells for desktop
        foreach ($rows as $row) {
            foreach ($row['cells'] as $cell) {
                $cell_style = '';
                if (!empty($cell['bg_color'])) $cell_style .= 'background-color: ' . esc_attr($cell['bg_color']) . ';';
                if (!empty($cell['text_color'])) $cell_style .= 'color: ' . esc_attr($cell['text_color']) . ';';
                
                $output .= '<div class="ct-table-cell" style="' . $cell_style . '">';
                $output .= wp_kses_post($cell['content']);
                $output .= '</div>';
            }
        }
        
        $output .= '</div>'; // .ct-table-desktop
        
        // Mobile version
        $output .= '<div class="ct-table-mobile">';
        
        foreach ($rows as $row_index => $row) {
            $output .= '<div class="ct-mobile-row">';
            
            foreach ($row['cells'] as $cell_index => $cell) {
                if (isset($headers[$cell_index])) {
                    $cell_style = '';
                    if (!empty($cell['bg_color'])) $cell_style .= 'background-color: ' . esc_attr($cell['bg_color']) . ';';
                    if (!empty($cell['text_color'])) $cell_style .= 'color: ' . esc_attr($cell['text_color']) . ';';
                    
                    $output .= '<div class="ct-mobile-cell" style="' . $cell_style . '">';
                    $output .= '<div class="ct-mobile-header">' . esc_html($headers[$cell_index]['content']) . '</div>';
                    $output .= '<div class="ct-mobile-content">' . wp_kses_post($cell['content']) . '</div>';
                    $output .= '</div>';
                }
            }
            
            $output .= '</div>'; // .ct-mobile-row
        }
        
        $output .= '</div>'; // .ct-table-mobile
        $output .= '</div>'; // .ct-table-container
        
        return $output;
    }
}

new ColorfulTablesPro();

А этот плагин я выложу в открытый доступ — может, кому-то ещё пригодится решение, выстраданное реальными потребностями.

P.S. Кстати, эту статью тоже помогал писать DeepSeek — я просто поделился мыслями, а он оформил их в связный текст. Работа в тандеме — это круто!

Скачать архив с плагином: скачать

 

А вы пользуетесь кастомными плагинами?

Поделитесь своим опытом в комментариях!

Метки: , , , , , , , , , , , , , , , , , , ,

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Реклама

Календарь

Октябрь 2025
Пн Вт Ср Чт Пт Сб Вс
 12345
6789101112
13141516171819
20212223242526
2728293031  

Вход и регистрация

Рубрики

Реклама

Мои сайты

Новые

Облако меток