From 4b1218306d52ffad856ad241df92d165f855384b Mon Sep 17 00:00:00 2001 From: Muhammad Sammy Date: Mon, 4 Jan 2021 22:42:34 +0200 Subject: [PATCH] feat: add ring utility classnames (closes #97) (#112) --- src/cli/core/ClassesGenerator.ts | 66 +++++++++++++++++++++------- src/cli/lib/defaultTailwindConfig.ts | 27 ++++++++++++ src/cli/lib/types/classes.ts | 7 ++- 3 files changed, 82 insertions(+), 18 deletions(-) diff --git a/src/cli/core/ClassesGenerator.ts b/src/cli/core/ClassesGenerator.ts index 0d3895ba..98872e74 100644 --- a/src/cli/core/ClassesGenerator.ts +++ b/src/cli/core/ClassesGenerator.ts @@ -104,7 +104,11 @@ export class ClassesGenerator implements IGenerator { private borders = (): Borders => { return { + // Add all non configurable classes in `borders` plugin. + // These are utilities that thier names never change e.g. border styles (dashed, solid etc.) ...nonConfigurableClassNames.borders, + + /* Dynamic border utils */ borderColor: this.generateClassesWithColors('borderColor'), borderOpacity: this.getGeneratedClassesWithOpacities().borderOpacities, borderRadius: Object.keys(this._theme.borderRadius).flatMap(radius => { @@ -115,6 +119,8 @@ export class ClassesGenerator implements IGenerator { const sides = ['t', 'r', 'b', 'l']; return sides.map(side => `border-${side}-${width}`).concat(`border-${width}`); }), + + /* Dynamic divide utilities */ divideColor: this.generateClassesWithColors('divideColor'), divideOpacity: this.getGeneratedClassesWithOpacities().divideOpacities, // divide width inherits its values from theme.borderWidth by default @@ -124,6 +130,15 @@ export class ClassesGenerator implements IGenerator { ) .concat('reverse') .flatMap(width => ['x', 'y'].map(axis => `divide-${axis}-${width}`)), + + /* Dynamic ring utilities */ + ringColor: this.generateClassesWithColors('ringColor'), + ringWidth: Object.keys(this._theme.ringWidth) + .map(x => 'ring-' + x) + .concat('inset'), + ringOpacity: this.getGeneratedClassesWithOpacities().ringOpacities, + ringOffsetColor: this.generateClassesWithColors('ringOffsetColor'), + ringOffsetWidth: Object.keys(this._theme.ringOffsetWidth).map(x => 'ring-offset-' + x), }; }; @@ -408,24 +423,37 @@ export class ClassesGenerator implements IGenerator { }; private generateClassesWithColors = (property: ClassesWithColors): string[] => { + // Get the key-value pairs of the passed property const [propertyKeys, propertyValues] = this._configScanner.getThemeProperty(property); - return propertyKeys - .filter(k => k !== 'default') // exclude `default` keys - .flatMap((colorName, i) => { - const colorValue = propertyValues[i]; // could be a `string` value or an `object` of shades. - - const utilName = property - .replace('Color', '') // gradientColorStops -> gradientStops, borderColor -> border etc. - .replace('Stops', '') // gradientStops -> gradient - .replace('background', 'bg'); - - if (typeof colorValue === 'object' && colorValue !== null) { - return Object.keys(colorValue).map(shade => `${utilName}-${colorName}-${shade}`); - } else { - return `${utilName}-${colorName}`; - } - }); + // Store a conversion of the property name into actual utility name + const utilName = property + .replace('Color', '') // gradientColorStops -> gradientStops, borderColor -> border etc. + .replace('Stops', '') // gradientStops -> gradient + .replace('ringOffset', 'ring-offset') + .replace('background', 'bg'); + + return ( + propertyKeys + // Exclude `DEFAULT` keys from the keys collection as they do not correspond to any classname. + .filter(k => k !== 'DEFAULT') + // Then, for every key of the property... + .flatMap((colorName, i) => { + // Get the value that corresponds to that key. NOTE: the value could be a `string` or an `object` of shades. + const colorValue = propertyValues[i]; + + // If the value is a nested object of color shades... + if (typeof colorValue === 'object' && colorValue !== null) { + // Loop over the deep objects and return the result for each key of the object. + return Object.keys(colorValue).map(shade => `${utilName}-${colorName}-${shade}`); + } + // Otherwise... + else { + // Return the result of merging the utility name with color value + return `${utilName}-${colorName}`; + } + }) + ); }; private getGeneratedClassesWithOpacities = (): ClassesWithOpacities => { @@ -433,7 +461,7 @@ export class ClassesGenerator implements IGenerator { // prettier-ignore type TOpacityProp = | 'divideOpacity' | 'textOpacity' | 'backgroundOpacity' - | 'borderOpacity' | 'placeholderOpacity' + | 'borderOpacity' | 'placeholderOpacity' | 'ringOpacity' const getOpacity = (themePropertyName: TOpacityProp, outputNamePrefix: string): string[] => { const generatedOpacities = generateOpacities(allOpacities, this._theme, themePropertyName); @@ -463,6 +491,7 @@ export class ClassesGenerator implements IGenerator { borderOpacities: getOpacity('borderOpacity', 'border'), divideOpacities: getOpacity('divideOpacity', 'divide'), placeholderOpacities: getOpacity('placeholderOpacity', 'placeholder'), + ringOpacities: getOpacity('ringOpacity', 'ring'), }; }; } @@ -473,6 +502,8 @@ type ClassesWithColors = | 'placeholderColor' | 'textColor' | 'borderColor' + | 'ringColor' + | 'ringOffsetColor' | 'gradientColorStops'; type ClassesWithOpacities = { @@ -482,4 +513,5 @@ type ClassesWithOpacities = { borderOpacities: string[]; divideOpacities: string[]; placeholderOpacities: string[]; + ringOpacities: string[]; }; diff --git a/src/cli/lib/defaultTailwindConfig.ts b/src/cli/lib/defaultTailwindConfig.ts index b4b9d7da..12b21ca9 100644 --- a/src/cli/lib/defaultTailwindConfig.ts +++ b/src/cli/lib/defaultTailwindConfig.ts @@ -303,6 +303,30 @@ export const defaultTailwindConfig = { padding: theme => theme('spacing'), placeholderColor: theme => theme('colors'), placeholderOpacity: theme => theme('opacity'), + ringColor: theme => ({ + DEFAULT: theme('colors.blue.500', '#3b82f6'), + ...theme('colors'), + }), + ringOffsetColor: theme => theme('colors'), + ringOffsetWidth: { + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, + ringOpacity: theme => ({ + DEFAULT: '0.5', + ...theme('opacity'), + }), + ringWidth: { + DEFAULT: '3px', + 0: '0px', + 1: '1px', + 2: '2px', + 4: '4px', + 8: '8px', + }, space: (theme, {negative}) => ({ ...theme('spacing'), ...negative(theme('spacing')), @@ -675,6 +699,9 @@ export const defaultTailwindConfig = { resize: ['responsive'], ringColor: ['responsive', 'dark', 'focus-within', 'focus'], ringOffsetColor: ['responsive', 'dark', 'focus-within', 'focus'], + ringOffsetWidth: ['responsive', 'focus-within', 'focus'], + ringOpacity: ['responsive', 'focus-within', 'focus'], + ringWidth: ['responsive', 'focus-within', 'focus'], space: ['responsive'], stroke: ['responsive'], strokeWidth: ['responsive'], diff --git a/src/cli/lib/types/classes.ts b/src/cli/lib/types/classes.ts index 16a02205..4bb3805c 100644 --- a/src/cli/lib/types/classes.ts +++ b/src/cli/lib/types/classes.ts @@ -54,7 +54,12 @@ type TBordersCategoryItem = | 'divideWidth' | 'divideOpacity' | 'divideColor' - | 'divideStyle'; + | 'divideStyle' + | 'ringColor' + | 'ringOpacity' + | 'ringOffsetColor' + | 'ringOffsetWidth' + | 'ringWidth'; type TEffectsCategoryItem = 'boxShadow' | 'opacity';