UNPKG

react-native-fast-image

Version:
451 lines (450 loc) • 16.1 kB
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } }); //#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let react = require("react"); react = __toESM(react); let react_native = require("react-native"); //#region src/constants.ts const resizeMode = { contain: "contain", cover: "cover", stretch: "stretch", center: "center", repeat: "repeat" }; const priority = { low: "low", normal: "normal", high: "high" }; const cacheControl = { immutable: "immutable", web: "web", cacheOnly: "cacheOnly" }; //#endregion //#region src/objectFit.ts function fromStyle(style, key) { if (Array.isArray(style)) { for (let i = style.length - 1; i >= 0; i--) { const found = fromStyle(style[i], key); if (found !== void 0) return found; } return; } if (typeof style === "number") { const flattened = react_native.StyleSheet.flatten(style); return flattened ? flattened[key] : void 0; } return style && typeof style === "object" ? style[key] : void 0; } const FITS = { fill: true, contain: true, cover: true, none: true, "scale-down": true }; function resolveObjectFit(objectFit, style) { const fit = objectFit != null ? objectFit : fromStyle(style, "objectFit"); return fit && FITS[fit] === true ? fit : "cover"; } //#endregion //#region src/warnings.ts const warned = /* @__PURE__ */ new Set(); function warnOnce(key, message, level) { if (!warned.has(key)) { warned.add(key); console[level](message); } } function warnIfChildren(children) { if (react.Children.toArray(children).length > 0) warnOnce("children", "react-native-fast-image: FastImage doesn't render children (since 10.0): use FastImageBackground for content over an image.", "error"); } //#endregion //#region src/objectPosition.ts const START = { fraction: 0, offset: 0 }; const CENTER = { fraction: .5, offset: 0 }; const END = { fraction: 1, offset: 0 }; /** The default, `50% 50%`. */ const CENTERED = { x: CENTER, y: CENTER }; const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i; function length(text) { const lower = text.toLowerCase(); if (lower.endsWith("%")) { const number = lower.slice(0, -1); return NUMBER.test(number) ? { fraction: Number(number) / 100, offset: 0 } : null; } if (lower.endsWith("px")) { const number = lower.slice(0, -2); return NUMBER.test(number) ? { fraction: 0, offset: Number(number) } : null; } return NUMBER.test(lower) && Number(lower) === 0 ? START : null; } function token(text) { const lower = text.toLowerCase(); if (lower === "left" || lower === "right" || lower === "top" || lower === "bottom" || lower === "center") return { keyword: lower }; const axis = length(text); return axis ? { length: axis } : null; } const X = { left: START, center: CENTER, right: END }; const Y = { top: START, center: CENTER, bottom: END }; const x = (t) => "length" in t ? t.length : X[t.keyword]; const y = (t) => "length" in t ? t.length : Y[t.keyword]; function fromEdge(edge, axis) { return edge === "left" || edge === "top" ? axis : { fraction: 1 - axis.fraction, offset: -axis.offset || 0 }; } function parse(text) { const tokens = []; for (const part of text.trim().split(/\s+/)) { const t = token(part); if (!t) return null; tokens.push(t); } if (tokens.length === 1) { const [a] = tokens; if ("keyword" in a && (a.keyword === "top" || a.keyword === "bottom")) return { x: CENTER, y: Y[a.keyword] }; return { x: x(a), y: CENTER }; } if (tokens.length === 2) { const [a, b] = tokens; const ax = x(a); const by = y(b); if (ax && by) return { x: ax, y: by }; if ("keyword" in a && "keyword" in b) { const bx = X[b.keyword]; const ay = Y[a.keyword]; if (bx && ay) return { x: bx, y: ay }; } return null; } if (tokens.length === 4) { const [a, b, c, d] = tokens; if (!("keyword" in a && "length" in b && "keyword" in c && "length" in d)) return null; const horizontal = (k) => k === "left" || k === "right"; const vertical = (k) => k === "top" || k === "bottom"; if (horizontal(a.keyword) && vertical(c.keyword)) return { x: fromEdge(a.keyword, b.length), y: fromEdge(c.keyword, d.length) }; if (vertical(a.keyword) && horizontal(c.keyword)) return { x: fromEdge(c.keyword, d.length), y: fromEdge(a.keyword, b.length) }; } return null; } const parsed = /* @__PURE__ */ new Map(); function objectLength(value) { if (typeof value === "number") return Number.isFinite(value) ? { fraction: 0, offset: value } : null; return typeof value === "string" && value.endsWith("%") ? length(value) : null; } /** * The position for objectPosition, or null if it isn't valid. A string is * CSS's syntax; an object has one of `top` and `bottom` and one of `left` * and `right`, each in points or a percentage, and a missing axis is * centered. Unset is centered. */ function resolveObjectPosition(position) { if (position == null) return CENTERED; if (typeof position === "string") { let result = parsed.get(position); if (result === void 0) { result = parse(position); if (parsed.size < 100) parsed.set(position, result); } return result; } if (typeof position !== "object") return null; const edges = position; const axis = (start, end) => { const hasStart = edges[start] != null; const hasEnd = edges[end] != null; if (hasStart && hasEnd) return null; if (!hasStart && !hasEnd) return CENTER; const edge = hasStart ? start : end; const value = objectLength(edges[edge]); return value && fromEdge(edge, value); }; const resolvedX = axis("left", "right"); const resolvedY = axis("top", "bottom"); return resolvedX && resolvedY ? { x: resolvedX, y: resolvedY } : null; } /** * The position for objectPosition, or centered if it isn't valid (as CSS * ignores it), with a warning in development. */ function objectPositionOrCentered(position) { const resolved = resolveObjectPosition(position); if (resolved) return resolved; if (typeof __DEV__ !== "undefined" && __DEV__) { const value = JSON.stringify(position); warnOnce(`objectPosition ${value}`, `react-native-fast-image: objectPosition ${value} isn't a position, so the image is centered. Use CSS's syntax with px or % (e.g. 'right 10px bottom 20%'), or { top | bottom, left | right } in points or percentages.`, "warn"); } return CENTERED; } //#endregion //#region src/specs/FastImageViewNativeComponent.ts var FastImageViewNativeComponent_default = (0, react_native.codegenNativeComponent)("FastImageView"); //#endregion //#region src/specs/NativeFastImageModule.ts var NativeFastImageModule_default = react_native.TurboModuleRegistry.get("FastImageModule"); //#endregion //#region src/index.tsx const resolveDefaultSource = (defaultSource) => { if (!defaultSource) return null; const resolved = react_native.Image.resolveAssetSource(defaultSource); if (!resolved) return null; return react_native.Platform.OS === "android" ? resolved.uri : resolved; }; function loopCount(loop) { if (loop === void 0) return -1; if (loop === true) return 0; if (loop === false) return 1; return Number.isFinite(loop) ? Math.max(1, Math.floor(loop)) : 0; } function loadResult(event) { var _event$width, _event$height, _event$error; return event.ok ? { ok: true, width: (_event$width = event.width) !== null && _event$width !== void 0 ? _event$width : 0, height: (_event$height = event.height) !== null && _event$height !== void 0 ? _event$height : 0 } : { ok: false, error: (_event$error = event.error) !== null && _event$error !== void 0 ? _event$error : "Failed to load the image" }; } function handledEvents({ onLoadStart, onProgress, onLoad, onError, onLoadEnd }) { return (onLoadStart ? 1 : 0) | (onProgress ? 2 : 0) | (onLoad ? 4 : 0) | (onError ? 8 : 0) | (onLoadEnd ? 16 : 0); } function usualFadeMs() { return react_native.Platform.OS === "ios" ? 250 : 300; } const NO_TRANSITION = { transitionDuration: 0, transitionBetweenImages: false, transitionSkipOnCacheHit: "memory" }; function resolveTransition(transition, base) { if (transition == null) return base; if (transition === false) return { ...base, transitionDuration: 0 }; const usual = base.transitionDuration > 0 ? base.transitionDuration : usualFadeMs(); if (transition === true) return { ...base, transitionDuration: usual }; const { duration, betweenImages, skipOnCacheHit } = typeof transition === "number" ? { duration: transition } : transition; const resolvedDuration = duration !== null && duration !== void 0 ? duration : usual; return { transitionDuration: typeof resolvedDuration === "number" && resolvedDuration > 0 ? resolvedDuration : 0, transitionBetweenImages: betweenImages !== null && betweenImages !== void 0 ? betweenImages : base.transitionBetweenImages, transitionSkipOnCacheHit: skipOnCacheHit !== null && skipOnCacheHit !== void 0 ? skipOnCacheHit : base.transitionSkipOnCacheHit }; } function builtInTransition() { return resolveTransition(react_native.Platform.OS === "android", NO_TRANSITION); } let defaultTransition; function FastImageBase({ source, defaultSource, tintColor, onLoadStart, onProgress, onLoad, onError, onLoadEnd, style, children, objectFit, objectPosition, loop, transition, forwardedRef, ...viewProps }) { var _props$recyclingKey, _defaultTransition; const { fallback, resizeMode: _resizeMode, ...props } = viewProps; if (__DEV__ && fallback !== void 0) warnOnce("fallback", "react-native-fast-image: `fallback` was removed in 10.0, and does nothing: images always load natively. Remove it, or use React Native's Image for those images.", "warn"); if (__DEV__) warnIfChildren(children); const resolvedTintColor = tintColor != null ? tintColor : fromStyle(style, "tintColor"); const fit = resolveObjectFit(objectFit, style); const position = objectPositionOrCentered(objectPosition); const sources = Array.isArray(source) ? source.length === 1 ? void 0 : source : void 0; const single = Array.isArray(source) ? source[0] : source; const resolvedSource = sources ? void 0 : react_native.Image.resolveAssetSource(single); const resolvedDefaultSource = resolveDefaultSource(defaultSource); return /* @__PURE__ */ react.default.createElement(FastImageViewNativeComponent_default, { ...props, ref: forwardedRef, style: [styles.image, style], recyclingKey: (_props$recyclingKey = props.recyclingKey) !== null && _props$recyclingKey !== void 0 ? _props$recyclingKey : void 0, loopCount: loopCount(loop), ...resolveTransition(transition, (_defaultTransition = defaultTransition) !== null && _defaultTransition !== void 0 ? _defaultTransition : builtInTransition()), source: resolvedSource, sources, defaultSource: resolvedDefaultSource, onFastImageLoadStart: onLoadStart, onFastImageProgress: onProgress, handledEvents: handledEvents({ onLoadStart, onProgress, onLoad, onError, onLoadEnd }), onFastImageLoad: onLoad, onFastImageError: onError, onFastImageLoadEnd: onLoadEnd && ((event) => onLoadEnd(loadResult(event.nativeEvent))), tintColor: resolvedTintColor, objectFit: fit, objectPositionX: position.x.fraction, objectPositionXOffset: position.x.offset, objectPositionY: position.y.fraction, objectPositionYOffset: position.y.offset }); } const FastImageMemo = (0, react.memo)(FastImageBase); const FastImageComponent = (0, react.forwardRef)((props, ref) => /* @__PURE__ */ react.default.createElement(FastImageMemo, { forwardedRef: ref, ...props })); FastImageComponent.displayName = "FastImage"; const noResult = { ok: false, error: "No result" }; function nativeModule() { if (!NativeFastImageModule_default) throw new Error("react-native-fast-image: the native module FastImageModule isn't in this app. Rebuild the app after installing the package (run pod install on iOS). It needs the New Architecture, and isn't in Expo Go: use a development build. In Jest, mock the function (e.g. jest.spyOn(FastImage, \"preload\"))."); return NativeFastImageModule_default; } const FastImage = FastImageComponent; FastImage.cacheControl = cacheControl; FastImage.priority = priority; FastImage.resizeMode = resizeMode; FastImage.setDefaults = ({ transition } = {}) => { if (transition === null) defaultTransition = void 0; else if (transition !== void 0) defaultTransition = resolveTransition(transition, builtInTransition()); }; const ONE_SOURCE = "Takes one source, not an array of sizes: pass the size to use"; const isSourceObject = (source) => typeof source === "object" && source !== null && !Array.isArray(source); FastImage.preload = (sources) => Promise.resolve(nativeModule().preload(sources.map((s) => isSourceObject(s) ? s : {}))).then((results) => sources.map((source, i) => { var _results$i; if (Array.isArray(source)) return { ok: false, error: ONE_SOURCE, uri: void 0 }; const uri = isSourceObject(source) ? source.uri : void 0; const result = (_results$i = results === null || results === void 0 ? void 0 : results[i]) !== null && _results$i !== void 0 ? _results$i : noResult; if (!result.ok) return { ...result, uri }; return typeof uri === "string" ? { ...result, uri } : { ok: false, error: "Invalid source: no uri", uri }; })); FastImage.clearMemoryCache = () => nativeModule().clearMemoryCache(); FastImage.clearDiskCache = () => nativeModule().clearDiskCache(); FastImage.configureCache = (limits) => { const set = {}; const reset = []; for (const [name, value] of Object.entries(limits !== null && limits !== void 0 ? limits : {})) if (value === null) reset.push(name); else if (typeof value === "number") set[name] = value; return nativeModule().configureCache({ ...set, reset }); }; FastImage.writeToCache = (source, file) => Array.isArray(source) ? Promise.resolve({ ok: false, error: ONE_SOURCE }) : Promise.resolve(nativeModule().writeToCache(isSourceObject(source) ? source : {}, file)); FastImage.getCachePath = (source) => Array.isArray(source) ? Promise.resolve({ ok: false, error: ONE_SOURCE }) : Promise.resolve(nativeModule().getCachePath(isSourceObject(source) ? source : {})); const styles = react_native.StyleSheet.create({ image: { overflow: "hidden" } }); /** * An image with content on top of it, like React Native's `ImageBackground`: a * view that the image fills, with the children on top (`FastImage` is a single * native view, which doesn't take children). The other props go to the image; * the ref is the view's. */ const FastImageBackground = (0, react.forwardRef)(({ style, imageStyle, imageRef, children, importantForAccessibility, ...props }, ref) => /* @__PURE__ */ react.default.createElement(react_native.View, { accessibilityIgnoresInvertColors: true, importantForAccessibility, style, ref }, /* @__PURE__ */ react.default.createElement(FastImageComponent, { ...props, importantForAccessibility, style: [react_native.StyleSheet.absoluteFill, imageStyle], ref: imageRef }), children)); FastImageBackground.displayName = "FastImageBackground"; //#endregion exports.FastImageBackground = FastImageBackground; exports.default = FastImage;