Skip to content

Commit

Permalink
resolve #88 - add hex | rgb | rgba support for highlightColor prop
Browse files Browse the repository at this point in the history
  • Loading branch information
chramos committed Oct 17, 2022
1 parent 4fbce8f commit 0285c8d
Showing 1 changed file with 54 additions and 37 deletions.
91 changes: 54 additions & 37 deletions src/SkeletonPlaceholder.tsx → src/skeleton-placeholder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import MaskedView from '@react-native-community/masked-view';
import * as React from 'react';
import {
Animated,
ColorValue,
Dimensions,
Easing,
LayoutRectangle,
Expand All @@ -25,9 +24,9 @@ type SkeletonPlaceholderProps = {
/**
* Determines the color of placeholder.
*/
backgroundColor?: ColorValue;
backgroundColor?: string;
/**
* Determines the highlight color of placeholder. Only hex values supported (#fff, #fff0, #ffffff, #ffffff00).
* Determines the highlight color of placeholder.
*/
highlightColor?: string;
/**
Expand Down Expand Up @@ -55,7 +54,15 @@ type SkeletonPlaceholderItemProps = ViewStyle & {

const SkeletonPlaceholder: React.FC<SkeletonPlaceholderProps> & {
Item: React.FC<SkeletonPlaceholderItemProps>;
} = ({children, enabled, backgroundColor, highlightColor, speed, direction, borderRadius}) => {
} = ({
children,
enabled = true,
backgroundColor = '#E1E9EE',
highlightColor = '#F2F8FC',
speed = 800,
direction = 'right',
borderRadius,
}) => {
const [layout, setLayout] = React.useState<LayoutRectangle>();
const animatedValueRef = React.useRef(new Animated.Value(0));
const isAnimationReady = Boolean(speed && layout?.width && layout?.height);
Expand Down Expand Up @@ -91,27 +98,6 @@ const SkeletonPlaceholder: React.FC<SkeletonPlaceholderProps> & {
};
}, [direction, WINDOW_WIDTH]);

const getTransparentColor = React.useCallback(() => {
if (!highlightColor) {
return undefined;
}

switch (highlightColor.length) {
case 4:
return `${highlightColor}0`; // #fff
case 5:
return `${highlightColor.substring(0, 4)}0`; // #fff5
case 7:
return `${highlightColor}00`; //#ffffff
case 9:
`${highlightColor.substring(0, 7)}00`; // #ffffff00
default:
throw new Error(
`Unsupported color format (${highlightColor}), only hex (#fff, #fff0, #ffffff, #ffffff00) supported.`,
);
}
}, [highlightColor]);

const placeholders = React.useMemo(() => {
if (!enabled) return null;

Expand All @@ -122,6 +108,11 @@ const SkeletonPlaceholder: React.FC<SkeletonPlaceholderProps> & {
);
}, [backgroundColor, children, borderRadius, enabled]);

const transparentColor = React.useMemo(
() => getTransparentColor(highlightColor.replace(/ /g, '')),
[highlightColor],
);

if (!enabled || !placeholders) return children;

if (!layout?.width || !layout.height)
Expand All @@ -130,8 +121,6 @@ const SkeletonPlaceholder: React.FC<SkeletonPlaceholderProps> & {
// https://github.com/react-native-linear-gradient/react-native-linear-gradient/issues/358
// to make transparent gradient we need to use original color with alpha

const transparentColor = getTransparentColor();

return (
<MaskedView style={{height: layout.height, width: layout.width}} maskElement={placeholders}>
<View style={[StyleSheet.absoluteFill, {backgroundColor}]} />
Expand All @@ -148,15 +137,6 @@ const SkeletonPlaceholder: React.FC<SkeletonPlaceholderProps> & {
);
};

SkeletonPlaceholder.defaultProps = {
backgroundColor: '#E1E9EE',
highlightColor: '#F2F8FC',
speed: 800,
direction: 'right',
enabled: true,
borderRadius: undefined,
};

SkeletonPlaceholder.Item = (props) => <View style={getItemStyle(props)}>{props.children}</View>;
SkeletonPlaceholder.Item.displayName = 'SkeletonPlaceholderItem';

Expand All @@ -176,7 +156,7 @@ const getItemStyle = ({

const transformToPlaceholder = (
rootElement: JSX.Element | JSX.Element[] | null,
backgroundColor: ColorValue | undefined,
backgroundColor: string | undefined,
radius: number | undefined,
) => {
if (!rootElement) return null;
Expand Down Expand Up @@ -247,3 +227,40 @@ const styles = StyleSheet.create({
});

export default SkeletonPlaceholder;

const getColorType = (color: string) => {
if (
new RegExp(
/^rgba\((0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),(0|0?\.\d|1(\.0)?)\)$/,
).test(color)
) {
return 'rgba';
}
if (
new RegExp(
/^rgb\((0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d)\)$/,
).test(color)
) {
return 'rgb';
}

if (new RegExp(/^#?([a-f\d]{3,4}|[a-f\d]{6}|[a-f\d]{8})$/i).test(color)) {
return 'hex';
}

throw `The provided color ${color} is not a valid (hex | rgb | rgba) color`;
};

const getTransparentColor = (color: string) => {
const type = getColorType(color);

if (type === 'hex') {
if (color.length < 6) {
return color.substring(0, 4) + '0';
}
return color.substring(0, 7) + '00';
}
//@ts-ignore
const [r, g, b] = color.match(/\d+/g);
return `rgba(${r},${g},${b},0)`;
};

0 comments on commit 0285c8d

Please sign in to comment.