@quintype/native-components 2.20.5 → 2.20.6

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [2.20.6](https://github.com/quintype/native-components/compare/v2.20.5...v2.20.6) (2023-06-15)
6
+
5
7
  ### [2.20.5](https://github.com/quintype/native-components/compare/v2.20.4...v2.20.5) (2023-03-21)
6
8
 
7
9
  ### [2.20.4](https://github.com/quintype/native-components/compare/v2.20.3...v2.20.4) (2023-02-01)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quintype/native-components",
3
- "version": "2.20.5",
3
+ "version": "2.20.6",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -17,10 +17,27 @@ const replaceDefaultProtocol = (content) => {
17
17
  }
18
18
  };
19
19
 
20
+ const removeWidthnHeight = (htmlContent) => {
21
+ let temp = htmlContent.split(' ');
22
+ let finalHtml = '';
23
+ if(temp[0]==='<iframe'){
24
+ for(let i=0; i<temp.length; i++){
25
+ if((temp[i].includes('width') || temp[i].includes('height')) && !temp[i].includes('/')){
26
+ continue;
27
+ }
28
+ finalHtml = finalHtml + temp[i] + " ";
29
+ }
30
+
31
+ return finalHtml;
32
+ }
33
+ return htmlContent;
34
+ }
35
+
20
36
  const getHTMLContent = (embedJs) => {
37
+ const width = Dimensions.get('window').width;
21
38
  const decodedContent = getDecodedContent(embedJs);
22
- const htmlContent = replaceDefaultProtocol(decodedContent);
23
-
39
+ let htmlContent = replaceDefaultProtocol(decodedContent)
40
+ htmlContent = removeWidthnHeight(htmlContent)
24
41
  const webViewScript = `
25
42
  <script type="application/javascript">
26
43
  var interValId;
@@ -41,6 +58,12 @@ const getHTMLContent = (embedJs) => {
41
58
  <html>
42
59
  <head>
43
60
  <meta name="viewport" content="width=device-width, initial-scale=1">
61
+ <style>
62
+ iframe{
63
+ width:${width-20}px;
64
+ height:${width-20}px;
65
+ }
66
+ </style>
44
67
  </head>
45
68
  <body>
46
69
  ${htmlContent}
@@ -6,7 +6,7 @@ import { LightBox } from '../LightBox';
6
6
  import { getImageHeight } from '../../utils';
7
7
 
8
8
  export const LightBoxImage = ({
9
- data, hero, currentLayout, additionalStyles,
9
+ data, hero, currentLayout, additionalStyles, elementType, onClickHandler = ()=>{}, index
10
10
  }) => {
11
11
  const { cdn, slug, metaData } = data;
12
12
  const { width, height } = metaData || {};
@@ -14,7 +14,12 @@ export const LightBoxImage = ({
14
14
  const imageUri = `${data?.cdn}/${data?.slug}`;
15
15
  const [showModal, setShowModal] = useState(false);
16
16
 
17
- const toggleModal = () => setShowModal(!showModal);
17
+ const toggleModal = () => {
18
+ if(elementType === 'gallery'){
19
+ onClickHandler(index);
20
+ } else{
21
+ setShowModal(!showModal)
22
+ }};
18
23
 
19
24
  const styles = {
20
25
  image: {
@@ -34,6 +39,7 @@ export const LightBoxImage = ({
34
39
  metaData={metaData}
35
40
  hero={hero || false}
36
41
  styles={cumulativeStyles}
42
+ elementType={elementType ? elementType : null}
37
43
  />
38
44
  </TouchableOpacity>
39
45
  </>
@@ -8,7 +8,7 @@ import Icon from 'react-native-vector-icons/FontAwesome';
8
8
  import { isTablet } from 'react-native-device-info';
9
9
  import { FallbackIcon } from '../../Icons/FallBackIcon';
10
10
  import { AppTheme } from '../../utils';
11
- import { getImageURL } from '../../utils/imageUtils';
11
+ import { getImageURL, getCustomResolutionImageURL } from '../../utils/imageUtils';
12
12
 
13
13
  const ResponsiveImageBase = (props) => {
14
14
  const [placeholder, setPlaceholder] = useState(true);
@@ -24,11 +24,15 @@ const ResponsiveImageBase = (props) => {
24
24
  width: props.imageWidth,
25
25
  height: (props.imageWidth * 9) / 16,
26
26
  },
27
- props.hero && {
27
+ (props.hero && props?.elementType !== 'gallery' ) && {
28
28
  width: '100%',
29
29
  height: HERO_IMAGE_HEIGHT,
30
30
  alignSelf: 'stretch',
31
31
  },
32
+ props?.elementType === 'gallery' && {
33
+ backgroundColor: COLORS.BRAND_BLACK
34
+ }
35
+
32
36
  ]);
33
37
 
34
38
  const placeholderStyle = {
@@ -43,7 +47,14 @@ const ResponsiveImageBase = (props) => {
43
47
  ) : (
44
48
  <FallbackIcon />
45
49
  );
46
- const imageUrl = getImageURL(props);
50
+
51
+ let imageUrl = ''
52
+
53
+ if(props?.elementType === 'gallery'){
54
+ imageUrl = getCustomResolutionImageURL(props, [1, 1]);
55
+ } else {
56
+ imageUrl = getImageURL(props);
57
+ }
47
58
 
48
59
  const userFallback = () => <Icon name="user" size={20} />;
49
60
 
@@ -71,7 +82,7 @@ const ResponsiveImageBase = (props) => {
71
82
  style={flattenedImageStyle}
72
83
  source={sourceURI}
73
84
  onLoad={onLoadHandler}
74
- resizeMode={FastImage.resizeMode.cover}
85
+ resizeMode={props.hero ? FastImage.resizeMode.cover : FastImage.resizeMode.contain}
75
86
  {...props}
76
87
  />
77
88
  {placeholder && (
@@ -1,39 +1,188 @@
1
1
  import PropTypes from 'prop-types';
2
- import React, { useContext } from 'react';
3
- import { View } from 'react-native';
2
+ import React, { useContext, useState, useRef } from 'react';
3
+ import { View, Modal, SafeAreaView, Dimensions, Image, FlatList, TouchableOpacity } from 'react-native';
4
4
  import { styles } from './styles';
5
5
  import { Text } from '../index';
6
6
  import { getScreenPercentageWidth, AppTheme } from '../../utils/index';
7
7
  import { LightBoxImage } from '../LightBoxImage';
8
+ import Icon from 'react-native-vector-icons/FontAwesome';
9
+ import FastImage from 'react-native-fast-image';
10
+ import { resolve } from 'path-browserify';
8
11
 
9
12
  export const StoryGallery = ({ cdn, card }) => {
10
13
  const { theme } = useContext(AppTheme);
14
+ const { COLORS, DARK_MODE } = theme;
11
15
  const galleryStyles = styles(theme);
12
16
  const storyElements = card['story-elements'];
17
+ const [showModal, setShowModal] = useState(false);
13
18
 
14
- return (
15
- <View style={galleryStyles.container}>
16
- <Text style={galleryStyles.titleText}>{card.title}</Text>
17
-
18
- <View style={galleryStyles.imgContainer}>
19
- {storyElements.map((element, index) => {
20
- const data = {
21
- cdn,
22
- slug: element['image-s3-key'],
23
- metaData: element['image-metadata'],
24
- imageWidth: getScreenPercentageWidth(100),
25
- };
26
- const indexTestForFullWidthStyles = storyElements.length % 3 == 1 ? index % 3 : (index + 1) % 3;
27
- const style = indexTestForFullWidthStyles
28
- ? galleryStyles.halfWidth
29
- : galleryStyles.fullWidth;
30
- return <LightBoxImage key={index} data={data} additionalStyles={style} />;
31
- })}
19
+ const [currentIndex, setCurrentTabIndex] = useState(0);
20
+
21
+ const flatlistRef = useRef();
22
+ const [index, setIndex] = useState(0);
23
+ const totalSlides = card["story-elements"]?.length;
24
+ const lastSlide = currentIndex + 1 === totalSlides;
25
+ const firstSlide = currentIndex === 0;
26
+ const [galleryLastImgUrl, setGalleryLastImageUrl] = useState('');
27
+
28
+ const onPressHandler = (index = 5) => {
29
+ setIndex(index)
30
+ setShowModal(true);
31
+ }
32
+
33
+ const moveNext = () => {
34
+ if (!lastSlide) {
35
+ flatlistRef.current.scrollToIndex({ index: Math.floor(Math.min(currentIndex + 1, totalSlides - 1))});
36
+ setCurrentTabIndex(currentIndex + 1);
37
+ }
38
+ };
39
+
40
+ const movePrev = () => {
41
+ if (!firstSlide) {
42
+ flatlistRef.current.scrollToIndex({ index: Math.ceil(Math.max(currentIndex - 1, 0)) });
43
+ setCurrentTabIndex(Math.max(currentIndex - 1, 0));
44
+ }
45
+ };
46
+
47
+ const showArrow = (direction) => {
48
+ const arrowStyles = direction === "left" ? galleryStyles.leftArrow : galleryStyles.rightArrow;
49
+ const onPressHandler = direction === "left" ? movePrev : moveNext;
50
+ const iconName = direction === "left" ? 'chevron-left' : 'chevron-right'
51
+ return (
52
+ <View style={arrowStyles}>
53
+ <TouchableOpacity onPress={onPressHandler}>
54
+ <Icon name={iconName} size={22} color={COLORS?.MONO7} />
55
+ </TouchableOpacity>
32
56
  </View>
33
- <Text style={galleryStyles.descText} lato>
34
- {card.description}
57
+ );
58
+ };
59
+
60
+ const handleScroll = (event) => {
61
+ const xOffset = event.nativeEvent?.contentOffset?.x;
62
+ const contentWidth = event.nativeEvent?.contentSize?.width;
63
+ const layoutWidth = event.nativeEvent?.layoutMeasurement?.width;
64
+ const value = (xOffset / contentWidth);
65
+ const leftThreshold = contentWidth / ( 2 * totalSlides );
66
+ const rightThreshold = contentWidth - ( 1.5 ) * layoutWidth;
67
+
68
+ if((leftThreshold <= xOffset && (layoutWidth > xOffset || rightThreshold > xOffset ))){
69
+ setCurrentTabIndex(value * totalSlides);
70
+ } else if(xOffset < leftThreshold){
71
+ setCurrentTabIndex(0);
72
+ } else if(xOffset >= rightThreshold){
73
+ setCurrentTabIndex(totalSlides-1);
74
+ }
75
+ }
76
+
77
+ const closeModalHandler = () => {
78
+ setShowModal(false);
79
+ setCurrentTabIndex(0);
80
+ }
81
+
82
+ const renderItem = (item) => {
83
+ const data = {
84
+ cdn,
85
+ slug: item.item['image-s3-key'],
86
+ metaData: item.item['image-metadata'],
87
+ };
88
+
89
+ const imageUri = `${data?.cdn}/${data?.slug}`;
90
+
91
+ return <View style={galleryStyles.carouselContainer}>
92
+ <FastImage
93
+ source={{uri:imageUri}}
94
+ resizeMode={FastImage.resizeMode.contain}
95
+ style={galleryStyles.carouselImage}
96
+ />
97
+ <Text primary style={galleryStyles.titleText}>
98
+ {item.item?.title}
35
99
  </Text>
36
100
  </View>
101
+ }
102
+
103
+ return (
104
+ <>
105
+ <View style={galleryStyles.container}>
106
+ <Text style={galleryStyles.titleText}>{card.title}</Text>
107
+
108
+ <View style={galleryStyles.imgContainer}>
109
+ {storyElements.map((element, index) => {
110
+
111
+ const data = {
112
+ cdn,
113
+ slug: element['image-s3-key'],
114
+ metaData: element['image-metadata'],
115
+ imageWidth: getScreenPercentageWidth(100),
116
+ };
117
+
118
+ if(storyElements.length >6 && index >= 5){
119
+ const imageUri = `${data?.cdn}/${data?.slug}`;
120
+ if(galleryLastImgUrl === ''){
121
+ setGalleryLastImageUrl(imageUri);
122
+ }
123
+ return null;
124
+ }
125
+
126
+ const style = galleryStyles.fullWidth;
127
+ return <TouchableOpacity>
128
+ <LightBoxImage onClickHandler={onPressHandler} hero={data.metaData['focus-point'] ? true : false} key={index} data={data} additionalStyles={style} elementType={card?.metadata?.type} index={index} />
129
+ </TouchableOpacity>
130
+ })}
131
+ {storyElements && storyElements.length > 6 && <TouchableOpacity onPress={()=>onPressHandler(5)} style={[galleryStyles.fullWidth, galleryStyles.showMoreImageContainer]}>
132
+ <FastImage
133
+ source={{uri: galleryLastImgUrl}}
134
+ resizeMode={FastImage.resizeMode.cover}
135
+ style={galleryStyles.showMoreImage}
136
+ />
137
+ <Text style={galleryStyles.showMoreText}>+{storyElements.length-5}</Text>
138
+ </TouchableOpacity>}
139
+ </View>
140
+ <Text style={galleryStyles.descText} lato>
141
+ {card.description}
142
+ </Text>
143
+ </View>
144
+ <Modal
145
+ visible={showModal}
146
+ animationType="slide"
147
+ transparent={false}
148
+ supportedOrientations={['portrait', 'landscape']}
149
+ onRequestClose={closeModalHandler}
150
+ onBackdropPress={closeModalHandler}
151
+ >
152
+ <SafeAreaView style={galleryStyles.safeAreaView}>
153
+ <View style={galleryStyles.modalContainer}>
154
+ <TouchableOpacity onPress={closeModalHandler}>
155
+ <View style={galleryStyles.close}>
156
+ <Icon name="times" size={20} color={DARK_MODE ? COLORS.BRAND_BLACK : COLORS.BRAND_WHITE} />
157
+ </View>
158
+ </TouchableOpacity>
159
+ <View style={galleryStyles.wrapper}>
160
+
161
+ <FlatList
162
+ ref={flatlistRef}
163
+ data={card["story-elements"]}
164
+ renderItem={renderItem}
165
+ keyExtractor={(item) => item.id}
166
+ horizontal
167
+ extraData={currentIndex}
168
+ onScroll={handleScroll}
169
+ initialScrollIndex={index}
170
+ onScrollToIndexFailed = { info => {
171
+ const wait = new Promise(resolve=>setTimeout(resolve, 500));
172
+ wait.then(()=>{
173
+ flatlistRef.current?.scrollToIndex({index: info.index, animated: true});
174
+ });
175
+ }}
176
+ />
177
+
178
+ {!firstSlide && showArrow("left")}
179
+
180
+ {!lastSlide && showArrow("right")}
181
+ </View>
182
+ </View>
183
+ </SafeAreaView>
184
+ </Modal>
185
+ </>
37
186
  );
38
187
  };
39
188
 
@@ -1,8 +1,15 @@
1
1
  import { StyleSheet, Dimensions } from 'react-native';
2
+ import { useContext } from 'react';
3
+ import { isTablet } from 'react-native-device-info';
4
+ import { AppTheme } from '../../utils';
2
5
 
3
- const { width: deviceWidth } = Dimensions.get('window');
4
6
 
5
- export const styles = ({ FONT_SIZE }) => StyleSheet.create({
7
+ export const styles = ({ FONT_SIZE }) => {
8
+ const { width: deviceWidth } = Dimensions.get('window');
9
+ const { theme } = useContext(AppTheme);
10
+ const { COLORS, DARK_MODE } = theme;
11
+
12
+ return StyleSheet.create({
6
13
  container: {
7
14
  width: '100%',
8
15
  paddingHorizontal: 10,
@@ -23,13 +30,102 @@ export const styles = ({ FONT_SIZE }) => StyleSheet.create({
23
30
  height: 124,
24
31
  },
25
32
  fullWidth: {
26
- width: deviceWidth - 20,
27
- height: 195,
28
- marginVertical: 10,
33
+ width: (deviceWidth - 30) / 3,
34
+ height: (deviceWidth - 30) / 3,
35
+ marginVertical: 2.5,
29
36
  },
30
37
  descText: {
31
38
  fontSize: FONT_SIZE.h2,
32
39
  opacity: 0.8,
33
40
  paddingTop: 10,
34
41
  },
35
- });
42
+ modalContainer: {
43
+ backgroundColor: DARK_MODE ? COLORS.MONO6 : COLORS.BRAND_BLACK,
44
+ flex: 1,
45
+ justifyContent: 'center',
46
+ },
47
+ safeAreaView: {
48
+ flex: 1,
49
+ backgroundColor: 'transparent',
50
+ },
51
+ close: {
52
+ justifyContent: 'flex-start',
53
+ padding: 10,
54
+ height: 40,
55
+ zIndex: 1,
56
+ marginTop: 25,
57
+ },
58
+ imageContainer: {
59
+ justifyContent: 'center',
60
+ alignItems: 'center',
61
+ width: '100%',
62
+ height: '100%',
63
+ },
64
+ portrait: {
65
+ width: '100%',
66
+ height: '80%',
67
+ },
68
+ wrapper: {
69
+ paddingHorizontal: 10,
70
+ flex:1,
71
+ backgroundColor:'black',
72
+ width:'100%'
73
+ },
74
+ titleText: {
75
+ fontWeight: "normal",
76
+ marginBottom: 10,
77
+ fontSize: FONT_SIZE.h3,
78
+ opacity: 0.8,
79
+ color:'white',
80
+ marginTop:10,
81
+ marginHorizontal:10
82
+ },
83
+ leftArrow: {
84
+ position: "absolute",
85
+ top: "46%",
86
+ right: "95%",
87
+ left: 20,
88
+ zIndex: 999,
89
+ width: 30,
90
+ height: 40,
91
+ backgroundColor: COLORS.TRANSPARENT_BLACK,
92
+ display: "flex",
93
+ alignItems: "center",
94
+ justifyContent: "center",
95
+ },
96
+ rightArrow: {
97
+ position: "absolute",
98
+ top: "46%",
99
+ left: "93%",
100
+ right: 20,
101
+ zIndex: 999,
102
+ width: 30,
103
+ height: 40,
104
+ backgroundColor: COLORS.TRANSPARENT_BLACK,
105
+ display: "flex",
106
+ alignItems: "center",
107
+ justifyContent: "center",
108
+ },
109
+ carouselImage: {
110
+ width: '100%',
111
+ height: '80%'
112
+ },
113
+ carouselContainer:{
114
+ width: deviceWidth - 10,
115
+ margin: 10
116
+ },
117
+ showMoreImageContainer:{
118
+ justifyContent: 'center',
119
+ alignItems: 'center'
120
+ },
121
+ showMoreImage:{
122
+ width: '100%',
123
+ height: '100%',
124
+ opacity: 0.5
125
+ },
126
+ showMoreText:{
127
+ fontSize: 30,
128
+ position: 'absolute',
129
+ color: COLORS.BRAND_WHITE
130
+ }
131
+ })};
@@ -118,3 +118,19 @@ export const getImageURL = (props) => {
118
118
  q: IMAGE_QUALITY,
119
119
  })}`;
120
120
  };
121
+
122
+ export const getCustomResolutionImageURL = (props, customAspectRatio) => {
123
+ const { theme } = useContext(AppTheme);
124
+ const { IMAGE_QUALITY } = theme;
125
+
126
+ const {
127
+ metaData, slug, imageWidth, cdn,
128
+ } = props;
129
+ const image = new FocusedImage(slug, metaData);
130
+
131
+ const imageCdn = cdn || 'https://www.quintype.com';
132
+ return `${imageCdn}/${image.path(customAspectRatio , {
133
+ w: getPixelRatioForDevice(imageWidth) || Math.round(deviceWidth),
134
+ q: IMAGE_QUALITY,
135
+ })}`;
136
+ }