Skip to content

Commit

Permalink
feat: add ring utility classnames (closes #97) (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
muhammadsammy committed Jan 4, 2021
1 parent 6c8db2d commit 4b12183
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 18 deletions.
66 changes: 49 additions & 17 deletions src/cli/core/ClassesGenerator.ts
Expand Up @@ -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 => {
Expand All @@ -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
Expand All @@ -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),
};
};

Expand Down Expand Up @@ -408,32 +423,45 @@ 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 => {
const allOpacities = this._configScanner.getTheme().opacity;

// 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);

Expand Down Expand Up @@ -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'),
};
};
}
Expand All @@ -473,6 +502,8 @@ type ClassesWithColors =
| 'placeholderColor'
| 'textColor'
| 'borderColor'
| 'ringColor'
| 'ringOffsetColor'
| 'gradientColorStops';

type ClassesWithOpacities = {
Expand All @@ -482,4 +513,5 @@ type ClassesWithOpacities = {
borderOpacities: string[];
divideOpacities: string[];
placeholderOpacities: string[];
ringOpacities: string[];
};
27 changes: 27 additions & 0 deletions src/cli/lib/defaultTailwindConfig.ts
Expand Up @@ -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')),
Expand Down Expand Up @@ -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'],
Expand Down
7 changes: 6 additions & 1 deletion src/cli/lib/types/classes.ts
Expand Up @@ -54,7 +54,12 @@ type TBordersCategoryItem =
| 'divideWidth'
| 'divideOpacity'
| 'divideColor'
| 'divideStyle';
| 'divideStyle'
| 'ringColor'
| 'ringOpacity'
| 'ringOffsetColor'
| 'ringOffsetWidth'
| 'ringWidth';

type TEffectsCategoryItem = 'boxShadow' | 'opacity';

Expand Down

0 comments on commit 4b12183

Please sign in to comment.