const pc = require('picocolors'); const plugin = require('tailwindcss/plugin'); const validate = require('./validate'); /** * * Setup tailwind-bootstrap-grid plugin * * @param {object} pluginOptions - plugin options * @param {number} [pluginOptions.gridColumns=12] - number of columns * @param {string} [pluginOptions.gridGutterWidth="1.5rem"] - spacing value * @param {object} [pluginOptions.gridGutters={ 0: 0 }] - gutter spacing variable classes * @param {boolean} [pluginOptions.generateContainer=true] - whether the plugin should generate .container class * @param {object} [pluginOptions.containerMaxWidths={}] - the `max-width` container value for each breakpoint * @param {boolean} [pluginOptions.rtl=false] - whether to enable rtl support * @param {boolean} [pluginOptions.respectImportant=true] - whether to respect important config option * @returns {*} - Tailwind CSS plugin */ module.exports = plugin.withOptions((pluginOptions) => (options) => { const { addComponents, config, corePlugins } = options; const screens = config('theme.screens'); const important = config('important'); const { gridColumns, gridGutterWidth, gridGutters, generateContainer, containerMaxWidths, rtl, respectImportant, } = validate({ screens })({ gridColumns: 12, gridGutterWidth: '1.5rem', gridGutters: { 0: 0 }, generateContainer: true, containerMaxWidths: {}, rtl: false, respectImportant: true, ...pluginOptions, }); if (generateContainer && corePlugins('container')) { console.warn( `⚠️ The ${pc.yellow( 'container', )} core plugin is enabled and you're also generating ${pc.green( '.container', )} class with the ${pc.bold( 'tailwind-bootstrap-grid', )} plugin. This might lead to unexpected styling issues, disable either of one.`, ); } const screenKeys = Object.keys(screens); const columns = Array.from(Array(gridColumns), (value, index) => index + 1); const rowColsSteps = columns.slice(0, Math.floor(gridColumns / 2)); const setImportant = (value) => respectImportant && important && value != null ? `${value} !important` : value; { // ============================================================================================= // Container // ============================================================================================= if (generateContainer) { addComponents( [ { '.container, .container-fluid': { width: setImportant('100%'), marginRight: setImportant('auto'), marginLeft: setImportant('auto'), paddingRight: setImportant( `var(--bs-gutter-x, calc(${gridGutterWidth} / 2))`, ), paddingLeft: setImportant( `var(--bs-gutter-x, calc(${gridGutterWidth} / 2))`, ), }, }, ...screenKeys.map((name) => ({ [`@screen ${name}`]: { '.container': { maxWidth: setImportant( containerMaxWidths[name] || screens[name], ), }, }, })), ], { respectImportant }, ); } } { // ============================================================================================= // Row // ============================================================================================= addComponents( { '.row': { '--bs-gutter-x': gridGutterWidth, '--bs-gutter-y': 0, display: 'flex', flexWrap: 'wrap', marginTop: 'calc(var(--bs-gutter-y) * -1)', marginRight: 'calc(var(--bs-gutter-x) / -2)', marginLeft: 'calc(var(--bs-gutter-x) / -2)', '& > *': { boxSizing: 'border-box', flexShrink: 0, width: '100%', maxWidth: '100%', paddingRight: 'calc(var(--bs-gutter-x) / 2)', paddingLeft: 'calc(var(--bs-gutter-x) / 2)', marginTop: 'var(--bs-gutter-y)', }, }, }, { respectImportant }, ); } { // ============================================================================================= // Columns // ============================================================================================= addComponents( [ { '.col': { flex: '1 0 0%', }, '.row-cols-auto': { '& > *': { flex: '0 0 auto', width: 'auto', }, }, }, ...rowColsSteps.map((rowCol) => ({ [`.row-cols-${rowCol}`]: { '& > *': { flex: '0 0 auto', width: `${100 / rowCol}%`, }, }, })), { '.col-auto': { flex: '0 0 auto', width: 'auto', }, }, ...columns.map((size) => ({ [`.col-${size}`]: { flex: '0 0 auto', width: `${(100 / gridColumns) * size}%`, }, })), ], { respectImportant }, ); } { // ============================================================================================= // Offsets // ============================================================================================= addComponents( [ ...[0, ...columns.slice(0, -1)].map((size) => { const margin = `${(100 / gridColumns) * size}%`; return rtl ? { [`[dir="ltr"] .offset-${size}`]: { marginLeft: margin }, [`[dir="rtl"] .offset-${size}`]: { marginRight: margin }, } : { [`.offset-${size}`]: { marginLeft: margin }, }; }), ], { respectImportant }, ); } { // ============================================================================================= // Gutters // ============================================================================================= if (Object.keys(gridGutters).length) { addComponents( Object.entries(gridGutters).map(([key, value]) => ({ [`.g-${key}, .gx-${key}`]: { '--bs-gutter-x': value, }, [`.g-${key}, .gy-${key}`]: { '--bs-gutter-y': value, }, })), { respectImportant }, ); } } { // ============================================================================================= // Ordering // ============================================================================================= addComponents( [ { '.order-first': { order: '-1' }, '.order-last': { order: gridColumns + 1 }, }, ...[0, ...columns].map((size) => ({ [`.order-${size}`]: { order: `${size}` }, })), ], { respectImportant }, ); } });