Игорь Надеин 8159c6f472 feat: Replace Bootstrap buttons with custom button system
### Custom Button System Implementation

Created modern button system to replace Bootstrap buttons:

- ✅ Created core/_buttons-custom.scss (550+ lines)
- ✅ CSS Custom Properties for easy customization
- ✅ 8 color variants (primary, secondary, success, danger, warning, info, light, dark)
- ✅ Outline and Ghost variants
- ✅ 4 sizes (sm, md, lg, xl)
- ✅ Special variants (block, icon, circle, link)
- ✅ Loading state with spinner animation
- ✅ Button groups support
- ✅ Modern animations (hover, active, focus)

### Template Migration

Migrated 65 HTML templates using automated Python script:
- Replaced .btn → .button
- Replaced .btn-primary → .button--primary
- Replaced .btn-outline-* → .button--outline button--*
- Replaced size classes .btn-sm → .button--sm
- Total changes: 354 → 347 button usages

### Results

- app.css: 137 KB → 148 KB (+11 KB for custom buttons)
- Templates changed: 65 files
- Remaining .btn classes: 133 (mostly btn-close for modals)
- New .button classes: 347

### Backward Compatibility

Kept @extend rules in _buttons-custom.scss for gradual migration.
Can be removed after complete migration.

Next step: Remove Bootstrap button imports from custom-bootstrap.scss
2026-01-15 14:23:44 +03:00

120 lines
4.8 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{# Универсальный компонент шапки страницы списка. См. документацию в PAGE_HEADER_SUMMARY.md #}
{# ===== ШАПКА СТРАНИЦЫ ===== #}
<div class="page-header">
<div class="d-flex align-items-center gap-2">
{# Иконка #}
{% if icon %}
<i class="{{ icon }} fs-2 text-primary flex-shrink-0"></i>
{% endif %}
{# Заголовок #}
<h2 class="page-title mb-0">{{ title }}</h2>
{# Счётчик (если есть) #}
{% if count is not None %}
<span class="page-count text-secondary">
{{ count_label|default:"Всего:" }} <span class="count-value">{{ count }}</span>
</span>
{% endif %}
{# Правая часть - кнопки и поиск #}
<div class="ms-auto d-flex align-items-center gap-2">
{# Дополнительные кнопки (slot) #}
{% if extra_buttons %}
{{ extra_buttons }}
{% endif %}
{# Поиск iOS-стиль #}
{% if not no_search and search_id %}
<div class="page-search ios-search">
<i class="bi-search"></i>
<input type="search"
id="{{ search_id }}"
class="ios-search-input"
placeholder="{{ search_placeholder|default:'Поиск...' }}"
autocomplete="off">
<button type="button" class="ios-search-clear" aria-label="Очистить">
<i class="bi-x-lg"></i>
</button>
</div>
{% endif %}
{# Кнопка раскрытия фильтров #}
{% if has_filters and filters_target %}
<button class="button button--outline button--secondary btn-circle page-filter-toggle"
type="button"
data-bs-toggle="collapse"
data-bs-target="#{{ filters_target }}"
aria-expanded="false"
aria-controls="{{ filters_target }}"
aria-label="Показать фильтры">
<i class="bi-funnel"></i>
</button>
{% endif %}
{# Кнопка создания (только если create_as_card не установлен) #}
{% if not no_create and not create_as_card %}
{% if create_modal %}
<button type="button"
class="button button--primary button--sm btn-pill button--icon page-create-button"
data-bs-toggle="modal"
data-bs-target="#{{ create_modal }}">
<i class="{{ create_icon|default:'bi-plus-lg' }}"></i>
<span class="btn-text">{{ create_text|default:"Создать" }}</span>
</button>
{% elif create_url %}
<a href="{{ create_url }}"
class="button button--primary button--sm btn-pill button--icon page-create-button">
<i class="{{ create_icon|default:'bi-plus-lg' }}"></i>
<span class="btn-text">{{ create_text|default:"Создать" }}</span>
</a>
{% endif %}
{% endif %}
</div>
</div>
{# ===== БЛОК ФИЛЬТРОВ (скрываемый) ===== #}
{% if has_filters and filters_target %}
<div class="collapse page-filters-collapse" id="{{ filters_target }}">
<div class="page-filters">
{% if filters_template %}
{# Включаем внешний шаблон фильтров #}
{% include filters_template %}
{% else %}
{# Или используем переданный HTML #}
{{ filters_html|safe }}
{% endif %}
</div>
</div>
{% endif %}
{# ===== КНОПКА СОЗДАНИЯ В ВИДЕ КАРТОЧКИ ===== #}
{% if not no_create and create_as_card %}
{% if create_modal %}
<div class="card mb-2">
<button type="button"
class="dept-row d-flex w-100 justify-content-center align-items-center gap-2 text-decoration-none border-0 bg-transparent"
data-bs-toggle="modal"
data-bs-target="#{{ create_modal }}"
role="button"
aria-label="{{ create_text|default:'Создать' }}">
<span class="card-title">{{ create_text|default:"Создать" }}</span>
<i class="{{ create_icon|default:'bi-plus-lg' }}"></i>
</button>
</div>
{% elif create_url %}
<div class="card mb-2">
<a href="{{ create_url }}"
class="dept-row d-flex w-100 justify-content-center align-items-center gap-2 text-decoration-none border-0 bg-transparent"
role="button"
aria-label="{{ create_text|default:'Создать' }}">
<span class="card-title">{{ create_text|default:"Создать" }}</span>
<i class="{{ create_icon|default:'bi-plus-lg' }}"></i>
</a>
</div>
{% endif %}
{% endif %}
</div>