Skip to content

Commit

Permalink
Implements a rgbUnit configuration option
Browse files Browse the repository at this point in the history
  • Loading branch information
elchininet committed Jul 16, 2023
1 parent fb0fe13 commit f741382
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 61 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ interface Options {
legacyCSS?: boolean; // defaults to false
spacesAfterCommas?: boolean; // defaults to false
anglesUnit?: 'none' | 'deg' | 'grad' | 'rad' | 'turn'; // defaults to 'none'
rgbUnit?: 'none' | 'percent'; // defaults to 'none'
}
```

Expand All @@ -178,12 +179,14 @@ interface Options {
| legacyCSS | yes | This options decides if the CSS output should be CSS Level 3 (legacy) or CSS Level 4 |
| spacesAfterCommas | yes | This options only takes place if `legacyCSS` is set to true. It decides if the comas should have a space after |
| anglesUnit | yes | This options only takes place if the output is an HSL CSS output. It sets the degrees units of the HSL hue angle. If `none` is used, the output will not have any unit but its value will be the `deg` one (degrees) |
| rgbUnit | yes | This options only takes place if the output is an RGB CSS output. It sets the color units of the output. If `none` is used the color values will be decimal between `0` and `255`. If percentages is used, the color values will be decimal with percentages between `0%` and `100%`. |

>Note: the library tries to detect some options automatically if you don‘t send them in the options object. These are the rules for this autodetection:
>
> * `legacyCSS`: if this option is set, then its value prevails, if it is not set, and the CSS input is provided in CSS Level 3, then this option will be `true`, otherwise it will take its default value which is `false`.
> * `spacesAfterCommas`: if this option is set, then its value prevails, if it is not set, and the CSS input is provided with spaces after the commas, then this option will be `true`. If the input is not consistent in this aspect, then it will take its default value which is `false` (This option only takes place if `legacyCSS` is `true` or it has been autodetected as `true`)
> * `anglesUnit`: if this option is set, then its value prevails, if it is not set, and the HSL CSS input is provided with an angle unit, then it will take that value, otherwise it will use the default one wich is `none`.
> * `rgbUnit`: if this option is set, then its value prevails, if it is not set, and the RGB CSS input is provided with percentages in its color values, then it will take the `pcent` value, otherwise it will use the default one wich is `none`.
###### Class instantiation examples

Expand Down
2 changes: 1 addition & 1 deletion dist/web/colortranslator.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/scripts/bundle.js

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions src/@types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,28 @@ export enum AnglesUnitEnum {
TURNS = 'turn'
}

export enum ColorUnitEnum {
NONE = 'none',
PERCENT = 'percent',
}

export type AnglesUnit = `${AnglesUnitEnum}`;
export type ColorUnit = `${ColorUnitEnum}`;

export interface Options {
decimals: number;
legacyCSS: boolean;
spacesAfterCommas: boolean;
anglesUnit: AnglesUnitEnum;
rgbUnit: ColorUnitEnum;
}

export type InputOptions = Partial<Omit<Options, 'anglesUnit'>> & {
anglesUnit?: AnglesUnit;
export type InputOptions = Partial<
Omit<
Options,
'anglesUnit' | 'rgbUnit'
>
> & {
anglesUnit?: string;
rgbUnit?: string;
};
30 changes: 19 additions & 11 deletions src/color/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
Color,
NumberOrString,
Options,
AnglesUnitEnum
AnglesUnitEnum,
ColorUnitEnum
} from '@types';
import {
ColorModel,
Expand Down Expand Up @@ -61,19 +62,26 @@ export const CSS = {
return getResultFromTemplate(template, values);
},
[ColorModel.RGB]: (color: RGBObject, options: Options): string => {
const { legacyCSS, spacesAfterCommas } = options;
const {
legacyCSS,
spacesAfterCommas,
rgbUnit
} = options;
const colorUnits = rgbUnit === ColorUnitEnum.PERCENT
? '%'
: '';
const comma = getComma(spacesAfterCommas);
const values = prepareColorForCss(color);
const template = legacyCSS
? (
values.length === 4
? `rgba({1}${comma}{2}${comma}{3}${comma}{4})`
: `rgb({1}${comma}{2}${comma}{3})`
? `rgba({1}${colorUnits}${comma}{2}${colorUnits}${comma}{3}${colorUnits}${comma}{4})`
: `rgb({1}${colorUnits}${comma}{2}${colorUnits}${comma}{3}${colorUnits})`
)
: (
values.length === 4
? 'rgb({1} {2} {3} / {4})'
: 'rgb({1} {2} {3})'
? `rgb({1}${colorUnits} {2}${colorUnits} {3}${colorUnits} / {4})`
: `rgb({1}${colorUnits} {2}${colorUnits} {3}${colorUnits})`
);
return getResultFromTemplate(template, values);
},
Expand All @@ -85,19 +93,19 @@ export const CSS = {
} = options;
const comma = getComma(spacesAfterCommas);
const values = prepareColorForCss(color);
const units = anglesUnit === AnglesUnitEnum.NONE
const angleUnits = anglesUnit === AnglesUnitEnum.NONE
? ''
: anglesUnit;
const template = legacyCSS
? (
values.length === 4
? `hsla({1}${units}${comma}{2}%${comma}{3}%${comma}{4})`
: `hsl({1}${units}${comma}{2}%${comma}{3}%)`
? `hsla({1}${angleUnits}${comma}{2}%${comma}{3}%${comma}{4})`
: `hsl({1}${angleUnits}${comma}{2}%${comma}{3}%)`
)
: (
values.length === 4
? `hsl({1}${units} {2}% {3}% / {4})`
: `hsl({1}${units} {2}% {3}%)`
? `hsl({1}${angleUnits} {2}% {3}% / {4})`
: `hsl({1}${angleUnits} {2}% {3}%)`
);
return getResultFromTemplate(template, values);
},
Expand Down
67 changes: 52 additions & 15 deletions src/color/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
HEXOutput,
ColorOutput,
Options,
AnglesUnitEnum
AnglesUnitEnum,
ColorUnitEnum
} from '@types';
import {
HEX,
Expand All @@ -34,6 +35,7 @@ import {
getDEC,
getHEX,
getBase255Number,
from255NumberToPercent,
getCMYKNumber,
hasProp,
percent,
Expand Down Expand Up @@ -291,23 +293,36 @@ export const translateColor = {
},

[ColorModel.RGB](color: RGBObject, options: Options): RGBObject {
if (hasProp<RGBObject>(color, 'a')) {
delete color.a;
const rgb = roundRGBObject(
color,
{
...options,
rgbUnit: ColorUnitEnum.NONE
}
);
if (hasProp<RGBObject>(rgb, 'a')) {
delete rgb.a;
}
return roundRGBObject(color, options);
return rgb;
},

RGBA(color: RGBObject, options: Options): RGBObject {
color.a = hasProp<RGBObject>(color, 'a')
const rgb = translateColor.RGB(color, options);
rgb.a = hasProp<RGBObject>(color, 'a')
? round(color.a)
: 1;
return roundRGBObject(color, options);
return rgb;
},

[ColorModel.HSL](color: RGBObject, options: Options): HSLObject {
const hsl = rgbToHSL(color.r, color.g, color.b);
delete hsl.a;
return roundHSLObject(hsl, options, false);
return roundHSLObject(
hsl,
{
...options,
anglesUnit: AnglesUnitEnum.NONE
});
},

HSLA(color: RGBObject, options: Options): HSLObject {
Expand Down Expand Up @@ -753,7 +768,10 @@ export const colorMixer = {
delete mix.a;
return (
css
? CSS.RGB(mix, options)
? CSS.RGB(
roundRGBObject(mix, options),
options
)
: translateColor.RGB(mix, options)
) as R;
},
Expand All @@ -766,7 +784,10 @@ export const colorMixer = {
const mix = this.mix(colors, mode);
return (
css
? CSS.RGB(mix, options)
? CSS.RGB(
roundRGBObject(mix, options),
options
)
: translateColor.RGBA(mix, options)
) as R;
},
Expand Down Expand Up @@ -809,11 +830,24 @@ export const colorMixer = {
};

export const roundRGBObject = (color: RGBObject, options: Options): RGBObject => {
const { decimals } = options;
const {
decimals,
rgbUnit
} = options;
const inPercentage = rgbUnit === ColorUnitEnum.PERCENT;
const r = inPercentage
? from255NumberToPercent(color.r, decimals)
: round(color.r, decimals);
const g = inPercentage
? from255NumberToPercent(color.g, decimals)
: round(color.g, decimals);
const b = inPercentage
? from255NumberToPercent(color.b, decimals)
: round(color.b, decimals);
return {
r: round(color.r, decimals),
g: round(color.g, decimals),
b: round(color.b, decimals),
r,
g,
b,
...(
hasProp<RGBObject>(color, 'a')
? {
Expand All @@ -824,15 +858,18 @@ export const roundRGBObject = (color: RGBObject, options: Options): RGBObject =>
};
};

export const roundHSLObject = (color: HSLObject, options: Options | null, applyDegreesConvertion = true): HSLObject => {
export const roundHSLObject = (
color: HSLObject,
options: Options | null
): HSLObject => {
const decimals = options
? options.decimals
: 0;
const anglesUnits = options
? options.anglesUnit
: AnglesUnitEnum.NONE;
return {
h: applyDegreesConvertion
h: anglesUnits !== AnglesUnitEnum.NONE
? round(
translateDegrees(
color.h,
Expand Down
9 changes: 7 additions & 2 deletions src/constants/options.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Options, AnglesUnitEnum } from '@types';
import {
Options,
AnglesUnitEnum,
ColorUnitEnum
} from '@types';
import { MAX_DECIMALS } from './numbers';

export const DEFAULT_OPTIONS: Options = {
decimals: MAX_DECIMALS,
legacyCSS: false,
spacesAfterCommas: false,
anglesUnit: AnglesUnitEnum.NONE
anglesUnit: AnglesUnitEnum.NONE,
rgbUnit: ColorUnitEnum.NONE
};
36 changes: 30 additions & 6 deletions src/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
InputOptions,
NumberOrString,
ColorInput,
AnglesUnitEnum
AnglesUnitEnum,
ColorUnitEnum
} from '@types';
import {
PCENT,
Expand Down Expand Up @@ -50,6 +51,9 @@ export const toHEX = (h: NumberOrString): string => {
return hex;
};

//---Convert from decimal 255 to percent
export const from255NumberToPercent = (value: number, decimals: number): number => round(value / 255 * 100, decimals);

//---Calculate a decimal 255 from an RGB color
export const getBase255Number = (color: string, alpha = false): number => {
if (!alpha && PCENT.test(color)) {
Expand Down Expand Up @@ -143,7 +147,7 @@ export const translateDegrees = (degrees: number, units: AnglesUnitEnum): number
return hue;
};

type MatchOptions<T = Omit<Options, 'decimals'>> = {
type MatchOptions<T = Omit<Options, 'decimals' | 'anglesUnit' | 'rgbUnit'>> = {
[K in keyof T]: number;
};

Expand All @@ -152,17 +156,29 @@ export const getOptionsFromColorInput = (options: InputOptions, ...colors: Color
const hslColors = cssColors
.filter((color: string): boolean => COLORREGS.HSL.test(color))
.map((color: string) => {
const hslMatch = color.match(COLORREGS.HSL);
const angle = hslMatch[1] || hslMatch[5];
const match = color.match(COLORREGS.HSL);
const angle = match[1] || match[5];
const unit = angle.match(HSL_HUE)[2];
return unit === ''
? AnglesUnitEnum.NONE
: unit as AnglesUnitEnum;
});
const rgbColors = cssColors
.filter((color: string): boolean => COLORREGS.RGB.test(color))
.map((color: string) => {
const match = color.match(COLORREGS.RGB);
const r = match[1] || match[5];
const g = match[2] || match[6];
const b = match[3] || match[7];
return (
PCENT.test(r) &&
PCENT.test(g) &&
PCENT.test(b)
);
});
const matchOptions: MatchOptions = {
legacyCSS: 0,
spacesAfterCommas: 0,
anglesUnit: 0
spacesAfterCommas: 0
};
cssColors.forEach((color: string): void => {
if (color.includes(',')){
Expand All @@ -176,6 +192,7 @@ export const getOptionsFromColorInput = (options: InputOptions, ...colors: Color
}
}
});

return {
decimals: typeof options.decimals === TypeOf.NUMBER
? options.decimals
Expand All @@ -198,6 +215,13 @@ export const getOptionsFromColorInput = (options: InputOptions, ...colors: Color
new Set(hslColors).size === 1
? hslColors[0]
: DEFAULT_OPTIONS.anglesUnit
),
rgbUnit: options.rgbUnit
? options.rgbUnit as ColorUnitEnum
: (
new Set(rgbColors).size === 1 && rgbColors[0]
? ColorUnitEnum.PERCENT
: DEFAULT_OPTIONS.rgbUnit
)
};
};
Loading

0 comments on commit f741382

Please sign in to comment.