@artsy/palette-mobile 11.1.0 → 11.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # v11.2.0 (Thu Jun 15 2023)
2
+
3
+ #### 🚀 Enhancement
4
+
5
+ - feat(image): Add new <Image> component [#117](https://github.com/artsy/palette-mobile/pull/117) ([@damassi](https://github.com/damassi))
6
+
7
+ #### Authors: 1
8
+
9
+ - Christopher Pappas ([@damassi](https://github.com/damassi))
10
+
11
+ ---
12
+
1
13
  # v11.1.0 (Thu Jun 15 2023)
2
14
 
3
15
  #### 🚀 Enhancement
@@ -0,0 +1,15 @@
1
+ /// <reference types="react" />
2
+ import { FlexProps } from "../Flex";
3
+ export interface ImageProps extends FlexProps {
4
+ /** Supplied aspect ratio of image. If none provided, defaults to 1 */
5
+ aspectRatio?: number;
6
+ /** Supplied width of image. If none provided, defaults to screen width */
7
+ width?: number;
8
+ /** Supplied height of image. If none provided, defaults to width / aspectRatio */
9
+ height?: number;
10
+ /** Resize on the fly using Gemini. Defaults to true */
11
+ performResize?: boolean;
12
+ /** Source url to the image */
13
+ src: string;
14
+ }
15
+ export declare const Image: React.FC<ImageProps>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Image = void 0;
7
+ const jsx_runtime_1 = require("react/jsx-runtime");
8
+ const moti_1 = require("moti");
9
+ const react_1 = require("react");
10
+ const react_native_1 = require("react-native");
11
+ const react_native_fast_image_1 = __importDefault(require("react-native-fast-image"));
12
+ const react_native_reanimated_1 = require("react-native-reanimated");
13
+ const createGeminiUrl_1 = require("../../utils/createGeminiUrl");
14
+ const useScreenDimensions_1 = require("../../utils/hooks/useScreenDimensions");
15
+ const Flex_1 = require("../Flex");
16
+ const Skeleton_1 = require("../Skeleton");
17
+ const Image = ({ aspectRatio, width, height, performResize = true, src, ...flexProps }) => {
18
+ const [loading, setLoading] = (0, react_1.useState)(true);
19
+ const dimensions = useImageDimensions({ aspectRatio, width, height });
20
+ let uri = src;
21
+ if (performResize) {
22
+ uri = (0, createGeminiUrl_1.createGeminiUrl)({
23
+ imageURL: src,
24
+ width: react_native_1.PixelRatio.getPixelSizeForLayoutSize(dimensions.width),
25
+ height: react_native_1.PixelRatio.getPixelSizeForLayoutSize(dimensions.height),
26
+ });
27
+ }
28
+ return ((0, jsx_runtime_1.jsxs)(Flex_1.Flex, { position: "relative", ...flexProps, children: [!!loading && ((0, jsx_runtime_1.jsx)(Flex_1.Flex, { position: "absolute", zIndex: 1, children: (0, jsx_runtime_1.jsx)(Skeleton_1.Skeleton, { children: (0, jsx_runtime_1.jsx)(Skeleton_1.SkeletonBox, { ...dimensions }) }) })), (0, jsx_runtime_1.jsx)(moti_1.MotiView, { animate: { opacity: loading ? 0 : 1 }, transition: { type: "timing", duration: 400, easing: react_native_reanimated_1.Easing.sin }, children: (0, jsx_runtime_1.jsx)(react_native_fast_image_1.default, { style: dimensions, onLoadStart: () => setLoading(true), onLoadEnd: () => setLoading(false), source: {
29
+ priority: react_native_fast_image_1.default.priority.normal,
30
+ uri,
31
+ } }) })] }));
32
+ };
33
+ exports.Image = Image;
34
+ const useImageDimensions = (props) => {
35
+ const screenDimensions = (0, useScreenDimensions_1.useScreenDimensions)();
36
+ const imageWidth = props.width ?? screenDimensions.width;
37
+ let imageHeight;
38
+ if (props.height) {
39
+ imageHeight = props.height;
40
+ }
41
+ else {
42
+ if (!props.aspectRatio) {
43
+ console.error("[CachedImage] Error: `aspectRatio` is required if `height` is not provided.");
44
+ }
45
+ const aspectRatio = props.aspectRatio ?? 1;
46
+ imageHeight = imageWidth / aspectRatio;
47
+ }
48
+ const dimensions = {
49
+ width: imageWidth,
50
+ height: imageHeight,
51
+ };
52
+ return dimensions;
53
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const jsx_runtime_1 = require("react/jsx-runtime");
4
+ const react_native_1 = require("@storybook/react-native");
5
+ const Image_1 = require("./Image");
6
+ (0, react_native_1.storiesOf)("Image", module)
7
+ .add("With default fallback screen width", () => ((0, jsx_runtime_1.jsx)(Image_1.Image, { aspectRatio: 0.62, src: "https://d32dm0rphc51dk.cloudfront.net/A983VUIZusVBKy420xP3ow/normalized.jpg" })))
8
+ .add("With width and height", () => ((0, jsx_runtime_1.jsx)(Image_1.Image, { width: 250, height: 400, src: "https://d32dm0rphc51dk.cloudfront.net/A983VUIZusVBKy420xP3ow/normalized.jpg" })))
9
+ .add("With aspect ratio", () => ((0, jsx_runtime_1.jsx)(Image_1.Image, { width: 250, aspectRatio: 0.62, src: "https://d32dm0rphc51dk.cloudfront.net/A983VUIZusVBKy420xP3ow/normalized.jpg" })))
10
+ .add("Without automatic resizing", () => ((0, jsx_runtime_1.jsx)(Image_1.Image, { aspectRatio: 0.62, performResize: false, src: "https://d32dm0rphc51dk.cloudfront.net/A983VUIZusVBKy420xP3ow/normalized.jpg" })));
@@ -0,0 +1 @@
1
+ export * from "./Image";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Image"), exports);
@@ -15,6 +15,7 @@ export * from "./EntityHeader";
15
15
  export * from "./Flex";
16
16
  export * from "./Header";
17
17
  export * from "./Histogram";
18
+ export * from "./Image";
18
19
  export * from "./Input";
19
20
  export * from "./Join";
20
21
  export * from "./LegacyScreen";
@@ -31,6 +31,7 @@ __exportStar(require("./EntityHeader"), exports);
31
31
  __exportStar(require("./Flex"), exports);
32
32
  __exportStar(require("./Header"), exports);
33
33
  __exportStar(require("./Histogram"), exports);
34
+ __exportStar(require("./Image"), exports);
34
35
  __exportStar(require("./Input"), exports);
35
36
  __exportStar(require("./Join"), exports);
36
37
  __exportStar(require("./LegacyScreen"), exports);
@@ -14,6 +14,7 @@ const react_native_safe_area_context_1 = require("react-native-safe-area-context
14
14
  const Theme_1 = require("../Theme");
15
15
  const Flex_1 = require("../elements/Flex");
16
16
  const Text_1 = require("../elements/Text");
17
+ const hooks_1 = require("../utils/hooks");
17
18
  const withTheme = (story) => ((0, jsx_runtime_1.jsxs)(Theme_1.Theme, { theme: "v3light", children: [(0, jsx_runtime_1.jsx)(Text_1.Text, { color: "red", children: "aaww" }), story()] }));
18
19
  exports.withTheme = withTheme;
19
20
  const atomStorage = (0, utils_1.createJSONStorage)(() => async_storage_1.default);
@@ -31,6 +32,6 @@ const useDarkModeSwitcher = (story) => {
31
32
  }, []);
32
33
  const isDarkMode = mode === "dark" || (mode === "system" && systemMode === "dark");
33
34
  const theme = isDarkMode ? "v3dark" : "v3light";
34
- return ((0, jsx_runtime_1.jsx)(react_native_safe_area_context_1.SafeAreaProvider, { children: (0, jsx_runtime_1.jsx)(Theme_1.Theme, { theme: theme, children: (0, jsx_runtime_1.jsxs)(Flex_1.Flex, { flex: 1, backgroundColor: "background", children: [(0, jsx_runtime_1.jsxs)(Flex_1.Flex, { flexDirection: "row", justifyContent: "space-around", children: [(0, jsx_runtime_1.jsxs)(Text_1.Text, { color: "orange", children: ["Dark mode: ", mode, " ", mode === "system" && "(" + systemMode + ")"] }), (0, jsx_runtime_1.jsx)(Text_1.LinkText, { color: "orange", onPress: () => setMode("light"), children: "light" }), (0, jsx_runtime_1.jsx)(Text_1.LinkText, { color: "orange", onPress: () => setMode("dark"), children: "dark" }), (0, jsx_runtime_1.jsx)(Text_1.LinkText, { color: "orange", onPress: () => setMode("system"), children: "system" })] }), story()] }) }) }));
35
+ return ((0, jsx_runtime_1.jsx)(hooks_1.ScreenDimensionsProvider, { children: (0, jsx_runtime_1.jsx)(react_native_safe_area_context_1.SafeAreaProvider, { children: (0, jsx_runtime_1.jsx)(Theme_1.Theme, { theme: theme, children: (0, jsx_runtime_1.jsxs)(Flex_1.Flex, { flex: 1, backgroundColor: "background", children: [(0, jsx_runtime_1.jsxs)(Flex_1.Flex, { flexDirection: "row", justifyContent: "space-around", children: [(0, jsx_runtime_1.jsxs)(Text_1.Text, { color: "orange", children: ["Dark mode: ", mode, " ", mode === "system" && "(" + systemMode + ")"] }), (0, jsx_runtime_1.jsx)(Text_1.LinkText, { color: "orange", onPress: () => setMode("light"), children: "light" }), (0, jsx_runtime_1.jsx)(Text_1.LinkText, { color: "orange", onPress: () => setMode("dark"), children: "dark" }), (0, jsx_runtime_1.jsx)(Text_1.LinkText, { color: "orange", onPress: () => setMode("system"), children: "system" })] }), story()] }) }) }) }));
35
36
  };
36
37
  exports.useDarkModeSwitcher = useDarkModeSwitcher;
@@ -0,0 +1,8 @@
1
+ export declare function createGeminiUrl({ imageURL, width, height, geminiHost, imageQuality, resizeMode, }: {
2
+ imageURL: string;
3
+ width: number;
4
+ height: number;
5
+ geminiHost?: string;
6
+ imageQuality?: number;
7
+ resizeMode?: "fit" | "fill";
8
+ }): string;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createGeminiUrl = void 0;
4
+ const react_native_1 = require("react-native");
5
+ function createGeminiUrl({ imageURL, width, height, geminiHost = "d7hftxdivxxvm.cloudfront.net", imageQuality = 80, resizeMode = "fit", }) {
6
+ const src = encodeURIComponent(imageURL);
7
+ const roundedHeight = Math.round(height);
8
+ const roundedWidth = Math.round(width);
9
+ const params = [
10
+ `height=${roundedHeight}`,
11
+ `quality=${imageQuality}`,
12
+ `resize_to=${resizeMode}`,
13
+ `src=${src}`,
14
+ `width=${roundedWidth}`,
15
+ ];
16
+ if (react_native_1.Platform.OS === "android" || (react_native_1.Platform.OS === "ios" && osMajorVersion() >= 14)) {
17
+ params.push("convert_to=webp");
18
+ }
19
+ return `https://${geminiHost}/?${params.join("&")}`;
20
+ }
21
+ exports.createGeminiUrl = createGeminiUrl;
22
+ const osMajorVersion = () => {
23
+ // eslint-disable-next-line no-constant-condition
24
+ if (typeof (react_native_1.Platform.Version === "string")) {
25
+ return parseInt(react_native_1.Platform.Version, 10);
26
+ }
27
+ else {
28
+ return react_native_1.Platform.Version;
29
+ }
30
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artsy/palette-mobile",
3
- "version": "11.1.0",
3
+ "version": "11.2.0",
4
4
  "description": "Artsy's design system for React Native",
5
5
  "scripts": {
6
6
  "android": "react-native run-android",
@@ -41,6 +41,7 @@
41
41
  "moti": "^0.25.3",
42
42
  "react-nanny": "^2.15.0",
43
43
  "react-native-collapsible-tab-view": "^6.1.4",
44
+ "react-native-fast-image": "^8.6.3",
44
45
  "react-native-pager-view": "^6.2.0",
45
46
  "react-spring": "8.0.23",
46
47
  "styled-system": "^5.1.5"