UNPKG

@tamagui/react-native-web-lite

Version:
355 lines 11.4 kB
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 __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Image_exports = {}; __export(Image_exports, { Image: () => ImageWithStatics, default: () => Image_default }); module.exports = __toCommonJS(Image_exports); var React = __toESM(require("react"), 1); var import_react_native_web_internals = require("@tamagui/react-native-web-internals"); var import_react_native_web_internals2 = require("@tamagui/react-native-web-internals"); var import_createElement = require("../createElement/index.cjs"); var import_PixelRatio = require("../PixelRatio/index.cjs"); var import_View = require("../View/index.cjs"); var import_jsx_runtime = require("react/jsx-runtime"); const ERRORED = "ERRORED"; const LOADED = "LOADED"; const LOADING = "LOADING"; const IDLE = "IDLE"; let _filterId = 0; const svgDataUriPattern = /^(data:image\/svg\+xml;utf8,)(.*)/; function createTintColorSVG(tintColor, id) { return tintColor && id != null ? /* @__PURE__ */(0, import_jsx_runtime.jsx)("svg", { style: { position: "absolute", height: 0, visibility: "hidden", width: 0 }, children: /* @__PURE__ */(0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */(0, import_jsx_runtime.jsxs)("filter", { id: `tint-${id}`, suppressHydrationWarning: true, children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)("feFlood", { floodColor: `${tintColor}` }, tintColor), /* @__PURE__ */(0, import_jsx_runtime.jsx)("feComposite", { in2: "SourceAlpha", operator: "atop" })] }) }) }) : null; } function getFlatStyle(style, blurRadius, filterId) { const flatStyle = import_react_native_web_internals.StyleSheet.flatten(style); const { filter, resizeMode, shadowOffset, tintColor } = flatStyle; const filters = []; let _filter = null; if (filter) { filters.push(filter); } if (blurRadius) { filters.push(`blur(${blurRadius}px)`); } if (shadowOffset) { const shadowString = (0, import_react_native_web_internals.createBoxShadowValue)(flatStyle); if (shadowString) { filters.push(`drop-shadow(${shadowString})`); } } if (tintColor && filterId != null) { filters.push(`url(#tint-${filterId})`); } if (filters.length > 0) { _filter = filters.join(" "); } delete flatStyle.blurRadius; delete flatStyle.shadowColor; delete flatStyle.shadowOpacity; delete flatStyle.shadowOffset; delete flatStyle.shadowRadius; delete flatStyle.tintColor; delete flatStyle.overlayColor; delete flatStyle.resizeMode; return [flatStyle, resizeMode, _filter, tintColor]; } function resolveAssetDimensions(source) { if (typeof source === "number") { const { height, width } = (0, import_react_native_web_internals2.getAssetByID)(source); return { height, width }; } else if (source != null && !Array.isArray(source) && typeof source === "object") { const { height, width } = source; return { height, width }; } } function resolveAssetUri(source) { let uri = null; if (typeof source === "number") { const asset = (0, import_react_native_web_internals2.getAssetByID)(source); let scale = asset.scales[0]; if (asset.scales.length > 1) { const preferredScale = import_PixelRatio.PixelRatio.get(); scale = asset.scales.reduce((prev, curr) => Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale) ? curr : prev); } const scaleSuffix = scale !== 1 ? `@${scale}x` : ""; uri = asset ? `${asset.httpServerLocation}/${asset.name}${scaleSuffix}.${asset.type}` : ""; } else if (typeof source === "string") { uri = source; } else if (source && typeof source.uri === "string") { uri = source.uri; } if (uri) { const match = uri.match(svgDataUriPattern); if (match) { const [, prefix, svg] = match; const encodedSvg = encodeURIComponent(svg); return `${prefix}${encodedSvg}`; } } return uri; } const Image = React.forwardRef((props, ref) => { const { accessibilityLabel, blurRadius, defaultSource, draggable, onError, onLayout, onLoad, onLoadEnd, onLoadStart, pointerEvents, source, style, ...rest } = props; if (process.env.NODE_ENV !== "production") { if (props.children) { throw new Error("The <Image> component cannot contain children. If you want to render content on top of the image, consider using the <ImageBackground> component or absolute positioning."); } } const [state, updateState] = React.useState(() => { const uri2 = resolveAssetUri(source); if (uri2 != null) { const isLoaded = import_react_native_web_internals2.ImageLoader.has(uri2); if (isLoaded) { return LOADED; } } return IDLE; }); const [layout, updateLayout] = React.useState({}); const hasTextAncestor = React.useContext(import_react_native_web_internals.TextAncestorContext); const hiddenImageRef = React.useRef(null); const filterRef = React.useRef(_filterId++); const requestRef = React.useRef(null); const shouldDisplaySource = state === LOADED || state === LOADING && defaultSource == null; const [flatStyle, _resizeMode, filter, tintColor] = getFlatStyle({}, blurRadius, filterRef.current); const resizeMode = props.resizeMode || _resizeMode || "cover"; const selectedSource = shouldDisplaySource ? source : defaultSource; const displayImageUri = resolveAssetUri(selectedSource); const imageSizeStyle = resolveAssetDimensions(selectedSource); const backgroundImage = displayImageUri ? `url("${displayImageUri}")` : null; const backgroundSize = getBackgroundSize(); const hiddenImage = displayImageUri ? (0, import_createElement.createElement)("img", { alt: accessibilityLabel || "", style: styles.accessibilityImage$raw, draggable: draggable || false, ref: hiddenImageRef, src: displayImageUri }) : null; function getBackgroundSize() { if (hiddenImageRef.current != null && (resizeMode === "center" || resizeMode === "repeat")) { const { naturalHeight, naturalWidth } = hiddenImageRef.current; const { height, width } = layout; if (naturalHeight && naturalWidth && height && width) { const scaleFactor = Math.min(1, width / naturalWidth, height / naturalHeight); const x = Math.ceil(scaleFactor * naturalWidth); const y = Math.ceil(scaleFactor * naturalHeight); return `${x}px ${y}px`; } } } function handleLayout(e) { if (resizeMode === "center" || resizeMode === "repeat" || onLayout) { const { layout: layout2 } = e.nativeEvent; onLayout && onLayout(e); updateLayout(layout2); } } const uri = resolveAssetUri(source); React.useEffect(() => { abortPendingRequest(); if (uri != null) { updateState(LOADING); if (onLoadStart) { onLoadStart(); } requestRef.current = import_react_native_web_internals2.ImageLoader.load(uri, function load(e) { updateState(LOADED); if (onLoad) { onLoad(e); } if (onLoadEnd) { onLoadEnd(); } }, function error() { updateState(ERRORED); if (onError) { onError({ nativeEvent: { error: `Failed to load resource ${uri} (404)` } }); } if (onLoadEnd) { onLoadEnd(); } }); } function abortPendingRequest() { if (requestRef.current != null) { import_react_native_web_internals2.ImageLoader.abort(requestRef.current); requestRef.current = null; } } return abortPendingRequest; }, [uri, requestRef, updateState, onError, onLoad, onLoadEnd, onLoadStart]); return /* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_View.View, { ...rest, "aria-label": accessibilityLabel, onLayout: handleLayout, pointerEvents, ref, style: [style, styles.root, hasTextAncestor && styles.inline, imageSizeStyle, flatStyle], children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(import_View.View, { style: [...[].concat(styles.image), resizeModeStyles[resizeMode], { backgroundImage, filter }, backgroundSize != null && { backgroundSize }], suppressHydrationWarning: true }), hiddenImage, createTintColorSVG(tintColor, filterRef.current)] }); }); Image.displayName = "Image"; const ImageWithStatics = Image; ImageWithStatics.getSize = function (uri, success, failure) { import_react_native_web_internals2.ImageLoader.getSize(uri, success, failure); }; ImageWithStatics.prefetch = function (uri) { return import_react_native_web_internals2.ImageLoader.prefetch(uri); }; ImageWithStatics.queryCache = function (uris) { return import_react_native_web_internals2.ImageLoader.queryCache(uris); }; const styles = import_react_native_web_internals.StyleSheet.create({ root: { flexBasis: "auto", overflow: "hidden", zIndex: 0 }, inline: { display: "inline-flex" }, image: { ...import_react_native_web_internals.StyleSheet.absoluteFillObject, backgroundColor: "transparent", backgroundPosition: "center", backgroundRepeat: "no-repeat", backgroundSize: "cover", height: "100%", width: "100%", zIndex: -1 }, accessibilityImage$raw: { ...import_react_native_web_internals.StyleSheet.absoluteFillObject, height: "100%", opacity: 0, width: "100%", zIndex: -1 } }); const resizeModeStyles = import_react_native_web_internals.StyleSheet.create({ center: { backgroundSize: "auto" }, contain: { backgroundSize: "contain" }, cover: { backgroundSize: "cover" }, none: { backgroundPosition: "0", backgroundSize: "auto" }, repeat: { backgroundPosition: "0", backgroundRepeat: "repeat", backgroundSize: "auto" }, stretch: { backgroundSize: "100% 100%" } }); var Image_default = ImageWithStatics;