Страницы, статьи и новости
Инструменты и плагины
Заготовка заголовок


Описание как пример заготовки. Пример текстового контента для дальнейшей кастомизации шаблона "Index36".

Редактировать шаблон вы можете на свое усмотрение и как вам угодно. Если у вас на это нет времени или недостаточно знаний - вы всегда можете заказать адаптацию шаблона сайта написав мне по контактам на GitHub или в личные сообщения на публичной странице сайта маркетплейса цифровых товаров

05.11.2025 01:07

Как решить конфликт Select2 и Bootstrap 5 в Cotonti Siena (0.9.26+)

При использовании Select2 с Bootstrap 5 в Cotonti Siena часто возникает проблема: после подключения стилей Bootstrap (в частности, класса form-select) Select2 перестаёт работать — поле становится неактивным, пропадает поиск, не отображаются теги.

Это происходит потому, что:

  • Bootstrap 5 переопределяет стили и поведение <select> через класс form-select.
  • Select2 полностью заменяет DOM-структуру <select> и не совместим с form-select.
  • Cotonti по умолчанию использует шаблон $R['input_select'], который вы добавляете form-select ко всем <select>.

 

Причина конфликта

$R['input_select'] = '<select class="form-select" name="{$name}"{$attrs}>{$options}</select>';

Этот шаблон применяется ко всем полям <select>, включая те, где используется Select2. В результате:

<select class="form-select user-input" multiple>...</select>

→ Select2 не может инициализироваться → поле "заморожено".


Правильное решение: использование $rc_name в cot_selectbox()

Cotonti умнее, чем кажется. Функция cot_selectbox() автоматически выбирает шаблон по имени поля:

$rc_name = preg_match('#^(\w+)\[(.*?)\]$#', $name, $mt) ? $mt[1] : $name;
  • rs[setuser][] → $rc_name = 'rs'
  • rtags[title] → $rc_name = 'rtags'
  • category → $rc_name = 'category'

→ Cotonti ищет шаблон: $R["input_select_{$rc_name}"] → если есть, использует его.

Решение: создаём отдельные шаблоны по префиксу

В файле темы:

themes/ваша_тема/ваша_тема.php

<?php
defined('COT_CODE') or die('Wrong URL.');

global $R;

// Для полей rs[...] — БЕЗ form-select (Select2)
$R['input_select_rs'] = '<select name="{$name}"{$attrs}>{$options}</select>';

// Для полей rtags[...] — тоже без form-select (если используется Select2)
$R['input_select_rtags'] = '<select name="{$name}"{$attrs}>{$options}</select>';

// Для всех остальных — с Bootstrap 5
$R['input_select'] = '<select class="form-select" name="{$name}"{$attrs}>{$options}</select>';

Как это работает

Поле$rc_nameШаблон ключа строки $Rform-select
rs[setuser][]rsinput_select_rsНет
rtags[title]rtagsinput_select_rtagsНет
ruserlangruserlanginput_selectДа

Ничего не меняем в коде

  • Не трогаем UsersHelper
  • Не переопределяем cot_selectbox()
  • Не используем хуки, CSS-хаки или Closure
  • Работает в ядре 0.9.26+

Дополнительно: Bootstrap 5 тема для Select2 (по желанию, то есть не обязательно)

Чтобы Select2 выглядел как Bootstrap:

<link href="https://cdn.jsdelivr.net/npm/@ttskch/[email protected]/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
$('.user-input').select2({
    theme: 'bootstrap-5',
    width: '100%'
});

Вывод

Не боритесь с form-select — управляйте шаблонами по имени поля.

$R['input_select_ПРЕФИКС'] = '<select ...>';  // без form-select
$R['input_select']       = '<select class="form-select" ...>'; // для остальных

Просто. Чисто. Лаконично. Без правок ядра. Без ошибок. Без магии. Решение найдено. Не стесняемся сказать спасибо!

 

Cotonti в действительности ищет ресурс с именем:

$R["input_select_{$rc_name}"]


где $rc_name — это имя поля без квадратных скобок и ДО их обїявления в исходном коде.
Если поле называется, например,

<select name="rs[setuser][]">


то Cotonti будет искать:

$R['input_select_rs']


и только если его нет, тогда уже возьмёт $R['input_select'].

Итого

Решение:

