bootstrap_five_layouts-1.0.x-dev/modules/bootstrap_five_layouts_css_loader/bootstrap_five_layouts_css_loader.module
modules/bootstrap_five_layouts_css_loader/bootstrap_five_layouts_css_loader.module
<?php
/**
* @file
* Bootstrap Five Layout CSS Loader module.
*
* Provides automatic loading of Bootstrap 5 grid and utilities CSS.
*/
/**
* Implements hook_help().
*/
function bootstrap_five_layouts_css_loader_help($route_name, $route_match) {
switch ($route_name) {
case 'help.page.bootstrap_five_layouts_css_loader':
$output = '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Bootstrap Five Layouts CSS Loader module provides automatic loading of Bootstrap 5 grid and utilities CSS. It offers flexible loading methods and exposes Bootstrap grid classes for programmatic use.') . '</p>';
$output .= '<h3>' . t('Features') . '</h3>';
$output .= '<ul>';
$output .= '<li>' . t('Automatic loading of Bootstrap 5 CSS libraries') . '</li>';
$output .= '<li>' . t('Multiple loading methods: page attachments, preprocess_page, and preprocess_html') . '</li>';
$output .= '<li>' . t('Access to Bootstrap grid classes via API functions') . '</li>';
$output .= '<li>' . t('Over 300+ Bootstrap grid classes available programmatically') . '</li>';
$output .= '</ul>';
$output .= '<h3>' . t('Usage') . '</h3>';
$output .= '<p>' . t('Once enabled, the module will automatically load Bootstrap 5 CSS according to your configuration. You can also use the provided API functions to access Bootstrap grid class information programmatically:') . '</p>';
$output .= _bootstrap_five_layouts_css_loader_overview_table();
return $output;
default:
return '';
}
}
function _bootstrap_five_layouts_css_loader_overview_table() {
$output = '<section id="classlist-helper">';
$output .= '<h1 id="classlist-tabs-header">' . t('BS5 Layouts CSS Loader » Class List Filter') . '</h1>';
$yaml_files = [
'bootstrap-grid-classes',
'bootstrap-utilities-classes',
];
$yaml_data = [];
foreach ($yaml_files as $yaml_file) {
$yaml_data[$yaml_file] = _bootstrap_five_layouts_css_loader_read_yaml($yaml_file);
}
$output .= '<ul id="panel-tab">';
foreach ($yaml_files as $yaml_file) {
$yaml_info = $yaml_data[$yaml_file];
if (!empty($yaml_info['data'])) {
$output .= '<li><a href="#' . $yaml_file . '" data-panel="'.$yaml_file.'-panel">' . $yaml_info['metadata']['title'] . ' <small>(<span class="count">'.$yaml_info['metadata']['total_classes'] . '</span>)</small></a></li>';
}
}
$output .= '</ul>';
foreach ($yaml_files as $yaml_file) {
$yaml_info = $yaml_data[$yaml_file];
// Only display if we have data
if (!empty($yaml_info['data'])) {
$output .= '<div id="'.$yaml_file.'-panel" class="panel"><h2 id="'.$yaml_file.'">' . $yaml_info['metadata']['title'] . '</h2>';
$output .= '<table class="table">';
$output .= '<thead><tr>';
foreach ($yaml_info['headers'] as $header) {
$output .= '<th>' . $header . '</th>';
}
$output .= '</tr></thead>';
$output .= '<tbody>';
foreach ($yaml_info['data'] as $class_info) {
$output .= '<tr>';
foreach ($class_info as $col=>$content) {
$output .= '<td>' . $content. '</td>';
}
$output .= '</tr>';
}
$output .= '</tbody>';
$output .= '</table>';
$output .= '<a href="#classlist-tabs-header">' . t('Back to top of Class Lists') . '</a>';
$output .= '</div>';
}
}
$output .= '</section>';
return $output;
}
/**
* Gets the configured Bootstrap library to load.
*
* @return string|null
* The library name to load, or NULL if disabled.
*/
function _bootstrap_five_layouts_css_loader_get_library() {
return 'bootstrap_five_layouts_css_loader/toolkit';
}
/**
* Gets the configured loading method.
*
* @return string
* The loading method.
*/
function _bootstrap_five_layouts_css_loader_get_method() {
$config = \Drupal::config('bootstrap_five_layouts_css_loader.settings');
return $config->get('load_method') ?: 'page_attachments';
}
/**
* Implements hook_preprocess_page().
*
* Attaches Bootstrap 5 CSS libraries to all pages when configured to do so.
*/
function bootstrap_five_layouts_css_loader_preprocess_page(&$variables) {
$method = _bootstrap_five_layouts_css_loader_get_method();
$library = _bootstrap_five_layouts_css_loader_get_library();
if ($method === 'preprocess_page' && $library) {
$variables['#attached']['library'][] = $library;
}
}
/**
* Implements hook_preprocess_html().
*
* Ensures Bootstrap CSS is loaded early in the HTML head when configured to do so.
*/
function bootstrap_five_layouts_css_loader_preprocess_html(&$variables) {
$method = _bootstrap_five_layouts_css_loader_get_method();
$library = _bootstrap_five_layouts_css_loader_get_library();
if ($method === 'preprocess_html' && $library) {
$variables['#attached']['library'][] = $library;
}
}
/**
* Implements hook_page_attachments().
*
* Default method to attach libraries globally.
*/
function bootstrap_five_layouts_css_loader_page_attachments(array &$attachments) {
$method = _bootstrap_five_layouts_css_loader_get_method();
$library = _bootstrap_five_layouts_css_loader_get_library();
if ($method === 'page_attachments' && $library) {
$attachments['#attached']['library'][] = $library;
}
// Attach class-helper library only on the help page
$route_match = \Drupal::routeMatch();
if ($route_match->getParameters()->get('name') === 'bootstrap_five_layouts_css_loader') {
$attachments['#attached']['library'][] = 'bootstrap_five_layouts_css_loader/class-helper';
}
}
/**
* Reads and parses the Bootstrap YAML files using Drupal's Config system.
*
* @param string $file
* The YAML file name (without extension).
*
* @return array
* An array with 'data', 'title', 'headers', and 'metadata' keys.
*/
function _bootstrap_five_layouts_css_loader_read_yaml($file = 'bootstrap-grid-classes') {
static $cached_files = [];
if (!isset($cached_files[$file])) {
$cached_files[$file] = ['data' => [], 'title' => '', 'headers' => [], 'metadata' => []];
// Use Drupal's Config system to load the YAML file
$config = \Drupal::config("bootstrap_five_layouts_css_loader.$file");
// Check if config has data
if ($config instanceof \Drupal\Core\Config\ImmutableConfig && $config->get()) {
// Extract metadata if available
$metadata = $config->get('metadata');
if ($metadata) {
$cached_files[$file]['metadata'] = $metadata;
// Get title from metadata, fallback to generated title
$cached_files[$file]['title'] = $metadata['title'];
}
// Process the data structure based on file type
if ($file === 'bootstrap-grid-classes') {
$processed = _bootstrap_five_layouts_css_loader_process_grid_yaml($config);
$cached_files[$file]['data'] = $processed['data'];
$cached_files[$file]['headers'] = $processed['headers'];
} else {
$processed = _bootstrap_five_layouts_css_loader_process_utilities_yaml($config);
$cached_files[$file]['data'] = $processed['data'];
$cached_files[$file]['headers'] = $processed['headers'];
}
}
}
return $cached_files[$file];
}
/**
* Processes a section of grid YAML data into a flat array for display.
*
* @param array $section_data
* The section data to process.
* @param array &$classes
* Reference to the classes array to populate.
* @param string $default_breakpoint
* The default breakpoint to use.
*/
function _bootstrap_five_layouts_css_loader_process_grid_section($section_data, &$classes, $default_breakpoint = 'XS/All') {
if (!$section_data) {
return;
}
// Check if this is a direct array (like containers, rows)
if (isset($section_data[0]) && isset($section_data[0]['class_name'])) {
// Direct array structure: [...]
foreach ($section_data as $item) {
if (isset($item['class_name'])) {
$classes[] = [
'class_name' => $item['class_name'],
'category' => $item['category'],
'breakpoint' => isset($item['breakpoint']) ? $item['breakpoint'] : $default_breakpoint,
'description' => $item['description'],
'width_spacing' => $item['width'],
];
}
}
} else {
// Object structure: {breakpoint: [...]} or {breakpoint: {breakpoint: "name", classes: [...]}}
foreach ($section_data as $breakpoint => $items) {
if (is_array($items)) {
$items_to_process = [];
$breakpoint_name = $default_breakpoint;
if (isset($items['classes']) && is_array($items['classes'])) {
// Structure: {breakpoint: {breakpoint: "name", classes: [...]}}
$items_to_process = $items['classes'];
$breakpoint_name = isset($items['breakpoint']) ? $items['breakpoint'] : $default_breakpoint;
} else {
// Structure: {breakpoint: [...]} - direct array
$items_to_process = $items;
$breakpoint_name = $default_breakpoint;
}
foreach ($items_to_process as $item) {
if (isset($item['class_name'])) {
$classes[] = [
'class_name' => $item['class_name'],
'category' => $item['category'],
'breakpoint' => isset($item['breakpoint']) ? $item['breakpoint'] : $breakpoint_name,
'description' => $item['description'],
'width_spacing' => $item['width'],
];
}
}
}
}
}
}
/**
* Processes grid YAML data into a flat array for display.
*
* @param \Drupal\Core\Config\Config $config
* The config object containing the YAML data.
*
* @return array
* An array with 'data' and 'headers' keys.
*/
function _bootstrap_five_layouts_css_loader_process_grid_yaml($config) {
$classes = [];
$headers = [t('Class Name'), t('Category'), t('Breakpoint'), t('Description'), t('Width/Spacing')];
// Process containers
$containers = $config->get('bootstrap_grid_classes.containers');
_bootstrap_five_layouts_css_loader_process_grid_section($containers, $classes);
// Process rows
$rows = $config->get('bootstrap_grid_classes.rows');
_bootstrap_five_layouts_css_loader_process_grid_section($rows, $classes);
// Process columns
$columns = $config->get('bootstrap_grid_classes.columns');
_bootstrap_five_layouts_css_loader_process_grid_section($columns, $classes);
// Process row columns
$row_columns = $config->get('bootstrap_grid_classes.row_columns');
_bootstrap_five_layouts_css_loader_process_grid_section($row_columns, $classes);
// Process offsets
$offsets = $config->get('bootstrap_grid_classes.offsets');
_bootstrap_five_layouts_css_loader_process_grid_section($offsets, $classes);
// Process gutters
$gutters = $config->get('bootstrap_grid_classes.gutters');
_bootstrap_five_layouts_css_loader_process_grid_section($gutters, $classes);
// Process responsive variants
$responsive_variants = $config->get('bootstrap_grid_classes.responsive_variants');
if ($responsive_variants) {
foreach ($responsive_variants as $variant_name => $variant_data) {
if (isset($variant_data['classes']) && is_array($variant_data['classes'])) {
_bootstrap_five_layouts_css_loader_process_grid_section($variant_data['classes'], $classes, $variant_data['breakpoint']);
}
}
}
return ['data' => $classes, 'headers' => $headers];
}
/**
* Processes utilities YAML data into a flat array for display.
*
* @param \Drupal\Core\Config\Config $config
* The config object containing the YAML data.
*
* @return array
* An array with 'data' and 'headers' keys.
*/
function _bootstrap_five_layouts_css_loader_process_utilities_yaml($config) {
$classes = [];
$headers = [t('Class Name'), t('Category'), t('Breakpoint'), t('Description')];
$utilities_classes = $config->get('bootstrap_utilities_classes');
if ($utilities_classes) {
foreach ($utilities_classes as $category => $breakpoints) {
foreach ($breakpoints as $breakpoint => $class_list) {
if (is_array($class_list)) {
foreach ($class_list as $class_data) {
// Handle both old string format and new object format
if (is_string($class_data)) {
// Fallback for old string format - use generic description
$classes[] = [
'class_name' => $class_data,
'category' => ucfirst($category),
'breakpoint' => _bootstrap_five_layouts_css_loader_map_breakpoint($breakpoint),
'description' => 'Utility class',
];
} else {
$classes[] = [
'class_name' => $class_data['class_name'],
'category' => ucfirst($category),
'breakpoint' => _bootstrap_five_layouts_css_loader_map_breakpoint($breakpoint),
'description' => $class_data['description'],
];
}
}
}
}
}
}
return ['data' => $classes, 'headers' => $headers];
}
/**
* Maps YAML breakpoint keys to display names.
*
* @param string $breakpoint
* The breakpoint key from YAML.
*
* @return string
* The display name for the breakpoint.
*/
function _bootstrap_five_layouts_css_loader_map_breakpoint($breakpoint) {
$breakpoint_map = [
'xs' => 'XS',
'sm' => 'SM (≥576px)',
'md' => 'MD (≥768px)',
'lg' => 'LG (≥992px)',
'xl' => 'XL (≥1200px)',
'xxl' => 'XXL (≥1400px)',
'print' => t('Print'),
];
return $breakpoint_map[$breakpoint] ?? ucfirst($breakpoint);
}
