react-native-inner-shadow
Version:
react native inner shadows with linear gradient design UI
231 lines (218 loc) • 7.89 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.computeShadowProperties = computeShadowProperties;
exports.getBackgroundColor = getBackgroundColor;
exports.getBorderRadius = getBorderRadius;
exports.getLinearDirection = getLinearDirection;
exports.getOuterShadowOffset = getOuterShadowOffset;
exports.isLinearProps = isLinearProps;
exports.numerify = numerify;
var _reactNativeSkia = require("@shopify/react-native-skia");
var _constants = require("./constants.js");
/**
* Converts a value to a number, returning a default value if the conversion fails.
*
* @privateRemarks
* At this time(17.Feb.2025), we do not support the way to convert the string (percentage) to a number.
*
* @template T - The type of the default value.
*
* @param value - The value to convert to a number.
*
* @returns The converted number, or the default value if the conversion fails.
*/
function numerify(value, defaultValue) {
const num = Number(value); // if value === null return 0
return Number.isNaN(num) ? defaultValue : num;
}
function getBorderRadius(style) {
const borderRadius = numerify(style?.borderRadius, null);
const topStartRadius = numerify(style?.borderTopStartRadius, borderRadius);
const topLeftRadius = numerify(style?.borderTopLeftRadius, topStartRadius ?? 0);
const topEndRadius = numerify(style?.borderTopEndRadius, borderRadius);
const topRightRadius = numerify(style?.borderTopRightRadius, topEndRadius ?? 0);
const bottomEndRadius = numerify(style?.borderBottomEndRadius, borderRadius);
const bottomRightRadius = numerify(style?.borderBottomRightRadius, bottomEndRadius ?? 0);
const bottomStartRadius = numerify(style?.borderBottomStartRadius, borderRadius);
const bottomLeftRadius = numerify(style?.borderBottomLeftRadius, bottomStartRadius ?? 0);
return {
borderRadius,
topLeftRadius,
topRightRadius,
bottomRightRadius,
bottomLeftRadius
};
}
/**
* getBackgroundColor retrieves the final background color
* from either:
* 1) props.backgroundColor
* 2) props.style.backgroundColor
* 3) BACKGROUND_COLOR
*
* This ensures there is always a valid color for the component’s background.
*
* {@link GetBackgroundColorProps | props} - The props object containing background color settings.
*
* @returns The final background color for the component.
*/
function getBackgroundColor({
backgroundColor,
styleBackground
}) {
const bgColor = backgroundColor ?? styleBackground ?? _constants.BACKGROUND_COLOR;
return bgColor;
}
/**
* computeShadowProperties determines the final configuration for both
* the main shadow and any reflected light. It merges default values
* with provided props to form a complete “shadow settings” object.
*
* - `shadowOffset` / `reflectedLightOffset`: how far the shadows/highlights
* are shifted in x and y.
* - `shadowColor` / `reflectedLightColor`: colors used for each effect.
* - `shadowBlur` / `reflectedLightBlur`: blur radius for the softness/spread
* of the shadow or highlight.
*
* {@link ShadowPropertyConfig} - The props object containing shadow-related settings.
*
* @returns `{
* shadowOffset, reflectedLightOffset, shadowColor, reflectedLightColor, shadowBlur, reflectedLightBlur }`
*/
function computeShadowProperties({
inset,
shadowOffset,
shadowBlur,
shadowColor,
reflectedLightOffset,
reflectedLightBlur,
reflectedLightColor
}) {
const shadowOffsetX = numerify(shadowOffset?.width, _constants.SHADOW_OFFSET_SCALE);
const shadowOffsetY = numerify(shadowOffset?.height, _constants.SHADOW_OFFSET_SCALE);
// By default, the reflected light offset is the inverse of the main shadow
// so it appears on the opposite corner/side.
// when `inset` property is `true`, the reflected light offset is opposite to the shadow offset
const reflectedLightOffsetX = calculateReflectedLightPosition({
inset,
reflectedLightScale: reflectedLightOffset?.width,
baseShadowOffset: shadowOffsetX
});
const reflectedLightOffsetY = calculateReflectedLightPosition({
inset,
reflectedLightScale: reflectedLightOffset?.height,
baseShadowOffset: shadowOffsetY
});
// "Blur" here maps to how soft or large the shadow/highlight is.
// The higher the number, the more diffuse the effect.
const finalShadowBlur = Math.max(shadowBlur ?? _constants.SHADOW_BLUR, 0);
const finalReflectedLightBlur = Math.max(reflectedLightBlur ?? _constants.REFLECTED_LIGHT_BLUR, 0);
// Fallback to the provided defaults if the user doesn't specify a color.
const finalShadowColor = shadowColor ?? _constants.SHADOW_COLOR;
const finalReflectedLightColor = reflectedLightColor ?? _constants.REFLECTED_LIGHT_COLOR;
// Construct the final offsets as objects for clarity.
const finalShadowOffset = {
width: shadowOffsetX,
height: shadowOffsetY
};
const finalReflectedLightOffset = {
width: reflectedLightOffsetX,
height: reflectedLightOffsetY
};
return {
shadowOffset: finalShadowOffset,
reflectedLightOffset: finalReflectedLightOffset,
shadowColor: finalShadowColor,
reflectedLightColor: finalReflectedLightColor,
shadowBlur: finalShadowBlur,
reflectedLightBlur: finalReflectedLightBlur
};
}
function calculateReflectedLightPosition({
inset,
reflectedLightScale,
baseShadowOffset
}) {
// When user provides a reflected light offset, use that. - which allows `0` and `null`
if (reflectedLightScale !== undefined) return reflectedLightScale;
// When shadow is 0, reflected light should be 0.
if (baseShadowOffset === 0) return 0;
// for matching reflected light offset direction based on inset
const scaleFactor = (baseShadowOffset + _constants.REFLECTED_LIGHT_OFFSET_SCALE) / 2;
// When inset is true, the reflected light should be opposite the shadow.
return inset ? -scaleFactor : scaleFactor;
}
/**
* `getOuterShadowOffset` calculates the outer shadow offset properties.
*
* {@link GetOuterShadowOffsetProps} - The props object containing outer shadow offset settings.
*
* @returns `{ shadowColor, shadowOffset, shadowBlur, shadowOpacity, shadowRadius, elevation, boxShadow }`
*/
function getOuterShadowOffset({
inset,
shadowColor,
shadowOffset,
shadowBlur,
shadowOpacity = _constants.SHADOW_OPACITY,
shadowRadius = _constants.SHADOW_RADIUS,
elevation = _constants.SHADOW_ELEVATION,
boxShadow
}) {
if (inset) return {};
return {
shadowColor,
shadowOffset,
shadowBlur,
shadowOpacity,
shadowRadius,
elevation,
boxShadow
};
}
/**
* `getLinearDirection` calculates the start and end points for a linear gradient
* based on the provided direction (from, to).
*
* - The direction is specified as a string, e.g., 'top', 'bottom', 'left', 'right'.
* - The width and height are used to calculate the midpoints for each direction.
*
* {@link GetLinearDirectionProps} - The props object containing linear direction settings.
*
* @returns `{ start, end }`
*/
function getLinearDirection({
width,
height,
from,
to
}) {
const top = (0, _reactNativeSkia.vec)(width / 2, 0);
const bottom = (0, _reactNativeSkia.vec)(width / 2, height);
const left = (0, _reactNativeSkia.vec)(0, height / 2);
const right = (0, _reactNativeSkia.vec)(width, height / 2);
const direction = {
top,
bottom,
left,
right
};
return {
start: direction[from],
end: direction[to]
};
}
/**
* `isLinearProps` checks if the provided props are for a linear gradient.
* If the `colors` property is an array, we assume it's a linear gradient.
*
* @param props - see {@link InnerShadowProps} and {@link LinearInnerShadowProps}
*
* @returns `true` if the props are for a linear gradient, `false` otherwise.
*/
function isLinearProps(props) {
return 'colors' in props && Array.isArray(props.colors);
}
//# sourceMappingURL=utils.js.map