HH/static/js/bootstrap-to-tailwind.js
2026-04-19 10:53:12 +03:00

366 lines
12 KiB
JavaScript

/**
* Bootstrap to Tailwind Migration Helper
*
* This script provides class mappings for migrating Bootstrap templates to Tailwind CSS.
* Usage: Include this script temporarily during migration to add Bootstrap-compatible classes.
*
* NOTE: This is a temporary compatibility layer. Templates should be properly migrated
* to use px360.css classes (hh-* prefix) for long-term maintainability.
*/
(function() {
// Bootstrap to Tailwind class mappings
const bootstrapToTailwind = {
// Container & Layout
'container': 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8',
'container-fluid': 'w-full px-4',
'row': 'flex flex-wrap -mx-2',
'col': 'w-full px-2',
'col-auto': 'w-auto px-2',
// Grid columns (simplified mapping)
'col-1': 'w-1/12 px-2',
'col-2': 'w-2/12 px-2',
'col-3': 'w-3/12 px-2',
'col-4': 'w-4/12 px-2',
'col-5': 'w-5/12 px-2',
'col-6': 'w-6/12 px-2',
'col-7': 'w-7/12 px-2',
'col-8': 'w-8/12 px-2',
'col-9': 'w-9/12 px-2',
'col-10': 'w-10/12 px-2',
'col-11': 'w-11/12 px-2',
'col-12': 'w-full px-2',
// Responsive breakpoints
'col-md-1': 'md:w-1/12 px-2',
'col-md-2': 'md:w-2/12 px-2',
'col-md-3': 'md:w-3/12 px-2',
'col-md-4': 'md:w-4/12 px-2',
'col-md-6': 'md:w-6/12 px-2',
'col-md-8': 'md:w-8/12 px-2',
'col-md-12': 'md:w-full px-2',
'col-lg-3': 'lg:w-3/12 px-2',
'col-lg-4': 'lg:w-4/12 px-2',
'col-lg-6': 'lg:w-6/12 px-2',
'col-lg-8': 'lg:w-8/12 px-2',
'col-lg-12': 'lg:w-full px-2',
// Display & Flex
'd-flex': 'flex',
'd-inline-flex': 'inline-flex',
'd-block': 'block',
'd-inline-block': 'inline-block',
'd-none': 'hidden',
'd-grid': 'grid',
'justify-content-start': 'justify-start',
'justify-content-end': 'justify-end',
'justify-content-center': 'justify-center',
'justify-content-between': 'justify-between',
'justify-content-around': 'justify-around',
'align-items-start': 'items-start',
'align-items-end': 'items-end',
'align-items-center': 'items-center',
'align-items-baseline': 'items-baseline',
'align-items-stretch': 'items-stretch',
'flex-row': 'flex-row',
'flex-column': 'flex-col',
'flex-wrap': 'flex-wrap',
'flex-nowrap': 'flex-nowrap',
'flex-grow-1': 'flex-grow',
'flex-shrink-0': 'flex-shrink-0',
// Spacing (simplified)
'm-0': 'm-0',
'm-1': 'm-1',
'm-2': 'm-2',
'm-3': 'm-4',
'm-4': 'm-6',
'm-5': 'm-8',
'mt-1': 'mt-1',
'mt-2': 'mt-2',
'mt-3': 'mt-4',
'mt-4': 'mt-6',
'mt-5': 'mt-8',
'mb-1': 'mb-1',
'mb-2': 'mb-2',
'mb-3': 'mb-4',
'mb-4': 'mb-6',
'mb-5': 'mb-8',
'ms-1': 'ms-1',
'ms-2': 'ms-2',
'ms-3': 'ms-4',
'ms-auto': 'ms-auto',
'me-1': 'me-1',
'me-2': 'me-2',
'me-3': 'me-4',
'me-auto': 'me-auto',
'p-0': 'p-0',
'p-1': 'p-1',
'p-2': 'p-2',
'p-3': 'p-4',
'p-4': 'p-6',
'p-5': 'p-8',
'px-1': 'px-1',
'px-2': 'px-2',
'px-3': 'px-4',
'px-4': 'px-6',
'py-1': 'py-1',
'py-2': 'py-2',
'py-3': 'py-4',
'py-4': 'py-6',
'gap-1': 'gap-1',
'gap-2': 'gap-2',
'gap-3': 'gap-4',
// Text
'text-start': 'text-left',
'text-end': 'text-right',
'text-center': 'text-center',
'text-truncate': 'truncate',
'text-wrap': 'break-words',
'text-nowrap': 'whitespace-nowrap',
'fw-bold': 'font-bold',
'fw-semibold': 'font-semibold',
'fw-normal': 'font-normal',
'fw-light': 'font-light',
'fst-italic': 'italic',
'text-muted': 'text-gray-500',
'text-primary': 'text-blue-600',
'text-success': 'text-green-600',
'text-danger': 'text-red-600',
'text-warning': 'text-yellow-600',
'text-info': 'text-blue-500',
'fs-1': 'text-5xl',
'fs-2': 'text-4xl',
'fs-3': 'text-3xl',
'fs-4': 'text-2xl',
'fs-5': 'text-xl',
'fs-6': 'text-base',
'small': 'text-sm',
// Background & Colors
'bg-primary': 'bg-blue-600',
'bg-secondary': 'bg-gray-500',
'bg-success': 'bg-green-500',
'bg-danger': 'bg-red-500',
'bg-warning': 'bg-yellow-500',
'bg-info': 'bg-blue-400',
'bg-light': 'bg-gray-100',
'bg-dark': 'bg-gray-800',
'bg-white': 'bg-white',
'bg-transparent': 'bg-transparent',
// Border
'border': 'border',
'border-0': 'border-0',
'border-top': 'border-t',
'border-end': 'border-e',
'border-bottom': 'border-b',
'border-start': 'border-s',
'rounded': 'rounded',
'rounded-0': 'rounded-none',
'rounded-1': 'rounded-sm',
'rounded-2': 'rounded',
'rounded-3': 'rounded-lg',
'rounded-circle': 'rounded-full',
// Sizing
'w-25': 'w-1/4',
'w-50': 'w-1/2',
'w-75': 'w-3/4',
'w-100': 'w-full',
'w-auto': 'w-auto',
'h-25': 'h-1/4',
'h-50': 'h-1/2',
'h-75': 'h-3/4',
'h-100': 'h-full',
'h-auto': 'h-auto',
// Position
'position-static': 'static',
'position-relative': 'relative',
'position-absolute': 'absolute',
'position-fixed': 'fixed',
'position-sticky': 'sticky',
// Overflow
'overflow-auto': 'overflow-auto',
'overflow-hidden': 'overflow-hidden',
// Visibility
'visible': 'visible',
'invisible': 'invisible',
// Screen readers
'visually-hidden': 'sr-only',
'visually-hidden-focusable': 'sr-only focus:not-sr-only',
};
// Component-specific mappings (these need more complex handling)
const componentMappings = {
// Table classes - use px360.css hh-table instead
'table': 'hh-table',
'table-hover': '', // hh-table has hover built-in
'table-striped': '', // would need separate class
'table-bordered': 'border-collapse border',
'table-sm': 'text-sm',
'table-responsive': 'overflow-x-auto',
// Card classes - use px360.css hh-card instead
'card': 'hh-card',
'card-body': 'hh-card-body',
'card-header': 'hh-card-header',
'card-footer': 'hh-card-footer',
'card-title': 'text-lg font-semibold',
'card-text': 'text-gray-600',
// Button classes - use px360.css hh-btn-* instead
'btn': 'hh-btn',
'btn-primary': 'hh-btn hh-btn-primary',
'btn-secondary': 'hh-btn hh-btn-secondary',
'btn-success': 'hh-btn hh-btn-success',
'btn-danger': 'hh-btn hh-btn-danger',
'btn-warning': 'hh-btn hh-btn-primary', // fallback
'btn-info': 'hh-btn hh-btn-primary', // fallback
'btn-light': 'hh-btn hh-btn-secondary',
'btn-dark': 'hh-btn hh-btn-primary',
'btn-link': 'hh-btn hh-btn-ghost',
'btn-sm': 'hh-btn-sm',
'btn-lg': 'hh-btn-lg',
'btn-block': 'hh-btn-block',
// Badge classes - use px360.css hh-badge-* instead
'badge': 'hh-badge',
'rounded-pill': 'rounded-full',
// Alert classes - use px360.css hh-alert-* instead
'alert': 'hh-alert',
'alert-success': 'hh-alert hh-alert-success',
'alert-danger': 'hh-alert hh-alert-danger',
'alert-warning': 'hh-alert hh-alert-warning',
'alert-info': 'hh-alert hh-alert-info',
'alert-dismissible': '', // would need JS handling
// Form classes - use px360.css hh-form-* instead
'form-control': 'hh-form-input',
'form-select': 'hh-form-select',
'form-label': 'hh-form-label',
'form-text': 'text-sm text-gray-500 mt-1',
'form-check': 'flex items-center gap-2',
'form-check-input': 'w-4 h-4',
'form-check-label': 'text-sm',
// Pagination classes - use px360.css hh-pagination instead
'pagination': 'hh-pagination',
'page-item': '', // handled by hh-pagination-btn
'page-link': 'hh-pagination-btn',
'active': 'active', // combined with pagination class
'disabled': 'opacity-50 cursor-not-allowed',
// List group
'list-group': 'divide-y divide-gray-200 border rounded-xl',
'list-group-item': 'p-4',
'list-group-item-action': 'hover:bg-gray-50 cursor-pointer',
// Breadcrumb classes - use px360.css hh-breadcrumb instead
'breadcrumb': 'hh-breadcrumb',
'breadcrumb-item': 'hh-breadcrumb-item',
'active': 'active',
// Dropdown - preserve original classes alongside new ones
'dropdown': 'hh-dropdown',
'dropdown-menu': 'hh-dropdown-menu dropdown-menu',
'dropdown-item': 'hh-dropdown-item dropdown-item',
'dropdown-toggle': 'dropdown-toggle',
'dropdown-divider': 'hh-dropdown-divider',
// Modal
'modal': 'hh-modal-overlay',
'modal-dialog': '', // part of overlay
'modal-content': 'hh-modal',
'modal-header': 'hh-modal-header',
'modal-body': 'hh-modal-body',
'modal-footer': 'hh-modal-footer',
'modal-title': 'hh-modal-title',
'fade': '', // handled by CSS
'show': 'active',
// Navbar
'navbar': 'flex items-center justify-between py-4',
'navbar-brand': 'text-xl font-bold',
'navbar-nav': 'flex items-center gap-4',
'nav-item': '',
'nav-link': 'text-gray-600 hover:text-gray-900',
// Tabs
'nav-tabs': 'hh-tabs',
'nav-link': 'hh-tab',
'active': 'active',
// Spinner
'spinner-border': 'hh-loading',
'spinner-border-sm': 'w-4 h-4',
};
// Merge all mappings
const allMappings = { ...bootstrapToTailwind, ...componentMappings };
// Function to convert Bootstrap classes to Tailwind
function convertBootstrapClasses(element) {
if (!element.classList) return;
const classes = Array.from(element.classList);
const newClasses = [];
let hasBootstrap = false;
classes.forEach(cls => {
if (allMappings[cls]) {
hasBootstrap = true;
if (allMappings[cls]) {
newClasses.push(...allMappings[cls].split(' ').filter(c => c));
}
} else {
newClasses.push(cls);
}
});
if (hasBootstrap) {
element.className = newClasses.join(' ');
}
// Recursively process children
Array.from(element.children).forEach(child => {
convertBootstrapClasses(child);
});
}
// Run conversion after DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
convertBootstrapClasses(document.body);
});
} else {
convertBootstrapClasses(document.body);
}
console.log('[Bootstrap→Tailwind] Migration helper loaded. Bootstrap classes converted to Tailwind/px360.css classes.');
})();