Pages, Articles & News
Tools & Plugins
Example / Placeholder Title


Description as an example placeholder. Sample text content for further customization of the "Index36" template.

You are free to edit and customize the template however you like. If you don’t have time or enough knowledge — you can always order template adaptation by contacting me via GitHub or private messages on the digital goods marketplace.

2025-11-05 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;
}

 

 

This post was edited by webitproff (2025-11-05 01:41, 7 months ago)
Account