UNPKG

xp.js-styled

Version:

Build performant styled components for Web, iOS and Android platforms.

328 lines 14.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.hexToRGB = exports.size = exports.color = exports.shade = exports.normalizeMediaQueries = exports.deepMerge = exports.deepMap = exports.splitProps = exports.mapShortcutProps = void 0; const react_native_1 = require("react-native"); const constants_1 = require("../constants"); const utils_1 = require("../utils"); /** * Maps shortcut props to their corresponding style properties while preserving original props. * * @param {Object} props - The input props object that may contain shortcut properties * @param {boolean} [props.center] - When true, applies centering styles for text, flexbox alignment and justification * @param {...*} [props.style] - Original style object that will be merged with generated styles * @returns {Object} A new props object with the original props and processed style properties **/ const mapShortcutProps = (props) => { let outputStyle = {}; for (const key in props) { if (key == "center") { outputStyle["textAlign"] = "center"; outputStyle["alignItems"] = "center"; outputStyle["justifyContent"] = "center"; } else if (key in constants_1.ShortcutStyleProps) { outputStyle[constants_1.ShortcutStyleProps[key]] = props[key]; } } return { ...props, style: { ...props === null || props === void 0 ? void 0 : props.style, ...outputStyle } }; }; exports.mapShortcutProps = mapShortcutProps; /** * Splits the input props object into separate props and style objects. * @param srcProps - The input props object containing both regular props and style props. * @returns An object containing the separated props and style objects. */ const splitProps = (srcProps) => { var _a, _b; if ((0, utils_1.isNullish)(srcProps) || !(0, utils_1.isObject)(srcProps) || Array.isArray(srcProps)) { return { elementProps: {}, styleProps: {}, }; } const props = { ...srcProps }; const output = { props: {}, style: {}, }; for (const key in props) { if (key == "center") { output.style["textAlign"] = "center"; output.style["alignItems"] = "center"; output.style["justifyContent"] = "center"; } else if (key in constants_1.ShortcutStyleProps) { output.style[constants_1.ShortcutStyleProps[key]] = props[key]; } if ((0, utils_1.isStyleProp)(key) || constants_1.ShortcutStyleProps[key]) { output.style[key] = props[key]; } else if (key != "style") { output.props[key] = props[key]; } } // output.style = {...output.style, ...mapShortcutProps(props)}; // Handle size, width, and height props if (props.size || props.width) { output.style["width"] = (_a = props.size) !== null && _a !== void 0 ? _a : props.width; } if (props.size || props.height) { output.style["height"] = (_b = props.size) !== null && _b !== void 0 ? _b : props.height; } return { elementProps: output.props, styleProps: { ...srcProps["style"], ...output.style }, }; }; exports.splitProps = splitProps; /** * Recursively maps nested objects, omitting specified keys. * @param props - Deep map configuration properties. * @param props.values - The object to transform. * @param props.match - Function to determine if a value should be transformed. * @param props.map - Function to transform matching values. * @param props.skipKeys - Array of keys to omit from the output. * @param props.onNesting - Optional callback for nested objects. * @param props.initialContext - Initial context object. * @returns Transformed object with skipped keys removed. */ const deepMap = ({ values, match, map, skipKeys = [], onNesting, initialContext }) => { var _a; if ((0, utils_1.isObject)(values)) { let output = {}; for (const key in values) { if (skipKeys.includes(key)) { // Skip this key entirely - don't add it to output continue; } let ctx = { ...initialContext, deep: (_a = initialContext === null || initialContext === void 0 ? void 0 : initialContext.deep) !== null && _a !== void 0 ? _a : 0 }; if ((0, utils_1.isObject)(values[key])) { if (onNesting) { ctx = onNesting({ values: values[key], key, ctx }); } output[key] = (0, exports.deepMap)({ values: values[key], match, map, skipKeys, onNesting, initialContext: { ...ctx, deep: ctx.deep + 1 }, }); } else if (match(values[key])) { output[key] = map({ key, value: values[key], ctx }); } else { output[key] = values[key]; } } return output; } return values; }; exports.deepMap = deepMap; /** * Merges an array of objects deeply, with support for arrays and objects. * Arrays are concatenated, objects are merged recursively. * * @param {(object|array)[]} objects - The array of objects or arrays to merge. * @param {string[]} [skipKeys=[]] - The array of keys to skip during the merge. * @returns {object|array} The merged result. */ const deepMerge = (objects, skipKeys = []) => { return objects.reduce((output, value) => { if ((0, utils_1.isNullish)(output)) { output = Array.isArray(value) ? [] : {}; } if ((0, utils_1.isNullish)(value)) return output; if (Array.isArray(value)) { return Array.isArray(output) ? [...output, ...value] : value; } if (Array.isArray(output)) { return value; } Object.keys(value).forEach((key) => { if (skipKeys.includes(key)) { return; } const currentValue = value[key]; const existingValue = output[key]; if (Array.isArray(currentValue) || Array.isArray(existingValue)) { output[key] = (0, exports.deepMerge)([existingValue, currentValue], skipKeys); } else if ((0, utils_1.isObject)(currentValue)) { output[key] = (0, exports.deepMerge)([existingValue, currentValue], skipKeys); } else if (!(0, utils_1.isNullish)(currentValue)) { output[key] = currentValue; } }); return output; }, undefined); }; exports.deepMerge = deepMerge; /** * Function to consume media queries based on breakpoints and the device color scheme. * @param values - Media query values. * @param breakpoints - Breakpoint sizes. * @returns Generated style with media queries applied on. */ const normalizeMediaQueries = (values, breakpoints) => { var _a; if ((0, utils_1.isObject)(values) && Object.keys(values).length > 0) { const colorScheme = react_native_1.Appearance.getColorScheme(); const width = react_native_1.Dimensions.get("window").width; const breakpointValues = breakpoints !== null && breakpoints !== void 0 ? breakpoints : constants_1.Breakpoints; const breakpointKeys = Object.keys(breakpointValues).sort((a, b) => breakpointValues[a] - breakpointValues[b]); let out = { ...values }; if (values[`@${react_native_1.Platform.OS}`]) { out = (0, exports.deepMerge)([out, (0, exports.normalizeMediaQueries)(values[`@${react_native_1.Platform.OS}`])]); } for (const breakpointKey of breakpointKeys) { const breakpointStyle = values["@" + breakpointKey]; if (breakpointStyle && width > breakpointValues[breakpointKey]) { out = (0, exports.deepMerge)([out, (0, exports.normalizeMediaQueries)(breakpointStyle)]); } } out = (0, exports.deepMerge)([out, (0, exports.normalizeMediaQueries)((_a = values[`@${colorScheme}`]) !== null && _a !== void 0 ? _a : {})]); for (const key in out) { if (key.startsWith("@")) { delete out[key]; } } return out; } return values; }; exports.normalizeMediaQueries = normalizeMediaQueries; /** * Function to generate shades of a color. * @param hex - Base color in hexadecimal format. * @param lumen - Luminosity value. 100|150|200|250|300|350|400|450|500|550|600|650|700|750|800|850|900 * @returns Generated shaded color. */ const shade = (hex, lumen) => { const rgb = (0, exports.hexToRGB)(hex); let factorValue = 0; if (lumen === 500) { factorValue = 0; } else if (lumen > 500) { factorValue = (lumen - 500) / 400; } else { factorValue = (500 - lumen) / 400; } factorValue *= 0.9; const newRgb = rgb.map((colorChannel) => { const newValue = colorChannel + factorValue * (lumen < 500 ? 255 - colorChannel : -colorChannel); return Math.min(255, Math.max(0, Math.round(newValue))); }); const newHex = newRgb.map((value) => value.toString(16).padStart(2, "0")).join(""); return `#${newHex}`; }; exports.shade = shade; /** * Function to resolve color values. * @param value - Color value to resolve. * @param colorScheme - Color scheme. * @param breakpoints - Breakpoint sizes. * @returns Resolved color value. */ const color = (value, colorScheme, breakpoints) => { if (value.startsWith("#") || value.startsWith("rgb") || value.startsWith("hsl")) { return value; } if (constants_1.ColorPallete[value]) { return constants_1.ColorPallete[value]; } if (colorScheme === null || colorScheme === void 0 ? void 0 : colorScheme[value]) { return colorScheme[value]; } const colorNames = Object.keys({ ...constants_1.ColorPallete, ...(0, exports.normalizeMediaQueries)(colorScheme, breakpoints) }).join("|"); const colorRegex = new RegExp(`\\b(?:${colorNames})\\.${constants_1.ColorIntensity}\\b`); if (colorRegex.test(value)) { const [colorName, lumen] = value.split("."); const baseColor = constants_1.ColorPallete[colorName]; if (baseColor) { return (0, exports.shade)(baseColor, parseInt(lumen)); } } return value; }; exports.color = color; /** * Function to resolve size values. * @param value - Size value to resolve. * @param theme - Theme schema. * @returns Resolved size value. */ const size = ({ key, value }, theme) => { var _a, _b, _c, _d, _e, _f; // Return numeric value directly if (typeof value === "number") { return value; } // Handle pixel values (e.g., "16px") if (typeof value === "string" && value.match(/^(\d+)px$/)) { const pixelValue = parseInt(value, 10); return !isNaN(pixelValue) ? pixelValue : value; } // Handle font weight resolution if the key is "fontWeight" if (typeof value === "string" && key === "fontWeight") { const weightValue = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.fontWeights) === null || _a === void 0 ? void 0 : _a[value]) !== null && _b !== void 0 ? _b : constants_1.FontWeights[value]; return weightValue !== undefined ? weightValue : value; } // Handle font size resolution if the key is "fontSize" if (typeof value === "string" && key === "fontSize") { const match = value.match(/\b(?:([2-9])xxl|xxs|xs|sm|md|lg|xl|xxl)\b/i); if (match) { const [fullMatch, multiplierStr] = match; const sizeKey = multiplierStr ? "xxl" : fullMatch.toLowerCase(); const multiplier = multiplierStr ? parseInt(multiplierStr) : 1; const fontSize = (_d = (_c = theme === null || theme === void 0 ? void 0 : theme.fontSizes) === null || _c === void 0 ? void 0 : _c[sizeKey]) !== null && _d !== void 0 ? _d : constants_1.FontSizes[sizeKey]; return fontSize !== undefined ? fontSize * multiplier : value; } return value; } // Handle spacing resolution as default if size key is valid if (typeof value === "string") { const match = value.match(/\b(?:([2-9])xxl|xxs|xs|sm|md|lg|xl|xxl)\b/i); if (match) { const [fullMatch, multiplierStr] = match; const sizeKey = multiplierStr ? "xxl" : fullMatch.toLowerCase(); const multiplier = multiplierStr ? parseInt(multiplierStr) : 1; const spacing = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.spacing) === null || _e === void 0 ? void 0 : _e[sizeKey]) !== null && _f !== void 0 ? _f : constants_1.Spacing[sizeKey]; return spacing !== undefined ? spacing * multiplier : value; } return value; } // Return original value if no match was found return value; }; exports.size = size; /** * Converts a hex color string to an RGB array. * @param {string} hex - The hex color string (e.g., "#FFFFFF", "FFFFFF", "#FFF", or "FFF"). * @returns {number[]} An array of three numbers representing the RGB values. * @throws {Error} If the hex string is not valid. */ const hexToRGB = (hex) => { // Match either 6-digit or 3-digit hex (with optional #) if (!/^#?([0-9A-Fa-f]{6}|[0-9A-Fa-f]{3})$/.test(hex)) { throw new Error("Invalid hex color string"); } // Remove # if present hex = hex.replace(/^#/, ""); // Convert 3-digit hex to 6-digit hex if (hex.length === 3) { hex = hex.split("").map(char => char + char).join(""); } // Convert to RGB values return hex .match(/\w\w/g) .map((hex) => parseInt(hex, 16)); }; exports.hexToRGB = hexToRGB; //# sourceMappingURL=transformers.js.map