$R['input_select_rtags'] = '<select name="{$name}"{$attrs}>{$options}</select>'; // имя поля rtags
$R['input_select_rs'] = '<select name="{$name}"{$attrs}>{$options}</select>'; // имя поля rs
// Для всех остальных — с Bootstrap 5
$R['input_select'] = '<select class="form-select" name="{$name}"{$attrs}>{$options}</select>';

и так по примеру выше для каждого поля, где будет и должен использоваться Select2


— это абсолютно корректный, “канонический” способ убрать Bootstrap-класс только для нужных полей (например, rs[...]), не ломая остальные.

Почему это работает

Cotonti автоматически парсит имя до квадратных скобок:
rs[setuser][] → $rc_name = 'rs'

Ищет $R['input_select_rs']

Мы задаем именно этот шаблон без form-select в строке кастомных ресурсов

$R['input_select_rs'] = '<select name="{$name}"{$attrs}>{$options}</select>'; // имя поля rs

Для всех остальных остаётся общий $R['input_select'] с Bootstrap

Это нативный способ сегментировать шаблоны по префиксу имени поля, и он даже чище, чем городить хардкором.

Узнать больше - смотреть Form generation API system/forms.php

сначала смотрим переменную

$rc_name

а затем

$rc = empty($R["input_select_{$rc_name}"]) ? (empty($custom_rc) ? 'input_select' : $custom_rc) : "input_select_{$rc_name}";

внутри функции 

/**
* Renders a dropdown
*
* @param string|string[] $chosen Selected value (or values array for mutli-select)
* @param string $name Dropdown name
* @param string[] $values Options available
* @param string[] $titles Titles for options
* @param bool $add_empty Allow empty choice
* @param string|array<string, string> $attrs Additional attributes as an associative array or a string
* @param string $custom_rc Custom resource string name
* @param bool $htmlspecialcharsBypass Bypass htmlspecialchars() for option titles and values
* @return string
*/
function cot_selectbox(
   $chosen,
   $name,
   $values,
   $titles = [],
   $add_empty = true,
   $attrs = '',
   $custom_rc = '',
   $htmlspecialcharsBypass = false
) {
    global $R, $cfg;
    if (!is_array($values)) {
        $values = explode(',', $values);
    }
    if (!is_array($titles)) {
        $titles = explode(',', $titles);
    }
   $msgSeparate = isset($cfg['msg_separate'])? $cfg['msg_separate'] : false;
    $use_titles = count($values) == count($titles);
    $input_attrs = cot_rc_attr_string($attrs);
    $chosen = cot_import_buffered($name, $chosen);
   $multi = is_array($chosen) && (mb_strpos($input_attrs, 'multiple') !== false);
    $error = $msgSeparate ? cot_implode_messages($name, 'error') : '';
    $rc_name = preg_match('#^(\w+)\[(.*?)\]$#', $name, $mt) ? $mt[1] : $name;
    $selected = (is_null($chosen) || $chosen === '' || $chosen == '00') ? ' selected="selected"' : '';
    $rc = empty($R["input_option_{$rc_name}"]) ? 'input_option' : "input_option_{$rc_name}";
   $options = '';
    if ($add_empty) {
        $options .= cot_rc($rc, [
            'value' => '',
            'selected' => $selected,
            'title' => $R['code_option_empty']
        ]);
    }
    foreach ($values as $k => $x) {
        $x = trim($x);
        $selected = ($multi && in_array($x, $chosen)) || (!$multi && $x == $chosen) ? ' selected="selected"' : '';
        $title = ($use_titles && !empty($titles[$k])) ? $titles[$k] : $x;
       if (!$htmlspecialcharsBypass) {
           $title = htmlspecialchars($title);
       }
        $options .= cot_rc($rc, [
            'value' => $htmlspecialcharsBypass ? $x : htmlspecialchars($x),
            'selected' => $selected,
            'title' => $title
        ]);
    }
    $rc = empty($R["input_select_{$rc_name}"]) ? (empty($custom_rc) ? 'input_select' : $custom_rc) : "input_select_{$rc_name}";
    $result = cot_rc($rc, [
        'name' => $name,
        'attrs' => $input_attrs,
        'error' => $error,
        'options' => $options
    ]);
    return $result;
}

 

 

Отредактировано: webitproff (05.11.2025 01:41, 7 месяцев назад)
Аккаунт